Terraform+GitLab CI/CD构建生产级云基础设施流水线 1. 项目概述这不是在搭云是在建一套可审计、可回滚、能扛住半夜三点告警的数字基建“Building A Robust and Efficient AWS Cloud Infrastructure with Terraform and GitLab CI/CD”——光看标题你可能以为这是又一篇讲“怎么用Terraform写几行代码起台EC2”的入门教程。但实际干过三年以上云平台建设的人一眼就明白这根本不是在部署资源而是在构建一套具备工业级韧性的数字基础设施交付流水线。核心关键词——Robust健壮、Efficient高效、Terraform声明式IaC、GitLab CI/CD自动化闭环——每一个词背后都对应着真实生产环境里踩过的坑、熬过的夜、被叫醒三次的凌晨告警。我带团队落地过7个中大型企业级AWS云平台从金融客户要求的“变更必须留痕双人复核5分钟内回滚”到电商大促前“自动扩缩容策略需通过压力测试验证后才允许合并”再到SaaS厂商“每个环境dev/staging/prod必须完全隔离且配置差异不可硬编码”——这些都不是文档里的漂亮话而是Terraform模块设计、GitLab CI阶段划分、Pipeline变量注入策略必须直面的约束条件。这篇文章不讲“Terraform基础语法”也不教“GitLab Runner怎么装”它聚焦于如何让每次git push真正成为一次可信、可控、可追溯的基础设施变更事件。适合正在从手动管理EC2跳转到IaC实践的运维工程师、负责云平台稳定性的SRE、以及需要向合规部门证明“我们真没偷偷改生产配置”的技术负责人。如果你还在用本地terminal敲terraform apply或者CI里把aws_access_key写进.gitlab-ci.yml那这篇就是为你写的止损指南。2. 整体架构设计与核心逻辑拆解为什么必须是TerraformGitLab CI/CD这个组合2.1 不是“能用就行”而是“必须解决这五个生产级死穴”很多团队尝试IaC时第一反应是“找个工具把脚本化”。但真实生产环境会立刻用五个问题打脸死穴1配置漂移Configuration Drift运维半夜手动改了个安全组规则救火没人记录两周后Terraform plan显示“要删掉这个规则”但业务方说“那个端口必须开”。——Terraform本身不解决漂移只有强制所有变更必须经由Pipeline触发状态文件只读权限每次apply前自动plan diff比对才能切断漂移源头。死穴2环境一致性失控dev环境用t3.microstaging用m5.largeprod却用c5.4xlarge——表面是成本考量实则是镜像版本、内核参数、监控埋点全部错位。Terraform模块化设计必须将环境差异抽象为变量如instance_type var.env prod ? c5.4xlarge : t3.micro而非复制粘贴代码GitLab CI通过CI_ENVIRONMENT_NAME环境变量注入确保同一份代码在不同环境跑出确定性结果。死穴3权限爆炸与密钥泄露把AWS密钥写进CI配置等于把公司保险柜钥匙钉在公告栏上。GitLab CI的Protected Variables Masked Values 变量作用域绑定到Protected Branches配合Terraform的aws provider使用IAM Role Assume而非Access Key才是唯一合规路径。我们曾因一个未Mask的TF_VAR_db_password导致数据库密码泄露后续所有密码类变量强制走GitLab CI的CI_JOB_TOKEN调用HashiCorp Vault API动态获取。死穴4变更不可追溯与责任模糊“谁在周三下午改了RDS参数”——查Git日志只能看到“update terraform config”看不到具体改了哪个参数、为什么改、是否经过评审。解决方案是GitLab Merge Request模板强制填写变更原因影响范围回滚步骤CI Pipeline在apply成功后自动提交state file的SHA256摘要到专用audit分支每次apply日志存档至S3并打上MR ID标签。现在审计组要查某次变更30秒内给出完整证据链。死穴5故障恢复时间MTTR超标生产环境挂了手动执行terraform destroy再apply等你敲完命令业务损失已超阈值。我们的方案是GitLab CI预置“emergency rollback”作业仅对特定MR标签如rollback-to-v1.2.3触发自动拉取指定版本state file并执行plan/apply全程90秒。去年黑色星期五支付网关因新版本API Gateway配置错误超时靠这个功能3分钟切回旧版业务零感知。提示Terraform alone ≠ IaC成熟度。真正的健壮性来自Terraform描述“要什么” GitLab CI控制“什么时候、谁、以什么权限做” AWS IAM定义“能做什么”三者的刚性咬合。少一环就是纸糊的防线。2.2 为什么不是GitHub Actions或JenkinsGitLab CI的不可替代性选型时我们对比过GitHub Actions、Jenkins、GitLab CI最终锁定GitLab的核心原因是原生深度集成与权限模型Merge Request驱动的PipelineGitHub Actions的workflow触发依赖branch push而GitLab CI天然绑定MR生命周期。这意味着plan作业可在MR创建时自动运行并评论diff结果apply作业仅在MR被批准且目标分支为main时触发destroy作业甚至可以设置为“仅允许Owner批准的MR”。这种基于代码评审流的控制是其他工具靠YAML hack无法实现的。Protected Branches Protected Variables的原子性GitLab允许将变量如AWS_ROLE_ARN的作用域精确绑定到main分支且该变量在非protected分支的Pipeline中根本不可见。而Jenkins的凭据插件需要额外配置Job DSLGitHub Actions的secrets在fork PR中默认不可用——但我们的场景要求staging环境的密钥绝不能在dev分支Pipeline中泄露GitLab的权限模型直接满足。Runner分组与标签调度的确定性我们为不同环境部署专用Runner组aws-prod-runnerEC2实例安装Terraform v1.5.7AWS CLI v2.13.0、aws-staging-runnerSpot实例Terraform v1.4.6。Pipeline中通过tags: [aws-prod-runner]硬性指定避免因Runner混用导致Terraform版本不一致引发state corruption。实测下来这种隔离使跨环境部署失败率从12%降至0.3%。内置Container Registry与Artifact缓存Terraform模块常依赖外部provider如hashicorp/aws每次Pipeline拉取耗时且不稳定。GitLab CI支持cache: {key: $CI_COMMIT_REF_SLUG, paths: [.terraform]}结合.terraform.lock.hcl校验首次运行后后续Pipeline平均提速68%。更关键的是我们把自研的Terraform Validator镜像推送到GitLab Container RegistryPipeline中直接image: registry.gitlab.com/our-org/validator:1.2无需每次编译。2.3 架构全景图三层防御体系整个系统不是线性流程而是三层嵌套的防御体系┌─────────────────────────────────────────────────────────────┐ │ 第一层代码即契约Code as Contract │ │ • Terraform模块严格遵循单一职责vpc/ecs/rds各成独立模块 │ │ • 所有输入变量带descriptiondefaultvalidation如cidr_block必须匹配^10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.│ │ • 输出变量强制命名规范output vpc_id { value aws_vpc.main.id } │ └─────────────────────────────────────────────────────────────┘ ↓ 触发条件MR合并到protected branch ┌─────────────────────────────────────────────────────────────┐ │ 第二层流水线即守门员Pipeline as Gatekeeper│ │ • stage: validate → 检查HCL语法变量约束模块调用合法性 │ │ • stage: plan → 对prod环境执行terraform plan -outtfplan -var-fileprod.tfvars │ │ • stage: apply → 仅当MR含[APPLY]标签且由SRE组成员批准才执行 │ │ • stage: audit → 自动归档plan输出、state file摘要、MR元数据到S3 │ └─────────────────────────────────────────────────────────────┘ ↓ 执行结果AWS资源状态变更 ┌─────────────────────────────────────────────────────────────┐ │ 第三层云平台即仪表盘Cloud as Dashboard │ │ • 所有Terraform state file加密存储于S3KMSbucket policy禁止delete │ │ • CloudWatch告警自动订阅state file S3事件异常修改实时通知Slack │ │ • 自研Dashboard展示各环境last applied time、MR关联ID、资源健康度 │ └─────────────────────────────────────────────────────────────┘这个设计让“基础设施即代码”从口号变成肌肉记忆开发提交代码时心里清楚“这次MR不只是改应用更是动了VPC路由表”SRE收到告警时第一反应是“去GitLab看MR详情而不是登录AWS Console”。3. 核心细节解析与实操要点Terraform模块设计与GitLab CI配置的魔鬼细节3.1 Terraform模块设计拒绝“万能模块”拥抱“场景化封装”很多团队失败的起点是试图写一个“能起任何资源”的超级模块。我们吃过亏一个aws-infra模块包含50变量其中30个永远用不到enable_nat_gateway和enable_transit_gateway开关互相冲突导致staging环境误启了跨区域TG。现在的原则是每个模块只解决一个明确场景且变量数量≤7个。以最常被滥用的VPC模块为例我们拆分为三个独立模块vpc-base仅创建VPC、IGW、默认路由表、基础标签。变量仅3个name,cidr_block,tags。vpc-public-subnets在指定AZ创建公有子网、NAT网关按需、弹性IP。变量4个vpc_id,azs,subnet_cidrs,create_nat。vpc-private-subnets创建私有子网、路由表、路由指向NAT或TG。变量3个vpc_id,azs,subnet_cidrs。这样做的好处是组合自由prod环境用vpc-basevpc-public-subnetsvpc-private-subnetsstaging环境可禁用NAT设create_nat false节省$23/月安全审计时只需检查vpc-public-subnets模块是否启用NAT不用在500行HCL里grep。实操心得模块的variables.tf必须包含validation块。例如vpc-base中对cidr_block的校验variable cidr_block { description VPC CIDR block (e.g., 10.0.0.0/16) type string validation { condition can(regex(^10\\.|^172\\.(1[6-9]|2[0-9]|3[0-1])\\.|^192\\.168\\., var.cidr_block)) error_message CIDR block must be in RFC 1918 private address space. } }这个正则表达式我们反复调试了11次——第一次漏了172.31.0.0/16导致staging环境创建失败第二次没转义.直接报错。现在它成了所有网络模块的标配。3.2 GitLab CI配置从.gitlab-ci.yml到生产级Pipeline的进化路径我们的.gitlab-ci.yml不是一蹴而就而是经历三个阶段迭代阶段1踩坑期简单四步流水线stages: - validate - plan - apply - notify validate: stage: validate script: - terraform init - terraform validate plan: stage: plan script: - terraform init - terraform plan -outtfplan→ 问题plan作业没有区分环境dev/staging/prod全用同一份tfvarsapply无审批机制MR一合并就执行。阶段2管控期环境隔离MR约束variables: TF_ROOT: environments/${CI_ENVIRONMENT_NAME} plan-prod: stage: plan script: - cd $TF_ROOT terraform init -backend-configkeyprod/terraform.tfstate - cd $TF_ROOT terraform plan -outtfplan -var-fileprod.tfvars rules: - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME main $CI_PIPELINE_SOURCE merge_request_event→ 进步用CI_ENVIRONMENT_NAME动态切换目录用rules限制仅MR事件触发。但仍有漏洞prod.tfvars明文存于仓库且apply仍无双人确认。阶段3生产级零信任Pipelinestages: - validate - plan - security-scan - apply - audit # 关键改进点 # 1. 变量全部从GitLab CI Variables注入绝不存文件 # 2. apply作业需双重确认MR标签 MR批准者组 # 3. 安全扫描集成Checkov security-scan: stage: security-scan image: bridgecrew/checkov:latest script: - checkov -d environments/ --framework terraform --quiet --compact allow_failure: false apply-prod: stage: apply script: - cd environments/prod terraform init -backend-configkeyprod/terraform.tfstate - cd environments/prod terraform apply -auto-approve tfplan rules: - if: $CI_MERGE_REQUEST_LABELS ~ /APPLY/ $CI_MERGE_REQUEST_APPROVED true $CI_MERGE_REQUEST_APPROVER_USERNAME in [sre-lead, cloud-architect] environment: production→ 现在apply-prod作业只有同时满足三个条件才执行MR打了APPLY标签、MR已被批准、批准者是SRE组指定成员。去年审计时这个配置让“未授权变更”项直接得满分。注意terraform apply -auto-approve tfplan中的tfplan文件必须由同一Pipeline的plan作业生成。我们曾因在不同Runner上执行plan/apply导致state file版本不一致apply时提示“state is out of date”。解决方案是所有作业共享同一Runner组并用artifacts: [tfplan]传递plan文件。3.3 状态文件State管理S3DynamoDB锁的实战配置Terraform state是IaC的心脏也是最易出事的地方。我们采用AWS官方推荐的S3DynamoDB方案但配置细节决定成败S3 Backend配置environments/prod/backend.tfterraform { backend s3 { bucket our-org-tfstate-prod key prod/terraform.tfstate region us-east-1 encrypt true dynamodb_table terraform-state-lock-prod # 关键强制使用KMS密钥而非S3 SSE-S3 kms_key_id arn:aws:kms:us-east-1:123456789012:key/abcd1234-5678-90ab-cdef-1234567890ab } }→ 为什么用KMS而非SSE-S3因为SSE-S3密钥由AWS托管无法审计谁在何时解密了stateKMS密钥可配置CloudTrail日志每次terraform state pull都会留下操作痕迹。DynamoDB锁表通过单独Terraform脚本创建resource aws_dynamodb_table terraform_state_lock { name terraform-state-lock-prod billing_mode PAY_PER_REQUEST hash_key LockID attribute { name LockID type S } # 关键添加TTL属性自动清理过期锁 server_side_encryption { enabled true } time_to_live { attribute_name ExpiresAt enabled true } }→ TTLTime To Live属性ExpiresAt是救命稻草。某次CI Runner崩溃terraform apply卡在lock状态TTL设为30分钟30分钟后DynamoDB自动删除锁避免Pipeline永久阻塞。实操心得S3 bucket policy必须精确到最小权限。我们曾用宽泛的Resource: arn:aws:s3:::our-org-tfstate-prod/*导致Terraform误删了备份目录。现在policy严格限定{ Version: 2012-10-17, Statement: [ { Effect: Allow, Principal: {Service: tff.amazonaws.com}, Action: [s3:GetObject, s3:PutObject], Resource: arn:aws:s3:::our-org-tfstate-prod/prod/* } ] }4. 实操过程与核心环节实现从零搭建可落地的Pipeline全流程4.1 环境准备GitLab Runner与AWS权限的最小化配置在AWS上部署GitLab Runner不是“装个agent”那么简单关键是权限最小化与资源隔离Step 1创建专用IAM Rolegitlab-runner-prod不继承任何现有Role从零开始附加策略AmazonEC2ReadOnlyAccess只读EC2信息AmazonS3FullAccess仅限our-org-tfstate-prodbucketAmazonDynamoDBFullAccess仅限terraform-state-lock-prodtable绝不附加AdministratorAccess我们曾因此被安全团队勒令下线整改。Step 2EC2实例启动脚本user-data#!/bin/bash yum update -y # 安装DockerRunner依赖 amazon-linux-extras install docker -y service docker start # 注册Runner关键--tag-list绑定环境 gitlab-runner register \ --non-interactive \ --url https://gitlab.com/ \ --registration-token GR1348941abcxyz... \ --executor docker \ --docker-image hashicorp/terraform:1.5.7 \ --description AWS Prod Terraform Runner \ --tag-list aws-prod-runner \ --run-untaggedfalse \ --lockedtrue \ --access-levelnot_protected→--tag-list aws-prod-runner确保Pipeline中tags: [aws-prod-runner]能精准调度--run-untaggedfalse防止未标记作业误跑。Step 3Runner配置/etc/gitlab-runner/config.toml[[runners]] name aws-prod-runner url https://gitlab.com/ token GR1348941abcxyz... executor docker [runners.docker] image hashicorp/terraform:1.5.7 privileged false # 绝不开启privileged mode volumes [/cache, /root/.aws:/root/.aws:ro] # AWS凭证只读挂载→privileged false是铁律。某次开启后Runner容器内可执行iptables导致网络策略被意外修改。4.2 Terraform模块实战构建高可用ECS集群的完整代码链以“部署一个自动扩缩容的ECS服务”为例展示模块化如何落地目录结构modules/ ├── ecs-cluster/ # 创建ECS集群、IAM角色、CloudWatch日志组 ├── ecs-service/ # 创建ECS服务、负载均衡器、Auto Scaling策略 └── ecs-task-definition/ # 定义Task容器镜像、CPU/内存、环境变量 environments/ ├── prod/ │ ├── main.tf # 调用三个模块 │ ├── variables.tf # 定义prod专属变量如min_capacity2 │ └── terraform.tfvars # 空所有变量由CI注入environments/prod/main.tf# 调用集群模块 module ecs_cluster { source ../../modules/ecs-cluster name prod-app-cluster tags local.common_tags } # 调用服务模块关键传入集群ID和ALB ARN module ecs_service { source ../../modules/ecs-service cluster_id module.ecs_cluster.cluster_id alb_target_group_arn module.alb.target_group_arn # ALB模块在另一处定义 min_capacity var.min_capacity max_capacity var.max_capacity } # 调用Task定义模块关键镜像URL由CI注入 module ecs_task_definition { source ../../modules/ecs-task-definition family prod-app container_image var.container_image_url # 如 registry.gitlab.com/our-org/app:prod-v2.1 cpu 1024 memory 2048 }GitLab CI变量注入Settings CI/CD VariablesKeyValueProtectedMaskedScopeTF_VAR_container_image_urlregistry.gitlab.com/our-org/app:prod-v2.1✓✓mainbranch onlyTF_VAR_min_capacity2✓✗mainbranch onlyTF_VAR_max_capacity10✓✗mainbranch only→TF_VAR_前缀让Terraform自动识别为变量Protected确保仅在protected分支可见Masked隐藏敏感值如镜像URL含token时。4.3 Pipeline执行实录一次完整的prod环境部署以MR !123标题“升级支付服务至v3.2修复PCI合规漏洞”为例记录Pipeline每一步Stage: validateRunner拉取代码执行terraform init→ 下载provider耗时23sterraform validate→ 检查HCL语法发现modules/ecs-task-definition/variables.tf第42行validation块缺失Pipeline失败MR被阻止合并。→ 开发补上校验后重试通过。Stage: plan切换到environments/prod目录执行terraform plan -outtfplan -var-fileprod.tfvars输出摘要Terraform will perform the following actions: # module.ecs_service.aws_appautoscaling_target.ecs_service_target will be updated in-place ~ resource aws_appautoscaling_target ecs_service_target { ~ min_capacity 2 - 4 # 扩容阈值提升 ~ max_capacity 10 - 20 }Pipeline自动将此diff评论到MR页面SRE确认后批准MR。Stage: security-scanCheckov扫描modules/ecs-task-definition报告Check: CKV_AWS_123: Ensure ECS Task Definition containers do not run as root FAILED for resource: aws_ecs_task_definition.app File: /modules/ecs-task-definition/main.tf:15-25→ 开发修改user 1001重新推送。Stage: applyMR含APPLY标签且已批准触发apply-prod作业执行terraform apply -auto-approve tfplan输出Apply complete! Resources: 0 added, 1 changed, 0 destroyed. Outputs: service_url https://prod-app.example.comPipeline自动将service_url写入MR评论并触发Slack通知。Stage: audit将tfplan文件、terraform state show摘要、MR链接打包为audit-123.zip上传至S3s3://our-org-audit-bucket/2023/10/15/同时向CloudWatch Events发送事件{event:tf_apply_success, mr_id:123, env:prod}整个过程从MR创建到服务上线耗时8分12秒全程无人工干预。5. 常见问题与排查技巧实录那些文档里不会写的血泪教训5.1 Terraform State Corruption当terraform plan突然说“要删掉整个VPC”现象某次MR合并后plan显示要销毁所有子网、路由表、甚至VPC本身但代码只改了一个安全组规则。根因分析检查S3 state file最后修改时间发现是3小时前早于本次MR查CloudTrail日志发现DeleteObject事件来自terraform state rm命令追溯到某开发在本地执行terraform state rm aws_vpc.main试图“跳过VPC创建”但忘记pushstate到S3解决方案立即行动从S3版本控制中恢复3小时前的state file我们开启S3 versioning保留30天长期防控在S3 bucket policy中添加显式拒绝{ Effect: Deny, Principal: *, Action: s3:DeleteObject, Resource: arn:aws:s3:::our-org-tfstate-prod/*, Condition: {StringNotEquals: {aws:username: tf-state-manager}} }在GitLab CIplan作业中加入state完整性校验# 检查state中VPC资源是否存在 terraform state list | grep aws_vpc.main || { echo CRITICAL: VPC missing from state!; exit 1; }注意S3版本控制不是万能的。我们曾因误删整个bucket而非单个object版本控制无效。现在所有state bucket启用MFA Delete删除操作需物理MFA设备确认。5.2 GitLab CI Pipeline卡在“Waiting for runner”Runner资源耗尽的真实原因现象Pipeline长时间显示“Waiting for runner”查看Runner状态显示“online”但无作业分配。排查路径登录Runner所在EC2执行gitlab-runner status→ 显示running查看/var/log/gitlab-runner/current日志 → 发现大量ERROR: Job failed (system failure): prepare environment: context deadline exceeded检查EC2资源free -h显示内存98%占用df -h显示/var/lib/docker分区100%满根因Docker镜像缓存未清理。Runner持续拉取hashicorp/terraform新版本旧镜像未删除占满磁盘。解决与预防立即清理docker system prune -a -f docker volume prune -f长期方案在Runner EC2的crontab中添加# 每日凌晨2点清理Docker 0 2 * * * /usr/bin/docker system prune -f --filter until48h /dev/null 21 # 清理超过30天的镜像 0 3 * * * /usr/bin/docker image prune -f --filter until720h /dev/null 21更优方案改用Docker Machine动态创建Runner作业完成后自动销毁实例彻底规避资源堆积。5.3 “Terraform doesn’t support this AWS feature yet”如何优雅处理Provider滞后现象AWS发布新服务如aws_appfabricTerraform AWS Provider尚未支持但业务急需上线。错误做法用local-exec调用AWS CLI → 失去IaC可审计性等待Provider更新 → 业务延期我们的方案Terraform External Data Source Lambda编写Lambda函数Python封装AWS SDK调用新服务API在Terraform中使用externaldata sourcedata external appfabric_app { program [aws, lambda, invoke, --function-name, tf-external-appfabric, --payload, jsonencode({actioncreate_app, nameprod-payments})] }Lambda返回JSONTerraform将其作为data source输出→ 这样既保持Terraform代码主体不变又绕过Provider限制。Lambda函数本身也用Terraform管理形成闭环。5.4 权限问题终极排查表当terraform apply报错AccessDenied按此顺序检查90%问题在此检查项命令/位置正确值示例常见错误1. Runner EC2 Rolecurl http://169.254.169.254/latest/meta-data/iam/security-credentials/gitlab-runner-prodRole名拼错或未附加策略2. Terraform Provider配置providers.tf中aws块region us-east-1region与S3 bucket region不一致3. S3 Backend权限S3 bucket policyResource: arn:aws:s3:::our-org-tfstate-prod/prod/*Resource写成arn:aws:s3:::our-org-tfstate-prod/*太宽泛4. DynamoDB锁表权限IAM Policy中dynamodb:DescribeTableResource: arn:aws:dynamodb:us-east-1:123456789012:table/terraform-state-lock-prodResource未指定具体table5. GitLab CI变量作用域GitLab Settings CI/CD VariablesScope: main branch only变量未勾选Protected在dev分支Pipeline中不可见实操心得在plan作业中加入权限诊断脚本# 检查S3访问 aws s3 ls s3://our-org-tfstate-prod/prod/ --no-sign-request 2/dev/null echo S3 access OK || echo S3 access FAILED # 检查DynamoDB锁表 aws dynamodb describe-table --table-name terraform-state-lock-prod --no-sign-request 2/dev/null echo DynamoDB OK || echo DynamoDB FAILED这个脚本让我们在Pipeline早期就暴露权限问题避免apply阶段才发现。6. 进阶扩展与未来演进从“能用”到“智能运维”的跨越这套架构不是终点而是智能运维的起点。我们已在生产环境验证的三个演进方向6.1 基于GitOps的自动修复Self-healing当CloudWatch检测到ECS服务任务数低于阈值如2自动触发GitLab CI Pipeline创建临时MR内容为“增加min_capacity至3”Pipeline执行plan→apply→ 自动合并全程无需人工介入MTTR从15分钟降至47秒。关键技术GitLab API Terraform CloudWatch EventBridge集成。6.2 成本优化引擎Cost Intelligence在plan阶段集成AWS Pricing Calculator API解析terraform plan输出的资源变更调用API计算月度成本变化如“增加2台c5.4xlarge预计多花$1,240/月”将成本影响写入MR评论强制开发权衡。效果Q3云账单环比下降18%因开发主动将非关键服务降配。6.3 合规即代码Compliance as Code将PCI DSS、HIPAA条款转化为Terraform Checkov策略自定义Checkov策略CKV_AWS_999: PCI Requirement 4.1 - Encrypt data in transit扫描ALB监听器是否启用TLS 1.2扫描RDS是否启用SSL强制连接Pipeline中security-scan阶段失败即阻断MR。审计时直接导出Checkov报告覆盖100%合规条目。最后分享一个小技巧我们给每个Terraform模块编写README.md

本月热点