Ansible + Terraform + Molecule Renovate 自动化依赖升级实战 2026:从 PR 误触发到生产回滚的 5 个真实陷阱
上周我在重构团队的 Ansible + Terraform + Molecule CI 流水线,目标是把所有第三方依赖(Ansible collection、Terraform provider、Python library、Docker base image)的升级用 Renovate 接管。
听起来很美 —— Renovate 自动开 PR、自动跑 Molecule 测试、自动合并。但实际跑了一周,5 个真实坑让我的自动化梦想破灭了 70%。这篇文章是我把每个坑挖出来、修了、再验证一遍的完整复盘。
读者跑完能拿到的成果:
- 一份覆盖 Ansible collection、Terrform provider、Python library、Docker image 4 类资产的 Renovate 完整配置
- 5 个真实生产陷阱(PR 雪崩/major 版本静默升级/lock 文件被回滚/molecule-plugins 协议 breaking/test 跳过导致生产炸)和最小修复
- Molecule 测试矩阵(multi-OS、multi-Python)+ GitHub Actions workflow 完整代码(带 cache 节省 4 分钟)
- 一份生产 playbook:用 Renovate + GitHub Actions + Slack 通知的"PR → 测试 → 合并 → 通知"闭环流程
架构预览
┌─ Renovate (self-hosted Docker container)
│ └─ 扫描 github.com/yaohehe/infra-ansible-terraform
│ ├─ scan repos every 60 minutes
│ ├─ match package.json / requirements.yml / Dockerfile
│ └─ open PR with: bump version + lock file + changelog snippet
│
├─ GitHub Actions
│ └─ on PR open / sync
│ ├─ molecule test -s default (Ubuntu 24.04)
│ ├─ molecule test -s compat (Debian 12)
│ ├─ terraform validate (fmt + init + plan in dry-run mode)
│ └─ ansible-lint → 自动评论
│
├─ Slack Webhook
│ └─ on success/failure → #infra-alerts
│
└─ Auto-merge(限 minor/patch)
└─ matched label: automerge:minor
├─ all CI ✅ → squash merge
└─ any CI ❌ → comment + require-human-review label
前置准备
- 系统环境:Ubuntu 24.04 LTS / Docker 26.1.5 + / Docker Compose v2
- 软件依赖:Node.js 20+(Renovate self-hosted 镜像要求)/ Python 3.12.4 / Terraform 1.10.x 或 OpenTofu 1.10.x
- 验证命令:
docker --version # Docker version 26.1.5, build a3c...
docker compose version # Docker Compose version v2.27.1
node --version # v20.16.0
tf --version # Terraform v1.10.4 / OpenTofu v1.10.4
ansible --version # ansible [core 2.17.7]
molecule --version # molecule 25.6.0 using python 3.12
硬件:至少 4GB RAM 的 VPS(Renovate self-hosted 默认占 1.8GB,Molecule 测试运行时 Docker in Docker 额外占 2GB)。
🚀 核心部署步骤
Step 1: Renovate 配置 + 元数据
在仓库根目录建 renovate.json:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":automergeMinor",
":automergeDigest",
":prHourlyLimit2",
"group:allNonMajor"
],
"timezone": "Asia/Shanghai",
"schedule": ["before 3am on monday"],
"prConcurrentLimit": 3,
"branchPrefix": "renovate/",
"commitBody": "Triggered by Renovate. See https://github.com/yaohehe for more.",
"rebaseWhen": "behind-base-branch",
"packageRules": [
{
"description": "Ansible collection minor 升级自动合并",
"matchDatasources": ["galaxy"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "squash"
},
{
"description": "Ansible collection major 升级必须人工 review",
"matchDatasources": ["galaxy"],
"matchUpdateTypes": ["major"],
"automerge": false,
"labels": ["breaking-change", "needs-review"]
},
{
"description": "Terraform provider 全部走人工 review",
"matchDatasources": ["terraform-provider"],
"automerge": false,
"labels": ["terraform", "needs-review"]
},
{
"description": "Docker base image minor 自动合并",
"matchDatasources": ["docker"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
],
"dependencyDashboard": true,
"dependencyDashboardApproval": false
}
每个字段:
- `config:recommended` —— Renovate 内置的最佳实践(启用了 vulnerability alerts、prPriority 等)
- `:automergeMinor` —— 所有 minor/patch 默认自动合并
- `:prHourlyLimit2` —— 每小时最多 2 个 PR(避免 PR 雪崩)
- `packageRules` —— 按数据源分类(galaxy/terraform-provider/docker)打不同合并策略
Step 2: Renovate self-hosted 启动
docker-compose.yml:
services:
renovate:
image: renovate/renovate:39.222.0
container_name: renovate
restart: unless-stopped
environment:
- RENOVATE_TOKEN=${RENOVATE_GITHUB_TOKEN}
- RENOVATE_AUTODISCOVER=true
- RENOVATE_REPOSITORIES_JSON=/tmp/repositories.json
- RENOVATE_PLATFORM=github
- RENOVATE_GIT_AUTHOR=Renovate Bot
- LOG_LEVEL=info
volumes:
- ./repositories.json:/tmp/repositories.json
- renovate-data:/usr/src/app/data
command: renovate
volumes:
renovate-data:
repositories.json:
[
"yaohehe/infra-ansible-terraform",
"yaohehe/ansible-role-postgres",
"yaohehe/terraform-vultr-modules"
]
启动:
mkdir -p renovate-data
echo "RENOVATE_GITHUB_TOKEN=ghp_xxxYourRealToken" > .env
docker compose up -d
docker compose logs -f renovate # 观察扫描日志
Step 3: Molecule CI workflow
.github/workflows/molecule.yml:
name: Molecule CI
on:
- pull_request
- push
jobs:
matrix:
name: Molecule on ${{ matrix.os }} / Python ${{ matrix.python }}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, ubuntu-22.04, debian-12]
python: ['3.12', '3.11']
exclude:
- os: debian-12
python: '3.12' # 不兼容组合
steps:
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ runner.os }}-${{ matrix.python }}-${{ hashFiles('requirements/*.txt') }}
- name: Install Python ${{ matrix.python }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install collections
run: |
ansible-galaxy collection install -r requirements.yml
- name: Run Molecule
run: |
molecule test -s ${{ matrix.os }} \
-- \
ANSIBLE_COLLECTIONS_PATH=$HOME/.ansible/collections
- name: ansible-lint
run: ansible-lint --profile=production
Step 4: Terraform validate workflow
.github/workflows/terraform.yml:
name: Terraform CI
on:
- pull_request
- push
jobs:
validate:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Setup Tofu
uses: opentofu/setup-opentofu@v2
with:
tofu_version: 1.10.4
- name: terraform fmt
run: tofu fmt -check -recursive
working-directory: terraform/
- name: terraform validate
run: |
tofu init -backend=false
tofu validate -json | jq '.valid' | grep -q true
working-directory: terraform/
💣 踩坑录与 5 个真实陷阱
这是最值钱的段落。
坑 1:凌晨 3 点一次性开 38 个 PR,repository 锁死 → Renovate 二维码扫描全卡
症状:第一次跑 schedule "before 3am on monday" 时,Renovate 把过去 6 个月积压的所有 minor/patch 一次性开 PR,整整 38 个 PR。GitHub 的 ref creation rate limit 触顶,Renovate 反复重试同一个 PR,整个 log 刷屏 "retry after 3600s"。
**根因**::prHourlyLimit2 只限制了每小时 PR 数,但**没有限制单次扫描的存量 PR 总量**。Renovate 默认会把过去所有未升级的 minor 全部补上。
修复:
{
"schedule": ["before 3am on monday"],
"prConcurrentLimit": 3,
"prHourlyLimit": 5,
"lockFileMaintenance": {
"enabled": true,
"schedule": ["before 3am on monday"],
"recreateWhen": "always"
},
"constraints": {
"ansible": ">=2.14.0 <3.0.0"
}
}
并加上 branchConcurrentLimit:
"branchConcurrentLimit": 2,
"prCommitsPerRunLimit": 2
我加完后第 2 周 PR 数从 38 个降到 8 个,CI 也顺利跑完。
坑 2:community.general 8.x 自动合并,新版 collection 用了 Python 3.12 死语法 → ansible-playbook 启动失败
**症状**:ansible-galaxy 自动把 community.general 从 7.x 升级到 8.0.0,但因为 PR 里有 matchUpdateTypes: ["minor"] 触发自动合并。我信任 automerge 直接 squash merge。
第二天跑 ansible-playbook site.yml 时:
TASK [community.general.seboolean] *
fatal: [host]: FAILED! => {"msg": "The 'community.general' collection uses ansible-core 2.15 or newer..."
**根因**:community.general 8.x 用了 ansible-core 2.15+ 才支持的 argparse 特性。automerge 默认 squash 模式下 commit 不带 changelog + 没跑 grep 检查依赖声明。
修复 —— 两个动作:
1. 关闭 automerge,改手工 review:
{
"matchDatasources": ["galaxy"],
"matchUpdateTypes": ["minor"],
"automerge": false,
"labels": ["needs-review"],
"schedule": ["after 10pm on friday"]
}
2. 在仓库根加 requirements.yml,**锁 ansible-core 范围**:
collections:
- name: community.general
version: ">=7.0.0,<8.0.0"
- name: ansible.posix
version: ">=1.5.0,<2.0.0"
并加 CI 步骤做兼容性检查(4 个真实步骤):
# ① 检查 collection 内 setup.py 的 python_requires
ansible-galaxy collection install community.general:8.0.0 -p /tmp/test-collections
python -c "
import ast, glob
for f in glob.glob('/tmp/test-collections/ansible_collections/community/general/setup.py'):
tree = ast.parse(open(f).read())
for node in ast.walk(tree):
if isinstance(node, ast.Call) and getattr(node.func, 'id', '') == 'setup':
for kw in node.keywords:
if kw.arg == 'python_requires':
print('python_requires:', kw.value.value)
"
# ② 检查 collection 的 meta/runtime.yml 声明 ansible_version
cat /tmp/test-collections/ansible_collections/community/general/meta/runtime.yml
# ③ 跑完整 Molecule
molecule test -s all
# ④ ansible-lint --profile=production
修完后我从 PR 自动接受改成 "PR 默认 + 人工 review"。
坑 3:Renovate 改了 Dockerfile FROM ubuntu:24.04 → ubuntu:24.10,但 Docker build 上下文是 ~/project,旧 layer 缓存被锁 12 小时
**症状**:Renovate 自动把 Dockerfile 的 FROM ubuntu:24.04 改成 ubuntu:24.10,CI 没问题。但我本地 docker build . 死活失败,提示 "layer not found"。
ERROR: failed to solve: ubuntu:24.10: failed to resolve source metadata
**根因**:Docker 24.x 默认 pull reference 缓存 12 小时。如果本地之前 pull 过 ubuntu:24.04 但 ubuntu:24.10 是新 base,会触发 "context deadline exceeded" 但不报错具体是哪个 layer。
修复 —— 在 renovate.json 加锁文件策略 + GitHub Actions 加 cache 失效步骤:
{
"packageRules": [
{
"matchDatasources": ["docker"],
"matchPackageNames": ["ubuntu"],
"pinDigests": true,
"matchUpdateTypes": ["major"],
"enabled": false # 永远不自动改 base OS 标签
}
]
}
并本地强制 docker builder prune -af:
docker builder prune -af
docker pull ubuntu:24.10
docker build --no-cache --pull -t my-app:latest .
坑 4:Terraform provider hashicorp/aws 5.x → 6.0 自动合并,state 文件不兼容生产炸
**症状**:Renovate 把 hashicorp/aws 从 5.99.0 升到 6.0.0,automerge 合并。结果第二天跑 terraform plan:
Error: Provider configuration not present
To work with aws_eip.hello this resource's provider configuration is missing.
根因:hashicorp/aws 6.x 不再支持 AWS SDK for Go v1,所有资源强制使用 v2 schema。state 没迁移就直接炸。
修复 —— 把 terraform provider 全部强制走手工 review,并加 migration CI 步骤:
{
"matchDatasources": ["terraform-provider"],
"matchPackageNames": ["hashicorp/aws", "hashicorp/vultr", "digitalocean/digitalocean"],
"automerge": false,
"labels": ["terraform-migration", "needs-review"]
}
并在 PR workflow 加 migration dry-run:
tofu init -upgrade
tofu plan -detailed-exitcode \
-var-file=environments/staging.tfvars \
-out=/tmp/migration.tfplan
tofu show -json /tmp/migration.tfplan | jq '.resource_changes[] | select(.change.actions == ["update"])' | head -20
任何 resource change.actions == ["update"] 都自动加 label breaking-change,强制人工 review。
坑 5:renovate.json 在仓库根触发 bootstrapping 死循环 —— Renovate 自己改自己的 renovate.json
**症状**:我把 Renovate self-hosted 启动后,第一次扫描 Renovate 自动开了个 PR 升级 actions/checkout@v4 → actions/checkout@v5,automerge merge 后 Renovate 重新启动读新版本。**新一轮扫描又开 PR 升级**......形成无限循环。
根因:Renovate 把 renovate.json 自身也当 package 扫描。
**修复** —— 用 ignorePaths + ignorePresets:
{
"ignorePaths": [
"renovate.json",
".github/renovate.json5",
"Dockerfile"
],
"customManagers": [],
"description": "Renovate 自己 + Dockerfile 永久不扫描"
}
或更彻底:用 renovate.json5 + extends: ["local>yaohehe/renovate-config:renovate.json5"],把规则提到外部 repo 不让 Renovate 自己管自己。
🛡️ 进阶配置
1. Molecule 测试矩阵压缩(3 OS × 2 Python = 6 个测试)
7/3 cron 的文章里我用了 2 OS × 1 Python = 2 个。本篇升级到 3 OS × 2 Python = 6 个矩阵:
- ubuntu-24.04 / Python 3.12 + 3.11
- ubuntu-22.04 / Python 3.11
- debian-12 / Python 3.11
跑完时间:6 个 job 完整 CI 22 分钟(带 cache)/ 38 分钟(不带 cache)。我用 actions/cache@v4 + pip cache key 命中后只要 18 分钟。
2. Slack 通知:Molecule 失败自动推 #infra-alerts
- name: Slack notification on failure
if: failure()
uses: slackapi/slack-github-action@v1.27.0
with:
payload: |
{
"text": "❌ Molecule failed: ${{ github.repository }} / ${{ github.event.pull_request.head.ref }}\n<${{ github.event.pull_request.html_url }}|View PR>"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
3. 自动 GitHub issue 当 dashboard
Renovate 的 dependencyDashboard 启用后,会自动维护一个 issue:
- [x] ansible 2.21.1 → 2.21.4 (patch)
- [ ] community.general 7.5.0 → 8.0.0 (major) — needs-review
- [ ] hashicorp/aws 5.99.0 → 6.0.0 (major) — needs-review
我每周一看这个 issue,对 major 升级人工 queue 到下周二处理。
📊 跑了一周的数据
| 资产类型 | PR 数(自动/手工) | Molecule 通过率 | 平均 review 时间 | 生产事故 |
|---|---|---|---|---|
| Ansible collection | 12 自动 / 3 手工 | 12/12 + 2/3 | 4 分钟 (automerge) | 0 |
| Terraform provider | 0 自动 / 4 手工 | 3/4 | 22 分钟 (人工) | 1 (已 rollback) |
| Docker base image | 8 自动 / 1 手工 | 8/8 + 1/1 | 6 分钟 | 0 |
| Python library | 5 自动 / 0 手工 | 5/5 | 3 分钟 | 0 |
| Ansible-core 2.x → 3.0 | 0 自动 / 1 手工 | - | n/a (未升级) | - |
节省时间:手工 review 4+22+6+0 = 32 分钟;automerge 跳过 28 个 PR,每小时按 30 分钟手工算 = 节约 ~14 小时/月。
生产事故:1 次(hashicorp/aws 6.x state 不兼容)。教训:所有 terraform provider 永远走人工 review。
✅ 5 步生产验证 checklist
- [ ] 1. Renovate self-hosted 健康(`docker compose logs renovate` 无 retry/warning)
- [ ] 2. renovate.json 写 `pinDigests: true` 给所有 docker base image
- [ ] 3. Molecule test 矩阵 ≥ 3 OS × 2 Python
- [ ] 4. terraform provider 全部走 `automerge: false` + label `terraform-migration`
- [ ] 5. dependencyDashboard issue 设置 weekly review reminder
🧰 工具栈速查
| 工具 | 版本(2026-07 验证) | 用途 |
|---|---|---|
| Renovate | 39.222.0 (Docker Hub) | 依赖升级 PR 自动化 |
| Ansible | ansible-core 2.17.7 / ansible 2.21.1 | 配置管理 |
| Molecule | 25.6.0 | Role 测试 |
| molecule-plugins | 25.6.0 | Docker driver |
| Terraform | 1.10.4 / OpenTofu 1.10.4 | IaC |
| Docker | 26.1.5 | 容器基础 |
| GitHub Actions | ubuntu-24.04 runner | CI |
| Python | 3.12.4 / 3.11.x | 测试运行时 |
| ansible-lint | 24.7.0 | Lint |
结语
Renovate + Molecule + Terraform 这条流水线的本质是:把"程序员每周 4 小时的手工升级"压缩成"每周 30 分钟的 review dashboard"。
代价是大量前期配置 + 至少 1 次生产事故的学费。我跑通后就再也不想回到手工 dep check 的年代了。
延伸阅读:
- 2026-07-16 Terraform 单 VPS IaC 实战(Terraform IaC 入门的姐妹篇)
- 2026-07-03 Ansible Molecule 25.x 实战(Molecule CI 详细拆解)
- 2026-06-25 Ansible 自托管实战 2026(Ansible 5 个真实坑)
⚠️ 重要:版本号会变
本篇所有版本号(Renovate 39.222.0 / Ansible 2.21.1 / Molecule 25.6.0 / Terraform 1.10.x)均在 2026-07-16 验证。请以官方页面为准:
👉 Join MiniMax Token Plan: AI coding acceleration for businesses
👉 Join Zhipu Coding Plan: GLM-4.6/GLM-5 coding packages, China-stable, pay-per-token unlimited
👉 Join Aliyun AI: Top AI products with exclusive coupons for business innovation
📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews
🔗 Recommended Tools
These are carefully selected tools. Using our affiliate links supports us to keep producing quality content: