November 1, 2022

Localstack を使って完全無料で AWS CLI と Terraform の自習環境を作る AlmaLinux9編

Localstackは、クラウドアプリケーションを開発するためのクラウドサービスエミュレーターをコンテナで提供してくれるものだけど、無料で使えるCommunity版でもAWSアカウント無しでAWS APIを叩けるようになるから、TerraformでAWS環境を作る時の入門、学習用に打って付け!

Community版ではRDSのAPI叩けないとかEC2は実際にVMとして動かないとか、Pro版との違いはあるけど、Terraform学習の入り口としては充分。

用意するもの

AlmaLinux 9

最小セットアップしたAlmaLinux 9.0 x86_64

メモリは2GBでもなんとか足りる

Docker

公式手順に従う

インストール

$ sudo yum install -y yum-utils
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
$ sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

自動起動有効化と起動

$ sudo systemctl enable --now docker

自分のユーザーに権限付与

$ sudo usermod -aG docker `whoami`

SSHを一度抜けて入り直す

確認

$ docker version
Client: Docker Engine - Community
 Version:           20.10.21
 API version:       1.41
 Go version:        go1.18.7
 Git commit:        baeda1f
 Built:             Tue Oct 25 18:02:16 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.21
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.7
  Git commit:       3056208
  Built:            Tue Oct 25 18:00:01 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.9
  GitCommit:        1c90a442489720eec95342e1789ee8a5e1b9536f
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Localstack

公式手順に従う

インストール

$ sudo yum install -y python3-pip
$ python3 -m pip install localstack

確認

$ localstack --version
1.2.0

Dockerイメージをpull

$ docker pull localstack/localstack

起動

$ localstack start -d

AWS CLI

公式手順に従う

インストール

$ sudo yum install -y unzip
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install

確認

$ aws --version
aws-cli/2.8.7 Python/3.9.11 Linux/5.14.0-70.26.1.el9_0.x86_64 exe/x86_64.almalinux.9 prompt/off

awslocal

エンドポイントをLocalstackに向けてAWS CLI実行してもいいけど、その辺をいい感じにしてくれるawscli-localを使う

公式手順に従う

インストール

$ python3 -m pip install awscli-local

確認

$ awslocal --version
aws-cli/2.8.7 Python/3.9.11 Linux/5.14.0-70.26.1.el9_0.x86_64 exe/x86_64.almalinux.9 prompt/off

tfenv

公式手順に従う

インストール

$ sudo yum install git
$ git clone --depth=1 https://github.com/tfutils/tfenv.git ~/.tfenv
$ mkdir ~/bin
$ ln -s ~/.tfenv/bin/* ~/bin/

確認

$ tfenv --version
tfenv 3.0.0

Terraform

tfenvを使う

インストール

$ tfenv install latest
$ tfenv use latest

確認

$ terraform version
Terraform v1.3.3
on linux_amd64

tflocal

AWS CLIと同じようにTerraformもエンドポイントをLocalstackに変えられるけど、awslocalと同じようにいい感じにしてくれるtflocalを使う方が楽

インストール

$ python3 -m pip install terraform-local

確認

$ tflocal version
Terraform v1.3.3
on linux_amd64

自習の入口

AWS CLIでVPC一覧を確認

$ awslocal ec2 describe-vpcs
{
    "Vpcs": [
        {
            "CidrBlock": "172.31.0.0/16",
            "DhcpOptionsId": "dopt-7a8b9c2d",
            "State": "available",
            "VpcId": "vpc-adead4e7",
            "OwnerId": "000000000000",
            "InstanceTenancy": "default",
            "Ipv6CidrBlockAssociationSet": [],
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-b6712cac",
                    "CidrBlock": "172.31.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": true,
            "Tags": []
        }
    ]
}

既に1つある

AWS CLIでVPCを作ってみる

$ awslocal ec2 create-vpc --cidr-block 10.0.0.0/16
{
    "Vpc": {
        "CidrBlock": "10.0.0.0/16",
        "DhcpOptionsId": "dopt-1a2b3c4d2",
        "State": "pending",
        "VpcId": "vpc-4b1cc5d5",
        "OwnerId": "000000000000",
        "InstanceTenancy": "default",
        "Ipv6CidrBlockAssociationSet": [],
        "CidrBlockAssociationSet": [
            {
                "AssociationId": "vpc-cidr-assoc-66e6af95",
                "CidrBlock": "10.0.0.0/16",
                "CidrBlockState": {
                    "State": "associated"
                }
            }
        ],
        "Tags": []
    }
}

再度確認

$ awslocal ec2 describe-vpcs
{
    "Vpcs": [
        {
            "CidrBlock": "172.31.0.0/16",
            "DhcpOptionsId": "dopt-7a8b9c2d",
            "State": "available",
            "VpcId": "vpc-adead4e7",
            "OwnerId": "000000000000",
            "InstanceTenancy": "default",
            "Ipv6CidrBlockAssociationSet": [],
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-b6712cac",
                    "CidrBlock": "172.31.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": true,
            "Tags": []
        },
        {
            "CidrBlock": "10.0.0.0/16",
            "DhcpOptionsId": "dopt-7a8b9c2d",
            "State": "available",
            "VpcId": "vpc-4b1cc5d5",
            "OwnerId": "000000000000",
            "InstanceTenancy": "default",
            "Ipv6CidrBlockAssociationSet": [],
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-66e6af95",
                    "CidrBlock": "10.0.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": []
        }
    ]
}

追加された

今度はTerraformでVPCを追加してみる
以下の内容でtfファイルを用意、名前は test.tf でも main.tf でもお好みで
書き方は公式ドキュメント参照

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.1.0.0/16"
}

terraform init

$ tflocal init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v4.37.0...
- Installed hashicorp/aws v4.37.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

terraform plan

$ tflocal plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.1.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if
you run "terraform apply" now.

意図したVPCが1つ作られそうだ。

適用してみる

$ tflocal apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.1.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.main: Creating...
╷
│ Error: error reading EC2 VPC (vpc-b9f4c5aa) Attribute (enableNetworkAddressUsageMetrics): InvalidParameterValue: Value enable_network_address_usage_metrics is invalid for parameter.
│       status code: 400, request id: CWMCFQLTVBC1HQEIYX2S3UKCEJU9M09T7JVS4T1W2HU2AATC5LMW
│
│   with aws_vpc.main,
│   on main.tf line 5, in resource "aws_vpc" "main":
│    5: resource "aws_vpc" "main" {

あら?Errorだ

AWS Providerの4.35.0から入ったenable_network_address_usage_metricsが悪さをしているらしいので、enable_network_address_usage_metricsがまだ入っていなかった4.34.0に戻す。
(Errorが出ないなら戻さなくていい)

$ cat main.tf
terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.34.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_vpc" "main" {
  cidr_block = "10.1.0.0/16"
}

再度terraform init

$ tflocal init -upgrade

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching "4.34.0"...
- Installing hashicorp/aws v4.34.0...
- Installed hashicorp/aws v4.34.0 (signed by HashiCorp)

Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

一旦潰す

$ tflocal destroy
aws_vpc.main: Refreshing state... [id=vpc-b9f4c5aa]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_vpc.main will be destroyed
  - resource "aws_vpc" "main" {
      - arn                              = "arn:aws:ec2:us-east-1:000000000000:vpc/vpc-b9f4c5aa" -> null
      - assign_generated_ipv6_cidr_block = false -> null
      - cidr_block                       = "10.1.0.0/16" -> null
      - default_network_acl_id           = "acl-8af5b4fb" -> null
      - default_route_table_id           = "rtb-4e2c6219" -> null
      - default_security_group_id        = "sg-1e077d9bc992d098c" -> null
      - dhcp_options_id                  = "dopt-7a8b9c2d" -> null
      - enable_classiclink               = false -> null
      - enable_classiclink_dns_support   = false -> null
      - enable_dns_hostnames             = false -> null
      - enable_dns_support               = true -> null
      - id                               = "vpc-b9f4c5aa" -> null
      - instance_tenancy                 = "default" -> null
      - ipv6_netmask_length              = 0 -> null
      - main_route_table_id              = "rtb-4e2c6219" -> null
      - owner_id                         = "000000000000" -> null
      - tags                             = {} -> null
      - tags_all                         = {} -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_vpc.main: Destroying... [id=vpc-b9f4c5aa]
aws_vpc.main: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed.

再度作る

$ tflocal apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.1.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.main: Creating...
aws_vpc.main: Creation complete after 0s [id=vpc-128325bd]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

今度は問題ない

AWS CLIで確認

$ awslocal ec2 describe-vpcs
{
    "Vpcs": [
        {
            "CidrBlock": "172.31.0.0/16",
            "DhcpOptionsId": "dopt-7a8b9c2d",
            "State": "available",
            "VpcId": "vpc-adead4e7",
            "OwnerId": "000000000000",
            "InstanceTenancy": "default",
            "Ipv6CidrBlockAssociationSet": [],
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-b6712cac",
                    "CidrBlock": "172.31.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": true,
            "Tags": []
        },
        {
            "CidrBlock": "10.0.0.0/16",
            "DhcpOptionsId": "dopt-7a8b9c2d",
            "State": "available",
            "VpcId": "vpc-4b1cc5d5",
            "OwnerId": "000000000000",
            "InstanceTenancy": "default",
            "Ipv6CidrBlockAssociationSet": [],
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-66e6af95",
                    "CidrBlock": "10.0.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": []
        },
        {
            "CidrBlock": "10.1.0.0/16",
            "DhcpOptionsId": "dopt-7a8b9c2d",
            "State": "available",
            "VpcId": "vpc-128325bd",
            "OwnerId": "000000000000",
            "InstanceTenancy": "default",
            "Ipv6CidrBlockAssociationSet": [],
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-a923e4e3",
                    "CidrBlock": "10.1.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": []
        }
    ]
}

問題ない

これでTerraformのドキュメントを見ながら色々と試せる環境ができた

© 2020 nissy-lab.com