前置知识: Git

基础设施即代码

4 minAdvanced2026/6/14

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

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.6"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  # 远程状态存储
  backend "s3" {
    bucket         = "terraform-state-prod"
    key            = "infrastructure/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "terraform-locks"
    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.29"

  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 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 模块

modules/
├── vpc/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── versions.tf
├── eks/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
└── rds/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

environments/
├── production/
│   ├── main.tf       # 引用模块
│   ├── backend.tf
│   └── terraform.tfvars
└── staging/
    ├── main.tf
    ├── backend.tf
    └── terraform.tfvars
# 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 结构

roles/nginx/
├── tasks/
│   └── main.yml
├── handlers/
│   └── main.yml
├── templates/
│   └── nginx.conf.j2
├── files/
│   └── ssl/
├── defaults/
│   └── main.yml
├── vars/
│   └── main.yml
└── meta/
    └── main.yml
# 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

base/
├── deployment.yaml
├── service.yaml
└── kustomization.yaml

overlays/
├── staging/
│   ├── kustomization.yaml
│   └── patch-replicas.yaml
└── production/
    ├── kustomization.yaml
    ├── patch-replicas.yaml
    └── patch-resources.yaml
# 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,防止不安全配置上线