基础设施即代码自动化:Terraform/CloudFormation 与 Atlantis

深度解析基础设施即代码在 GitHub Actions 上的自动化流水线,涵盖 Terraform fmt/validate/plan/apply 全流程、远程状态与锁、plan 审阅门禁、Atlantis 机器人集成、CloudFormation 替代方案、审批与回滚策略,以及基于 OIDC 临时凭证的安全加固。

基础设施变更比应用代码变更的风险更高——一次错误的 terraform apply 可能销毁生产数据库。IaC 自动化因此在「自动化」之上叠加了「审阅与门禁」:用 CI 执行 fmt/validate/plan,把 plan 结果贴回 PR 供人审阅,通过后再由受控流程执行 apply。本文以 Terraform 为主线,覆盖远程状态、Atlantis、CloudFormation 与 OIDC 安全,构建可审计的基础设施变更流水线。


一、Terraform 工作流概览

1.1 标准生命周期

代码提交 → format 校验 → validate 语法 → plan 变更预览
    → 人工审阅 plan → apply 执行变更 → 状态回写远程存储
阶段命令是否修改基础设施是否可安全反复执行
格式化terraform fmt -check❌✅
语法校验terraform validate❌✅
变更预览terraform plan❌✅
执行变更terraform apply✅⚠️ 需门禁
销毁terraform destroy✅❌ 极高风险

1.2 分层环境策略

├── environments/
│   ├── dev/        # 每次 PR 自动 plan + apply
│   ├── staging/    # main 分支 plan,审批后 apply
│   └── prod/       # Release/tag 触发,多层审批 apply

二、fmt / validate / plan 自动门禁

2.1 完整 CI 配置

name: Terraform CI
on:
  pull_request:
    paths:
      - 'infra/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: infra/prod
    permissions:
      contents: read
      pull-requests: write      # 用于写 PR 评论
      id-token: write           # OIDC 云认证
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.9.0
          terraform_wrapper: true     # 开启 wrapper 以便捕获 plan 输出

      - name: Terraform fmt
        run: terraform fmt -check -recursive

      - name: Terraform Init
        run: terraform init

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color
        continue-on-error: true       # plan 失败不阻断,便于在评论中展示

      - name: Post plan to PR
        uses: actions/github-script@v7
        env:
          PLAN: "${{ steps.plan.outputs.stdout }}"
        with:
          script: |
            const plan = process.env.PLAN || '';
            const body = `#### Terraform Plan 📖\n\`\`\`hcl\n${plan}\n\`\`\``;
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body
            });

一句话:terraform_wrapper: true 让 plan 的 stdout 可以作为 step 输出被捕获,再通过 github-script 以评论形式贴回 PR——「plan 进 PR」是 IaC 审阅的起点。

2.2 plan 失败的处理

      - name: Plan status
        if: steps.plan.outputs.exitcode != 0
        run: |
          echo "::error::Terraform plan 存在错误,请检查评论内容"
          exit 1

三、远程状态与锁

3.1 为什么需要远程状态

Terraform 将「期望状态 vs 实际资源」的映射保存在 state 文件中。本地 state 在多人与 CI 场景下必然冲突——远程 state 是团队协作的前提。

3.2 S3 + DynamoDB 后端

# infra/prod/backend.tf
terraform {
  backend "s3" {
    bucket         = "my-org-tfstate"
    key            = "prod/terraform.tfstate"
    region         = "ap-southeast-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"   # 锁表,防止并发 apply
  }
}

3.3 锁与并发

场景无锁有锁(DynamoDB)
两个 CI 并发 apply状态损坏、资源覆盖后到者等待/失败
plan 与 apply 冲突读到过期状态锁保护
人机并发同上同上

CI 中通过 concurrency 在 workflow 层再加一道防线:

concurrency:
  group: terraform-prod-apply
  cancel-in-progress: false

一句话:S3 存状态、DynamoDB 加锁、workflow concurrency 串行化——三层并发防线让「多人 + CI + 手动」同时操作也不会破坏状态。


四、plan 审阅门禁

4.1 把审阅做成流程

仅把 plan 贴在 PR 评论区还不够,必须让「审阅通过」成为 apply 的前置条件。推荐两种模式:

模式机制优点缺点
环境审批门禁apply job 挂 environment,required reviewers 审批原生、可审计审批粒度是整个 job
分支保护 + 评论确认要求 terraform plan 检查通过 + 显式 /apply 评论灵活需 Atlantis 或自定义脚本

4.2 环境门禁示例

jobs:
  apply:
    runs-on: ubuntu-latest
    environment:
      name: production-tf
    concurrency:
      group: tf-apply-prod
      cancel-in-progress: false
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.9.0
      - name: Terraform Init & Plan
        run: |
          terraform init
          terraform plan -out=tfplan
      - name: Terraform Apply
        run: terraform apply tfplan -auto-approve

4.3 apply 使用 plan 文件

-out=tfplan 是审阅门禁的关键配套:apply 的不是「重新计算的最新计划」,而是审阅过的那一份,避免「审阅后到 apply 之间状态又变化」造成偏差。

terraform plan -out=tfplan      # 生成可审阅计划
terraform apply tfplan           # 只执行该计划

五、Atlantis 机器人集成

5.1 Atlantis 是什么

Atlantis 是专为 Terraform 设计的 GitHub 机器人:监听 PR 评论,将 /atlantis plan、/atlantis apply 转换为真实执行,并把结果回贴到 PR。它天然把「审阅」与「执行」绑定在代码评审上下文里。

5.2 自托管 Atlantis + GitHub Actions 的两种模式

模式说明适用
独立 Atlantis 服务自托管服务器 + webhook,独立于 Actions团队已有 Atlantis 基础设施
Actions 模拟 plan 评论用 Actions 执行 plan,用评论实现类 Atlantis 交互不想引入额外服务

5.3 atlantis.yaml 配置

# atlantis.yaml(仓库根目录)
version: 3
projects:
  - dir: infra/dev
    workspace: default
    autoplan:
      enabled: true            # PR 自动 plan
  - dir: infra/prod
    workspace: default
    autoplan:
      enabled: true
    apply_requirements:
      - approved              # 需要 PR approval 才能 apply
      - mergeable             # 需要 PR 可合并

5.4 交互流程

开发者提交 PR → Atlantis 自动执行 plan 并评论
开发者评论 /atlantis plan → 重新 plan
审阅者 Approve PR
开发者评论 /atlantis apply → 执行 apply,评论结果

一句话:Atlantis 把「plan/apply」变成 PR 评论级的 ChatOps——apply_requirements: [approved, mergeable] 保证只有通过审阅且可合并的 PR 才能动基础设施。


六、CloudFormation 替代方案

6.1 CloudFormation + GitHub Actions

AWS 原生的 IaC 方案不需要独立 state 管理,直接用 Actions 编排:

name: CloudFormation Deploy
on:
  push:
    branches: [main]
    paths: ['cloudformation/**']

jobs:
  deploy-cfn:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/CFNDeployRole
          aws-region: ap-southeast-1

      - name: Validate template
        run: |
          aws cloudformation validate-template \
            --template-body file://cloudformation/app.yaml

      - name: Deploy stack
        uses: aws-actions/aws-cloudformation-github-deploy@v1
        with:
          name: my-app-stack
          template: cloudformation/app.yaml
          capabilities: CAPABILITY_NAMED_IAM
          no-fail-on-empty-changeset: "1"

6.2 Terraform vs CloudFormation 选型

维度TerraformCloudFormation
云厂商多云(AWS/Azure/GCP)仅 AWS
状态管理自管(S3/锁)托管(ChangeSet)
审阅体验plan 输出友好ChangeSet 查看需控制台
生态巨大 Provider 生态AWS 原生函数完整
团队技能需学 HCL需学 YAML/JSON + 伪参数

6.3 ChangeSet 审阅门禁

CloudFormation 对应「审阅计划」的概念是 ChangeSet:先生成、审阅通过后再执行:

      - name: Create ChangeSet
        run: |
          aws cloudformation create-change-set \
            --stack-name my-app \
            --change-set-name review-$(date +%s) \
            --template-body file://cloudformation/app.yaml

七、审批与回滚

7.1 回滚策略

层级方式适用
Terraform 状态恢复旧 state + apply可逆资源变更
镜像/代码重新部署上一个版本应用层问题
数据库手动还原(需 DBA)数据迁移出错

7.2 用 tag 触发可回滚的发布

on:
  push:
    tags: ['release-*']

jobs:
  tf-apply:
    runs-on: ubuntu-latest
    environment: production-tf
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.ref }}       # 精确 checkout tag 对应代码
      - uses: hashicorp/setup-terraform@v3
      - run: |
          terraform init
          terraform plan -out=tfplan
          terraform apply tfplan -auto-approve

一句话:每次发布对应一个不可变 tag + 一个审阅过的 plan 文件——需要回滚时 checkout 旧 tag 重新 apply,即「基础设施版本化」。

7.3 危险操作双人确认

      - name: Require explicit confirm
        run: |
          echo "确认销毁/重建操作?请在下游审批后再执行"

八、OIDC 临时凭证安全

8.1 为什么 IaC 最需要 OIDC

基础设施权限极大(可删库、可改网络),把 AWS Access Key 存为长期 Secrets 的风险不可接受。OIDC 让 workflow 通过短期 JWT 换取云凭证,凭证几分钟即过期。

permissions:
  id-token: write
  contents: read

steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/GHATerraformRole
      aws-region: ap-southeast-1
      role-duration-seconds: 900

8.2 IAM 信任策略收紧到 repo/分支

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:my-org/infra-repo:ref:refs/heads/main"
        }
      }
    }
  ]
}

8.3 安全对比

凭证方案生命周期泄露影响推荐度
IAM 用户 Access Key长期极大(可删基础设施)❌
Secrets 存 AK/SK长期极大❌
OIDC Role15 分钟有限(单次会话)✅✅

8.4 IaC 安全清单

  • 远程 state 加密 + 开启锁表
  • 所有云凭证走 OIDC 短期凭证
  • IAM trust policy 限定到 refs/heads/main
  • plan 产物必须经人审阅后才能 apply
  • apply job 挂 environment + required reviewers
  • 危险操作(destroy)单独高权限环境
  • 生产 apply 设置 concurrency 串行化
  • 保留完整 plan/apply 审计日志

总结

IaC 自动化是对「自动化」与「审阅」的平衡艺术。

环节手段保障
质量门禁fmt + validate语法与格式正确
变更预览plan → PR 评论变更可见可审
执行门禁environment + required reviewers审批后执行
状态安全S3 + DynamoDB 锁 + concurrency无并发破坏
凭证安全OIDC + 细粒度 trust policy短期、最小权限
回滚能力tag + 审阅过的 plan 文件可版本化回退

一句话:让机器做 plan 把变更讲清楚,让人做 approve 把风险关住,让 apply 只执行审阅过的那一份计划——这就是基础设施变更的完整治理闭环。


延伸阅读:

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「github-actions」更多文章

  1. 移动端 CI/CD:Flutter/iOS/Android 构建与签名
  2. 环境保护与部署门禁:环境规则、审批与 CD 流程
  3. 工作流安全加固与供应链防御