CI/CD 与工具链
GitLab CI、Terragrunt。
CI/CD 与工具链
这一课你将能做到
- 设计一条包含校验、计划、评审、应用四阶段的 Terraform 流水线
- 用 OIDC 让 GitLab/GitHub 无需保存 AWS 密钥即可 AssumeRole 部署
- 说出
terraform test、Terragrunt、Atlantis 各自解决什么问题、什么时候引入
为什么要学
前 10 课的命令都在你自己电脑上跑。一旦进入团队,「某个人本机 apply」就是生产事故的温床:凭证散落在个人机器、没有评审记录、谁改了什么不可追溯。正确姿势是把 Terraform 操作收进 CI/CD:代码合并触发自动 plan,人工在合并请求里评审,apply 由有权限的流水线在手动门禁后执行,全过程留痕。
本课还解决一个反复出现的安全痛点:CI 怎么连云?把 AK/SK 存进 CI Secret 仍是长期密钥,泄露面大。AWS 与主流代码平台支持 OIDC 联邦身份,流水线用短命的临时凭证工作,本机零静态密钥。本站的基础设施仓库就运行在这套模型上。
先建立图像
一条标准流水线:
push / MR
│
▼
┌──────────┐ fmt 检查 + validate + 安全扫描(第 12 课)
│ verify │
│
▼
┌──────────┐ init -input=false + plan -out=tfplan(计划作为产物传递)
│ plan │ plan 结果贴到 MR 评论,等人评审
│
▼ 手动点击(main 分支 / protected tag)
┌──────────┐
│ apply │ apply tfplan ← 保证执行的就是评审过的计划
│
▼
云资源变更完成,输出可供后续部署阶段消费
身份链路:
GitLab Job ─签名 JWT(OIDC)─▶ AWS IAM Identity Provider
│ 校验 audience/subject
▼
AssumeRole → 临时 AK/Token(最长 1 小时)
跟着做
步骤 1:在 AWS 配置 OIDC 信任
先在 AWS IAM 新建 Identity Provider,Provider URL 填你的 GitLab 地址(如 https://gitlab.com),Audience 填 https://gitlab.com。再建部署角色,信任关系(trust policy)示例:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/gitlab.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"gitlab.com:aud": "https://gitlab.com"
},
"StringLike": {
"gitlab.com:sub": "project:your-group/coderoadmap-infra:ref_type:branch:ref:main"
}
}
}
]
}
sub 条件把角色锁死到「指定仓库的 main 分支流水线」,这是防止仓库内任意流水线冒用角色的关键。角色权限按最小集合授予(可用托管的 job 函数,apply 与 plan 拆两个角色更严格)。
步骤 2:编写 GitLab CI 流水线
在基础设施仓库根目录创建 .gitlab-ci.yml:
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
TF_IN_AUTOMATION: "true"
AWS_REGION: "ap-northeast-1"
TF_ROOT: "${CI_PROJECT_DIR}"
stages: [verify, plan, apply]
image: hashicorp/terraform:1.7
fmt:validate:
stage: verify
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com
script:
- terraform fmt -check -recursive
- terraform init -backend=false
- terraform validate
plan:dev:
stage: plan
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com
script:
- |
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \
$(aws sts assume-role-with-web-identity
--role-arn arn:aws:iam::123456789012:role/gitlab-tf-dev
--web-identity-token $GITLAB_OIDC_TOKEN
--role-session-name "gitlab-${CI_JOB_ID}"
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text))
- terraform init -input=false
- terraform plan -input=false -out=tfplan -var-file=dev.tfvars
- terraform show tfplan
artifacts:
paths: [tfplan]
expire_in: 7 days
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
apply:dev:
stage: apply
needs: [plan:dev]
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com
script:
- # 同样的 assume-role-with-web-identity 导出临时凭证
- terraform init -input=false
- terraform apply -input=false tfplan
when: manual
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
environment:
name: dev