一、概述
Terraform 是 HashiCorp 推出的基础设施即代码(IaC)工具,通过声明式配置文件实现云资源和基础设施的自动化部署与管理。本文详细介绍 Terraform 在生产环境中的部署流程、最佳实践及常见问题解决方案。
1.1 核心优势
- 声明式配置:描述期望状态,Terraform 自动计算变更
- 状态管理:通过状态文件追踪资源变更历史
- 模块化设计:支持代码复用和团队协作
- 多云支持:统一语法管理 AWS、Azure、GCP、阿里云等
1.2 适用场景
- 云资源批量部署与变更管理
- 多环境(dev/staging/prod)基础设施一致性保障
- 基础设施版本控制与审计
- 灾难恢复与快速重建
二、环境准备与安装
2.1 系统要求
| 组件 |
最低要求 |
推荐配置 |
| 操作系统 |
Linux/macOS/Windows |
Ubuntu 22.04+ |
| CPU |
2 核 |
4 核+ |
| 内存 |
2GB |
8GB+ |
| 磁盘 |
1GB |
10GB+ |
1 2 3 4 5 6 7
| wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list sudo apt update && sudo apt install terraform
terraform version
|
2.3 配置 Provider
创建 providers.tf 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| terraform { required_version = ">= 1.6.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } aliyun = { source = "aliyun/aliyun" version = "~> 1.240.0" } } # 配置远程状态后端(推荐) backend "s3" { bucket = "terraform-state-prod" key = "project/terraform.tfstate" region = "ap-southeast-1" } }
|
2.4 初始化工作目录
1 2 3 4 5 6 7 8
| terraform init
terraform init -upgrade
terraform validate
|
三、核心配置详解
3.1 变量定义(variables.tf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| variable "environment" { description = "部署环境" type = string default = "prod" validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "环境必须是 dev、staging 或 prod" } }
variable "instance_type" { description = "EC2 实例类型" type = string default = "t3.medium" }
variable "vpc_cidr" { description = "VPC CIDR 块" type = string default = "10.0.0.0/16" }
variable "tags" { description = "资源标签" type = map(string) default = { Project = "MyApp" ManagedBy = "Terraform" } }
|
3.2 本地值(locals.tf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| locals { common_tags = merge( var.tags, { Environment = var.environment CreatedAt = timestamp() } ) instance_config = { dev = { type = "t3.small", count = 1 } staging = { type = "t3.medium", count = 2 } prod = { type = "t3.large", count = 4 } } current_instance = local.instance_config[var.environment] }
|
3.3 资源定义(main.tf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| # VPC resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = local.common_tags }
# 子网 resource "aws_subnet" "public" { count = 2 vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = data.aws_availability_zones.available.names[count.index] tags = merge(local.common_tags, { Type = "Public" }) }
# 安全组 resource "aws_security_group" "app" { name = "${var.environment}-app-sg" description = "应用安全组" vpc_id = aws_vpc.main.id ingress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = local.common_tags }
# EC2 实例 resource "aws_instance" "app" { count = local.current_instance.count ami = data.aws_ami.ubuntu.id instance_type = local.current_instance.type subnet_id = aws_subnet.public[count.index % 2].id vpc_security_group_ids = [aws_security_group.app.id] root_block_device { volume_type = "gp3" volume_size = 50 encrypted = true } tags = merge(local.common_tags, { Name = "${var.environment}-app-${count.index + 1}" }) }
|
3.4 数据源查询(data.tf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] # Canonical filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] } }
data "aws_availability_zones" "available" { state = "available" }
data "aws_caller_identity" "current" {}
|
3.5 输出定义(outputs.tf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| output "vpc_id" { description = "VPC ID" value = aws_vpc.main.id }
output "public_subnet_ids" { description = "公共子网 ID 列表" value = aws_subnet.public[*].id }
output "instance_public_ips" { description = "实例公网 IP 列表" value = aws_instance.app[*].public_ip }
output "security_group_id" { description = "安全组 ID" value = aws_security_group.app.id }
|
四、模块化部署
4.1 创建可复用模块
目录结构:
1 2 3 4 5 6
| modules/ └── vpc/ ├── main.tf ├── variables.tf ├── outputs.tf └── README.md
|
modules/vpc/main.tf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| resource "aws_vpc" "this" { cidr_block = var.cidr_block enable_dns_hostnames = true enable_dns_support = true tags = var.tags }
resource "aws_internet_gateway" "this" { vpc_id = aws_vpc.this.id tags = var.tags }
resource "aws_route_table" "public" { vpc_id = aws_vpc.this.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.this.id } tags = var.tags }
|
modules/vpc/variables.tf:
1 2 3 4 5 6 7 8 9 10
| variable "cidr_block" { description = "VPC CIDR" type = string }
variable "tags" { description = "资源标签" type = map(string) default = {} }
|
modules/vpc/outputs.tf:
1 2 3 4 5 6 7
| output "vpc_id" { value = aws_vpc.this.id }
output "route_table_id" { value = aws_route_table.public.id }
|
4.2 调用模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| module "vpc" { source = "./modules/vpc" cidr_block = "10.0.0.0/16" tags = { Name = "production-vpc" } }
module "eks" { source = "terraform-aws-modules/eks/aws" version = "~> 20.0" cluster_name = "prod-cluster" cluster_version = "1.28" vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.subnet_ids eks_managed_node_groups = { default = { instance_types = ["t3.medium"] min_size = 2 max_size = 10 desired_size = 3 } } }
|
五、状态管理最佳实践
5.1 远程状态配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| # S3 + DynamoDB(AWS) terraform { backend "s3" { bucket = "terraform-state-prod" key = "infra/terraform.tfstate" region = "ap-southeast-1" encrypt = true dynamodb_table = "terraform-locks" } }
# Azure Blob Storage terraform { backend "azurerm" { resource_group_name = "tfstate-rg" storage_account_name = "tfstateprod" container_name = "tfstate" key = "infra.terraform.tfstate" } }
|
5.2 状态锁定
使用 DynamoDB 防止并发修改:
1 2 3 4 5 6 7 8 9 10
| resource "aws_dynamodb_table" "terraform_locks" { name = "terraform-locks" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" attribute { name = "LockID" type = "S" } }
|
5.3 状态文件操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| terraform state list
terraform state show aws_instance.app[0]
terraform state mv aws_instance.old aws_instance.new
terraform state rm aws_instance.deprecated
terraform import aws_instance.existing i-1234567890abcdef0
|
六、工作流与协作
6.1 标准操作流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| git pull origin main
terraform init
terraform fmt -recursive
terraform validate
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
git add . && git commit -m "feat: add new resources" git push origin main
|
6.2 多环境管理
目录结构:
1 2 3 4 5 6 7 8 9 10
| environments/ ├── dev/ │ ├── terraform.tfvars │ └── backend.tf ├── staging/ │ ├── terraform.tfvars │ └── backend.tf └── prod/ ├── terraform.tfvars └── backend.tf
|
dev/terraform.tfvars:
1 2 3
| environment = "dev" instance_type = "t3.small" vpc_cidr = "10.0.0.0/16"
|
prod/terraform.tfvars:
1 2 3
| environment = "prod" instance_type = "t3.large" vpc_cidr = "10.1.0.0/16"
|
6.3 CI/CD 集成
GitLab CI 示例(.gitlab-ci.yml):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| stages: - validate - plan - apply
variables: TF_IN_AUTOMATION: "true" TF_INPUT: "false"
validate: stage: validate script: - terraform init - terraform fmt -check -recursive - terraform validate
plan: stage: plan script: - terraform init - terraform plan -out=tfplan artifacts: paths: - tfplan expire_in: 1 hour when: manual
apply: stage: apply script: - terraform init - terraform apply -auto-approve tfplan dependencies: - plan when: manual environment: production
|
七、常见问题与解决方案
7.1 Provider 认证问题
问题:Error: Invalid provider configuration
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export AWS_ACCESS_KEY_ID=your_key export AWS_SECRET_ACCESS_KEY=your_secret export AWS_DEFAULT_REGION=ap-southeast-1
mkdir -p ~/.aws cat > ~/.aws/credentials << EOF [default] aws_access_key_id = your_key aws_secret_access_key = your_secret EOF
export ARM_CLIENT_ID=xxx export ARM_CLIENT_SECRET=xxx export ARM_SUBSCRIPTION_ID=xxx export ARM_TENANT_ID=xxx
|
7.2 状态文件损坏
问题:Error: Failed to load state
解决方案:
1 2 3 4 5 6 7 8 9
| cp terraform.tfstate terraform.tfstate.backup
terraform state pull > state.json terraform state push state.json
aws s3 cp s3://bucket/terraform.tfstate.backup ./terraform.tfstate
|
7.3 资源漂移检测
问题:手动变更导致状态不一致
解决方案:
1 2 3 4 5 6 7 8
| terraform plan -refresh-only -out=drift.tfplan terraform show drift.tfplan
terraform apply -refresh-only -auto-approve
|
7.4 大规模资源管理
问题:资源数量过多导致 plan/apply 缓慢
解决方案:
1 2 3 4 5 6 7 8 9
| terraform apply -target=aws_instance.app terraform apply -target=module.vpc
export TF_PARALLELISM=10
|
7.5 敏感信息管理
问题:密钥硬编码在配置中
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # 使用变量 + 环境变量 variable "db_password" { type = string sensitive = true }
# 使用 Secrets Manager data "aws_secretsmanager_secret_version" "db_cred" { secret_id = "prod/db/credentials" }
resource "aws_db_instance" "main" { password = data.aws_secretsmanager_secret_version.db_cred.secret_string }
# 使用 Vault Provider provider "vault" { address = "https://vault.example.com" }
data "vault_generic_secret" "db" { path = "secret/data/prod/db" }
|
八、安全最佳实践
8.1 最小权限原则
IAM Policy 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:Describe*", "ec2:RunInstances", "ec2:TerminateInstances" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::terraform-state-prod/*" } ] }
|
8.2 状态文件加密
1 2 3 4 5 6 7 8 9 10 11
| # S3 服务端加密 backend "s3" { bucket = "terraform-state-prod" encrypt = true kms_key_id = "arn:aws:kms:region:account:key/key-id" }
# Azure 加密 backend "azurerm" { # 默认启用加密 }
|
8.3 审计与合规
1 2 3 4 5 6 7 8 9 10
| resource "aws_cloudtrail" "terraform_audit" { name = "terraform-audit" s3_bucket_name = aws_s3_bucket.audit.id include_global_service_events = true is_multi_region_trail = true }
tfsec . --format sarif --out tfsec-results.sarif
|
九、性能优化
9.1 加速 Plan/Apply
1 2 3 4 5 6 7 8
| terraform apply -parallelism=25
terraform plan -refresh=false
export TF_PLUGIN_CACHE_DIR=~/.terraform.d/plugin-cache
|
9.2 优化模块结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| # 避免过度嵌套 # 不好:module.a.module.b.module.c # 好:扁平化调用
# 使用 for_each 替代 count(更稳定) resource "aws_instance" "app" { for_each = var.instances ami = data.aws_ami.ubuntu.id instance_type = each.value.type tags = { Name = each.key } }
|
十、总结
Terraform 作为基础设施即代码的核心工具,能够显著提升运维效率和基础设施一致性。关键要点:
- 状态管理:使用远程后端 + 状态锁定
- 模块化:复用代码,降低维护成本
- 版本控制:所有配置纳入 Git 管理
- CI/CD 集成:自动化验证和部署
- 安全优先:敏感信息使用 Secrets 管理
- 定期审计:检测资源漂移,确保合规
通过遵循本文的最佳实践,可以构建可靠、可维护的基础设施自动化体系。
参考资源