【GitOps·进阶篇】与 Terraform 集成:基础设施的 GitOps 管理

前言

前面我们讲的 GitOps 都是在管 Kubernetes 集群内的资源。但一个完整的应用系统还包括 VPC、数据库、负载均衡器等云资源。这些基础设施能否也用 GitOps 管理?答案是 Terraform + GitOps,也叫 "GitOps for Infrastructure"。


一、为什么基础设施也需要 GitOps

传统基础设施管理的问题

复制代码
运维人员 → AWS Console 手动创建 VPC/EC2/RDS
  - 没有审计记录
  - 环境不一致(开发/测试/生产手动建)
  - 灾难恢复困难(谁记得建了什么?)
  - 权限管理混乱

GitOps for Infrastructure 的价值

复制代码
Git 仓库(Terraform 代码)
  ├── VPC、子网、路由表
  ├── RDS 实例
  ├── IAM 策略
  ├── S3 桶
  └── CloudWatch 告警
      ↓ GitOps Controller
  自动 plan & apply
  → 基础设施与 Git 声明一致

二、Terraform 基础回顾

基本工作流

bash 复制代码
# 初始化
terraform init

# 查看变更计划
terraform plan

# 应用变更
terraform apply

# 销毁资源
terraform destroy

示例:创建 AWS VPC

hcl 复制代码
# main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name      = "production-vpc"
    ManagedBy = "terraform"
  }
}

# 子网
resource "aws_subnet" "public" {
  count             = 3
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 1}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]
  tags = {
    Name = "public-subnet-${count.index + 1}"
  }
}

# 输出
output "vpc_id" {
  value = aws_vpc.main.id
}

三、Terraform Controller:Terraform Operator for Kubernetes

方案对比

方案 原理 优势 劣势
Terraform Controller (Flux) TF 代码存 Git,Controller 执行 与 Flux 深度集成,CRD 管理 功能较新
Terraform Operator (KubeVela) CRD 定义 TF 模块 灵活,支持多云 依赖 KubeVela
Atlantis PR 驱动,手动 approve 成熟稳定 非 GitOps 自动化
Crossplane 原生 K8s CRD 管理云资源 真正的 K8s 原生 学习曲线高

安装 Terraform Controller (Flux)

bash 复制代码
# 添加 Helm 仓库
helm repo add tf-controller https://tf-controller.github.io/tf-controller
helm repo update

# 安装
helm install tf-controller tf-controller/tf-controller \
  --namespace tf-system \
  --create-namespace \
  --set rbac.create=true \
  --set serviceAccount.create=true

四、用 Terraform Controller 管理基础设施

Terraform 资源 CRD

yaml 复制代码
apiVersion: tf.isaaguilar.com/v1alpha2
kind: Terraform
metadata:
  name: aws-vpc
  namespace: default
spec:
  tfdir: git::https://github.com/org/infra-repo.git//vpc?ref=main
  # 也可以用本地路径或 OCI 镜像
  # tfdir: /tmp/terraform/vpc

  # 并发执行
  parallelism: 4

  # 审批策略
  approvePlan: auto      # auto = 自动 apply,manual = 需手动

  # 变量
  vars:
    - key: region
      value: "us-east-1"
    - key: environment
      value: "production"
    - key: vpc_cidr
      value: "10.0.0.0/16"

  # 敏感变量通过 Secret 引用
  varsFrom:
    - key: aws-credentials
      kind: Secret
      name: aws-creds

  # 状态存储配置
  backend:
    s3:
      bucket: terraform-state-prod
      key: aws-vpc/terraform.tfstate
      region: us-east-1

  # 执行前 Hook
  preInit:
    - "terraform fmt -check"

  # 输出到 Secret
  outputs:
    - key: vpc_id
      moduleOutputName: vpc_id

完整示例:管理 AWS RDS

hcl 复制代码
# infra-repo/rds/main.tf
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

variable "instance_class" {
  type    = string
  default = "db.t3.medium"
}

variable "allocated_storage" {
  type    = number
  default = 20
}

resource "aws_db_instance" "main" {
  identifier           = "production-db"
  engine               = "postgres"
  engine_version       = "15.4"
  instance_class       = var.instance_class
  allocated_storage    = var.allocated_storage
  db_name              = "appdb"
  username             = "admin"
  password             = var.db_password
  vpc_id               = var.vpc_id
  subnet_ids           = var.subnet_ids
  skip_final_snapshot  = false
  final_snapshot_identifier = "production-db-final"

  backup_retention_period = 7
  backup_window           = "03:00-05:00"
  maintenance_window      = "Mon:05:00-Mon:07:00"

  tags = {
    Environment = "production"
    ManagedBy   = "terraform-gitops"
  }
}

output "db_endpoint" {
  value     = aws_db_instance.main.endpoint
  sensitive = true
}
yaml 复制代码
# GitOps 仓库中的 Terraform CR
apiVersion: tf.isaaguilar.com/v1alpha2
kind: Terraform
metadata:
  name: production-rds
  namespace: default
spec:
  tfdir: git::https://github.com/org/infra-repo.git//rds?ref=main
  approvePlan: auto
  vars:
    - key: instance_class
      value: "db.r6g.large"
    - key: allocated_storage
      value: "100"
    - key: vpc_id
      value: "vpc-abc123"
    - key: subnet_ids
      value: '["subnet-aaa","subnet-bbb","subnet-ccc"]'
  varsFrom:
    - key: DB_PASSWORD
      kind: Secret
      name: rds-credentials
  backend:
    s3:
      bucket: terraform-state-prod
      key: rds/terraform.tfstate
      region: us-east-1
  outputs:
    - key: db_endpoint
      moduleOutputName: db_endpoint

输出跨资源引用

yaml 复制代码
# VPC Terraform 输出 vpc_id
# RDS Terraform 引用 VPC 的输出值
apiVersion: tf.isaaguilar.com/v1alpha2
kind: Terraform
metadata:
  name: production-rds
spec:
  tfdir: git::https://github.com/org/infra-repo.git//rds?ref=main
  vars:
    - key: vpc_id
      # 引用另一个 Terraform 资源的输出
      value: "${production-vpc.vpc_id}"

五、GitOps 工作流

完整流水线

复制代码
开发者修改 Terraform 代码
  ↓
提交 PR 到 Git 仓库
  ↓
 Atlantis / TF Controller 执行 terraform plan
  ↓
 PR 评论显示 plan 结果
  ↓
 Code Review & Approve
  ↓
 Merge to main
  ↓
 TF Controller 自动 terraform apply
  ↓
 基础设施变更生效
  ↓
 状态写回 S3 后端

目录结构

复制代码
infra-repo/
├── .sops.yaml              # SOPS 加密配置
├── modules/
│   ├── vpc/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   ├── rds/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── outputs.tf
│   └── eks/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
├── environments/
│   ├── staging/
│   │   ├── vpc.yaml         # TF Controller CR
│   │   ├── rds.yaml
│   │   └── eks.yaml
│   └── production/
│       ├── vpc.yaml
│       ├── rds.yaml
│       └── eks.yaml
└── policies/
    └── cost-guardrails.yaml  # 成本控制策略

六、Atlantis:PR 驱动的 Terraform

安装 Atlantis

bash 复制代码
# Helm 安装
helm repo add atlantis https://runatlantis.github.io/helm-charts
helm repo update

helm install atlantis atlantis/atlantis \
  --namespace atlantis \
  --create-namespace \
  --set github.user=your-bot \
  --set github.token=ghp_xxx \
  --set github.secret=your-webhook-secret \
  --set atlantis.url=https://atlantis.example.com

atlantis.yaml 配置

yaml 复制代码
# 仓库根目录的 atlantis.yaml
version: 3
projects:
  - name: staging-vpc
    dir: environments/staging/vpc
    workspace: staging
    autoplan:
      when_modified: ["*.tf", "*.tfvars"]
      enabled: true
    apply_requirements: [approved, mergeable]

  - name: production-vpc
    dir: environments/production/vpc
    workspace: production
    autoplan:
      when_modified: ["*.tf"]
      enabled: true
    apply_requirements: [approved, mergeable, undiverged]

  - name: production-rds
    dir: environments/production/rds
    workspace: production
    autoplan:
      when_modified: ["*.tf"]
      enabled: true
    apply_requirements: [approved, mergeable]

PR 工作流

复制代码
1. 开发者提交 PR → 修改 RDS 实例类型
2. Atlantis 自动执行 terraform plan
3. PR 评论显示:
   "Plan: 1 to add, 0 to change, 0 to destroy"
   "aws_db_instance.main: instance_class db.t3.medium → db.r6g.large"
4. Reviewer 审核 plan 结果
5. 评论 "atlantis apply -d environments/production/rds"
6. Atlantis 执行 terraform apply
7. 基础设施变更生效

七、安全与最佳实践

状态管理

hcl 复制代码
# 使用远程 S3 后端 + DynamoDB 锁
terraform {
  backend "s3" {
    bucket         = "terraform-state-prod"
    key            = "infra/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    encrypt        = true
  }
}

# DynamoDB 锁表(防并发)
resource "aws_dynamodb_table" "terraform_locks" {
  name         = "terraform-locks"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
  attribute {
    name = "LockID"
    type = "S"
  }
}

权限隔离

yaml 复制代码
# 生产环境使用独立 ServiceAccount + IRSA
apiVersion: v1
kind: ServiceAccount
metadata:
  name: terraform-production
  namespace: tf-system
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/terraform-prod-role
---
# Terraform CR 使用该 SA
apiVersion: tf.isaaguigar.com/v1alpha2
kind: Terraform
metadata:
  name: production-rds
spec:
  serviceAccount: terraform-production

成本防护

yaml 复制代码
# 在 Terraform 模块中添加成本预估
spec:
  prePlan:
    - "infracost breakdown --path . --format json --out-file infracost.json"
    - "infracost diff --path infracost.json --compare-to main"
  postPlan:
    - "if [ $(cat infracost-diff.json | jq '.totalMonthlyCost' | tr -d '\"' | awk '{print int($1)}') -gt 100 ]; then echo 'Cost exceeds $100/month limit'; exit 1; fi"

⚠️ 踩坑提示

  • 永远不要删除 terraform.tfstate 文件

  • 生产环境 approvePlan 设为 manual,配合 PR 审批

  • 使用 terraform import 接管手动创建的资源,避免 State 冲突

  • Terraform 版本固定,避免自动升级导致 breaking change


要点回顾

维度 Kubernetes GitOps Infrastructure GitOps
管理对象 K8s 资源(Deployment/Service) 云资源(VPC/RDS/S3)
工具 ArgoCD / Flux Terraform Controller / Atlantis
声明式 YAML CRD HCL 代码
状态管理 etcd S3 + DynamoDB
审批方式 自动同步 PR 驱动
  • Terraform Controller 将 Terraform 引入 K8s GitOps 生态
  • Atlantis 适合 PR 驱动的工作流
  • 远程状态 + DynamoDB 锁是生产标配
  • 跨资源输出实现 VPC → RDS → EKS 的引用链
  • 成本防护用 Infracost 在 plan 阶段拦截

下一篇预告

应用交付、渐进式发布、基础设施管理都有了。下一篇 【GitOps·落地篇】企业级 GitOps 平台架构:迁移路径与最佳实践 将从全局视角讲解如何搭建企业级 GitOps 平台,以及从传统模式到 GitOps 的迁移路径。

相关推荐
heimeiyingwang1 天前
【GitOps·进阶篇】密钥管理:Sealed Secrets、SOPS 与 External Secrets
gitops
heimeiyingwang2 天前
【GitOps·Flux篇】通知与告警:Webhook 集成与事件通知
flux·gitops
heimeiyingwang2 天前
【GitOps·Flux篇】多集群与多租户:Tenant 模型与权限隔离
flux·gitops
heimeiyingwang3 天前
【GitOps·Flux篇】核心概念:Source、Kustomization与HelmRelease
helm·flux·gitops
heimeiyingwang7 天前
【GitOps·ArgoCD篇】RBAC与多租户:团队权限隔离实战
argocd·gitops
heimeiyingwang8 天前
【GitOps·ArgoCD篇】健康检查与资源钩子:自定义健康状态
argocd·gitops
heimeiyingwang9 天前
【GitOps·ArgoCD篇】与 Kustomize 集成:多环境配置管理
kustomize·argocd·gitops
heimeiyingwang10 天前
【GitOps·ArgoCD篇】同步策略:自动同步、手动同步与同步钩子
argocd·gitops
heimeiyingwang14 天前
【GitOps·入门篇】工具生态:ArgoCD、Flux、Jenkins X 对比选型
jenkins·flux·argocd·gitops