前置知识: Git

基础设施即代码

3 min高级

IaC 理念、Terraform、Ansible、Pulumi 与 GitOps 实践。

学习目标

本文是「运维与中间件」模块的第 20 篇,难度定位为高级。重点内容:IaC 理念、Terraform、Ansible、Pulumi 与 GitOps 实践。

主要章节:

    1. IaC 理念
    1. Terraform
    1. Ansible
    1. Pulumi
    1. GitOps
    1. IaC 安全
  • ……共 7 个章节

1. IaC 理念

1.1 核心原则

原则描述
声明式描述期望状态,而非操作步骤
版本控制所有配置纳入 Git 管理
幂等性多次执行结果一致
不可变替换而非修改基础设施
自助服务代码即文档,可重复执行

1.2 IaC 工具分类

类型代表工具特点
配置管理Ansible, Chef, Puppet管理已有服务器的配置
资源编排Terraform, Pulumi, CDK创建和管理云资源
容器编排Helm, Kustomize管理 K8s 应用
GitOpsArgoCD, FluxGit 驱动的持续部署

2. Terraform

2.1 核心概念

概念描述
Provider云厂商插件(AWS/Azure/GCP/阿里云)
Resource基础设施资源(VM/VPC/数据库)
Module可复用的配置包
State资源状态文件
Plan预览变更
Apply执行变更

2.2 基础配置

# provider.tf
terraform {
  required_version = ">= 1.10" # 1.10(2024-11)引入临时资源(ephemeral)与 S3 原生状态锁
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  # 远程状态存储
  backend "s3" {
    bucket       = "terraform-state-prod"
    key          = "infrastructure/terraform.tfstate"
    region       = "us-east-1"
    use_lockfile = true # Terraform 1.10+ 使用 S3 原生条件写锁,不再依赖 DynamoDB
    encrypt      = true
  }
}

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

2.3 资源定义

# vpc.tf
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name        = "${var.project}-vpc"
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_subnet" "public" {
  count                   = length(var.public_subnet_cidrs)
  vpc_id                  = aws_vpc.main.id
  cidr_block              = var.public_subnet_cidrs[count.index]
  availability_zone       = var.azs[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "${var.project}-public-${count.index + 1}"
  }
}

resource "aws_subnet" "private" {
  count             = length(var.private_subnet_cidrs)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.azs[count.index]

  tags = {
    Name = "${var.project}-private-${count.index + 1}"
  }
}

# variables.tf
variable "project" {
  description = "Project name"
  type        = string
  default     = "myapp"
}

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "public_subnet_cidrs" {
  type    = list(string)
  default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}

variable "private_subnet_cidrs" {
  type    = list(string)
  default = ["10.0.10.0/24", "10.0.20.0/24", "10.0.30.0/24"]
}

2.4 EKS 集群

# eks.tf
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = "${var.project}-${var.environment}"
  cluster_version = "1.33" # 上线前查官方支持矩阵选择当前受支持版本

  vpc_id     = aws_vpc.main.id
  subnet_ids = aws_subnet.private[*].id

  cluster_endpoint_private_access = true
  cluster_endpoint_public_access  = true

  cluster_addons = {
    coredns    = {}
    kube-proxy = {}
    vpc-cni    = {}
  }

  eks_managed_node_groups = {
    general = {
      min_size       = 2
      max_size       = 10
      desired_size   = 3
      instance_types = ["t3.medium"]

      labels = {
        role = "general"
      }
    }
    monitoring = {
      min_size       = 1
      max_size       = 3
      desired_size   = 1
      instance_types = ["t3.small"]

      labels = {
        role = "monitoring"
      }
      taints = [{
        key    = "dedicated"
        value  = "monitoring"
        effect = "NO_SCHEDULE"
      }]
    }
  }
}

2.5 状态管理

版本提示:Terraform 1.10 起 S3 后端支持 use_lockfile 原生锁(DynamoDB 锁 变为兼容选项);1.7 起支持 moved 块重构资源、config-driven import; 1.10 引入临时资源(ephemeral resources)与 write-only 参数演进,密钥可 不落状态文件。许可方面 Terraform 自 1.6 起采用 BUSL,开源替代为 OpenTofu (语法基本兼容,示例同样适用)。

# 状态操作
terraform state list                    # 列出资源
terraform state show aws_vpc.main       # 查看资源详情
terraform state mv aws_vpc.old aws_vpc.new  # 重命名
terraform state rm aws_vpc.orphan       # 移除(不删除实际资源)

# 导入已有资源
terraform import aws_vpc.main vpc-12345678

# 工作区(环境隔离)
terraform workspace new staging
terraform workspace select production
terraform workspace list

2.6 模块化

flowchart TD
    T0["modules/"]
    T1["vpc/"]
    T2["main.tf"]
    T3["variables.tf"]
    T4["outputs.tf"]
    T5["versions.tf"]
    T6["eks/"]
    T7["main.tf"]
    T8["variables.tf"]
    T9["outputs.tf"]
    T10["rds/"]
    T11["main.tf"]
    T12["variables.tf"]
    T13["outputs.tf"]
    T14["environments/"]
    T15["production/"]
    T16["main.tf       # 引用模块"]
    T17["backend.tf"]
    T18["terraform.tfvars"]
    T19["staging/"]
    T20["main.tf"]
    T21["backend.tf"]
    T22["terraform.tfvars"]
    T0 --> T1
    T5 --> T6
    T9 --> T10
    T10 --> T11
    T10 --> T12
    T10 --> T13
    T13 --> T14
    T14 --> T15
    T18 --> T19
    T19 --> T20
    T19 --> T21
    T19 --> T22
# environments/production/main.tf
module "vpc" {
  source = "../../modules/vpc"

  project     = var.project
  environment = "production"
  cidr_block  = "10.0.0.0/16"
}

module "eks" {
  source = "../../modules/eks"

  project     = var.project
  environment = "production"
  vpc_id      = module.vpc.vpc_id
  subnet_ids  = module.vpc.private_subnet_ids
}

3. Ansible

3.1 Inventory

# inventory/production.ini
[web]
web1 ansible_host=10.0.1.10
web2 ansible_host=10.0.1.11

[db]
db1 ansible_host=10.0.10.20

[monitoring]
prometheus ansible_host=10.0.20.10
grafana ansible_host=10.0.20.11

[production:children]
web
db
monitoring

[production:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key

3.2 Playbook

# playbooks/deploy-web.yml
- name: Deploy Web Application
  hosts: web
  become: true
  vars:
    app_version: '2.0.0'
    app_port: 8080

  tasks:
    - name: Ensure app directory exists
      file:
        path: /opt/myapp
        state: directory
        owner: appuser
        group: appgroup

    - name: Pull Docker image
      community.docker.docker_image:
        name: 'registry.example.com/myapp:{{ app_version }}'
        source: pull

    - name: Run application container
      community.docker.docker_container:
        name: myapp
        image: 'registry.example.com/myapp:{{ app_version }}'
        state: started
        restart_policy: unless-stopped
        ports:
          - '{{ app_port }}:8080'
        env:
          NODE_ENV: production
          DATABASE_URL: '{{ vault_database_url }}'
        volumes:
          - /opt/myapp/data:/app/data

    - name: Wait for application to be ready
      uri:
        url: 'http://localhost:{{ app_port }}/health'
        status_code: 200
      register: result
      until: result is success
      retries: 10
      delay: 5

    - name: Notify deployment
      slack:
        token: '{{ vault_slack_token }}'
        msg: 'Deployed myapp {{ app_version }} to {{ inventory_hostname }}'
      delegate_to: localhost
      run_once: true

3.3 Role 结构

flowchart TD
    T0["roles/nginx/"]
    T1["tasks/"]
    T2["main.yml"]
    T3["handlers/"]
    T4["main.yml"]
    T5["templates/"]
    T6["nginx.conf.j2"]
    T7["files/"]
    T8["ssl/"]
    T9["defaults/"]
    T10["main.yml"]
    T11["vars/"]
    T12["main.yml"]
    T13["meta/"]
    T14["main.yml"]
    T0 --> T1
    T2 --> T3
    T4 --> T5
    T6 --> T7
    T8 --> T9
    T10 --> T11
    T12 --> T13
    T13 --> T14
# roles/nginx/tasks/main.yml
- name: Install Nginx
  apt:
    name: nginx
    state: present
    update_cache: true

- name: Configure Nginx
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
  notify: Reload Nginx

- name: Ensure Nginx is running
  service:
    name: nginx
    state: started
    enabled: true

# roles/nginx/handlers/main.yml
- name: Reload Nginx
  service:
    name: nginx
    state: reloaded

4. Pulumi

4.1 Pulumi vs Terraform

维度TerraformPulumi
语言HCLPython/TypeScript/Go/C#
学习曲线需学 HCL用已有编程语言
测试有限原生单元测试
逻辑声明式命令式 + 声明式
状态自管理Pulumi Cloud 或自管理

4.2 Pulumi 示例

# __main__.py
import pulumi
import pulumi_aws as aws

# 配置
config = pulumi.Config()
environment = config.require("environment")

# VPC
vpc = aws.ec2.Vpc("main-vpc",
    cidr_block="10.0.0.0/16",
    enable_dns_hostnames=True,
    tags={"Name": f"myapp-{environment}-vpc"}
)

# 子网
for i, az in enumerate(["us-east-1a", "us-east-1b"]):
    aws.ec2.Subnet(f"subnet-{i}",
        vpc_id=vpc.id,
        cidr_block=f"10.0.{i+1}.0/24",
        availability_zone=az,
        tags={"Name": f"myapp-{environment}-subnet-{i}"}
    )

# EKS 集群
cluster = aws.eks.Cluster("my-cluster",
    role_arn=cluster_role.arn,
    vpc_config=aws.eks.ClusterVpcConfigArgs(
        subnet_ids=[s.id for s in subnets]
    ),
    version="1.29"
)

# 输出
pulumi.export("cluster_name", cluster.name)
pulumi.export("cluster_endpoint", cluster.endpoint)

5. GitOps

5.1 GitOps 原则

原则描述
声明式系统描述必须是声明式的
版本控制期望状态存储在 Git
自动拉取自动从 Git 拉取并应用
持续协调软件代理持续协调状态

5.2 Flux

# flux-system/gotk-sync.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: flux-system
  namespace: flux-system
spec:
  interval: 1m
  ref:
    branch: main
  url: https://github.com/org/myapp-manifests

---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: flux-system
  namespace: flux-system
spec:
  interval: 10m
  path: ./clusters/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: flux-system
  validation: server

5.3 Kustomize

flowchart TD
    T0["base/"]
    T1["deployment.yaml"]
    T2["service.yaml"]
    T3["kustomization.yaml"]
    T4["overlays/"]
    T5["staging/"]
    T6["kustomization.yaml"]
    T7["patch-replicas.yaml"]
    T8["production/"]
    T9["kustomization.yaml"]
    T10["patch-replicas.yaml"]
    T11["patch-resources.yaml"]
    T0 --> T1
    T0 --> T2
    T0 --> T3
    T3 --> T4
    T4 --> T5
    T7 --> T8
    T8 --> T9
    T8 --> T10
    T8 --> T11
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
patches:
  - path: patch-replicas.yaml
  - path: patch-resources.yaml

# overlays/production/patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 5

6. IaC 安全

6.1 安全实践

实践描述
状态加密加密远程状态存储
密钥管理使用 Vault/云 KMS,不硬编码
PR 审查所有变更需 Code Review
Plan 审查审查 terraform plan 输出
最小权限CI/CD 使用最小必要权限
** drifted 检测**定期检测配置漂移

6.2 Terraform 安全扫描

# tfsec - Terraform 安全扫描
tfsec .

# checkov - 策略扫描
checkov -d .

# terrascan - 合规扫描
terrascan scan -t aws

# 在 CI 中集成
- name: Security Scan
  run: |
    tfsec --soft-fail .
    checkov -d . --framework terraform

7. 小结

IaC 是现代运维的基础:

  1. Terraform 是多云资源编排的事实标准,HCL 语法简洁
  2. Ansible 适合配置管理和应用部署,无需 Agent
  3. Pulumi 用编程语言写 IaC,适合有编程背景的团队
  4. GitOps 是 K8s 部署的最佳实践,ArgoCD 和 Flux 是主流工具
  5. 状态管理是 Terraform 的关键,必须使用远程后端和锁
  6. 安全扫描应集成到 CI/CD,防止不安全配置上线