查看“︁Terraform解说”︁的源代码
←
Terraform解说
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:terraform]] ==main.tf== <pre> terraform { required_version = ">= 1.5.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 6.0" } } } provider "aws" { region = var.aws_region } # -------------------------------------------------- # VPC # -------------------------------------------------- resource "aws_vpc" "lab" { cidr_block = "10.10.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Name = "ssh-port-lab-vpc" } } # -------------------------------------------------- # Public Subnet # -------------------------------------------------- resource "aws_subnet" "public" { vpc_id = aws_vpc.lab.id cidr_block = "10.10.1.0/24" availability_zone = "${var.aws_region}a" map_public_ip_on_launch = true tags = { Name = "ssh-port-lab-public" } } # -------------------------------------------------- # Internet Gateway # -------------------------------------------------- resource "aws_internet_gateway" "lab" { vpc_id = aws_vpc.lab.id tags = { Name = "ssh-port-lab-igw" } } # -------------------------------------------------- # Route Table # -------------------------------------------------- resource "aws_route_table" "public" { vpc_id = aws_vpc.lab.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.lab.id } tags = { Name = "ssh-port-lab-public-rt" } } resource "aws_route_table_association" "public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.public.id } # -------------------------------------------------- # Security Group # -------------------------------------------------- resource "aws_security_group" "ssh" { name = "ssh-port-lab-sg" description = "Allow SSH for SSH port migration lab" vpc_id = aws_vpc.lab.id ingress { description = "SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [var.my_ip] } egress { description = "Allow all outbound traffic" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "ssh-port-lab-sg" } } # -------------------------------------------------- # EC2 Key Pair # -------------------------------------------------- resource "aws_key_pair" "lab" { key_name = var.key_name public_key = file(var.public_key_path) } # -------------------------------------------------- # Amazon Linux 2023 AMI # -------------------------------------------------- data "aws_ssm_parameter" "al2023" { name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64" } # -------------------------------------------------- # Launch Template # -------------------------------------------------- resource "aws_launch_template" "lab" { name_prefix = "ssh-port-lab-" image_id = data.aws_ssm_parameter.al2023.value instance_type = "t3.micro" key_name = aws_key_pair.lab.key_name vpc_security_group_ids = [ aws_security_group.ssh.id ] tag_specifications { resource_type = "instance" tags = { Name = "ssh-port-lab-ec2" } } } # -------------------------------------------------- # Auto Scaling Group # -------------------------------------------------- resource "aws_autoscaling_group" "lab" { name = "ssh-port-lab-asg" min_size = 1 max_size = 1 desired_capacity = 1 vpc_zone_identifier = [ aws_subnet.public.id ] launch_template { id = aws_launch_template.lab.id version = "$Latest" } health_check_type = "EC2" tag { key = "Name" value = "ssh-port-lab-asg-instance" propagate_at_launch = true } } 这个 `main.tf` 很适合拿来理解 Terraform,因为它基本把 AWS Infra 里最常见的几个概念串起来了: **VPC → Subnet → Internet Gateway → Route Table → Security Group → Key Pair → AMI → Launch Template → ASG** 我按你现在这个实验的实际运行路径,从上到下解释。 --- # 1. Terraform 本身的版本和 Provider ```hcl terraform { required_version = ">= 1.5.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 6.0" } } } ``` 这里**不是创建 AWS 资源**,而是在告诉 Terraform: > “这个项目需要什么 Terraform 版本,以及什么 Provider。” --- ### `required_version` ```hcl required_version = ">= 1.5.0" ``` 意思: ```text Terraform >= 1.5.0 ``` 比如: ```text Terraform 1.4 ❌ Terraform 1.5 ✅ Terraform 1.6 ✅ Terraform 1.13 ✅ ``` --- ### `required_providers` ```hcl required_providers { aws = { source = "hashicorp/aws" version = "~> 6.0" } } ``` Terraform 自己并不知道: ```hcl resource "aws_instance" ... ``` 是什么意思。 真正知道 AWS API 怎么调用的是: **AWS Provider** 所以: ```text Terraform │ ▼ AWS Provider │ ▼ AWS API │ ▼ VPC / EC2 / ASG / SG ... ``` --- ### `source` ```hcl source = "hashicorp/aws" ``` 就是: > 使用 HashiCorp 官方的 AWS Provider。 --- ### `version` ```hcl version = "~> 6.0" ``` 表示使用 6.x 系列的 provider,避免 Terraform 随便升级到不兼容的大版本。 --- # 2. Provider ```hcl provider "aws" { region = var.aws_region } ``` 这个才是告诉 AWS Provider: > “我们操作哪个 AWS Region?” 你在 `variables.tf` 中定义了: ```hcl variable "aws_region" { default = "ap-east-1" } ``` 所以最终相当于: ```hcl provider "aws" { region = "ap-east-1" } ``` 也就是: ```text Hong Kong ap-east-1 ``` --- # 3. VPC ```hcl resource "aws_vpc" "lab" { cidr_block = "10.10.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Name = "ssh-port-lab-vpc" } } ``` 这是我们创建的第一个 AWS 资源。 Terraform 的基本语法: ```hcl resource "TYPE" "NAME" { ... } ``` 这里: ```hcl resource "aws_vpc" "lab" ``` 可以理解成: ```text resource type = aws_vpc resource name = lab ``` Terraform 内部引用它的时候: ```hcl aws_vpc.lab ``` --- ### CIDR ```hcl cidr_block = "10.10.0.0/16" ``` 我们的 VPC: ```text 10.10.0.0/16 ``` 范围比较大: ```text 10.10.0.0 ↓ 10.10.255.255 ``` 然后我们后面从里面划一个 subnet: ```text VPC 10.10.0.0/16 │ └── subnet 10.10.1.0/24 ``` --- ### DNS ```hcl enable_dns_hostnames = true enable_dns_support = true ``` 让 VPC 支持 AWS DNS 功能以及实例 DNS hostname。 对于 EC2 / AWS Infra 环境,一般建议开启。 --- # 4. Public Subnet ```hcl resource "aws_subnet" "public" { vpc_id = aws_vpc.lab.id cidr_block = "10.10.1.0/24" availability_zone = "${var.aws_region}a" map_public_ip_on_launch = true tags = { Name = "ssh-port-lab-public" } } ``` 这里开始出现一个非常重要的 Terraform 概念: ```hcl vpc_id = aws_vpc.lab.id ``` 这不是手写: ```hcl vpc_id = "vpc-0123456789" ``` 而是: > 把刚才创建的 VPC 的 ID 自动传给 Subnet。 --- ## `aws_vpc.lab.id` 可以拆成: ```text aws_vpc │ └── lab │ └── id ``` 也就是: ```text 资源类型 → 资源名称 → 属性 ``` Terraform 会自动建立依赖关系: ```text aws_vpc.lab │ ▼ aws_subnet.public ``` 所以 Terraform 知道: > 先创建 VPC,再创建 Subnet。 这就是 Terraform 非常核心的: **Dependency Graph** --- ## Subnet CIDR ```hcl cidr_block = "10.10.1.0/24" ``` 这个 subnet 位于: ```text 10.10.0.0/16 ``` 里面。 所以: ```text VPC 10.10.0.0/16 └── Public Subnet 10.10.1.0/24 ``` --- ## Availability Zone ```hcl availability_zone = "${var.aws_region}a" ``` 如果: ```hcl var.aws_region = "ap-east-1" ``` Terraform 会得到: ```text ap-east-1a ``` --- ## `map_public_ip_on_launch` ```hcl map_public_ip_on_launch = true ``` 意思是: > 在这个 subnet 中启动的 EC2,默认分配 Public IPv4。 所以我们后面才能: ```bash ssh ec2-user@18.x.x.x ``` --- # 5. Internet Gateway ```hcl resource "aws_internet_gateway" "lab" { vpc_id = aws_vpc.lab.id tags = { Name = "ssh-port-lab-igw" } } ``` Internet Gateway: **IGW** 它负责让 VPC 和 Internet 之间进行 Internet connectivity。 关系: ```text Internet │ ▼ Internet Gateway │ ▼ VPC │ ▼ Subnet │ ▼ EC2 ``` 注意: **仅仅创建 IGW 并不能让 EC2 上网。** 还需要 Route Table。 --- # 6. Route Table ```hcl resource "aws_route_table" "public" { vpc_id = aws_vpc.lab.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.lab.id } tags = { Name = "ssh-port-lab-public-rt" } } ``` 这里非常重要。 ```hcl cidr_block = "0.0.0.0/0" ``` 意思: > 所有不是本地 VPC 的 IPv4 流量。 然后: ```hcl gateway_id = aws_internet_gateway.lab.id ``` 意思: > 把这些流量送到 Internet Gateway。 所以: ```text 0.0.0.0/0 │ ▼ Internet Gateway ``` --- # 7. Route Table Association 前面只是创建了 Route Table。 现在要告诉 AWS: > “这个 Route Table 应该用于哪个 Subnet?” ```hcl resource "aws_route_table_association" "public" { subnet_id = aws_subnet.public.id route_table_id = aws_route_table.public.id } ``` 最终: ```text Public Subnet 10.10.1.0/24 │ │ association ▼ Public Route Table │ └── 0.0.0.0/0 → IGW ``` 于是这个 subnet 才真正成为 Public Subnet。 --- # 8. Security Group ```hcl resource "aws_security_group" "ssh" { name = "ssh-port-lab-sg" description = "Allow SSH for SSH port migration lab" vpc_id = aws_vpc.lab.id ``` Security Group 可以理解成: > EC2 的虚拟防火墙。 我们这个实验第一阶段只允许 SSH: ```text Internet │ │ TCP 22 ▼ Security Group │ ▼ EC2 ``` --- ## Inbound ```hcl ingress { description = "SSH" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = [var.my_ip] } ``` 这里非常重要。 ```hcl from_port = 22 to_port = 22 ``` 允许: ```text TCP/22 ``` --- ### `var.my_ip` 比如: ```hcl my_ip = "1.2.3.4/32" ``` 那么只有: ```text 1.2.3.4 ``` 可以访问: ```text EC2:22 ``` 而不是: ```hcl 0.0.0.0/0 ``` 这样实验环境安全很多。 --- # 9. Egress ```hcl egress { description = "Allow all outbound traffic" from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } ``` 这是: > EC2 出去的流量全部允许。 例如: ```text EC2 │ ├── yum ├── curl ├── AWS API └── Internet ``` 都可以出去。 --- # 10. Key Pair ```hcl resource "aws_key_pair" "lab" { key_name = var.key_name public_key = file(var.public_key_path) } ``` 这个资源非常值得理解。 你本地: ```text ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub ``` Terraform: ```hcl public_key = file(var.public_key_path) ``` 读取: ```text id_ed25519.pub ``` 然后上传到 AWS: ```text AWS EC2 Key Pair ``` --- ## 注意一个非常重要的事情 Terraform **没有把你的 private key 上传 AWS**。 流程是: ```text 你的电脑 id_ed25519 ← Private Key │ │ 不上传 X id_ed25519.pub ← Public Key │ ▼ Terraform │ ▼ AWS Key Pair ``` 所以你执行: ```bash ssh -i ~/.ssh/id_ed25519 ec2-user@18.x.x.x ``` 的时候: ```text 你的 private key │ ▼ EC2 sshd │ ▼ 验证 AWS 中对应的 public key ``` --- # 11. AL2023 AMI 这里是整个代码里一个很典型的 **data source**: ```hcl data "aws_ssm_parameter" "al2023" { name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64" } ``` 注意: ```text resource ``` 和: ```text data ``` 是不一样的。 --- ## `resource` 例如: ```hcl resource "aws_vpc" "lab" ``` 意思: > Terraform **创建/管理**这个 AWS 资源。 --- ## `data` ```hcl data "aws_ssm_parameter" "al2023" ``` 意思: > Terraform **查询已有的信息**。 这里 Terraform 查询 AWS Systems Manager Parameter Store: ```text /aws/service/ami-amazon-linux-latest/ al2023-ami-kernel-default-x86_64 ``` AWS 会告诉 Terraform 当前 Amazon Linux 2023 的 AMI ID。 例如可能类似: ```text ami-xxxxxxxxxxxxxxxxx ``` 这样我们就不用硬编码: ```hcl image_id = "ami-123456789" ``` 因为 Amazon Linux AMI 会更新。 --- # 12. Launch Template ```hcl resource "aws_launch_template" "lab" { name_prefix = "ssh-port-lab-" image_id = data.aws_ssm_parameter.al2023.value instance_type = "t3.micro" key_name = aws_key_pair.lab.key_name vpc_security_group_ids = [ aws_security_group.ssh.id ] ``` 这个资源非常重要。 **Launch Template 可以理解成 EC2 的“启动模板”。** 它定义: ```text EC2 应该长什么样? ``` 包括: ```text AMI Instance Type SSH Key Security Group User Data Disk Network ... ``` --- ## AMI ```hcl image_id = data.aws_ssm_parameter.al2023.value ``` 刚才: ```text data.aws_ssm_parameter.al2023 ``` 查到的是 SSM Parameter。 它的: ```text .value ``` 就是实际 AMI ID。 所以: ```text SSM Parameter │ ▼ AMI ID │ ▼ Launch Template │ ▼ EC2 ``` --- # 13. Instance Type ```hcl instance_type = "t3.micro" ``` 就是: ```text EC2 = t3.micro ``` 我们的实验不需要很大的机器。 --- # 14. Key Name ```hcl key_name = aws_key_pair.lab.key_name ``` 又出现了 Terraform dependency。 它不是: ```hcl key_name = "ssh-port-lab-key" ``` 而是: ```text aws_key_pair.lab │ └── key_name ``` 所以 Terraform 自动知道: ```text Key Pair ↓ Launch Template ↓ EC2 ``` --- # 15. Security Group ```hcl vpc_security_group_ids = [ aws_security_group.ssh.id ] ``` 就是把刚才创建的: ```text ssh-port-lab-sg ``` 绑定到 EC2。 所以最终: ```text EC2 │ └── Security Group │ └── TCP 22 ``` --- # 16. Tag Specification ```hcl tag_specifications { resource_type = "instance" tags = { Name = "ssh-port-lab-ec2" } } ``` 给 EC2 打 Tag: ```text Name = ssh-port-lab-ec2 ``` 这样 AWS Console 里面很容易找到。 --- # 17. ASG 最后是: ```hcl resource "aws_autoscaling_group" "lab" { name = "ssh-port-lab-asg" min_size = 1 max_size = 1 desired_capacity = 1 ``` 这就是我们真正要实验的 **Auto Scaling Group**。 --- ## 三个数字 ```hcl min_size = 1 max_size = 1 desired_capacity = 1 ``` 现在: ```text 最少 1 最多 1 期望 1 ``` 所以: ```text ASG │ └── EC2 × 1 ``` 如果这台 EC2 被人为 terminate: ```text EC2 ↓ terminated ↓ ASG 发现少了一台 ↓ 重新创建 EC2 ``` 这就是 ASG 的核心价值。 --- # 18. ASG 使用哪个 Subnet? ```hcl vpc_zone_identifier = [ aws_subnet.public.id ] ``` 意思: > ASG 创建的 EC2 放进这个 subnet。 所以: ```text ASG │ └── Public Subnet │ └── EC2 ``` --- # 19. ASG 使用哪个 Launch Template? ```hcl launch_template { id = aws_launch_template.lab.id version = "$Latest" } ``` 这个特别重要。 意思: > ASG 创建 EC2 时,按照这个 Launch Template 来创建。 完整关系: ```text ┌── Security Group │ ├── Key Pair │ └── AL2023 AMI │ ▼ Launch Template │ ▼ ASG │ ▼ EC2 ``` 这就是为什么我们没有直接写: ```hcl resource "aws_instance" ``` 而是: ```text Launch Template + ASG ``` 因为我们的最终实验就是要研究: **ASG 中 EC2 的 SSH 配置发生变化以后,如何保持 SFTP 用户侧连接不变。** --- # 20. Health Check ```hcl health_check_type = "EC2" ``` 意思 ASG 使用 EC2 状态来判断实例是否健康。 如果 EC2: ```text running → unhealthy/terminated ``` ASG 可以重新补一台。 --- # 21. ASG Tag ```hcl tag { key = "Name" value = "ssh-port-lab-asg-instance" propagate_at_launch = true } ``` 这里: ```hcl propagate_at_launch = true ``` 非常重要。 意思: > ASG 创建 EC2 的时候,把这个 Tag 自动传给 EC2。 所以: ```text ASG │ └── Name=ssh-port-lab-asg-instance │ ▼ EC2 Instance ``` --- # 最后把整个 `main.tf` 串起来 你现在不要把这些 Terraform resource 当成孤立的代码。 真正应该理解成这个依赖图: ```text AWS Provider │ ▼ VPC 10.10.0.0/16 │ ┌───────────┴───────────┐ ▼ ▼ Internet Gateway Subnet │ 10.10.1.0/24 │ │ │ ▼ │ Route Table │ │ │ 0.0.0.0/0 │ │ └───────────────────────┘ Security Group │ │ TCP 22 ▼ Key Pair │ │ ▼ AL2023 AMI │ ▼ Launch Template │ ┌─────┴──────┐ │ │ ▼ ▼ Key Pair Security Group │ │ └─────┬──────┘ ▼ ASG │ min=1 max=1 │ ▼ EC2 × 1 │ ▼ SSH :22 ``` ### 你现在最应该记住的 5 个 Terraform 概念 结合你现在做 AWS Infra,我建议先把这五个吃透: | Terraform | 你这个实验中的例子 | 含义 | | ----------- | --------------------------------- | ------------------ | | `resource` | `aws_vpc.lab` | **创建/管理** AWS 资源 | | `data` | `aws_ssm_parameter.al2023` | **查询**已有信息 | | `variable` | `var.aws_region` | 输入参数 | | `attribute` | `aws_vpc.lab.id` | 获取资源的属性 | | dependency | `aws_subnet.public → aws_vpc.lab` | Terraform 自动建立资源依赖 | 尤其是: ```hcl vpc_id = aws_vpc.lab.id ``` ```hcl key_name = aws_key_pair.lab.key_name ``` ```hcl vpc_security_group_ids = [ aws_security_group.ssh.id ] ``` ```hcl image_id = data.aws_ssm_parameter.al2023.value ``` 这四种写法是你以后阅读公司 Terraform `.tf` 文件时**非常常见的模式**。 另外,你这次 `terraform plan` 显示 **9 to add**,本质上就是 Terraform 根据这些资源和依赖关系,计算出它需要创建的 AWS 对象。 </pre>
返回
Terraform解说
。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
查看
阅读
查看源代码
查看历史
更多
搜索
导航
首页
我的导航
关于我
shell
python
ops
linuxchina.net
blog.linuxchina
最近更改
随机页面
帮助
工具
链入页面
相关更改
特殊页面
页面信息