1、对python服务进行打包
编写DockerFile
# 私有化 AI Infra 引擎 v2 — 联调(.249 bigDataServer)与线上(.42/.43/.44)**同一镜像**, # 运行时 -e APP_ENV=test|prod 选烧进镜像的双档配置 (二文件合并案 DEPLOY §2 手术件4)。 # base 锁 bullseye (glibc 2.31): CentOS7 kernel 3.10+seccomp 拦 glibc>=2.34 的 clone3 → pthread EPERM # (DEPLOY §5 头号坑, 两台宿主均 CentOS7; 万一必须升 base, 退路 --security-opt seccomp=unconfined) # docker.io 海外直连慢/不稳 (10-20 .249 实测拉取 4 分钟): 从零构建的机器可换国内前缀 # FROM docker.m.daocloud.io/library/python:3.11-slim-bullseye — 同一枚镜像走 DaoCloud 公共代理 FROM python:3.11-slim-bullseye ENV PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PYTHONUTF8=1 \ TZ=Asia/Shanghai \ APP_ENV=test # 10-20 D4 两级实测, apt 层整体绕掉: ①.249 出口到 deb.debian.org(Fastly 境外)80 不通; # ②换阿里云后索引通但 bullseye-security 文件 404 (Debian11 已 2026-08 停止常规支持, 镜像池清空索引滞后)。 # 原 apt 只为装 curl(HEALTHCHECK 用)+tzdata(base 镜像 python:3.11-slim 自带): # curl → 健康检查改 python urllib (镜像内必有); tzdata → 仅剩时区软链, ln 即可 RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime WORKDIR /app COPY requirements.txt . # torch 先走 CPU 专用索引: PyPI 默认轮=CUDA 捆绑 (+5GB, 破 "全量 2~3GB" 打包结论); # 装得 2.13.0+cpu 后, 后续 requirements 的 torch==2.13.0 按 PEP440 视为已满足不再回拉 # 10-20 用户口径「海外源大概率不通, 尽量国内」: torch 轮走上交大 pytorch-wheels 镜像 # (备选 南大 https://mirror.nju.edu.cn/pytorch-wheels/cpu); pypi 走阿里云; # 国内镜像若同步滞后缺 2.13.0 版本 → 回退官方 https://download.pytorch.org/whl/cpu (443 实测可达) RUN pip install --index-url https://mirror.sjtu.edu.cn/pytorch-wheels/cpu torch==2.13.0 \ && pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ COPY . . EXPOSE 10021 # 健康检查不依赖 curl (apt 层已绕掉, 见上): 用镜像内 python 打 /health, 200=healthy HEALTHCHECK --interval=30s --timeout=5s --start-period=120s \ CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:10021/health',timeout=4).getcode()==200 else 1)" # SERVER_HOST/PORT(10021) 由 config 读 .env.{APP_ENV}; Linux 默认可用 proactor-free 循环, # run.py 的 Selector 策略仅 win32 生效 — 容器内与开发机同一条启动入口 CMD ["python", "run.py"]Dockerfile 逐行代码解析
联调(.249 bigDataServer) 与 线上(.42/.43/.44) · 同一镜像
运行时 -e APP_ENV=test|prod 选烧进镜像的双档配置(二文件合并案 DEPLOY §2 手术件4)
──────────────────────────────────────────────────
一、基础镜像选型与 glibc 兼容性说明
# base 锁 bullseye (glibc 2.31): CentOS7 kernel 3.10+seccomp 拦 glibc>=2.34 的 clone3 → pthread EPERM # (DEPLOY §5 头号坑, 两台宿主均 CentOS7; 万一必须升 base, 退路 --security-opt seccomp=unconfined) # docker.io 海外直连慢/不稳 (10-20 .249 实测拉取 4 分钟): 从零构建的机器可换国内前缀 # FROM docker.m.daocloud.io/library/python:3.11-slim-bullseye — 同一枚镜像走 DaoCloud 公共代理 FROM python:3.11-slim-bullseye |
【解析】本 Dockerfile 服务于"私有化 AI Infra 引擎 v2"项目。该镜像同时用于联调环境和线上环境(服务器 IP 尾缀分别为 .249、.42、.43、.44)。通过 Docker 运行时环境变量 APP_ENV 取值 test 或 prod 来切换配置档位,属于 DEPLOY 文档第 2 节"手术件 4"中定义的二文件合并方案。
基础镜像锁定 Debian 11 "bullseye",其内置 glibc 2.31。原因是部署宿主机均为 CentOS 7(内核 3.10),该内核的 seccomp 安全策略会拦截 glibc >= 2.34 引入的 clone3 系统调用,导致 pthread 创建线程时报 EPERM(权限拒绝)错误——这是 DEPLOY 文档第 5 节标注的"头号坑"。万一未来必须升级到更高版本的基础镜像,应急方案是启动容器时添加 --security-opt seccomp=unconfined 以关闭 seccomp 过滤(降低安全性,仅应急用)。
Docker Hub 默认域名 docker.io 在国内直连速度慢且不稳定,在 .249 服务器上实测拉取一次需要 4 分钟。对于从零构建镜像的机器,可以将 FROM 行替换为 DaoCloud 公共代理前缀:FROM docker.m.daocloud.io/library/python:3.11-slim-bullseye,两者指向同一枚官方镜像,只是通过国内镜像加速器拉取,速度更快。
──────────────────────────────────────────────────
二、环境变量定义
ENV PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PYTHONUTF8=1 \ TZ=Asia/Shanghai \ APP_ENV=test |
【解析】设置以下环境变量:
PYTHONUNBUFFERED=1:关闭 Python 输出缓冲,确保日志实时写入容器 stdout,便于 docker logs 查看;
PIP_NO_CACHE_DIR=1:pip 不缓存下载包,减小镜像体积;
PYTHONUTF8=1:强制 Python 使用 UTF-8 编码,避免中文乱码;
TZ=Asia/Shanghai:设置容器时区为上海(东八区);
APP_ENV=test:应用运行环境标识,默认 test;运行时可通过 -e APP_ENV=prod 覆盖为生产环境。
──────────────────────────────────────────────────
三、apt 源问题说明(为何跳过 apt install)
# 10-20 D4 两级实测, apt 层整体绕掉: ①.249 出口到 deb.debian.org(Fastly 境外)80 不通; # ②换阿里云后索引通但 bullseye-security 文件 404 (Debian11 已 2026-08 停止常规支持, 镜像池清空索引滞后)。 # 原 apt 只为装 curl(HEALTHCHECK 用)+tzdata(base 镜像 python:3.11-slim 自带): # curl → 健康检查改 python urllib (镜像内必有); tzdata → 仅剩时区软链, ln 即可 |
【解析】在 .249 和 .42 两台服务器上的实测发现两个问题:
问题①:.249 服务器出口流量无法访问 deb.debian.org(经 Fastly CDN 境外节点)的 80 端口,网络连接被拒。
问题②:更换为阿里云 apt 镜像源后,索引文件虽能连通,但 bullseye-security 仓库的文件返回 404。原因是 Debian 11 已于 2026 年 8 月停止常规支持(EOL),镜像池已清空旧索引但同步有滞后。
结论:直接跳过 apt 安装层。原来 apt 只需装两个包——curl(用于 HEALTHCHECK 健康检查)和 tzdata(时区数据)。但 base 镜像 python:3.11-slim 已自带 tzdata,只需创建时区软链接即可;而 curl 的健康检查功能可改用 Python 内置的 urllib 模块替代(镜像中必然存在)。因此本 Dockerfile 完全不执行 apt-get install,避免网络问题导致构建失败。
──────────────────────────────────────────────────
四、时区设置
RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime |
【解析】通过符号链接将 /etc/localtime 指向上海时区文件,效果等同于安装了 tzdata 包并设置时区为 Asia/Shanghai。参数 -f 表示强制覆盖已存在的文件,-s 表示创建符号链接。这样容器内所有进程的时间戳均显示为北京时间(UTC+8),且无需额外安装 tzdata 包,减少了镜像层和网络依赖。
──────────────────────────────────────────────────
五、工作目录与依赖文件复制
WORKDIR /app COPY requirements.txt . |
【解析】WORKDIR /app:将容器内的工作目录设置为 /app。后续所有 COPY、RUN、CMD 等指令的相对路径都相对于 /app,也是容器启动后 shell 进入的默认目录。
COPY requirements.txt .:将宿主机当前目录下的 requirements.txt 文件复制到容器工作目录 /app 下。这一步单独复制依赖文件是为了利用 Docker 层缓存机制——如果 requirements.txt 未变,后续的 pip install 层会命中缓存,大幅加快重建速度。
──────────────────────────────────────────────────
六、PyTorch 安装策略说明
# torch 先走 CPU 专用索引: PyPI 默认轮=CUDA 捆绑 (+5GB, 破 "全量 2~3GB" 打包结论); # 装得 2.13.0+cpu 后, 后续 requirements 的 torch==2.13.0 按 PEP440 视为已满足不再回拉 # 10-20 用户口径「海外源大概率不通, 尽量国内」: torch 轮走上交大 pytorch-wheels 镜像 # (备选 南大 https://mirror.nju.edu.cn/pytorch-wheels/cpu); pypi 走阿里云; # 国内镜像若同步滞后缺 2.13.0 版本 → 回退官方 https://download.pytorch.org/whl/cpu (443 实测可达) |
【解析】PyTorch 的安装策略包含三个关键设计:
策略①(CPU 专用索引):先通过 CPU 专用索引源安装 torch==2.13.0+cpu 版本。如果不指定 CPU 专用索引,PyPI 默认提供的 wheel 包会捆绑 CUDA 运行时库,额外增加约 5GB 体积,破坏"全量镜像 2~3GB"的体积控制目标。
策略②(PEP440 版本匹配):先用 CPU 索引装上 2.13.0+cpu 版本后,后续 requirements.txt 中若声明 torch==2.13.0,pip 按 PEP440 版本匹配规则会认为已满足依赖而不再重新拉取(+cpu 后缀被视为兼容版本)。
策略③(国内镜像源优先级):主选上海交大 pytorch-wheels 镜像,备选南京大学镜像,PyPI 通用源用阿里云,最终兜底为 PyTorch 官方 CPU 轮子(443 端口实测可达)。
──────────────────────────────────────────────────
七、Python 依赖包安装
RUN pip install --index-url https://mirror.sjtu.edu.cn/pytorch-wheels/cpu torch==2.13.0 \ && pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ |
【解析】分两步安装 Python 依赖:
第一步:通过上海交大 pytorch-wheels 镜像源安装 CPU 版本的 PyTorch 2.13.0。--index-url 指定单个索引源(不使用 --extra-index-url,避免混入 CUDA 版本)。
第二步(&& 串联):第一步成功后,通过阿里云 PyPI 镜像安装 requirements.txt 中列出的其余依赖。-i 指定阿里云镜像源加速下载。
两步分开执行的好处是——如果只需更新业务依赖而不改 torch 版本,Docker 可以复用第一步的缓存层,无需重新下载 PyTorch(约 200MB+)。
──────────────────────────────────────────────────
八、复制应用代码
COPY . . |
【解析】将宿主机当前目录下的所有文件和目录递归复制到容器工作目录 /app 中。这是最后一步 COPY,因为应用代码变更频率最高,放在最后可以最大化利用 Docker 层缓存——只要 requirements.txt 不变,后续的 pip install 层都会命中缓存。
──────────────────────────────────────────────────
九、端口声明
EXPOSE 10021 |
【解析】声明容器运行时监听 TCP 10021 端口。注意:EXPOSE 仅做文档声明,不会自动映射端口到宿主机。实际端口映射需在 docker run 时使用 -p 参数完成(如 docker run -p 10021:10021),或在 k8s Deployment 的 service 中配置端口映射。
──────────────────────────────────────────────────
十、健康检查配置
# 健康检查不依赖 curl (apt 层已绕掉, 见上): 用镜像内 python 打 /health, 200=healthy HEALTHCHECK --interval=30s --timeout=5s --start-period=120s \ CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:10021/health',timeout=4).getcode()==200 else 1)" |
【解析】配置容器健康检查(Health Check),让 Docker 引擎定期探测容器是否正常运行:
--interval=30s:每隔 30 秒执行一次健康检查;
--timeout=5s:检查命令执行超过 5 秒视为失败;
--start-period=120s:容器启动后给予 120 秒的宽限期(应用冷启动可能需要较长时间初始化)。
检查命令使用Python 内置的 urllib.request 模块向容器自身 http://127.0.0.1:10021/health 端点发起 HTTP 请求。若返回 HTTP 状态码 200 则标记为 healthy,若返回其他状态码或请求失败则标记为 unhealthy。
不选用 curl 的原因:前面跳过了 apt 安装层(避免网络问题),镜像中没有 curl 命令。Python 标准库的 urllib 在任何 Python 镜像中都内置可用,无需额外安装。
──────────────────────────────────────────────────
十一、容器启动命令
# SERVER_HOST/PORT(10021) 由 config 读 .env.{APP_ENV}; Linux 默认可用 proactor-free 循环, # run.py 的 Selector 策略仅 win32 生效 — 容器内与开发机同一条启动入口 CMD ["python", "run.py"] |
【解析】设置容器启动时执行的默认命令:启动 Python 应用入口文件 run.py。
补充说明:服务器地址和端口(10021)由应用配置文件读取 .env.test 或 .env.prod 文件获取,具体读取哪个文件取决于运行时环境变量 APP_ENV 的值。run.py 中包含的 Selector 策略(事件循环选择器)仅对 Windows 系统生效;在 Linux 容器环境和开发机(通常也是 Linux)上,Python 默认使用 proactor 事件循环,因此容器内和生产开发环境使用同一条启动命令即可,无需区分平台。
──────────────────────────────────────────────────
十二、关键设计决策总结
设计决策 | 原因 |
基础镜像选 bullseye | CentOS7 宿主机 kernel 3.10 不兼容 glibc>=2.34 的 clone3 调用 |
不用 apt install | .249 服务器无法访问 deb.debian.org;阿里云镜像 bullseye-security 文件 404 |
不用 curl 做健康检查 | apt 层已跳过,镜像内无 curl;改用 Python 内置 urllib |
torch 走 CPU 专用索引 | 避免 PyPI 默认 wheel 捆绑 CUDA 运行时(+5GB),控制镜像体积在 2~3GB |
国内镜像源优先 | 海外源大概率不通;主选交大镜像,备选南大/阿里云/官方 |
时区设置用 ln 软链 | 不装 tzdata 包也能设置时区,减少镜像层和网络依赖 |
2、操作命令
tar -xzf pkai_src_v0.1_*.tar.gz
cd /opt/wqo_python/python_knowlage_ai/
docker rm -f ai-agent
docker rmi aiinfra-engine:v0.1
有问题的镜像可以删除
cd /opt/wqo_python/python_knowlage_ai
docker build -t aiinfra-engine:v0.1 .
docker build -t aiinfra-engine:v0.1 .
docker images
先看 10021 是否被占、有无同名残留容器:
docker ps -a --filter name=ai-agent
ss -lntp | grep 10021
1. 开发机推模型(约 809M = rerank 565M + embed 184M + cv 60M):
rz -e
docker rm -f ai-agent
docker 启动命令
docker run -d --name ai-agent --restart=unless-stopped -p 10021:10021 --memory=4g -e APP_ENV=test --log-opt max-size=100m --log-opt max-file=5 -v /opt/wqo_python/models:/app/models:ro -v /opt/wqo_python/logs:/app/logs aiinfra-engine:v0.1docker run -d --name ai-agent --restart=unless-stopped -p 10021:10021 --memory=4g -e APP_ENV=test --log-opt max-size=100m --log-opt max-file=5 -v /opt/wqo_python/models:/app/models:ro -v /opt/wqo_python/logs:/app/logs aiinfra-engine:v0.1
docker logs -f ai-agent
在本地测试
set PYTHONUTF8=1&& .venv\Scripts\python.exe scripts\smoke_agent_http.py http://192.168.1.249:10021----------------------------------------------------------------------------------------------------------------
更新镜像的操作
docker run -d --name ai-agent --restart=unless-stopped -p 10021:10021 --memory=4g -e APP_ENV=test --log-opt max-size=100m --log-opt max-file=5 -v /opt/wqo_python/models:/app/models:ro -v /opt/wqo_python/logs:/app/logs aiinfra-engine:v0.1
cd /opt/wqo_python/python_knowlage_ai docker build -t aiinfra-engine:v0.1 . docker rm -f ai-agent docker run -d --name ai-agent --restart=unless-stopped -p 10021:10021 --memory=4g -e APP_ENV=test --log-opt max-size=100m --log-opt max-file=5 -v /opt/wqo_python/models:/app/models:ro -v /opt/wqo_python/logs:/app/logs aiinfra-engine:v0.1 docker images -a | grep aiinfra # v0.1=新ID, 另有一行 <none>=旧镜像 docker image ls -f dangling=true # 单看无名的 # 按 ID 删单枚(最精确, 比如咱自己的旧 v0.1 残影) docker rmi 5f6c03434f78cd /opt/wqo_python/python_knowlage_ai -------待验证
bash scripts/deploy_test.sh
2、.244 部署方案与计划 -------------待讨论
244上
mkdir -p /opt/wqo_python/{models,logs}
上传 models.zip
unzip models.zip
【镜像搬运】249 上 docker save aiinfra-engine:v0.1 | gzip → scp 244 → docker load(§7 ship 口径,千兆内网约 1 分钟级)。
docker save aiinfra-engine:v0.1
【起容器】照 deploy_test.sh 的 run 段(-e APP_ENV=test、双卷、4g、log-opt)。小手术:给 deploy_test.sh 加个 SKIP_BUILD=1 开关即可复用(当前脚本 build 是死步);不加就手工 run。
【验收】curl :10024:10021/health 全绿 → 开发机 smoke_agent_http.py http://192.168.1.244:10021 36 项 → verify_agent_contract.py --db → 沙盒平方和项(依 248:2375 通)→ eval_rag --limit 2 护基线 0.982/5.000。