Docker 化测试环境:一致性交付

专栏 :Python 自动化测试从入门到实战

适读人群 :测试工程师 / Python 开发者 / DevOps 入门

预计阅读:13 分钟


一、上接第 6 篇

第 6 篇《CI/CD 集成:GitHub Actions》 中,我们解决了:

  • GitHub Actions 的 Workflow / Job / Step 三层模型
  • pytest 在每次提交后自动运行、Allure 报告归档
  • 矩阵策略、缓存、覆盖率与 PR 质量门禁

但有一个隐患没解决:

"在我机器上能跑"------这个经典甩锅,CI 也救不了。

本地 Python 3.12、CI 是 3.11、同事是 3.10;有人装了 redis、有人没装;依赖版本靠 pip freeze 撞大运......

本篇进入 L4 收官阶段 ,用 Docker 回答一个交付问题:

如何让测试环境从"本地 → CI → 线上"完全一致?

答案就是 容器化 ------ 一次构建,处处运行


二、为什么需要 Docker 化测试环境?

没有容器时,环境靠"口口相传":

维度 裸机 / venv Docker 容器
环境一致性 各机器差异大 镜像即标准,处处一致
依赖隔离 venv 只隔离 Python 系统库、进程、网络全隔离
复现成本 重装半天 docker pull 即复现
并发场景 端口/数据库冲突 独立网络、随起随销毁
交付物 一堆 README 一个镜像(Image)

Docker = 把"环境"变成可版本控制的产物


三、核心概念(5 分钟入门)

概念 含义
Image 只读模板(环境的快照)
Container Image 的运行实例
Dockerfile 构建 Image 的脚本(可版本控制)
docker-compose 编排多个容器(应用 + DB + Redis)
Volume 持久化数据(测试结果、缓存)
Network 容器间私有网络

一句话 :Dockerfile 定义环境 → docker build 出镜像 → docker run 起容器跑测试。


四、项目结构(沿用专栏体系)

复制代码
pytest-series/
├── .github/
│   └── workflows/
│       └── test.yml
├── docker/
│   ├── Dockerfile              ← 测试镜像(本文核心)
│   └── docker-compose.yml      ← 编排测试 + 依赖服务
├── src/
│   └── calculator.py
├── tests/
│   ├── conftest.py
│   ├── test_calculator.py
│   └── test_allure.py
├── requirements.txt
├── pytest.ini
└── README.md

沿用 src/calculator.py + tests/ 结构,Dockerfile 与 compose 放在独立的 docker/ 目录,职责清晰。


五、第一个 Dockerfile:打包测试环境

docker/Dockerfile

dockerfile 复制代码
# 基础镜像:固定小版本,避免静默升级
FROM python:3.12-slim

# 环境变量:隔离 Python 路径、关闭 pyc 缓存
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PATH="/app/.venv/bin:$PATH"

# 系统依赖(如需 mysqlclient / psycopg2 在此装)
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 先复制依赖文件,利用 Docker 层缓存
WORKDIR /app
COPY requirements.txt .
RUN python -m venv .venv && \
    pip install --no-cache-dir -r requirements.txt

# 复制项目源码
COPY . .

# 默认命令:跑 pytest(可被 docker run 覆盖)
CMD ["pytest", "tests/", "-v"]

要点

  • 固定基础镜像版本python:3.12-slim 而非 latest,保证可复现
  • 依赖分层 :先 COPY requirements.txt 再 COPY 源码,改动代码时不重装依赖,构建快
  • 用 venv:与 CI 行为一致,隔离更彻底
  • CMD 可被覆盖docker run img pytest tests/test_allure.py 临时指定用例

六、构建与本地运行

bash 复制代码
# 在项目根目录构建(注意末尾的点)
docker build -f docker/Dockerfile -t pytest-series:dev .

# 运行全部测试
docker run --rm pytest-series:dev

# 挂载本地代码,改完即跑(开发态)
docker run --rm -v $(pwd):/app pytest-series:dev

# 只跑某个文件 + 生成 Allure 结果
docker run --rm -v $(pwd)/allure-results:/app/allure-results \
    pytest-series:dev pytest tests/ --alluredir=allure-results

关键技巧-v 挂载让容器内实时读到本地改动,Allure 报告落到宿主机,无需进容器取。


七、docker-compose:编排测试 + 依赖服务

真实项目测试常依赖 数据库、Redis、Mock 服务,用 compose 一把拉起:

yaml 复制代码
# docker/docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: ..
      dockerfile: docker/Dockerfile
    volumes:
      - ../allure-results:/app/allure-results
    depends_on:
      - postgres
      - redis
    environment:
      - DATABASE_URL=postgresql://test:test@postgres:5432/testdb
      - REDIS_URL=redis://redis:6379/0
    command: pytest tests/ --alluredir=allure-results

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
      POSTGRES_DB: testdb
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

启动:docker compose -f docker/docker-compose.yml up --abort-on-container-exit

--abort-on-container-exit:任一容器失败立即整体退出,CI 能正确拿到非零状态码。


八、容器化测试架构图

宿主机 → 挂载代码/结果目录 → 测试容器(pytest + venv)→ 依赖网络 → Postgres / Redis 容器;Allure 结果通过 Volume 落回宿主机。


九、与 CI 结合(承接第 6 篇)

把第 6 篇的 test.yml 改造为在容器内跑测试,实现"本地 = CI":

yaml 复制代码
# .github/workflows/test.yml(增量)
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -f docker/Dockerfile -t pytest-series:ci .

      - name: Run tests in container
        run: |
          docker run --rm \
            -v ${{ github.workspace }}/allure-results:/app/allure-results \
            pytest-series:ci pytest tests/ --alluredir=allure-results

      - name: Upload Allure report
        uses: actions/upload-artifact@v4
        with:
          name: allure-report
          path: allure-results

收益 :CI 不再装 Python、不再 pip install,只负责 build 镜像 + run 容器,环境与本地完全一致。


十、镜像分层与缓存策略

复制代码
Layer 1: python:3.12-slim          ← 基础层(几乎不变)
Layer 2: apt-get + pip venv        ← 系统/依赖层(偶尔变)
Layer 3: COPY requirements.txt    ← 依赖声明(变则重建)
Layer 4: COPY . .                  ← 源码层(每次变)

优化原则越稳定的内容越靠上 ,最大化复用缓存。构建耗时通常降低 50%~80%

💡 进阶:用 多阶段构建(build 阶段编译、runtime 阶段只留产物)进一步缩小镜像体积。


十一、镜像体积与安全最佳实践

实践 做法
用 slim/alpine 基础镜像 从 ~1GB 降到 ~150MB
多阶段构建 丢弃编译工具链
非 root 运行 USER 1000 避免权限过大
固定版本标签 不用 latest
扫描漏洞 docker scout / Trivy
.dockerignore 排除 .git.venv、报告等

十二、数据卷与结果持久化

测试结果必须落回宿主机,否则容器销毁即丢失:

yaml 复制代码
volumes:
  - ../allure-results:/app/allure-results   # Allure 报告
  - ../coverage.xml:/app/coverage.xml       # 覆盖率
  - pytest-cache:/app/.pytest_cache         # 缓存(命名卷,跨运行复用)

命名卷 vs 绑定挂载

  • 绑定挂载-v 绝对路径):方便宿主机直接读,适合报告
  • 命名卷:由 Docker 管理,适合缓存,生命周期独立于容器

十三、环境一致性验证(三处同一)

为确保"本地 = CI = 线上",验证三处使用同一镜像

场景 命令
本地开发 docker run --rm -v $(pwd):/app img
CI docker run --rm img(GitHub Actions)
线上/预发 docker run --rm img pytest tests/ --cov

只要镜像哈希一致,环境就一致 ------ 这就是容器化的核心价值。


十四、完整 Dockerfile + compose 一览

dockerfile 复制代码
# docker/Dockerfile(生产级)
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app

COPY requirements.txt .
RUN python -m venv .venv && \
    .venv/bin/pip install --no-cache-dir -r requirements.txt

COPY --chown=1000:1000 . .
USER 1000

CMD ["pytest", "tests/", "-v"]

compose 见 第七节,两者配合即构成完整交付单元。


十五、常见坑速查

问题 原因 解决
容器内连不上 DB 用了 localhost 改用服务名 postgres
挂载后权限拒绝 宿主机 UID 不匹配 USER 1000 + 目录授权
依赖装了却找不到 没用 venv 的 pip .venv/bin/pip
报告没生成 没挂载 Volume -vallure-results
构建越来越慢 层顺序不当 稳定内容靠上

十六、小结

✅ Docker 化 = 把"环境"变成可版本控制的镜像

✅ Dockerfile 分层:稳定的内容靠上 ,最大化缓存复用

✅ docker-compose 编排 app + DB + Redis ,一键起停

✅ Volume 保证 Allure/覆盖率结果持久化

✅ CI 只负责 build + run,本地 = CI = 线上一致性交付


十七、下一篇预告

👉 《从零搭建生产级测试项目:专栏收官实战》

你将学到:

  • 把 L1~L4 全部知识整合为一个完整项目
  • 测试分层、目录规范、CI/CD 全流程
  • 可复用的项目脚手架与最佳实践清单

相关推荐
风景的人生1 小时前
Linux虚拟机网络故障排查与解决方案
运维·网络·ssh
..Dauntless..1 小时前
【Linux】权限问题——拥有者、所属组和其他用户的协调
linux·运维·服务器
平行云2 小时前
实时云渲染信创架构解析:从GPU池化到全栈适配的技术演进
linux·unity·docker·ue5·webgl·数字孪生·实时云渲染
handler012 小时前
【Linux】信号:内核的“敲门声”
linux·运维·服务器·c++·c·信号·signal
lucybean012 小时前
食品加工行业污水处理曝气装置采购避坑与合规筛选指南
运维
harmony&2 小时前
DevOps进阶:SonarQube 代码审计与 Harbor 镜像仓库实战
运维·devops
Neighbor_OldY2 小时前
CSRF跨站请求伪造攻击检测与应急处置实战:伪造请求识别与防护落地
运维·安全·web安全
生活爱好者!2 小时前
NAS能批量做短视频?docker部署MoneyPrinterTurbo
运维·docker·容器
考虑考虑2 小时前
Redis8.8新特性
运维·redis·后端