资讯中心

Terraform实战03:数据源与远程状态管理

📅 2026/8/8 7:37:04
Terraform实战03:数据源与远程状态管理
Terraform实战03数据源与远程状态管理本篇目标学会把terraform.tfstate存到远程S3实现团队协作和状态安全。学会用data source查询已有的AWS资源避免硬编码。学完本篇你将掌握Backend配置把state存到S3 DynamoDB锁Data Source查询已存在的VPC、AMI、子网等资源resource与data的区别远程状态管理的最佳实践前置条件已完成前两篇练习理解terraform.tfstate的作用为什么需要远程状态前两篇练习中terraform.tfstate都存在本地。这在个人练习时没问题但生产环境会出大问题问题后果文件在本地别人看不到团队多人协作时每人一份state互相覆盖文件可能丢失误删或换电脑后Terraform不知道之前创建了什么两个人同时apply可能互相冲突资源状态混乱解决方案把state存到S3共享存储 DynamoDB并发锁。什么是Data Source之前我们用resource创建新资源。但有时候你想引用一个已经存在的资源不是Terraform创建的比如账号里已有的默认VPCAWS官方发布的最新AMI别人创建的子网这时候用data source??只查询不创建。resourcedata作用创建新资源查询已有资源语法resource aws_vpc main { }data aws_vpc default { }引用aws_vpc.main.iddata.aws_vpc.default.iddestroy时会被删除不会删除只是查询架构图┌─────────────────────────────────────────────────────────┐ │ 本次实操分两步 │ │ │ │ Step 1: backend-setup/ │ │ ┌─────────────────────────────────────────────┐ │ │ │ 创建 S3 Bucket存state文件 │ │ │ │ 创建 DynamoDB Table并发锁 │ │ │ │ state → 本地先有鸡才有蛋 │ │ │ └─────────────────────────────────────────────┘ │ │ ↓ 拿到桶名 │ │ Step 2: main-project/ │ │ ┌─────────────────────────────────────────────┐ │ │ │ 配置 backend s3state存到S3 │ │ │ │ data source 查询默认VPC、最新AMI │ │ │ │ 用查询结果创建EC2 │ │ │ │ state → S3远程存储不在本地 │ │ │ └─────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘目录结构03-remote-state/ ├── backend-setup/ # 第一步创建存state的基础设施 │ ├── main.tf │ └── outputs.tf └── main-project/ # 第二步使用远程state data source ├── main.tf ├── variables.tf └── outputs.tf完整代码Step 1backend-setup/main.tfterraform { required_providers { aws { source hashicorp/aws version ~ 5.0 } } required_version 1.0 } provider aws { region us-east-1 } # 【新资源】S3 Bucket存储 terraform.tfstate resource aws_s3_bucket terraform_state { bucket tf-practice-state-${random_id.suffix.hex} # 桶名全球唯一加随机后缀 force_destroy true # 练习用允许非空删除。生产环境设为false tags { Name terraform-state-bucket } } # 【新资源】S3版本控制保留state历史版本方便回滚 resource aws_s3_bucket_versioning terraform_state { bucket aws_s3_bucket.terraform_state.id versioning_configuration { status Enabled } } # 【新资源】S3加密state可能包含敏感信息 resource aws_s3_bucket_server_side_encryption_configuration terraform_state { bucket aws_s3_bucket.terraform_state.id rule { apply_server_side_encryption_by_default { sse_algorithm AES256 } } } # 【新资源】DynamoDB Table状态锁 # apply前写入锁记录完成后释放防止多人同时操作 resource aws_dynamodb_table terraform_lock { name tf-practice-state-lock billing_mode PAY_PER_REQUEST # 按需计费不用不花钱 hash_key LockID # Terraform固定要求的字段名 attribute { name LockID type S # 字符串类型 } tags { Name terraform-state-lock } } # 【新资源】随机后缀确保S3桶名全球唯一 resource random_id suffix { byte_length 4 }Step 1backend-setup/outputs.tfoutput s3_bucket_name { description State存储的S3桶名配置backend时需要 value aws_s3_bucket.terraform_state.bucket } output dynamodb_table_name { description 状态锁的DynamoDB表名 value aws_dynamodb_table.terraform_lock.name }Step 2main-project/main.tfterraform { required_providers { aws { source hashicorp/aws version ~ 5.0 } } required_version 1.0 # # 【新概念】Backend 配置 # 把 terraform.tfstate 存到 S3 # bucket第一步创建的S3桶名 # keystate文件在桶中的路径 # dynamodb_table锁表名 # backend s3 { bucket tf-practice-state-25d6adcc # 替换为你的桶名 key 03-remote-state/terraform.tfstate # state在S3中的路径 region us-east-1 dynamodb_table tf-practice-state-lock # DynamoDB锁表 encrypt true # 加密存储 } } provider aws { region var.region } # # 【新概念】Data Source查询默认VPC # 每个AWS账号每个区域都有一个默认VPC # 语法data 资源类型 本地名称 { 过滤条件 } # data aws_vpc default { default true } # 【新概念】Data Source查询最新AMI # 好处不用硬编码AMI ID每次apply自动获取最新版本 data aws_ami amazon_linux { most_recent true # 取最新的 owners [amazon] # 只看Amazon官方的 filter { name name values [al2023-ami-2023*-x86_64] # 名称匹配 } filter { name state values [available] # 状态为可用 } } # Data Source查询默认VPC中的子网列表 data aws_subnets default { filter { name vpc-id values [data.aws_vpc.default.id] # 引用上面查到的VPC ID } } # 安全组创建在默认VPC中 resource aws_security_group allow_ssh { name tf-practice-data-source-sg description Allow SSH vpc_id data.aws_vpc.default.id # 【注意】引用data source不是resource ingress { from_port 22 to_port 22 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 { Name tf-practice-data-source-sg } } # EC2实例用data source查到的AMI和子网创建 resource aws_instance web { ami data.aws_ami.amazon_linux.id # 引用查到的最新AMI instance_type t3.micro subnet_id data.aws_subnets.default.ids[0] # 用默认VPC的第一个子网 vpc_security_group_ids [aws_security_group.allow_ssh.id] tags { Name tf-practice-data-source-ec2 } }Step 2main-project/outputs.tfoutput vpc_id { description 默认VPC的IDdata source查询得到 value data.aws_vpc.default.id } output vpc_cidr { description 默认VPC的CIDR value data.aws_vpc.default.cidr_block } output ami_id { description 查询到的最新Amazon Linux 2023 AMI ID value data.aws_ami.amazon_linux.id } output ami_name { description AMI名称 value data.aws_ami.amazon_linux.name } output instance_id { description EC2实例ID value aws_instance.web.id } output instance_public_ip { description EC2公网IP value aws_instance.web.public_ip }新增关键字段说明backend “s3” 配置字段含义示例bucketS3桶名tf-practice-state-25d6adcckeystate文件在桶中的路径03-remote-state/terraform.tfstateregionS3桶所在区域us-east-1dynamodb_tableDynamoDB锁表名tf-practice-state-lockencrypt是否加密存储truedata source 语法data 资源类型 本地名称 { # 过滤条件 } # 引用方式data.资源类型.本地名称.属性data “aws_ami” 常用字段字段含义most_recent true符合条件的取最新的一个ownersAMI的所有者amazon AWS官方filter过滤条件可以有多个S3 Bucket 相关资源资源作用aws_s3_bucket创建桶本身aws_s3_bucket_versioning开启版本控制aws_s3_bucket_server_side_encryption_configuration配置加密DynamoDB Table 关键字段字段含义name表名billing_mode PAY_PER_REQUEST按需计费不用不花钱hash_key LockID主键名Terraform固定要求操作步骤与实际输出Step 1创建S3和DynamoDBcdbackend-setup/ terraform init terraform apply -auto-approve输出Apply complete! Resources: 5 added, 0 changed, 0 destroyed. Outputs: dynamodb_table_name tf-practice-state-lock s3_bucket_name tf-practice-state-25d6adcc拿到桶名tf-practice-state-25d6adcc填入 main-project 的 backend 配置中。Step 2初始化main-project连接S3 backendcdmain-project/ terraform init关键输出Successfully configured the backend s3! Terraform has been successfully initialized!看到Successfully configured the backend s3说明远程backend配置成功。Step 3Plan观察状态锁和data sourceterraform plan关键输出Acquiring state lock. This may take a few moments... ← DynamoDB锁生效 data.aws_vpc.default: Read complete [idvpc-08d6fd2471d76f590] ← 查到默认VPC data.aws_ami.amazon_linux: Read complete [idami-07a5b367e8dc8bd92] ← 查到最新AMI Plan: 2 to add, 0 to change, 0 to destroy. Releasing state lock. This may take a few moments... ← 释放锁Step 4Applyterraform apply -auto-approve输出Apply complete! Resources: 2 added, 0 changed, 0 destroyed. Outputs: ami_id ami-07a5b367e8dc8bd92 ami_name al2023-ami-2023.12.20260803.3-kernel-6.1-x86_64 instance_id i-0bc5bd728ea41fc0a instance_public_ip 54.204.233.51 vpc_cidr 172.31.0.0/16 vpc_id vpc-08d6fd2471d76f590Step 5验证state不在本地# 本地没有tfstate文件lsmain-project/*.tfstate# 输出No such file or directory# state在S3上aws s3lss3://tf-practice-state-25d6adcc/03-remote-state/# 输出2026-08-06 16:02:28 12926 terraform.tfstateStep 6销毁反序销毁# 先销毁main-project它的state在S3上S3不能先删cdmain-project/ terraform destroy -auto-approve# Destroy complete! Resources: 2 destroyed.# 再销毁backend-setupcdbackend-setup/ terraform destroy -auto-approve# Destroy complete! Resources: 5 destroyed.注意销毁顺序必须先销毁使用S3 backend的项目再销毁S3桶本身。否则Terraform找不到state文件会报错。原理讲解远程State的工作流程terraform apply 执行时 1. 从S3下载最新的 terraform.tfstate 2. 在DynamoDB写入锁记录防止其他人同时操作 3. 对比代码和state计算变更 4. 执行变更 5. 更新state文件上传回S3 6. 释放DynamoDB中的锁Data Source vs Resource 对比resourcedata作用创建新资源查询已有资源关键字resourcedata引用前缀无直接aws_vpc.main.id加data.data.aws_vpc.default.iddestroy时会被删除不会被删除使用场景需要Terraform管理生命周期的资源引用别人创建的、或AWS默认提供的资源Data Source 的常见使用场景场景Data Source好处查最新AMIdata aws_ami不用硬编码AMI ID自动获取最新版本引用默认VPCdata aws_vpc不用手动去控制台查VPC ID获取当前账号信息data aws_caller_identity动态获取Account ID查询可用区列表data aws_availability_zones不用硬编码AZ名称引用其他项目的statedata terraform_remote_state跨项目引用资源信息为什么backend-setup的state还在本地先有鸡还是先有蛋的问题你想把state存到S3 → 需要先有S3桶创建S3桶需要Terraform → 这次执行的state存哪→ 只能存本地所以backend-setup是个特殊项目它的state永远在本地。它只负责创建存state的基础设施之后所有其他项目的state都存到它创建的S3桶里。踩坑记录问题1backend配置修改后需要重新init场景修改了bucket名称或key路径后直接 plan/apply。报错Backend initialization required解决每次修改backend配置后必须重新执行terraform init。问题2销毁顺序错误场景先销毁了S3桶再尝试销毁使用该桶的项目。报错Failed to load state: S3 bucket does not exist解决必须先destroy使用远程state的项目再destroy backend基础设施。问题3backend配置不能使用变量场景想在backend块中使用var.bucket_name。报错Variables may not be used here原因backend配置在Terraform最早期阶段加载此时变量还没解析。解决backend配置只能写硬编码值或者用-backend-config参数在init时传入。费用说明资源费用S3 Bucket存state文件几KB几乎为0S3 版本控制几乎为0DynamoDB Table按需计费不用不花钱EC2 t3.micro~$0.0104/小时本次练习总花费 $0.01延伸思考面试常见问题面试问题答案要点Terraform的state存在哪里默认本地生产环境用S3远程存储 DynamoDB锁多人同时执行terraform apply会怎样有DynamoDB锁的话第二个人会被拒绝没有锁会导致state冲突data source和resource有什么区别data只查询不创建resource创建并管理生命周期backend配置能不能用变量不能只能硬编码或用 -backend-config 传入state文件包含敏感信息怎么办S3加密存储 版本控制 限制IAM访问权限小结本篇核心收获远程State解决了team协作和state安全的问题S3存储 DynamoDB锁Data Source让你可以引用已存在的资源不用硬编码IDbackend配置写在terraform块中修改后需要重新init销毁顺序很重要先销毁使用远程state的项目再销毁backend基础设施data source的AMI查询是生产中的最佳实践避免AMI过期问题下一篇预告Terraform实战04变量、本地值与条件表达式下一篇我们将学习用locals定义本地计算值条件表达式三元运算符变量类型约束list、map、objectterraform.tfvars文件的使用让代码更灵活、更可配置参考链接本系列配套代码GitHubTerraform Backend配置文档Terraform Data Sources文档AWS S3 Bucket Terraform文档AWS DynamoDB Table Terraform文档Terraform State管理最佳实践