Docker容器化部署PDF翻译工具:从Dockerfile到docker-compose
前言之前写了几篇 PDF 批量翻译的脚本文章,有不少读者反馈:在自己机器上能跑,但部署到团队内部的 Windows/Mac 同事机器上,环境配置成了大问题——Python 版本不一致、依赖库冲突、代理配置麻烦。最终的解决方案是 Docker。把整个翻译链路封装成镜像,任何拉取镜像的人docker run一行就能用。本文以PDF 翻译工具的自托管部署为例,完整走一遍:写 Dockerfile写 docker-compose 多服务编排处理跨平台镜像构建实战中常见的几个坑环境准备Docker 24docker-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:requests2.31.0 tqdm4.66.0 pdfplumber0.10.0 click8.1.0关键技巧:把requirements.txt单独 COPY 一次,利用 Docker 的层缓存。后续只改app/目录时,不会重装依赖,构建快很多。构建并验证dockerbuild-tpdf-translator:1.0.dockerrun--rmpdf-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 --frombuilder /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.9services:api:build:context:.dockerfile:Dockerfileimage:pdf-translator:1.0container_name:pdf-translator-apicommand:[python,app/server.py,--host,0.0.0.0,--port,8000]ports:-8000:8000environment:-API_KEY${API_KEY}-REDIS_URLredis://redis:6379/0-LOG_LEVELINFOvolumes:-./uploads:/app/uploads-./outputs:/app/outputsdepends_on:redis:condition:service_healthyrestart:unless-stoppedhealthcheck:test:[CMD,curl,-f,http://localhost:8000/health]interval:30stimeout:10sretries:3redis:image:redis:7-alpinecontainer_name:pdf-translator-redisports:-6379:6379volumes:-redis-data:/datahealthcheck:test:[CMD,redis-cli,ping]interval:10stimeout:5sretries:3worker:image:pdf-translator:1.0container_name:pdf-translator-workercommand:[python,app/worker.py]environment:-API_KEY${API_KEY}-REDIS_URLredis://redis:6379/0volumes:-./uploads:/app/uploads-./outputs:/app/outputsdepends_on:-api-redisrestart:unless-stoppeddeploy:replicas:2# 水平扩展 2 个 workervolumes:redis-data:启动:docker-composeup-ddocker-composelogs-fapi五、Step 4: 跨平台镜像构建(踩坑重点)如果你团队既有 Mac 又有 Windows Linux 服务器,跨平台镜像是个常见痛点。方案 A: 一次性构建多平台镜像dockerbuildx create--usedockerbuildx build\--platformlinux/amd64,linux/arm64\-tpdf-translator:1.0\--push.注意:--push会推到你配置的 registry。Mac M1 (ARM64) 构建的镜像在 x86 服务器上跑,必须用linux/amd64显式指定。方案 B: 使用 manifest 镜像(可选)如果你的镜像要分发到内网多台不同架构的机器,可以创建 manifest list:dockermanifest create pdf-translator:1.0\your-registry/pdf-translator:1.0-amd64\your-registry/pdf-translator:1.0-arm64dockermanifest push pdf-translator:1.0六、实战中常见的几个坑坑 1: 文件中文名编码问题Docker for Windows WSL 2 的中文文件名,有时会变成乱码。强烈建议:Docker 内部统一使用 UTF-8容器 ENV 设置:ENV LANGC.UTF-8 \ LC_ALLC.UTF-8 \ PYTHONIOENCODINGutf-8坑 2: 时区不一致容器默认 UTC,日志时间会差 8 小时:ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone或者在 docker-compose 用environment:environment:-TZAsia/Shanghai坑 3: 容器重启丢日志容器默认日志写到 stdout,但宿主机没有持久化。建议:用loggingdriver 集中日志服务(Loki/ELK)或者挂载/var/log目录services:api:logging:driver:json-fileoptions:max-size:10mmax-file:3坑 4: 代理配置如果你的服务器需要通过代理访问外网(很多办公网是这样),Docker 构建时常常遇到:# 构建期代理 ENV HTTP_PROXYhttp://your-proxy:7897 \ HTTPS_PROXYhttp://your-proxy:7897运行时通过环境变量注入:services:api:environment:-HTTP_PROXYhttp://host.docker.internal:7897注意:Windows Docker Desktop 场景下,宿主机代理地址用host.docker.internal而不是127.0.0.1(容器内 127.0.0.1 是容器自己)。七、生产化部署 checklist上线前自检:镜像版本 tag 写具体版本号,不用latesthealthcheck 写好,k8s/docker-compose 都看得到日志结构化输出(JSON 格式)API Key 通过 secret 管理,不写在镜像里volumes 持久化数据(上传文件、翻译结果)镜像定期扫描漏洞(docker scan)资源限制加好(memory / cpu limit)services:api:deploy:resources:limits:memory:1Gcpus:1.0八、回顾与下一步到这里,我们已经:写了基础 Dockerfile 和多阶段构建版本用 docker-compose 编排了多服务处理了跨平台构建解决了几个常见的中文路径、时区、代理坑接下来还可以做接入 GitHub Actions 自动构建镜像用 Kubernetes 部署 docker-compose(kompose 工具转译)接入 Prometheus Grafana 做监控镜像推送到 Harbor 自建仓库总结Docker 不只是换个环境跑,更是把运维复杂度从团队每个人身上,集中到镜像里。一次构建,全员可用。这就是工程化的价值。后续如果有时间,会写一篇Kubernetes 部署 PDF 翻译服务的进阶文章,敬请期待。标签:Docker、Python、容器化、PDF翻译、DevOps