1. drift 的产生成因
一句话总结: drift 是「云端现状偏离期望状态」;主要成因是手工改动、控制台操作、其他工具覆盖与资源被删除,检测的价值在于「发现偏离,而非消除所有偏离」。
Infrastructure drift(漂移)指:Terraform 声明的期望状态,与实际运行环境不一致。Terraform 的 refresh 阶段每次都会对比 state 与真实云资源,凡是「配置里没有、云上却有」或「配置写了、云上被改」的差异,都算漂移。
期望状态(代码+state) 实际状态(云端) drift
instance_type = t3.micro instance_type = t3.large ✗ 属性被改
configmap 定义了 3 个 key 只剩 1 个 key ✗ 被删
安全组放行 80/443 多放行了 22 ✗ 被加
整个 EC2 不存在 存在(手工建的) ✗ 游离资源
1.1 六大成因
| 成因 | 典型场景 |
|---|---|
| 控制台手工操作 | 运维直接在 AWS 控制台改安全组 |
| 应急热修 | 线上紧急 scale、改端口后没走 IaC |
| 其他工具覆盖 | Ansible/脚本/其他 IaC 改同一资源 |
| 资源被删 | 手工删除实例、桶、数据库 |
| 系统异步变化 | 自动扩缩容、自动清理任务 |
| 上游变更 | 依赖的资源被他人重建导致引用失效 |
1.2 漂移的两面性
漂移是「现状与声明的差」。它不全是坏事——紧急修复可能是合理的;但未被记录的漂移意味着「你以为的配置不是实际配置」,这正是事故与审计风险的温床。
2. plan 差异审计
一句话总结:
terraform plan是漂移的第一道探测器,refresh 对比后把差异输出为计划;审计 plan 是每个 CI 流水线与周期扫描的标配动作。
2.1 plan 是天然的差异报告
# 默认就刷新并对比,输出差异
terraform plan
# 只看刷新与差异,不进入计划生成细节
terraform plan -refresh-only
# 差异审计退出码:0=无差异 1=有差异 2=错误
terraform plan -detailed-exitcode
echo $?
2.2 用退出码做门禁
在 CI 里用 -detailed-exitcode 判断是否有漂移,把结果变成「可自动判定的信号」。
#!/bin/bash
set -e
terraform init -backend-config=$ENV.backend.tfvars
# 只做差异审计,不 apply
terraform plan -detailed-exitcode -out=plan.bin
exitcode=$?
if [ $exitcode -eq 1 ]; then
echo "::warning::检测到配置差异(可能为漂移),请人工确认"
exit 0 # 审计阶段不阻断,仅告警
fi
2.3 plan 差异的分类
| 差异类型 | 是否漂移 | 处理 |
|---|---|---|
| 配置修改引起 | 否(期望变更) | 正常 apply |
| state 与实际不符 | 是 | refresh 后判断 |
| 云上多出资源 | 是 | 决定纳入 or 删除 |
| 云上资源缺失 | 是 | 决定重建 or 接受 |
一句话:plan 输出的是「代码意图 vs 现状」,审计的人要能区分「这是我的改动」和「这是别人/系统造成的漂移」。
3. driftctl 扫描工具
一句话总结: driftctl 是独立于 Terraform 的漂移扫描器,直接对云端资源与 IaC 代码做覆盖比对,能发现「state 之外的游离资源」。
driftctl(现已并入 CloudSkiff/Scanner)不依赖 state 文件,它扫描云端真实资源,与 Terraform 代码声明做覆盖比对,找出未被代码管理的资源。
3.1 基本用法
# 扫描当前配置声明的资源是否与云端一致
driftctl scan
# 指定云平台 profile 与区域
driftctl scan --from aws+tf --region ap-northeast-1
# 只输出 JSON 报告
driftctl scan --output json://- > drift.json
3.2 典型输出解读
Found 3 resource(s)
- 2 managed by IaC (Terraform)
- 1 not managed by Terraform
1 resource(s) drifted
Unmanaged resources:
- aws_security_group sg-xxxx (created out of band)
Unmanaged(游离)资源就是 driftctl 的独有价值:它们连 state 里都没有,是纯手工或旁路创建的。
3.3 driftctl 与 plan 的互补
| 工具 | 视角 | 发现 |
|---|---|---|
| terraform plan | state↔云端 | state 内资源的属性漂移 |
| driftctl | 代码↔云端 | 未纳管的游离资源 |
| 两者结合 | 全量 | 属性漂移 + 游离资源全覆盖 |
4. 自动化收敛
一句话总结: 收敛是「把漂移拉回期望状态」,推荐「计划驱动 + 审批」的自动修复,避免直接
apply造成不可预期变更。
4.1 收敛的三种策略
| 策略 | 做法 | 适用 |
|---|---|---|
| 手动修复 | 人工改回/重跑 apply | 低频、高风险资源 |
| 自动收敛 | CI 定时 plan→apply | 可重建、低风险资源 |
| 纳入管理 | 把游离资源 import 进 state | 合理资源,转为受管 |
4.2 自动化收敛流水线
# 伪代码:定时漂移检测与收敛
schedule: "0 3 * * *" # 每天凌晨
steps:
- terraform plan -detailed-exitcode
- if drift: 生成差异报告并通知
- approve(人工/自动门禁)
- terraform apply
- 复核 plan 无新漂移
4.3 收敛后再漂移的防护
- 锁定控制台权限:高危资源禁止控制台直改,走 IaC。
- 变更即记录:所有变更入口收敛到 PR + CI。
- 约束工具链:其他工具只准读,不准写受管资源。
- 标签策略:受管资源打
managed-by: terraform标签,供扫描器识别。
5. 合规与审计
一句话总结: 漂移直接破坏合规审计——审计要的是「声明即实际」,漂移让「配置=实际」的承诺失效;因此把漂移检测接入合规门禁是治理刚需。
5.1 漂移对合规的冲击
| 合规要求 | 漂移的影响 |
|---|---|
| 配置即真相 | 实际与配置不一致,审计无法信任何一方 |
| 安全基线 | 安全组被改、加密被关,基线失效 |
| 变更可追溯 | 手工热修没有变更记录 |
| 数据保留 | 备份策略被改,留存违规 |
5.2 把漂移接入合规报告
#!/bin/bash
# 合规扫描:任何漂移都生成违规记录
terraform plan -detailed-exitcode >/tmp/plan.txt
if [ $? -eq 1 ]; then
echo "DRIFT detected at $(date)" >> compliance-drift.log
cat /tmp/plan.txt
exit 1 # 有漂移即不合规
fi
5.3 策略即代码
配合 OPA/Conftest,把「禁止未纳管资源」「安全组不得放行 0.0.0.0/0」等规则写进策略,扫描结果自动判合规。
# policy/drift.rego
package main
deny[msg] {
unmanaged := input.unmanaged[_]
msg := sprintf("未纳管资源: %v", [unmanaged.id])
}
6. 定期漂移扫描体系设计
一句话总结: 一套漂移体系 = 定期扫描 + 差异分级 + 通知分流 + 收敛与豁免,扫描频率按资源风险分级,不搞一刀切。
6.1 分级扫描频率
| 资源风险 | 扫描频率 | 收敛方式 |
|---|---|---|
| 安全敏感(IAM/SG/加密) | 每次 PR + 每日 | 阻断式合规 |
| 业务核心(数据库/计算) | 每日 | 告警 + 人工审批 |
| 低风险(标签/普通资源) | 每周 | 定期批量收敛 |
6.2 扫描到收敛的闭环
定时扫描 → 差异报告 → 分级告警 → 人/自动决策 → 收敛 apply → 复扫确认
6.3 定时任务示例
扫描与收敛用 cron 或 CI 定时器驱动,频率与收敛策略按环境配置:
# crontab:每天 03:00 全量扫描,每周一 04:00 收敛低风险环境
0 3 * * * /usr/local/bin/terraform-drift-scan --env prod
0 4 * * 1 /usr/local/bin/terraform-converge-lowrisk --env dev
6.4 豁免与噪音管理
- 已知的合理漂移(自动扩缩容)用豁免列表排除。
- 告警按影响面分级,避免「狼来了」导致告警疲劳。
- 每次收敛后记录原因,形成漂移趋势报表。
7. 常见坑
一句话总结: 漂移治理的坑集中在误删真实资源、自动收敛扩大事故、扫描噪音与豁免滥用,核心是「收敛要有护栏,扫描要有区分」。
| 坑 | 现象 | 正确做法 |
|---|---|---|
| apply 自动收敛误删 | 手动热修的资源被删 | 收敛前 diff 评审 + 备份 |
| plan 噪音 | 每次都有无关差异 | 配置 -refresh-only 单独看漂移 |
| 豁免滥用 | 漂移被大面积豁免 | 豁免必须带理由与时限 |
| driftctl 权限过大 | 只读扫描给了写权限 | 扫描用只读 credential |
| 收敛竞态 | 扫描与人工修改撞车 | 加锁 + 非高峰窗口 |
7.1 安全收敛模板
#!/bin/bash
set -e
# 只对允许自动收敛的目录执行
cd "$ENV_DIR"
terraform plan -detailed-exitcode -out=plan.bin || exitcode=$?
# 有差异且非豁免 → 先记录再走审批
if [ "${exitcode:-0}" -eq 1 ]; then
echo "drift: $(date) $ENV_DIR" >> /var/log/drift.log
if grep -q "$ENV_DIR" /etc/terraform/auto-approve.list; then
terraform apply -auto-approve plan.bin
else
echo "需人工审批,跳过收敛"
exit 1
fi
fi
8. 总结
漂移检测与收敛,把「声明式 IaC」的信任闭环补完:
| 环节 | 要点 |
|---|---|
| 成因 | 手工、热修、其他工具、删除、异步变化 |
| 检测 | plan 差异 + 退出码门禁 |
| 游离资源 | driftctl 扫描「未纳管」资源 |
| 收敛 | 计划驱动 + 审批,避免裸 apply |
| 合规 | 漂移即不合规,策略即代码 |
| 体系 | 分级扫描 + 通知 + 豁免 |
| 坑 | 收敛护栏、只读扫描、豁免治理 |
一句话收尾:漂移治理的目标不是「零漂移」,而是「每个漂移都被看见、被决策、被记录」。把检测、收敛、合规与豁免串成闭环,IaC 的「声明即实际」承诺才能真正兑现。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。