Terraform on AWS 入门实战:从 EC2 实例部署到状态管理、CI/CD、成本估算与模块复用

【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 【免费下载链接】refine 项目地址: https://gitcode.com/GitHub_Trending/re/refine

本篇指南聚焦于使用 HashiCorp 的 Terraform 在 Amazon Web Services(AWS)上做基础设施即代码(IaC)的完整落地路径。你将掌握从 AWS 凭证配置、编写第一个 main.tf 部署 EC2 实例,到 Terraform 状态远端化、多环境 Workspace 管理、CI/CD 自动化、资源依赖处理、成本估算与密钥管理的实战方案,读完即可独立完成一套可维护、可协作、可审计的 AWS 基础设施交付流程。本文整理自 Refine 文档站的生态集成博客(documentation/blog/2024-09-12-terraform-aws.md),并结合仓库内的文档站配置做了路径与上下文的校正。

为什么用 Terraform 管理 AWS 基础设施

随着应用规模扩大,跨多环境、多区域的基础设施管理成为运维痛点:手工在控制台上点选资源既枯燥又易错,而自行编写脚本又需要大量工程投入。Terraform 这类 IaC 工具给出的解法是——用声明式的可复用配置来定义一切资源,从存储桶到 Kubernetes 集群都可以通过配置文件拉起。

本指南面向初学者,按以下脉络展开:

  1. 前置条件:AWS 账户与凭证、AWS CLI、Terraform 安装;
  2. 为 Terraform 配置 AWS 访问凭证;
  3. 编写第一个 Terraform 配置并部署一台 EC2 实例;
  4. 状态管理(S3 远端 State)、多环境 Workspaces、安全最佳实践;
  5. 用 CI/CD 流水线自动化 Terraform;
  6. 处理资源依赖、修改已有基础设施、销毁清理;
  7. 成本估算、敏感数据管理、模块复用。

无论你是已经在用 AWS、还是想以 IaC 方式探索 AWS,这套流程都能覆盖绝大多数基础设施的交付场景。

前置条件

开始用 Terraform 管理 AWS 基础设施之前,需要准备三样东西:

AWS 账户与凭证

需要一个 AWS 账户(免费注册即可)。创建账户时会得到一个拥有全部权限的 root 用户。

按安全最佳实践,你应该为 Terraform 单独创建一个权限受限的 IAM 用户:在 IAM 服务中新建一个用户,并妥善保存生成的 Access KeySecret Access Key——Terraform 将使用这对密钥对 AWS 账户进行身份认证与操作。

AWS CLI

AWS CLI 允许你从命令行管理 AWS 服务。按你的操作系统下载安装后,运行 aws configure,按提示填入 Access Key 完成配置:

aws configure

Terraform 本体

Terraform 以单一二进制文件形式分发。为你的系统下载对应的 Terraform 二进制后,即可在终端执行 Terraform 命令。记得将其加入 PATH 环境变量,保证全局可用:

# 验证安装
terraform version

配置 Terraform 使用的 AWS 凭证

账户与工具就绪后,需要为 Terraform 配置与 AWS 交互的访问凭证。常见的两种方式是:

方式一:环境变量(Access Key / Secret Key)

创建 IAM 用户时 AWS 生成的 Access Key 与 Secret Key,相当于程序化访问 AWS API 的"用户名 + 密码"。可以直接通过环境变量传给 Terraform:

export AWS_ACCESS_KEY_ID=<your_access_key>
export AWS_SECRET_ACCESS_KEY=<your_secret_key>

Terraform 在需要凭证时会主动检查这两个环境变量。

方式二:~/.aws/credentials 文件

另一种做法是把密钥保存进 AWS CLI 默认读取的本地凭证文件(Linux/macOS 上位于 ~/.aws/credentials):

[default]
aws_access_key_id = <your_access_key>
aws_secret_access_key = <your_secret_key>

Terraform 会自动检查该文件。两种途径配置好之后,Terraform 就具备在你的 AWS 账户中构建基础设施的能力了。

补充说明:Terraform 的 AWS Provider 实际上遵循一套标准凭证链(环境变量 → ~/.aws/credentials → 实例元数据 / 角色等),上述两种方式是最常用的前两级。原文仅演示了前两种,实际项目中还会用到 IAM Role、~/.aws/config 中切换 profile 等途径,可按需扩展。

编写第一个 Terraform 配置:部署 EC2 实例

Terraform 代码使用 HCL(HashiCorp Configuration Language) 编写,配置文件以 .tf 结尾,用于声明 Provider 与资源。

声明 AWS Provider

按 AWS Provider 的文档要求,创建 main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

要点说明:

  • required_providers 显式声明依赖的 Provider 及其来源与版本约束。~> 5.0 表示兼容 5.x 系列;对生产环境,更严格的版本钉扎(如精确到小版本)可以减少意外升级带来的破坏。
  • provider "aws" 块中的 region = "us-east-1" 决定了后续资源默认创建在哪个区域。

定义 EC2 实例资源

接下来定义一台 EC2 实例:

resource "aws_instance" "refine-dev" {
  ami           = "ami-0fc5d935ebf8bc3bc"
  instance_type = "t2.micro"

  tags = {
    Name = "refine-dev"
  }
}
  • ami:镜像 ID。原文使用了一个 us-east-1 区域的免费 Linux 镜像(Ubuntu)。注意 AMI ID 是区域相关的,换区域时需要在 EC2 控制台的 AMI 列表里重新确认,或使用 data 源按名称动态查询,这是原文没有展开的一个常见坑点;
  • instance_type = "t2.micro":属于 AWS 免费额度(Free Tier)覆盖的规格,适合入门实验,避免产生意外账单;
  • tags 中的 Name 让实例在控制台中可读、可检索。

合并后完整的 main.tf 如下:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

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

resource "aws_instance" "refine-dev" {
  ami           = "ami-0fc5d935ebf8bc3bc"
  instance_type = "t2.micro"
}

init / plan / apply:Terraform 的标准工作流

配置就绪后,按三步执行:

terraform init

初始化 Terraform 工作目录,并下载 AWS Provider 插件。

terraform plan

Terraform 读取配置文件,计算并输出将要创建的基础设施(资源清单与变更计划)。确认计划无误后执行:

terraform apply

Terraform 调用 AWS API 实际创建出这台 EC2 实例。init → plan → apply 这条链路是 Terraform 的核心循环,后续的修改、销毁都围绕它展开。

状态管理:把 State 放到 S3 远端后端

Terraform 通过 State 文件追踪它所管理的基础设施。State 记录了"配置应该是什么样"与"云上实际是什么样"之间的映射,是 Terraform 做差异计算(plan)的依据。

默认 State 保存在本地 .terraform.tfstate 文件中,这在团队协作中会引发覆盖与冲突问题。推荐做法是把 State 存储到 AWS S3 等远端后端,让团队成员共享同一份状态:

terraform {
  backend "s3" {
    bucket = "my-terraform-state-bucket"
    key    = "terraform.tfstate"
    region = "us-west-2"
  }
}

配置远端 State 后,团队成员读写的是同一份状态文件,避免互相覆盖。生产环境通常还会在 S3 backend 上叠加 DynamoDB 状态锁dynamodb_table 参数)来防止两个 apply 并发执行;从源码结构看这类配置属于 backend 块的可选参数,可按团队规模逐步引入。

Workspaces:一份配置管理多套环境

Workspaces 允许你在同一份配置上维护 dev、staging、production 等独立环境——每个 Workspace 拥有独立的 State,而无需复制多份配置文件:

terraform workspace new dev
terraform workspace new prod
terraform workspace select dev

select 切换后,后续 plan / apply 操作的都将是该 Workspace 的独立状态。这为"一套配置、多套环境"提供了轻量的多环境管理方案;配合变量文件(如 dev.tfvars / prod.tfvars)可以实现环境与参数解耦。

安全最佳实践:IAM 角色与策略

基础设施的安全核心在于最小权限。两个方面值得在配置中落实:

为 EC2 实例绑定 IAM 角色

避免把长期有效的 Access Key 硬编码进实例或代码里,改用 IAM 实例角色,让实例通过角色临时获取凭证。原文给出的示例:

resource "aws_iam_instance_profile" "ec2_profile" {
  name = "example_profile"
  role = aws_iam_role.ec2_role.name
}

resource "aws_instance" "example" {
  ami                    = "ami-0fc5d935ebf8bc3bc"
  instance_type          = "t2.micro"
  iam_instance_profile   = aws_iam_instance_profile.ec2_profile.name
}

注意:该示例引用了 aws_iam_role.ec2_role,实际使用中还需要声明对应的 aws_iam_role 资源(含角色信任策略与权限策略),并遵循最小权限原则——只授予实例真正需要的 API 权限。

策略执行层

原文还提到可引入 Terraform Sentinel(HashiCorp 的策略即代码产品),在 planapply 阶段强制执行安全策略,例如禁止创建公网暴露的高权限资源。这类策略门禁适合在组织层面统一部署。

用 CI/CD 流水线自动化 Terraform

把 Terraform 接入 CI/CD 流水线(如 GitHub Actions、Jenkins)后,基础设施变更与应用代码一样走"提交 → 评审 → 自动部署"的闭环。原文给出的 GitHub Actions 工作流示例:

name: Terraform Apply
on:
  push:
    branches:
      - main

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1
      - name: Terraform Init
        run: terraform init
      - name: Terraform Plan
        run: terraform plan
      - name: Terraform Apply
        run: terraform apply -auto-approve

落地时的几点工程化补充:

  • Secrets 注入:CI 环境中 AWS 凭证应通过流水线的密钥管理(如 Actions Secrets)注入为 AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY 环境变量,而不是写死在 workflow 里;
  • init 参数:使用 S3 远端 State 时,terraform init 会自动读取配置中的 backend 块完成迁移,无需额外参数;
  • 审批环节-auto-approve 适合低风险的 dev 环境;对生产环境,更稳妥的做法是流水线只跑 plan 并输出结果,apply 走人工审批或受保护分支触发。

自动化后,团队的基础设施交付更快、更可复现。

处理资源依赖

Terraform 会根据配置中的引用关系自动推导资源依赖并决定创建顺序;在引用关系无法表达依赖时,才需要显式声明。原文用 depends_on 举例:

resource "aws_instance" "web" {
  ami           = "ami-0fc5d935ebf8bc3bc"
  instance_type = "t2.micro"
}

resource "aws_elb" "web_elb" {
  depends_on = [aws_instance.web]
  instances  = [aws_instance.web.id]
}

其中 depends_on 强制 web_elbweb 实例创建完成后再创建;而 instances 通过引用 aws_instance.web.id 也天然建立了依赖。实践中优先依赖"引用即依赖"的隐式推导,仅在引用不成立(例如仅需要执行顺序保证)时才使用 depends_on

说明:原文示例中 aws_elb(第一代负载均衡器)与 instance/instances 参数是旧版资源写法,新版资源 aws_lb(Application/Network LB)的挂载方式不同。作为理解 depends_on 语义的示例是够用的,生产环境请以当前 Provider 版本的资源文档为准。

修改基础设施:变更即代码

IaC 的关键价值之一是基础设施可以随代码持续演进。原文演示了一个小改动:给已存在的实例补充标签:

resource "aws_instance" "refine-dev" {
  ami           = "ami-0fc5d935ebf8bc3bc"
  instance_type = "t2.micro"

  tags = {
    Name = "refine-dev"
  }
}

保存后重新走一遍循环:

terraform plan
terraform apply

plan 会输出为实例添加标签所需的变更,确认后 apply 完成更新。这里需要澄清原文的一处表述:添加 tags 属于 Terraform 的原地更新(in-place update),实例不会被销毁重建;只有当变更属性无法原地修改时(例如某些网络或镜像类变更),Terraform 才会销毁并重建资源。另外,原文称更新过程会保持"同一 IP 地址"——对普通公网 IP 这并不成立,若需要固定 IP,应额外绑定 Elastic IP 并通过 aws_eip 资源管理。

用代码管理基础设施的直接收益:协作更顺畅、可接入版本控制、变更可审计可自动化——所有修改都能通过 Pull Request 评审,而不是在生产控制台上"手工热修"。

清理资源:terraform destroy

实验完成后,及时销毁资源以避免持续计费是最佳实践:

terraform destroy

Terraform 会列出所有将被销毁的资源并要求确认,输入 yes 后即可删除 AWS 上对应的基础设施(本文场景中即那台 EC2 实例及其关联的网络安全组)。

这正是声明式配置相对控制台操作的又一优势:Terraform 依据 State 精确知道"它创建过什么、要删什么",你不必手工排查孤儿资源,也不必手工解开纠缠的依赖关系。

需要重建时,随时重新 terraform apply 即可——配置代码就是唯一事实来源(single source of truth)。

成本估算:部署前用 Infracost 预知账单

意外的云账单是 IaC 流程中常见的问题。可以在部署前使用 Infracost 对 Terraform 配置做成本估算,提前看清基础设施决策的费用影响:

infracost breakdown --path=./path_to_your_terraform

它会解析你的 Terraform 配置(或 plan 文件),输出各项资源的预估月成本。将 Infracost 接入 plan 阶段或 CI 流水线后,成本影响就可以像代码评审意见一样被讨论和把关。

管理与 Secrets:别把密钥写进配置

访问密钥等敏感信息不应硬编码进 .tf 文件。两种常用手段:

环境变量传递

export AWS_ACCESS_KEY_ID=<your_access_key>
export AWS_SECRET_ACCESS_KEY=<your_secret_key>

声明 sensitive 变量

在 Terraform 配置中把变量标记为敏感,plan / apply 输出会自动隐藏其值:

variable "secret_key" {
  type      = string
  sensitive = true
}

调用时通过 TF_VAR_ 前缀环境变量传值(如 export TF_VAR_secret_key=...)或经 CI 密钥管理注入。对更复杂的密钥体系,可进一步接入 AWS Secrets Manager,让密钥的轮换与分发由 AWS 侧统一管理。

模块(Modules):复用与组织基础设施代码

模块是 Terraform 复用和整理基础设施代码的基本单元,对 EC2 实例、VPC 这类重复性搭建尤其有用,能让代码保持 DRY(Don't Repeat Yourself)。原文的模块调用示例:

module "ec2_instance" {
  source        = "./modules/ec2"
  instance_type = "t2.micro"
  ami           = "ami-0fc5d935ebf8bc3bc"
  tags = {
    Name = "refine-dev"
  }
}

source 指向本地模块目录(也可以是远程模块地址),模块内部的 variable / output 定义其接口。通过模块,团队可以把标准组件(如"合规 VPC + 子网 + 安全组")沉淀成可复用单元,避免重复造轮子。

小结

回到 IaC 的价值主线:配置代码是基础设施的唯一事实来源。需要复现一个月前搭建的环境?直接 apply 那个历史提交即可,不必翻阅口口相传的运维笔记;变更统一走 Pull Request,与 CI/CD 流水线无缝集成,告别的正是"雪花式"的生产热修。

推荐的落地路径:先把 Terraform 配置纳入版本控制、对团队开放协作,再逐步把更多云上基础设施转化为代码——从单台 EC2 起步,最终具备用 Terraform 管理生产应用基础设施的能力。

本文整理自 Refine 文档站博客 documentation/blog/2024-09-12-terraform-aws.md(最后更新于 2024-09-12,补充了状态管理、模块、密钥、Workspace、安全、CI/CD、依赖与成本估算章节),该文档随 Refine 文档站(Docusaurus 构建,站点配置见 documentation/docusaurus.config.js)对外发布。文中 Terraform 命令与 HCL 示例可直接复制使用;AMI ID、Provider 版本与 aws_elb 等 AWS 侧细节请以当前 Provider 版本和你所在区域的实际资源为准。

【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 【免费下载链接】refine 项目地址: https://gitcode.com/GitHub_Trending/re/refine

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐