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存储桶。

page4.png

设置桶策略。

part5.png
{
  "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 文件中管理的资源。

请参考

bannerAds