本文将介绍:

  1. 北冥坞系统502报错的故障排查与解决方案
  2. 无法注册账号的临时解决方案

本文将涉及如下常用命令:

curl http://localhost:8088/    #测试本机8088端口是否启用

docker stop learnware-backend    #停止运行learnware-backend容器(该操作不会丢失数据)
docker start learnware-backend   #启动停止运行的容器
sudo docker compose -f docker-compose.yaml -p learnwaresingle down    #卸载容器(你对容器的修改将丢失,请在备份后再运行此命令!)
sudo docker compose -f docker-compose.yaml -p learnwaresingle up -d    #重新部署容器
docker ps    #查看容器运行状况和端口配置
docker exec -it learnware-backend /bin/sh    #进入learnware-backend容器的CLI界面
docker logs learnware-backend    #查看learnware-backend容器日志
docker cp ./bert_cache learnware-backend:/root/.cache/huggingface/transformers/bert-base-uncased    #将服务器的特定文件或文件夹拷贝到容器特定目录内

clash &    #后台运行clash
wget "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=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&suffix=tar.gz" -O GeoLite2-Country.tar.gz    #下载特定文件
curl -I https://www.google.com --proxy http://127.0.0.1:7890    #测试代理的端口能否访问谷歌
ps aux | grep clash    #查看后台运行的clash进程
kill <PID>    #删除多余的clash进程
curl -X PUT "http://127.0.0.1:9090/proxies/🔰%20选择节点" -H "Content-Type: application/json" -d '{"name":"🇺🇲 美国W02"}'    #设置节点为美国W02

502故障排查与解决方案

问题描述
在服务器上部署的北冥坞系统中,注册账号会出现502 Bad Gateway报错。暑假,我尝试在本机部署的北冥坞系统中注册账号,也同样会出现该报错。
但我没做任何修改,开学后本机的北冥坞系统有不报错,可以正常注册了。

原因猜测

  • (一)前后端交互问题:前端注册账号的http请求的格式或目标链接不正确,无法正常发送到后端,需要修改特定代码或配置文件才能使其正常交互
  • (二)端口开放问题:特定端口没有开放,导致服务器和容器交互存在问题,或前后端交互出现问题
  • (三)后端运行问题:后端没能正常运行或启动
    大家都倾向于认为问题是一或二,我本来也是这么想的,但排查发现真正的问题其实是三。
    下面尝试回溯下排查过程(由于当时输出的时候没截图,所以有些命令的输出难以复现)

检查request请求
通过浏览器开发者工具,可以看到502报错的request请求如下:

Request URL:
http://xxx.xxx.xxx.90:5173/api/auth/register
Request Method:
POST
Status Code:
502 Bad Gateway
Remote Address:
xxx.xxx.xxx.90:5173
Referrer Policy:
strict-origin-when-cross-origin
HTTP/1.1 502 Bad Gateway
Server: nginx/1.13.8
Date: Sat, 21 Sep 2024 17:04:30 GMT
Content-Type: text/html
Content-Length: 575
Connection: keep-alive
POST /api/auth/register HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 92
Content-Type: application/json
Host: xxx.xxx.xxx.90:5173
Origin: http://xxx.xxx.xxx.90:5173
Referer: http://xxx.xxx.xxx.90:5173/register
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36

注意,`Request URL:
http://xxx.xxx.xxx.90:5173/api/auth/register`说明它是通过这个url来注册的,并没有想有的队员想象中链接到官方的服务器那边了。
其次,这个url也符合注册账号时的url的常识,即通过当前网址的某个特定路径路由到后端。
所以,基本可以确定http请求的格式是没问题的,但这并不能保证前后端的交互一定没问题,如果后端特定端口没能正常开放,前端仍然访问不到后端。

检查端口号

(base) dachuang@master:~/Beimingwu/deploy/docker-compose$ docker ps
IMAGE                                   PORTS                                               NAMES
lamda/bm-system-backend:0.0.1           8088/tcp                                            learnware-monitor
lamda/bm-system-backend:0.0.1           8088/tcp           learnware-backend
lamda/bm-system-admin-frontend:0.0.1    80/tcp, 0.0.0.0:5174->5173/tcp, :::5174->5173/tcp   learnware-admin-frontend
redis                                   6379/tcp                                            learnware-redis
lamda/bm-system-frontend:0.0.1          80/tcp, 0.0.0.0:5173->5173/tcp, :::5173->5173/tcp   learnware-frontend
xiaohang07/slices:v9                    0.0.0.0:8888->8888/tcp, :::8888->8888/tcp           jovial_greider

可以看到,learnware-frontend和learnware-admin-frontend分别绑定了5173和5174端口,而我们关心的后端容器绑定了8088端口。

测试8088端口是否开放
运行docker exec -it learnware-backend /bin/sh进入容器,测试该8088端口是否处于监听状态:

# netstat -tuln | grep 8088
tcp        0      0 0.0.0.0:8088            0.0.0.0:*               LISTEN

# curl http://localhost:8088/

^C
# curl http://localhost:8088/api/auth/register


^C

可以看到,尽管8088确实处于监听状态,但是8088在容器内也没法正常访问,这就非常奇怪,我默认是端口配置出了问题,决定先试着改一下相关配置,但其实猜测大概率不是端口的问题。

修改后端容器的端口配置

(base) dachuang@master:~/Beimingwu/deploy/docker-compose$ vim docker-compose.yaml

  backend:
    image: lamda/bm-system-backend:0.0.1
    build:
      context: .
      dockerfile: Dockerfile_backend
    container_name: learnware-backend
    restart: always
    networks:
    - learnware

#expose:
#- '8088'
    ports:
      - "8088:8088"   # 改为和前端一样的配置方式

    volumes:
      - backend_data:/backend_data
      - learnware_storage:/root/.learnware
  frontend:

重启容器

sudo docker compose -f docker-compose.yaml -p learnwaresingle down    
sudo docker compose -f docker-compose.yaml -p learnwaresingle up -d    

重新查看端口状况

(base) dachuang@master:~/Beimingwu/deploy/docker-compose$ docker exec -it learnware-backend /bin/sh

# netstat -tuln | grep 8088
tcp        0      0 0.0.0.0:8088            0.0.0.0:*               LISTEN

# curl http://localhost:8088/

^C

运行结果不变,但是同样的端口映射方式,前端却可以正常访问。
8088端口确实处于监听状态,但却curl不到应有的响应,这说明不是端口的问题,而是后端的问题!

检查后端运行状况

(base) dachuang@master:~/Beimingwu/deploy/docker-compose$ docker logs learnware-backend

...
  File "/root/miniconda3/lib/python3.8/site-packages/transformers/tokenization_utils_base.py", line 2008, in from_pretrained
    raise EnvironmentError(
OSError: Can't load tokenizer for '/root/.cache/huggingface/transformers/bert-base-uncased/models--bert-base-uncased'. If you were trying to load it from 'https://huggingface.co/models', make sure you don't have a local directory with the same name. Otherwise, make sure '/root/.cache/huggingface/transformers/bert-base-uncased/models--bert-base-uncased' is the correct path to a directory containing all relevant files for a BertTokenizerFast tokenizer.
[2024-09-22 07:21:04 +0000] [29] [INFO] Worker exiting (pid: 29)
[2024-09-22 07:21:04 +0000] [1] [ERROR] Worker (pid:28) exited with code 3
[2024-09-22 07:21:04 +0000] [1] [ERROR] Worker (pid:29) was sent SIGTERM!
[2024-09-22 07:21:04 +0000] [1] [ERROR] Shutting down: Master
[2024-09-22 07:21:04 +0000] [1] [ERROR] Reason: Worker failed to boot....

通过和ChatGPT交流(https://chatgpt.com/share/66ef91b4-d2c8-8010-bff1-3a33bb4e8c81)了解到,后端实际上尝试访问huggingface下载“bert-base-uncased”模型,但是下载一直失败,导致后端容器其实在不停重启(boot....)。
其实,之前使用docker exec -it learnware-backend /bin/sh尝试进入容器时就可以发现,往往进入一会便自动退出了,我本来以为是服务器不稳定导致的,实际是容器因为报错不断重启的结果。

尝试解决
既然问题确定了,就该考虑解决方案了,有三个解决思路,按照治标到治本可以这么排序:

  1. 尝试去掉访问huggingface的相关代码,并对其他代码进行相应调整,来至少先让后端不会出现报错重启的情况。
  2. 尝试将相关文件先下载到本地,然后从本地上传到服务器,在从服务器上传到容器,最后修改容器内响应代码,让它不尝试访问huggingface,而是直接从本地加载相应文件。
  3. 配置好代理,让容器内可以正常访问huggingface。
    我没给服务器配过代理,也懒得折腾代理(结果最后还是折腾了),所以我是不太倾向于选③的。
    然后我尝试考虑②,我专门去bert-base-uncased模型的huggingface页面看了下,好家伙,一个模型3G,这得传多久。尽管很大,我还是率先尝试了②,我把3G的模型相关文件都下载到了本机,然后尝试把它们从本机传到服务器,结果上传速度只有100k(我试了两种方式都是100k),按这速度要传10h,直接把我整不会了。
    于是我决定试一下①,但①其实很难搞,因为有很多变量来来回回都用到self.tokenizer变量了,而self.tokenizer是基于这些huggingface文件初始化的,这样来来回回要改的变量很多,很容易改出问题来,所以①其实不太靠谱。
    本来已经想放弃了,但我稍微多想了一下,后端要从huggingface上下载一个3G的模型实际上是不符合常理的,加载这么大的模型干什么用?能正常下载的话得下载多久?没下载完会不会影响后端使用?
    于是我跟GPT交流了下,尝试在本地运行相关加载代码看看到底下载了个啥,结果一弄发现其实就加载了1M左右的配置文件,这样的话,②就应该是一个可行的方案了。

本地下载相关文件

from transformers import BertTokenizerFast

# 下载并缓存模型到指定目录
tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased", cache_dir="./bert_cache")

上传文件到服务器

scp -r ./bert_cache daxxxx@xxx.xxx.xxx.90:/tmp

上传文件到容器

docker cp ./bert_cache learnware-backend:/root/.cache/huggingface/transformers/bert-base-uncased

修改相关代码
(要先进入容器)

vim /learnware/learnware/market/heterogeneous/organizer/hetero_map/feature_extractor.py 

        #self.tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased", cache_dir=cache_dir)

        from transformers import BertTokenizerFast
        self.tokenizer = BertTokenizerFast.from_pretrained(
            "/root/.cache/huggingface/transformers/bert-base-uncased",
            local_files_only=True  # 防止联网下载
        )

再次查看日志

(待补充)

可以看到容器仍然在不断重启,但报错变了,所以我们的修改是有影响的。
经过反思,发现报错是因为我的相对路径配得有问题。
所以最终正确的代码如下:

        #self.tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased", cache_dir=cache_dir)

        from transformers import BertTokenizerFast
        self.tokenizer = BertTokenizerFast.from_pretrained(
            "/root/.cache/huggingface/transformers/bert-base-uncased/models--bert-base-uncased/snapshots/86b5e0934494bd15c9632b12f734a8a67f723594",
            local_files_only=True  # 防止联网下载
        )

解决账号注册问题

502报错解决后,便可以成功登录管理员账号了。但如果你尝试自己注册账号就会发现,它会发一个成功发送验证邮件的提示,但你实际验证邮件发不出来,导致账号没有通过验证。

我首先尝试在管理员账号的后台看看能不能修改验证状态,无果。
然后我尝试找到存储用户数据的数据库文件,能不能通过修改数据库数据来通过验证,成功。

进入容器CLI并找到数据库文件

(base) dachuang@master:~/Beimingwu$ docker exec -it learnware-backend /bin/sh
# cd /backend_data
# ls
backup
database.db
datasets
...

其中,database.db即为北冥坞系统的数据库文件(该文件由系统自动创建,无需手动创建)

查看用户数据

# sqlite3 database.db    #打开数据库文件

sqlite> .tables          #查看所有表单
tb_global_counter
tb_user                  #tb_user即为用户表单
...

sqlite> PRAGMA table_info(tb_user);   #查看用户表单内的字段
_info(tbguser):934494bd15c9632612f734a8a67f723594
0| id| INTEGER| 1||1
1|username|TEXT|1||0      #用户名
2 password TEXT 1||0      #密码
3|email| TEXT|1||0        #邮箱
4| role| INTEGER| 1|0|0   #角色
5| nickname| TEXT|1||0    #昵称
...
8|email_confirm_time|DATETIME|0||0   #邮箱验证时间

sqlite> SELECT* FROM tb_user LIMIT 10;   #查看用户数据
1|admin|$2b$12$uhXw8KyIKmgEaNVWaBGFrubf3.gqx7IYpB5cdK1B0hI0USm5I4z7q|admin@localhost|2|admin
itrator 2024-07-25 08:18:28.828013| 2024-07-25 08:18:28.828013
2|1zy|$2b$12$R/TUd1EaHTERTx823H7J7eynBt5LG6VK/rn8IdjHc6AuRi5skrpli|1571547820@qq.com|1|lzy|2
024-09-22 07:26:33.314545||
3|dream.w|$2b$12$X?XAuWV4ĨiB83L1s/3mTuE.SCBE.jxD0tcK1PWqiIV/1EX09FXV6|2024172050@mail.hfut.
edu.cn|@|dream.w|2024-09-22 07:56:10.394471|1
4|qwa| $2b$12$uXR1DWeICGWY.3Q4nokiRuvdrkBl17Svud1XjaC7C4HNL98Uwp23a|2023217514@mail.hfut.edu.
cn |0|qwq| 2024-09-22 07:57:00.665925||
5|Billows|$2b$12$qvMBQC0YmGXzLhMJ/968H,TUh2hUSJDcMKV8X0kYDQDmR1DXMZya. |2022215010@mail.hfut.
edu.cn|@|Billows|2024-09-22 07:58:17.27344411
sqlite>

可以看到,尽管邮箱验证还没通过,但是用户的注册数据已经写到数据库了,所以便可以合理猜测只要修改email_confirm_time为一个合适的值,便相当于通过验证了。

设置所有用户都通过邮箱验证

sqlite> UPDATE tb_user
···· SET email_confirm_time = '2024-07-22 06:10:17.754347'
···· WHERE email_confirm_time IS NULL;
最后修改:2024 年 10 月 06 日
如果觉得我的文章对你有用,请随意赞赏