Docker容器化部署PDF翻译工具:从Dockerfile到docker-compose

前言

之前写了几篇 PDF 批量翻译的脚本文章,有不少读者反馈:在自己机器上能跑,但部署到团队内部的 Windows/Mac 同事机器上,环境配置成了大问题——Python 版本不一致、依赖库冲突、代理配置麻烦。

最终的解决方案是 Docker。把整个翻译链路封装成镜像,任何拉取镜像的人 docker run 一行就能用。

本文以"PDF 翻译工具的自托管部署"为例,完整走一遍:

  1. 写 Dockerfile
  2. 写 docker-compose 多服务编排
  3. 处理跨平台镜像构建
  4. 实战中常见的几个坑

环境准备

  • 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,但宿主机没有持久化。建议:

  • logging driver + 集中日志服务(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 编排了多服务
  • 处理了跨平台构建
  • 解决了几个常见的中文路径、时区、代理坑

接下来还可以做

  1. 接入 GitHub Actions 自动构建镜像
  2. 用 Kubernetes 部署 docker-compose(kompose 工具转译)
  3. 接入 Prometheus + Grafana 做监控
  4. 镜像推送到 Harbor 自建仓库

总结

Docker 不只是"换个环境跑",更是把运维复杂度从团队每个人身上,集中到镜像里。一次构建,全员可用。这就是工程化的价值。

后续如果有时间,会写一篇"Kubernetes 部署 PDF 翻译服务"的进阶文章,敬请期待。


标签:Docker、Python、容器化、PDF翻译、DevOps

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值