gitlab ci中pytest找不到测试的根源是环境未激活和路径不明确,需显式创建虚拟环境、指定测试路径并加-v参数验证;docker构建失败多因缓存顺序、权限或c依赖缺失;nginx代理websocket需严格配置http/1.1升级头;服务退出需确保异步主循环持续运行。

pytest 在 CI 中找不到测试、pip install 装错包、Docker build 失败——这些问题不是配置太复杂,而是关键环节没对齐。Python Web 应用的 CI/CD 流水线,核心是环境隔离、路径明确、输出可控,不是堆功能。
GitLab CI 里 pytest 找不到测试怎么办?
本地能跑,CI 报 No tests were found,八成是 pytest 没扫到目录,或虚拟环境没激活。GitLab Runner 不自动激活 Python 环境,哪怕用了 python:3.9-slim 镜像。
- 必须在
before_script 中显式创建并激活虚拟环境:python -m venv venv && source venv/bin/activate
- 安装依赖必须走这个环境:
pip install -r requirements.txt && pip install pytest pytest-cov
- 显式指定测试路径:
pytest tests/ --cov=src/ --cov-report=term-missing(假设源码在 src/)
- 加
-v 参数确认实际加载了哪些文件,避免因缺少 <strong>init</strong>.py 或命名不规范(如 test_helper.py)被跳过
Docker build 失败常见卡点
Dockerfile 看起来没问题,但 CI 构建时总在 COPY 或 RUN pip install 阶段失败,往往和缓存、路径或权限有关。
-
COPY requirements.txt . 必须放在 COPY . . 之前,否则 pip 缓存失效,重装所有包
- 不要用
pip install --user:CI 环境通常没写入 ~/.local 权限,会静默失败
- 如果项目依赖 C 扩展(如
psycopg2),别用 python:3.9-slim,换 python:3.9 或加 build-essential 和 libpq-dev
- 构建阶段用
--no-cache-dir,但 CI 中更推荐用 pip cache dir + GitLab cache 配置复用 wheel
Nginx 反向代理 WebSocket 时连接被拒绝
服务在容器里跑起来了,curl http://localhost:8000/ws 能通,但前端连 wss://yourdomain.com/ws 报 400 或直接断开,问题大概率出在 Nginx 配置或 TLS 终结上。
-
proxy_http_version 1.1 和 proxy_set_header Upgrade $http_upgrade 必须同时存在,缺一不可
-
proxy_set_header Connection "Upgrade" 的引号不能丢,Nginx 会把它当变量解析
- SSL 证书路径必须绝对且可读,
ssl_certificate 和 ssl_certificate_key 文件得在容器内真实存在(比如通过 volume 挂载或构建进镜像)
- 如果用 Let’s Encrypt,确保 ACME challenge 路径没被其他 location 规则拦截
部署后服务没起来,日志只显示 exited with code 0
Supervisor 或 systemd 声称启动成功,但 netstat -tuln | grep :8765 查不到端口,说明进程启动后立刻退出,不是崩溃,而是主循环没维持住。
- Python WebSocket 服务(如
websockets.serve())必须用 asyncio.get_event_loop().run_until_complete() 或 asyncio.run() 启动,不能只是定义 server 对象就完事
- Supervisor 配置里
autorestart=true 有用,但得先让进程真正进入长运行状态;加 stderr_logfile 和 stdout_logfile 才能看到第一行报错
- Docker 容器默认以 CMD 进程为 PID 1,如果该进程退出,整个容器就 stop —— 别用
python app.py && tail -f /dev/null 这种伪后台,改用 exec python app.py
before_script 中显式创建并激活虚拟环境:python -m venv venv && source venv/bin/activate
pip install -r requirements.txt && pip install pytest pytest-cov
pytest tests/ --cov=src/ --cov-report=term-missing(假设源码在 src/)-v 参数确认实际加载了哪些文件,避免因缺少 <strong>init</strong>.py 或命名不规范(如 test_helper.py)被跳过Docker build 失败常见卡点
Dockerfile 看起来没问题,但 CI 构建时总在 COPY 或 RUN pip install 阶段失败,往往和缓存、路径或权限有关。
-
COPY requirements.txt .必须放在COPY . .之前,否则 pip 缓存失效,重装所有包 - 不要用
pip install --user:CI 环境通常没写入~/.local权限,会静默失败 - 如果项目依赖 C 扩展(如
psycopg2),别用python:3.9-slim,换python:3.9或加build-essential和libpq-dev - 构建阶段用
--no-cache-dir,但 CI 中更推荐用pip cache dir+ GitLabcache配置复用 wheel
Nginx 反向代理 WebSocket 时连接被拒绝
服务在容器里跑起来了,curl http://localhost:8000/ws 能通,但前端连 wss://yourdomain.com/ws 报 400 或直接断开,问题大概率出在 Nginx 配置或 TLS 终结上。
-
proxy_http_version 1.1 和 proxy_set_header Upgrade $http_upgrade 必须同时存在,缺一不可
-
proxy_set_header Connection "Upgrade" 的引号不能丢,Nginx 会把它当变量解析
- SSL 证书路径必须绝对且可读,
ssl_certificate 和 ssl_certificate_key 文件得在容器内真实存在(比如通过 volume 挂载或构建进镜像)
- 如果用 Let’s Encrypt,确保 ACME challenge 路径没被其他 location 规则拦截
部署后服务没起来,日志只显示 exited with code 0
Supervisor 或 systemd 声称启动成功,但 netstat -tuln | grep :8765 查不到端口,说明进程启动后立刻退出,不是崩溃,而是主循环没维持住。
- Python WebSocket 服务(如
websockets.serve())必须用 asyncio.get_event_loop().run_until_complete() 或 asyncio.run() 启动,不能只是定义 server 对象就完事
- Supervisor 配置里
autorestart=true 有用,但得先让进程真正进入长运行状态;加 stderr_logfile 和 stdout_logfile 才能看到第一行报错
- Docker 容器默认以 CMD 进程为 PID 1,如果该进程退出,整个容器就 stop —— 别用
python app.py && tail -f /dev/null 这种伪后台,改用 exec python app.py
proxy_http_version 1.1 和 proxy_set_header Upgrade $http_upgrade 必须同时存在,缺一不可proxy_set_header Connection "Upgrade" 的引号不能丢,Nginx 会把它当变量解析ssl_certificate 和 ssl_certificate_key 文件得在容器内真实存在(比如通过 volume 挂载或构建进镜像)exited with code 0
Supervisor 或 systemd 声称启动成功,但 netstat -tuln | grep :8765 查不到端口,说明进程启动后立刻退出,不是崩溃,而是主循环没维持住。
- Python WebSocket 服务(如
websockets.serve())必须用asyncio.get_event_loop().run_until_complete()或asyncio.run()启动,不能只是定义 server 对象就完事 - Supervisor 配置里
autorestart=true有用,但得先让进程真正进入长运行状态;加stderr_logfile和stdout_logfile才能看到第一行报错 - Docker 容器默认以 CMD 进程为 PID 1,如果该进程退出,整个容器就 stop —— 别用
python app.py && tail -f /dev/null这种伪后台,改用exec python app.py
WebSocket 服务的健康检查容易被忽略:它不像 HTTP 服务有 /health 端点,端口监听成功 ≠ 业务可用。真要验证,得写个轻量客户端在 CI 阶段连一下 ws://localhost:8765 发个 ping,否则上线后才发现握手失败,已经晚了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











