前言
之前写了几篇 PDF 批量翻译的脚本文章,有不少读者反馈:在自己机器上能跑,但部署到团队内部的 Windows/Mac 同事机器上,环境配置成了大问题——Python 版本不一致、依赖库冲突、代理配置麻烦。
最终的解决方案是 Docker。把整个翻译链路封装成镜像,任何拉取镜像的人 docker run 一行就能用。
本文以"PDF 翻译工具的自托管部署"为例,完整走一遍:
- 写 Dockerfile
- 写 docker-compose 多服务编排
- 处理跨平台镜像构建
- 实战中常见的几个坑
环境准备
- Docker 24+
- docker-compose v2+
- 目标镜像基础:FROM python:3.11-slim
一、为什么这个场景适合 Docker 化
PDF 翻译场景有几个特点,天然适合 Docker:
- 无状态:任务调度、上传、下载,所有数据可外部化
- 依赖固定:Python + requests + tqdm + pdfplumber,几乎不变
- 可水平扩展:并发任务,只需多开容器实例
Docker 化能给团队带来的核心好处:
- 新员工入职 5 分钟上手(只需拉镜像)
- 屏蔽各机器环境的差异(Windows、Mac、Linux)
- CI/CD 流水线直接用同一镜像部署
二、Step 1: 写一个基础的 Dockerfile
先给一个能用的最小版本:
# 基础镜像
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 系统依赖(用于 pdfplumber / pdftotext 等)
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpoppler-cpp-dev \
&& rm -rf /var/lib/apt/lists/*
# 先复制 requirements 单独一层,利用 Docker 缓存
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 应用代码
COPY app/ ./app/
COPY translate_cli.py .
# 入口
ENTRYPOINT ["python", "translate_cli.py"]
CMD ["--help"]
requirements.txt:
requests>=2.31.0
tqdm>=4.66.0
pdfplumber>=0.10.0
click>=8.1.0
关键技巧:把
requirements.txt单独 COPY 一次,利用 Docker 的层缓存。后续只改app/目录时,不会重装依赖,构建快很多。
构建并验证
docker build -t pdf-translator:1.0 .
docker run --rm pdf-translator:1.0 --help
三、Step 2: 多阶段构建优化镜像大小
上面的镜像大约 800MB,因为带了 gcc 编译工具。可以改用多阶段构建,把构建期依赖留在第一阶段,运行时只保留必要文件:
# ===== 阶段 1:构建依赖 =====
FROM python:3.11-slim AS builder
WORKDIR /build
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libpoppler-cpp-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --target=/build/deps -r requirements.txt
# ===== 阶段 2:运行时镜像 =====
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends \
libpoppler-cpp-dev \
&& rm -rf /var/lib/apt/lists/*
# 复制依赖
COPY --from=builder /build/deps /usr/local/lib/python3.11/site-packages
COPY app/ ./app/
COPY translate_cli.py .
ENTRYPOINT ["python", "translate_cli.py"]
CMD ["--help"]
这样构建出来的镜像约 380MB,瘦了 50%。对生产部署来说,镜像大小直接关系到拉取和启动速度。
四、Step 3: docker-compose 多服务编排
实际部署时,通常需要多个服务:
api:HTTP 接口worker:异步翻译任务消费者(可选 Celery)redis:任务队列monitor:日志聚合(可选 ELK)
docker-compose.yml:
version: "3.9"
services:
api:
build:
context: .
dockerfile: Dockerfile
image: pdf-translator:1.0
container_name: pdf-translator-api
command: ["python", "app/server.py", "--host", "0.0.0.0", "--port", "8000"]
ports:
- "8000:8000"
environment:
- API_KEY=${API_KEY}
- REDIS_URL=redis://redis:6379/0
- LOG_LEVEL=INFO
volumes:
- ./uploads:/app/uploads
- ./outputs:/app/outputs
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
redis:
image: redis:7-alpine
container_name: pdf-translator-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
worker:
image: pdf-translator:1.0
container_name: pdf-translator-worker
command: ["python", "app/worker.py"]
environment:
- API_KEY=${API_KEY}
- REDIS_URL=redis://redis:6379/0
volumes:
- ./uploads:/app/uploads
- ./outputs:/app/outputs
depends_on:
- api
- redis
restart: unless-stopped
deploy:
replicas: 2 # 水平扩展 2 个 worker
volumes:
redis-data:
启动:
docker-compose up -d
docker-compose logs -f api
五、Step 4: 跨平台镜像构建(踩坑重点)
如果你团队既有 Mac 又有 Windows + Linux 服务器,跨平台镜像是个常见痛点。
方案 A: 一次性构建多平台镜像
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t pdf-translator:1.0 \
--push .
注意:
--push会推到你配置的 registry。Mac M1 (ARM64) 构建的镜像在 x86 服务器上跑,必须用linux/amd64显式指定。
方案 B: 使用 manifest 镜像(可选)
如果你的镜像要分发到内网多台不同架构的机器,可以创建 manifest list:
docker manifest create pdf-translator:1.0 \
your-registry/pdf-translator:1.0-amd64 \
your-registry/pdf-translator:1.0-arm64
docker manifest push pdf-translator:1.0
六、实战中常见的几个坑
坑 1: 文件中文名编码问题
Docker for Windows + WSL 2 的中文文件名,有时会变成乱码。强烈建议:
- Docker 内部统一使用 UTF-8
- 容器 ENV 设置:
ENV LANG=C.UTF-8 \ LC_ALL=C.UTF-8 \ PYTHONIOENCODING=utf-8
坑 2: 时区不一致
容器默认 UTC,日志时间会差 8 小时:
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
或者在 docker-compose 用 environment:
environment:
- TZ=Asia/Shanghai
坑 3: 容器重启丢日志
容器默认日志写到 stdout,但宿主机没有持久化。建议:
- 用
loggingdriver + 集中日志服务(Loki/ELK) - 或者挂载
/var/log目录
services:
api:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
坑 4: 代理配置
如果你的服务器需要通过代理访问外网(很多办公网是这样),Docker 构建时常常遇到:
# 构建期代理
ENV HTTP_PROXY=http://your-proxy:7897 \
HTTPS_PROXY=http://your-proxy:7897
运行时通过环境变量注入:
services:
api:
environment:
- HTTP_PROXY=http://host.docker.internal:7897
注意:Windows + Docker Desktop 场景下,宿主机代理地址用
host.docker.internal而不是127.0.0.1(容器内 127.0.0.1 是容器自己)。
七、生产化部署 checklist
上线前自检:
- 镜像版本 tag 写具体版本号,不用
latest - healthcheck 写好,k8s/docker-compose 都看得到
- 日志结构化输出(JSON 格式)
- API Key 通过 secret 管理,不写在镜像里
- volumes 持久化数据(上传文件、翻译结果)
- 镜像定期扫描漏洞(
docker scan) - 资源限制加好(memory / cpu limit)
services:
api:
deploy:
resources:
limits:
memory: 1G
cpus: "1.0"
八、回顾与下一步
到这里,我们已经:
- 写了基础 Dockerfile 和多阶段构建版本
- 用 docker-compose 编排了多服务
- 处理了跨平台构建
- 解决了几个常见的中文路径、时区、代理坑
接下来还可以做
- 接入 GitHub Actions 自动构建镜像
- 用 Kubernetes 部署 docker-compose(kompose 工具转译)
- 接入 Prometheus + Grafana 做监控
- 镜像推送到 Harbor 自建仓库
总结
Docker 不只是"换个环境跑",更是把运维复杂度从团队每个人身上,集中到镜像里。一次构建,全员可用。这就是工程化的价值。
后续如果有时间,会写一篇"Kubernetes 部署 PDF 翻译服务"的进阶文章,敬请期待。
标签:Docker、Python、容器化、PDF翻译、DevOps

706

被折叠的 条评论
为什么被折叠?



