一句话结论:用缓存策略 + 测试并行化 + Docker 分层优化,把 CI 流水线从 22.5 分钟砍到 3.8 分钟,构建时间降低 83%。文末附完整配置代码。
📊 先看数据
| 阶段 | 优化前 | 优化后 | 降幅 |
|---|---|---|---|
| 依赖安装 | 6 min 23 s | 0 s(命中缓存) | 100% |
| 测试执行 | 10 min 47 s | 2 min 08 s | 80% |
| Docker 构建 | 4 min 05 s | 28 s(代码变更) | 89% |
| 总计 | 22 min 27 s | 3 min 48 s | 83% |
环境:Python 3.11 + pytest + GitHub Actions(4 核 Runner)+ Docker
一、瓶颈定位:先测量,再动刀
原始 GitHub Actions 流水线 4 个阶段:
Install → Lint → Test → Build
用 time 埋点逐阶段计时后,耗时分布一目了然:
matlab
Dependency Install ████████████████████░░░░░░ 6.4 min (29%)
Pytest (serial) ██████████████████████████ 10.8 min (48%)
Docker Build ████████████░░░░░░░░░░░░░░ 4.1 min (18%)
Lint Check ██░░░░░░░░░░░░░░░░░░░░░░░░ 1.2 min (5%)
结论:依赖安装 + 测试 + Docker 占了 95% 的时间,逐个击破。
二、缓存策略:pip install 从 6 分钟到 0 秒
问题
每次 CI 从 PyPI 重新下载 40+ 个包,仅 numpy + pandas 就拖 2 分钟。
方案:缓存整个 venv
yaml
- name: Cache virtualenv
uses: actions/cache@v4
with:
path: .venv
key: ${{ runner.os }}-venv-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-venv-
- name: Install dependencies
run: |
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
关键点 :hashFiles('requirements.txt') --- 依赖文件不变,hash 不变,直接命中缓存。
⚠️ 踩坑 :
pip freeze > requirements.txt会带本地路径引用,导致 hash 每次都变 → 缓存永远不命中。手动清理或用pip-compile替代。
三、测试并行化:pytest-xdist 实战
问题
312 个测试用例串行跑,单进程,10 分钟+。
方案:一行命令落地并行
bash
pip install pytest-xdist
pytest -n auto --dist loadscope
-n auto:自动检测 CPU 核数--dist loadscope:按模块分组分发,同一文件的测试在同一进程,避免 fixture 冲突
踩坑修复:SQLite 并行冲突
加并行后炸了 40+ 个测试:
arduino
FAILED tests/test_user_api.py::test_create_user - Database is locked
根因:多进程抢同一个 SQLite 文件。修法:进程级隔离。
python
# conftest.py
import pytest
import tempfile
import os
@pytest.fixture(scope="session")
def db_path():
"""每个 session 独立临时数据库"""
tmp = tempfile.mkdtemp()
yield os.path.join(tmp, "test.db")
@pytest.fixture
def db_session(db_path):
"""每个测试函数独立事务"""
engine = create_engine(f"sqlite:///{db_path}")
# ...
效果
| 配置 | 耗时 | 提速 |
|---|---|---|
| 串行 | 10 min 47 s | 基准 |
| 2 核 + xdist | 5 min 12 s | 2.1× |
| 4 核 + loadscope | 2 min 08 s | 5.0× |
四、Docker 分层优化:利用 BuildKit 缓存
问题
原 Dockerfile:COPY . . → 每次代码变更都重建全部层,包括 pip install。
方案:多阶段构建 + 依赖前置
dockerfile
FROM python:3.11-slim AS builder
WORKDIR /app
# 第 1 层:依赖文件(很少变)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 第 2 层:源码(经常变)
COPY . .
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=builder /app /app
CMD ["python", "main.py"]
配合 .dockerignore:
bash
__pycache__
*.pyc
.venv
.git
.pytest_cache
.env
效果
| 场景 | 原方案 | 优化后 |
|---|---|---|
| 首次构建 | 4 min 05 s | 3 min 50 s |
| 只改代码(高频场景) | 4 min 05 s | 28 s |
五、完整 CI 配置(可直接使用)
yaml
name: CI
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Cache venv
uses: actions/cache@v4
with:
path: .venv
key: ${{ runner.os }}-venv-${{ hashFiles('requirements.txt') }}
- name: Install
run: |
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
- name: Lint
run: |
source .venv/bin/activate
ruff check .
test:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Cache venv
uses: actions/cache@v4
with:
path: .venv
key: ${{ runner.os }}-venv-${{ hashFiles('requirements.txt') }}
- name: Install
run: |
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
- name: Test
run: |
source .venv/bin/activate
pytest -n auto --dist loadscope --junitxml=reports/junit.xml
build:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- name: Build Docker
run: docker build -t app:${{ github.sha }} .
六、方法论总结
| 原则 | 具体做法 |
|---|---|
| 先测量 | time 埋点 + pytest --durations=10 定位瓶颈 |
| 上缓存 | GitHub Actions cache + Docker layer cache + pip cache |
| 并行化 | pytest-xdist,注意共享资源隔离 |
| 做减法 | .dockerignore 排除无关文件,减少构建上下文 |
💬 你在 CI 上踩过什么坑?评论区聊聊。
📌 下一篇预告:《Docker Compose 本地开发环境搭建,别再在生产环境 Debug 了》
🏷️ 标签:Python · CI/CD · DevOps · 测试 · GitHub Actions
2026 年 6 月 | 约 2800 字 | 预计阅读 7 分钟