Terraform 进阶指南
这是在部署内部实践中使用的资料。我决定公开分享一下。
Terraform 是什么
这是一种工具,用于以代码管理HashiCorp公司提供的各种云服务。通过将基础设施等配置转化为代码,可以更容易地进行协作、重复利用和版本控制。此外,它还具有许多方便的功能,例如dry run和资源依赖性可视化,用于实现所谓的基础设施即代码。
HashiCorp的Terraform
太长不看
.tf文件中描述了基础设施的配置,并且一旦运行命令,该工具将根据该配置构建基础设施。
建立环境
创建Terraform IAM用户
创建一个用于Terraform的IAM用户。请参考以下URL创建IAM用户。由于这是实践操作,我们将创建一个具有Admin权限的用户。
由于SecretAccessKey不会重新生成,请务必妥善保存,如果丢失请重新创建账户。
创建IAM用户(控制台)
安装AWS CLI
请安装AWS CLI并进行配置。请参考以下URL获取安装方法。
在macOS上安装AWS CLI
配置AWS CLI
完成安装后,进行AWS CLI配置,并确认能够执行AWS CLI。
$ aws --version
$ aws configure
$ aws s3api list-buckets # S3 のバケット一覧を表示するコマンド
通过 tfenv 进行安装
由于Terraform更新频繁且仍存在许多错误,因此强烈建议使用tfenv,使得更轻松地切换版本。
$ brew install tfenv
$ tfenv -v
$ tfenv list-remote # インストール可能なバージョンをリモートから取得して列挙します。
$ tfenv install 0.12.10 # 0.12.10 をインストール
$ tfenv use 0.12.10 # 0.12.10 を使用する
$ tfenv list # インストール済み
$ terraform --version # バージョン一覧
通过创建.terraform-version文件,可以实现类似于.ruby-version的版本管理等功能。
$ touch .terraform-version
$ tfenv use 0.12.10 # このディレクトリ以下で 0.12.10 を使用する
你好,世界。
用Terraform创建一个名为HelloWorld的S3存储桶。
$ mkdir handson
$ cd handson
$ touch main.tf
请创建一个名为main.tf的文件,并按照以下方式进行描述。
在tekitouna.domain.nyuryoku的位置,将其替换为S3存储桶的名称,并使用可作为URL的字符串作为全局唯一的任意值。
# Provider
provider "aws" {
profile = "default"
region = "ap-northeast-1"
}
# Resource
resource "aws_s3_bucket" "tekitouna_domain_nyuryoku" {
bucket = "tekitouna.domain.nyuryoku"
}
使用terraform init命令下载AWS提供者,并使用terraform plan命令查看在main.tf中新创建的资源。
$ terraform init
$ terraform plan
将显示有关新创建资源的信息,可以看到一个名为tekitouna.domain.nyuryoku的新bucket被创建。
Terraform will perform the following actions:
# aws_s3_bucket.tekitouna_domain_nyuryoku will be created
+ resource "aws_s3_bucket" "tekitouna_domain_nyuryoku" {
+ acceleration_status = (known after apply)
+ acl = "private"
+ arn = (known after apply)
+ bucket = "tekitouna.domain.nyuryoku"
+ bucket_domain_name = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
+ versioning {
+ enabled = (known after apply)
+ mfa_delete = (known after apply)
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
如果没有问题,执行terraform apply命令来创建S3存储桶。
$ terraform apply
执行以下命令可以确认新创建了一个S3存储桶。
aws s3api list-buckets | grep tekitouna.domain.nyuryoku
成功应用后,将生成 terraform.tfstate 文件。terraform.tfstate 中记录了 Terraform 管理的基础设施的状态。由于这是一个实践操作,我们将跳过详细的解释,但在多人开发中,建议将 tfstate 文件放置在 S3 等位置上。
使用Terraform将tfstate文件存储在S3中进行管理
{
"version": 4,
"terraform_version": "0.12.10",
"serial": 1,
"lineage": "6a7bf262-a78d-decf-166f-9f1eac238641",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_s3_bucket",
"name": "tekitouna_domain_nyuryoku",
"provider": "provider.aws",
"instances": [
{
"schema_version": 0,
"attributes": {
"acceleration_status": "",
"acl": "private",
"arn": "arn:aws:s3:::tekitouna.domain.nyuryoku.xxxx",
"bucket": "tekitouna.domain.nyuryoku.xxxx",
"bucket_domain_name": "tekitouna.domain.nyuryoku.xxxx.s3.amazonaws.com",
"bucket_prefix": null,
"bucket_regional_domain_name": "tekitouna.domain.nyuryoku.xxxx.s3.ap-northeast-1.amazonaws.com",
"cors_rule": [],
"force_destroy": false,
"hosted_zone_id": "Z2M4EHUR26P7ZW",
"id": "tekitouna.domain.nyuryoku.xxxx",
"lifecycle_rule": [],
"logging": [],
"object_lock_configuration": [],
"policy": null,
"region": "ap-northeast-1",
"replication_configuration": [],
"request_payer": "BucketOwner",
"server_side_encryption_configuration": [],
"tags": null,
"versioning": [
{
"enabled": false,
"mfa_delete": false
}
],
"website": [],
"website_domain": null,
"website_endpoint": null
},
"private": "bnVsbA=="
}
]
}
]
}
接下来将删除创建的存储桶。
$ terraform destroy
将显示被删除资源的内容。
Terraform will perform the following actions:
# aws_s3_bucket.tekitouna_domain_nyuryoku will be destroyed
- resource "aws_s3_bucket" "tekitouna_domain_nyuryoku" {
- acl = "private" -> null
- arn = "arn:aws:s3:::tekitouna.domain.nyuryoku.xxxx" -> null
- bucket = "tekitouna.domain.nyuryoku.xxxx" -> null
- bucket_domain_name = "tekitouna.domain.nyuryoku.xxxx.s3.amazonaws.com" -> null
- bucket_regional_domain_name = "tekitouna.domain.nyuryoku.xxxx.s3.ap-northeast-1.amazonaws.com" -> null
- force_destroy = false -> null
- hosted_zone_id = "Z2M4EHUR26P7ZW" -> null
- id = "tekitouna.domain.nyuryoku.xxxx" -> null
- region = "ap-northeast-1" -> null
- request_payer = "BucketOwner" -> null
- tags = {} -> null
- versioning {
- enabled = false -> null
- mfa_delete = false -> null
}
}
Plan: 0 to add, 0 to change, 1 to destroy.
如果更改内容没有问题,将执行操作。
现在可以通过 Terraform 进行创建和删除。
尝试将从控制台创建的资源导入到Terraform。
请参考以下图片中的步骤创建一个作为Web服务器公开的S3存储桶。

设置桶策略。

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::tekitouna.domain.nyuryoku/*"
}
]
}
完成设置策略后,创建名为index.html、error.html的html文件,并将其更新到存储桶中。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<h1>タイトル</h1>
<p>Amazon S3を使って静的なウェブページを公開してみた</p>
</body>
</html>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
<h1>Error</h1>
<p>エラーページです</p>
</body>
</html>
等一会儿后,访问S3文件的URL,确认文件是否显示出来。
以下是两个网页的链接:
1. http://tekitouna.domain.nyuryoku.s3-website-ap-northeast-1.amazonaws.com/index.html
2. http://tekitouna.domain.nyuryoku.s3-website-ap-northeast-1.amazonaws.com/error.html
我打算使用Terraform来管理接下来创建的S3存储桶。
使用以下命令,将创建的S3存储桶纳入Terraform的管理范围内。
$ terraform import aws_s3_bucket.tekitouna_domain_nyuryoku tekitouna.domain.nyuryoku
使用计划命令来确认 main.tf 文件与手动创建的 S3 存储桶之间的差异。
$ terraform plan
填补差异 -> 执行 terraform plan -> 反复执行填补差异的操作。
由于无法消除以下的差异,所以我们忽略了它。
+ acl = "none"
+ force_destroy = true
我完成了。刚才的手工操作已经成功转化为代码了。
resource "aws_s3_bucket" "tekitouna_domain_nyuryoku" {
bucket = "tekitouna.domain.nyuryoku"
website {
index_document = "index.html"
}
}
resource "aws_s3_bucket_policy" "tekitouna_domain_nyuryoku" {
bucket = aws_s3_bucket.tekitouna_domain_nyuryoku.id
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::tekitouna.domain.nyuryoku/*"
}
]
}
POLICY
}
主要.tf文件和S3存储桶之间已经没有差异。
我們試著新增[附加功能]。
我创建了一个在之前作为Web服务器发布的S3存储桶。
然而,当访问不存在的页面时,会显示“Access Denied”错误。
我将设置当访问不存在的页面时,将被重定向到错误页面。
重定向机制已在S3中准备就绪。
请参考以下网站,在控制台上进行设置。
当使用S3的静态网站时,在发生404错误时,将用户重定向到另一个域名。
设定后,执行terraform plan命令来确认当前的main.tf文件与S3存储桶之间的差异。
# aws_s3_bucket.tekitouna_domain_nyuryoku will be updated in-place
~ resource "aws_s3_bucket" "tekitouna_domain_nyuryoku" {
acl = "private"
arn = "arn:aws:s3:::tekitouna.domain.nyuryoku"
bucket = "tekitouna.domain.nyuryoku"
bucket_domain_name = "tekitouna.domain.nyuryoku.s3.amazonaws.com"
bucket_regional_domain_name = "tekitouna.domain.nyuryoku.s3.ap-northeast-1.amazonaws.com"
force_destroy = false
hosted_zone_id = "Z2M4EHUR26P7ZW"
id = "tekitouna.domain.nyuryoku"
region = "ap-northeast-1"
request_payer = "BucketOwner"
tags = {}
website_domain = "s3-website-ap-northeast-1.amazonaws.com"
website_endpoint = "tekitouna.domain.nyuryoku.s3-website-ap-northeast-1.amazonaws.com"
versioning {
enabled = false
mfa_delete = false
}
~ website {
error_document = "error.html"
index_document = "index.html"
- routing_rules = jsonencode(
[
- {
- Condition = {
- HttpErrorCodeReturnedEquals = "404"
}
- Redirect = {
- ReplaceKeyPrefixWith = "error.html?referer="
}
},
]
) -> null
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
为了填充显示的差异,按照以下方式编写,并执行terraform plan以确保没有差异。
resource "aws_s3_bucket" "tekitouna_domain_nyuryoku" {
bucket = "tekitouna.domain.nyuryoku"
website {
error_document = "error.html"
index_document = "index.html"
routing_rules = <<EOF
[
{
"Condition": {
"HttpErrorCodeReturnedEquals": "404"
},
"Redirect": {
"ReplaceKeyPrefixWith": "error.html?referer="
}
}
]
EOF
}
}
resource "aws_s3_bucket_policy" "tekitouna_domain_nyuryoku" {
bucket = aws_s3_bucket.tekitouna_domain_nyuryoku.id
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::tekitouna.domain.nyuryoku/*"
}
]
}
EOF
}
打扫
通过执行 “terraform destroy” 命令,可以删除所有在 .tfstate 文件中管理的资源。