需求场景
python项目,开发环境以及可供测试的环境为X86架构下的LINUX服务器,但正式环境需要部署在ARM架构下的麒麟服务器,且正式环境后续可能会长时间处于断网状态,需要一份跨架构的部署方案。
解决思路
在 X86 上打包、在 ARM(麒麟 Linux)上运行,最大的难点就在于二进制兼容性——X86 编译出的可执行文件(无论是用 PyInstaller 还是其它方式)都无法直接在 ARM 上跑。下面分别说一下两种方案的利弊,供你决策时参考:
Docker 多架构镜像
优点
- 环境一致性
通过 Dockerfile 明确声明基础镜像、系统包、Python 版本、依赖库版本等,避免「开发机能跑、线上机跑不起来」的坑。 - 多平台支持
利用 Docker 官方提供的多架构基础镜像(如python:3.x-slim
),配合 Docker Buildx,可以一次性编译出适用于 amd64、arm64 等多种架构的镜像。 - 零交叉编译成本
你不需要在 X86 上搞复杂的交叉编译工具链,只要在 Dockerfile 里写好指令,Buildx 就能在后台(或远程 builder)帮你做。
缺点
- 镜像体积
即便用slim
或alpine
,完整 Python 运行时的镜像通常也在几十 MB 以上,如果部署环境对镜像大小有苛刻要求,可能需要你做深度瘦身。 - 运维依赖
线上机器要跑 Docker;如果你的正式环境是「只能跑二进制、不能装 Docker」的受限环境,则不可用。
PyInstaller 打包成单文件可执行
优点
- 无须容器化
只要拿到一个可执行文件,线上直接部署即可,不用关心 Docker 或容器平台。 - 单文件交付
如果团队习惯把交付物放到文件服务器、再由运维拷贝到目标机器,这种方式更简单。
缺点
- 必须在 ARM 机器上打包
PyInstaller 打出来的 ELF 可执行档会绑定打包时的 CPU 架构;在 X86 上编的程序跑不了 ARM。你要么在真正的 ARM Kirin 机器上打包,要么在 X86 上借助 QEMU + chroot/交叉编译环境才能打出 ARM 二进制,配置成本高。 - 依赖隐蔽
Python 动态库、C 扩展包、系统库等都要一一收集到打包目录,版本不对就可能「缺少依赖」或「符号找不到」。
Docker 多架构镜像
下面给出一个 从 X86 机器打包,到 脱网 ARM 麒麟服务器离线部署 的全流程示例。假设你的项目结构和文件列表如下:
myapp/
├── langou_model_mainfun.py
├── langou_model_dataprocessing.py
├── langou_model_function.py
├── langou_model_param.py
├── requirements.txt
└── .dockerignore
1. 在 X86 机上准备项目
创建
.dockerignore
(减少镜像体积)
在myapp/
下新建.dockerignore
,内容示例:__pycache__/ *.pyc .git .idea *.log
编写
Dockerfile
在myapp/
下新建Dockerfile
,内容如下:FROM docker.1ms.run/library/python:3.9-slim WORKDIR /app # 复制依赖并安装 RUN pip3 install --no-cache-dir torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制所有源码文件 COPY . . # (可选)健康检查:确保至少能 import 主模块 HEALTHCHECK --interval=30s --timeout=3s \ CMD python -c "import langou_model_mainfun" || exit 1 # 容器启动时执行主脚本 CMD ["python", "langou_model_mainfun.py"]
2. 在 X86 机上启用 Buildx + QEMU
- 检查 Docker 版本 & Buildx
docker version
docker buildx version
- 注册 QEMU 模拟器(支持在本机模拟 ARM 构建,使用国内镜像)
docker run --rm --privileged docker.1ms.run/multiarch/qemu-user-static --reset -p yes
- 创建并切换到 buildx Builder
docker buildx create --name mybuilder --use --driver docker-container --driver-opt image=docker.1ms.run/moby/buildkit:buildx-stable-1
docker buildx inspect --bootstrap
3. 构建 ARM64 镜像并导出为离线包
在 myapp/
目录下运行:
docker buildx build \
--platform linux/arm64 \
--tag myapp:arm64-v1.0 \
--output type=docker,dest=./myapp_arm64_v1.0.tar \
.
--platform linux/arm64
:生成 ARM64 架构镜像--output type=docker,dest=...tar
:将镜像导出为tar
包
执行完后,你会在 myapp/
目录看到 myapp_arm64_v1.0.tar
。
4. 拷贝镜像包到服务器
假设你在开发机上生成了 myapp_arm64_v1.0.tar
,用 scp
、USB 或其他方式,把它放到服务器上的某个目录,比如 /opt/deploy/myapp/
:
# 在开发机上
scp myapp_arm64_v1.0.tar root@arm-server:/opt/deploy/myapp/
5. 在服务器上加载镜像
cd /opt/deploy/myapp/
docker load -i myapp_arm64_v1.0.tar
运行后你应该能看到类似:
Loaded image: myapp:arm64-v1.0
6. 运行容器
在 docker run
时指定 --restart
参数:
docker run -d \
--name myapp \
--restart always \
-v /opt/deploy/myapp/conf:/app/conf:ro \
-e SOME_ENV=foo \
myapp:arm64-v1.0
--restart no
(默认)容器退出后不重启--restart on-failure[:max-retries]
--restart always
宿主机重启后始终重启容器--restart unless-stopped
类似always
,但如果你手动docker stop
过,就不会再自动重启
推荐使用
--restart unless-stopped
,这样即使遇到异常也重启,但运维手动停过就不会被“顽固”重启。
7. 验证运行状态
docker ps
docker logs myapp
docker ps
:查看容器是否在运行docker logs myapp
:查看启动日志,确认没有错误
Tip
- 如果你不需要挂载任何外部文件,则直接:
docker run -d --name myapp myapp:arm64-v1.0
- 要停止或删除容器:
docker stop myapp
docker rm myapp