尝试将AWS资源转换为Terraform配置

开场白 shū

今年也到了圣诞节日历活动的时候。因为我的生日是12月6日,只要到了这个日期就会被指定。
这次我默默地隐藏起来,但被某个前辈指定了,所以就写出来了。
不过,生日那天已经被填满了,所以我会在生日前一天写!(某个前辈,和I Sam同一天生日)

我想写一下有关Terraform的文章。

章节目录

    • terraformとは

 

    • terraform化したリソース

 

    • terraformの基本

 

    • EC2のterraform化

 

    • SecurityGroupのterraform化

 

    • Route53のterraform化

 

    まとめ?

Terraform是什么?

Terraform是一个以IaC的方式来管理主要的公共云资源,如AWS和GCP等。
例如,可以将Route53的DNS记录编码化,使其更容易进行审查;也可以一次性地更改EC2实例的大小。
由于我们公司使用AWS,所以我想写一下将AWS资源转换为Terraform的经验。
不过,我对Terraform并不是很了解,所以如果我写错了什么,请笑着原谅我。


使用Terraform进行资源建设

    • EC2

 

    • SecurityGroup

 

    • ELB

 

    • Route53

 

    • ECS Fargate

 

    etc…

我将上述内容进行了Terraform化处理,但考虑到全部都写出来会很繁琐,所以打算只写一些像EC2、SecurityGroup和Route53这样的基本要素吧。(不确定是否符合主流做法。)


基本概念是关于terraform

在创建AWS资源之前,我们将先介绍terraform的基本知识。在编写terraform时,需要确定使用哪种最佳实践,而本次我们将采用被称为模块化写法的方式进行编写。

模块化是什么?

.
├── env                            # env...主にステージで分けられることが多い
│   ├── product
│   │   ├── default_variable.tf
│   │   └── main.tf
│   ├── staging
│   │   ├── default_variable.tf
│   │   └── main.tf
│   └── dev
│       ├── default_variable.tf
│       └── main.tf
├── modules                        # modules ... AWSリソースごとに分けられることが多い
│   ├── Route53
│   │   ├── variable.tf
│   │   └── main.tf
│   ├── ELB
│   │   ├── variable.tf
│   │   ├── alb.tf
│   │   └── nlb.tf
│   ├── EC2
│   │   ├── variable.tf
│   │   └── main.tf
│   ├── other_resource...
.
.
.

terraform以模块化形式编写的代码遵循类似于env(环境)和modules(各个资源)的目录结构,所以被称为模块化terraform。
虽然我此次编写的代码与此相似,但实际上它们是不同的(事实上,不知不觉中变得不同了…)。

以下为所书写的文件/文件夹结构。
.
├── env
│   ├── product
│   │   ├── default_variable.tf
│   │   └── main.tf
│   ├── staging
│   │   ├── default_variable.tf
│   │   └── main.tf
│   └── dev
│       ├── default_variable.tf
│       └── main.tf
├── modules                       # ここもステージごとに作っちゃった。
│   │                             # dev環境にしかないインスタントもあるのでなんかこうなってしまった。
│   ├── product
│   │   ├── alb.tf
│   │   ├── securitygroup.tf
│   │   ├── alb.tf
│   │   ├── variable.tf
│   │   └── other.tf
│   ├── staging
│   │   ├── alb.tf
│   │   ├── securitygroup.tf
│   │   ├── alb.tf
│   │   ├── variable.tf
│   │   └── other.tf
│   ├── dev
│   │   ├── alb.tf
│   │   ├── securitygroup.tf
│   │   ├── alb.tf
│   │   ├── variable.tf
│   │   └── other.tf
.
.
.

这个在自己的使用上还是可以的,但是在dev、staging和product分别要重复写类似的内容,所以这样做也不太好。分配资源时,有一些实例只能在dev环境中使用,还有一些DNS记录也只存在于dev环境中,这真的很麻烦啊…好了,接下来是什么问题?

我們將查看 ./env/dev/main.tf 需要的記述。

terraform {
    backend "s3" {
        bucket  = "terraform_state"
        key     = "dev.tfstate"
        region  = "ap-northeast-1"
        profile = "terraform-dev"
    }

    required_providers {
        aws = {
            source  = "hashicorp/aws"
            version = "3.72.0"
        }
    }
}

provider "aws" {
    region  = "ap-northeast-1"
    profile = "terraform-dev"
}

module "module_name" {
    source = "../../modules/dev/"

    # 環境ごとに違う部分を定義。variable.tfにて読み込まないとダメ。
    vpc_cidr = xxx.xxx.0.0/16
    subent = xxx.xxx.xxx.0/24
    .
    .
    .

请按顺序解释

Name説明backend “s3″S3にstateファイルを保存するという意味。ローカルなども指定できたはず。bucketS3のバケット名を記載keystateファイル名を記載。初回実行時はないのが当たり前なのでどんなファイルのstateファイルにしたいかを書く。regionAWSの利用しているリージョンを記載profileAWSを操作するprofileを指定。IAMでterraform用のユーザなり何なりを作成しなければいけない。
Name説明required_providersどのパブリッククラウドを利用するのかを記載awsAWSのリソースを使うと宣言sourcehashicorpのawsを使うという宣言。(だと思う。)versionsourceのバージョンを指定。
Name説明provider “aws”どのパブリッククラウドを利用するのかを記載regionAWSの利用しているリージョンを記載profileAWSを操作するprofileを指定。IAMでterraform用のユーザなり何なりを作成しなければいけない。backend で設定したprofileと同じになると思う。
Name説明module “module_name”module_name はなんでもOK。あとから変えるのはめちゃくちゃめんどいのでちゃんと考えて付けたほうが良い。sourceどのモジュールを実行するかを記載するvpc_cidrこの辺は環境によって差分がある場合、ここで変数定義しておくと後々変更等が楽。

这里只是关于env的说明。
default_variable.tf是没有必要的,所以我没有写。
为了进行应用程序的版本升级等操作,我只是将版本号等信息从main.tf中提取出来。

嗯,话题有点长,基本上就是这样。
终于要开始写关于每个资源的内容了。


在每个资源之前,

在每个资源之前解释一下 variable.tf。
为了在 module 中使用在 ./env/dev/main.tf 中定义的变量,我们会创建一个名为 variable.tf 的文件。(其实名字可以随便取。)

./模块/开发/变量.tf
variable "vpc_cidr" {}
variable "subent" {}

# 他にも ./env/dev/main.tf で定義した変数があれば記載
variable "..." {}
variable "..." {}
variable "..." {}

只需进行以下记录,即可在模块内使用 var.vpc_cidr 这种写法。


Terraform 模块的共通写法。

基本上

在中文中,我们可以这样表达:
通过”AWS资源名称” “独特的模块名称”的方式进行声明。

resource "aws_instance" "ec2-example-com" {

}
resource "aws_security_group" "access-from-vpc" {

}

类似那种感觉。

好,我们在掌握了这一点之后,继续讲解各种资源的使用方法吧。


EC2 可弹性变化

# aws_instance はEC2のことだよ。という宣言的な意味合い。
# resource_nameはterraform内でユニークの値にする必要がある。
# あとで変更も可能だけど、結構めんどくさいのでわかりやすく、被らないようにしたほうが良い。
resource "aws_instance" "ec2-example-com" { 
    ami                         = "ami-xxxxxxxxxxxxxxxxx"         # マーケットプレイスなどのAMI_IDを記載
    availability_zone           = "ap-northeast-1c"               # 作成するAZを記載
    ebs_optimized               = true                            # boolean あとで変更するとインスタンスが作り直されるので注意!
    instance_type               = "t3.small"                      # インスタンスサイズを記載
    monitoring                  = false                           # 詳細モニタリングを有効にするかどうか?かな?わからん
    key_name                    = "key-name"                      # デフォルトユーザでsshするための鍵を記載
    subnet_id                   = var.subnet                      # ./env/dev/main.tf で定義した値を使用
    vpc_security_group_ids      = [
        aws_security_group.hogehoge.id, 
        aws_security_group.fugafuga.id
        ]                                                         # アタッチするセキュリティグループを記載。
                                                                  # aws_security_group.hogehogeみたいな書き方はいい感じにググってください。

    associate_public_ip_address = true                            # boolean PublicIPをアタッチするかどうか
    private_ip                  = "xxx.xxx.xxx.xxx"               # ローカルIPを記載
    source_dest_check           = true                            # よくわからん。

    lifecycle {
        ignore_changes = [associate_public_ip_address]            # associate_public_ip_addressこれが変わっても無視するという設定
    }

    # ここからは無くてもいい
    root_block_device {
        volume_type           = "gp3"
        volume_size           = 20
        delete_on_termination = true

        tags = {
            Name = "dev.example.com"
        }
    }

    tags = {
        "Name" = "dev.example.com"                                 # 主にホスト名とかを記載 
    }
}

就可以创建实例了。此外,如果存在现有资源,可以在上述terraform之后编写这样的代码。

terraform import [モジュール名] [インスタンスID]`
# 例)
# terraform import module.module_name.aws_instance.ec2-example-com i-xxxxxxxx
# という感じ。

安全组

resource "aws_security_group" "default-securitygroup" {
    name        = "hogehoge-securitygroup"                              # SecurityGroup名を記載。確かあとで変更しようと思うと作り直されるような気がするので注意
    description = "attach all ec2"                                      # 説明を書く
    vpc_id      = "var.vpc_id"                                          # 変数化してると楽。

    # ingress は外からの通信(inbound)の許可設定
    ingress {
        description     = ""                                            # 説明
        from_port       = 22                                            # 何番ポートから何番ポートまで空けるのかを指定
        to_port         = 22                                            # 今回でいうと22番ポートのみ空けている
        protocol        = "tcp"
        cidr_blocks     = [ xxx.xxx.xxx.xxx/32, yyy.yyy.yyy.0/24 ]      # Cidrで指定したり1つのIPを指定して空けることができる
    }

    # egressは内からの通信(outbound)許可設定
    # 基本的にingressと似ているので割愛
    egress {
        from_port       = 0
        to_port         = 0
        protocol        = "-1"
        cidr_blocks     = ["0.0.0.0/0"]
    }

    # なくてもいい
    tags = {
        "Name" = "hogehoge"
    }
}

您可以根据上述写法大致创建安全组。
而且,如果有现有资源,在上述类似的方式下,可以编写terraform。

terraform import [モジュール名] [セキュリティグループID]
# 例)
# terraform import module.module_name.aws_security_group.default-securitygroup sg-xxxxxxxx
# という感じ。

路由53

# Aliasレコード
resource "aws_route53_record" "ec2-example-com-Alias" {
    zone_id = "xxxxxxxxxxxxxxxxxxxx"                               # ホストゾーンIDを記載
    name    = "alias.ec2.example.com"                              # 登録したいレコード名
    type    = "A"                                                  # AliasレコードのタイプはAレコードなのでAを書く。

    alias {
        name    = "dualstack.hogehoge.ap-northeast-1.elb.amazonaws.com"  # ELBやCloudFrontなどのDNSレコードを記載
        zone_id = "xxxxxxxxxxxxx"                                        # ELBとかのホストゾーンは(確か)固定なのでググって調べる
        evaluate_target_health = true
    }
}
# Aレコード
resource "aws_route53_record" "ec2-example-com-A" {
    zone_id = "xxxxxxxxxxxxxxxxxxxx"                               # ホストゾーンIDを記載
    name    = "a.ec2.example.com"                                  # 登録したいレコード名
    type    = "A"                                                  # AレコードなのでAを書く。
    records = ["xxx.xxx.xxx.xxx"]                                  # IPアドレスを書く
    ttl     = "300"

}
# CNAMEレコード
resource "aws_route53_record" "ec2-example-com-CNAME" {
    zone_id = "xxxxxxxxxxxxxxxxxxxx"                               # ホストゾーンIDを記載
    name    = "cname.ec2.example.com"                              # 登録したいレコード名
    type    = "CNAME"                                              # CNAMEレコードなのでCNAMEを書く。
    records = ["a.ec2.example.com"]                                # CNAME先を書く
    ttl     = "300"

}
# MXレコード
resource "aws_route53_record" "ec2-example-com-MX-0" {
    zone_id = "xxxxxxxxxxxxxxxxxxxx"                               # ホストゾーンIDを記載
    name    = "a.ec2.example.com"                                  # 登録したいレコード名
    type    = "MX"                                                 # MXレコードなのでMXを書く。
    records = ["0 mail.ec2.example.com"]                           # Priorityとレコードを書く
    ttl     = "300"

}

可以大致创建安全组。
此外,如果存在现有资源,可以在上述的terraform代码之后进行编写。

terraform import [モジュール名] [ゾーンID_レコード名_レコードタイプ_]
# 例)
# terraform import module.module_name.aws_route53_record.ec2-example-com-Alias Z4KAPRWWNC7JR_dev.example.com_Alias
# という感じ。
# Route53のimportが一番めんどくさい。。。

总结

我以相当随便的方式写了一下有关terraform的写作方法。我希望这对将要使用terraform来进行IaC的人有所帮助。AWS和公共云的资源变更有些不容易审查,有时只能通过截图来备份。我想进行IaC化,不断进行运维错误修正和提高效率。这是前一天我的生日的时候想到的。


参考

terraform – 变形金刚

bannerAds