タイトルのとおりです。楽しかった。
以前にECSで遊んだ記事2つ
1つ目の記事では、NLB有無でかかった時間を比べたのだけど
- NLBあり + Service Connectあり
- NLBなし + Service Connectあり
と、サービスコネクトをどちらも有効にした状態で比べてしまっていた。じゃあ、と2つ目の記事では、
- NLBなし + Service Connectあり
- NLBなし + Service Connectなし
で比べていた。そして、比べるべきだったのはこれだったなぁ、またこんどやろう、と思ったのがこの組み合わせ。
- NLBあり + Service Connectなし
- NLBなし + Service Connectあり
今日は、これを見てみようと思う。(終わった後の僕より「見てないよ!」)
Terragrunt + OpenTofu
前回は、手でポチポチやったので、今回はTerragruntを使うことにする。せっかくだからOpenTofuを使ってみるか。Terragruntは、Terraformのちょっとかゆいところをかいてくれるやつ。OpenTofuはTerraformのライセンス変更起因でフォークされたやつ。brewで入れといた。
❯ brew install terragrunt ❯ terragrunt -v terragrunt version 0.54.20 ❯ brew install opentofu ❯ tofu -v OpenTofu v1.6.0 on darwin_arm64
OpenTofuは今はTerraformとほぼ同じだけど、今後Terraformとは別の道を進むことになると思うから、どちらにどんな機能が入っていくか・周辺ツールがどんな対応をしていくか、をよく見ておきたいなと思っている。どちらも2.0くらいになったときが考えるポイントになりそう。今のところはTerraformを使い続けるでいいんじゃないかなと思ってる。
プロジェクトフォルダを作る
適当に作った
❯ mkdir -p ecs20240121/infra
❯ cd ecs20240121/infra
Terragruntの設定とは別で、ecspresso用の設定も作るだろうなと思ったので、Terragrunt用にはinfraフォルダを作って使うことにした。
Terragruntで必要なので、ここに空の terragrunt.hcl
ファイルを作っておく。
❯ touch terragrunt.hcl
僕のマシンにはTerraformもインストールされているので、初期状態ではTerragruntはTerraformを使うようになっている。
❯ terragrunt terragrunt-info | grep TerraformBinary "TerraformBinary": "terraform",
ので、OpenTofuを使うように設定する。
TerragruntからOpenTofuを使う設定
Terragrunt 0.52.0のリリースノートにこう書いてあるので、Option 2の環境変数を使おうかな。
https://github.com/gruntwork-io/terragrunt/releases/tag/v0.52.0
- Option 1: Remove terraform binary from PATH
- Option 2: Define env variable TERRAGRUNT_TFPATH=tofu
- Option 3: When launching terragrunt, specify --terragrunt-tfpath tofu
環境変数の切り替えにはdirenvを使うか。と思ったら入ってなかったのでインストール。よりみち楽しい。
direnvをインストール
❯ brew install direnv
zshを使っているので下記の内容を ~/.zshrc
に追記した
eval "$(direnv hook zsh)"
追加した設定を読み込んでおく
❯ source ~/.zshrc
TerragruntからOpenTofuを使う設定に戻ってきた
infraディレクトリに入ったら TERRAGRUNT_TFPATH=tofu
が有効になるようにしたいので .envrc
を作る
❯ echo 'export TERRAGRUNT_TFPATH=tofu' > .envrc direnv: error <略>/ecs20240121/infra/.envrc is blocked. Run `direnv allow` to approve its content
と言われるので許可しておく
❯ direnv allow direnv: loading <略>/ecs20240121/infra/.envrc direnv: export +TERRAGRUNT_TFPATH
これで、このフォルダに入ったらOpenTofuが使われるようになった。わーい。
❯ terragrunt terragrunt-info | grep TerraformBinary "TerraformBinary": "tofu",
この記事ではクレデンシャルを入れるつもりはないから気にしなくていいんだけど、一応、安全のために .envrc
は .gitignore
に入れておこうか。
❯ cp .envrc example.envrc ❯ echo '.envrc' > ../.gitignore
できた!・・・って、満足してこの記事を終わりにしようと思ってしまった。何をやろうとしてたんだっけ。ECSか。でも、その前にリモートステートの設定入れておくか。よりみち楽しい!
S3バックエンド
ステートを管理するのにS3を使いたいんだけど、このS3のバケット自体をTerraform(というかOpenTofuだけど)で管理するのは少しめんどくさい。そこをTerragruntがいい感じにやってくれるので、その設定を入れる。
さっき空で作成した terragrunt.hcl
をこんな風にする。
remote_state { backend = "s3" config = { bucket = "ecs20240121" key = "${path_relative_to_include()}/terraform.tfstate" encrypt = true region = "ap-northeast-1" } }
dynamodb_table
でステートをロックする設定もあるけど、今回はそこまでは入れなかった。
参照:https://terragrunt.gruntwork.io/docs/reference/config-blocks-and-attributes/#remote_state
で、このTerragruntで作ったステートをOpenTofuから使うようにするために main.tf
を用意してこんな感じに設定を書いておく。
terraform { backend "s3" {} }
これで terragrunt init
を実行すると、S3バケットないけど作る?って聞かれるので作るって答える。そうそう、僕の環境では、AWSには接続できるように環境変数を設定済み。
❯ terragrunt init Remote state S3 bucket ecs20240121 does not exist or you don't have permissions to access it. Would you like Terragrunt to create it? (y/n) y Initializing the backend... Successfully configured the backend "s3"! OpenTofu will automatically use this backend unless the backend configuration changes. Initializing provider plugins... OpenTofu has been successfully initialized!
作られてた。わいわい。
AWS Providerを入れる
じゃAWS Provider入れるか。main.tf
をこんな風にして。
terraform { backend "s3" {} required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } provider "aws" { region = "ap-northeast-1" }
からの terragrunt init
❯ terragrunt init Initializing the backend... Initializing provider plugins... - Finding hashicorp/aws versions matching "~> 5.0"... - Installing hashicorp/aws v5.33.0... - Installed hashicorp/aws v5.33.0 (signed, key ID 0C0AF313E5FD9F80)
.terraform
も .gitignore
に追加しとこ。忘れる前に。
❯ echo '.terraform/' >> ../.gitignore
インフラを作る
これで、だいたい準備ができたので、適当にNLBやECSを作ってこ。ファイルを分けることが多いかなとは思うけど今回は趣味だし、全部 main.tf
に書く、でいいか。
適当に名前を用意
あると便利なのでローカル変数を用意。なんでもいい。
locals { main_name = "ecs20240121" }
VPCの設定
VPCとPublic Subnetを用意する。これも普通だったらPrivate Subnetを用意したり、マルチAZにしたりすると思うけど今回は1つで全部やることにする。
######################################### # VPC ######################################### resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" } resource "aws_subnet" "main" { vpc_id = aws_vpc.main.id availability_zone = "ap-northeast-1a" cidr_block = "10.0.1.0/24" } ######################################### # Internet Gateway ######################################### resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id } resource "aws_route_table" "main" { vpc_id = aws_vpc.main.id } resource "aws_route" "main" { route_table_id = aws_route_table.main.id gateway_id = aws_internet_gateway.main.id destination_cidr_block = "0.0.0.0/0" } resource "aws_route_table_association" "main" { route_table_id = aws_route_table.main.id subnet_id = aws_subnet.main.id }
Security Groupを用意
NLBにもSGが設定できるようになったのでNLB用と、APP用で用意。ポートは80番だけあけてたらいっかという気持ち。
######################################### # Security Groups ######################################### resource "aws_security_group" "nlb" { name = "${local.main_name}-sg-nlb" vpc_id = aws_vpc.main.id } resource "aws_security_group" "app" { name = "${local.main_name}-sg-app" vpc_id = aws_vpc.main.id } resource "aws_vpc_security_group_ingress_rule" "nlb_ingress" { security_group_id = aws_security_group.nlb.id cidr_ipv4 = "0.0.0.0/0" ip_protocol = "tcp" from_port = 80 to_port = 80 } resource "aws_vpc_security_group_egress_rule" "nlb_egress" { security_group_id = aws_security_group.nlb.id referenced_security_group_id = aws_security_group.app.id ip_protocol = "-1" } resource "aws_vpc_security_group_ingress_rule" "app_ingress_nlb" { security_group_id = aws_security_group.app.id referenced_security_group_id = aws_security_group.nlb.id ip_protocol = "tcp" from_port = 80 to_port = 80 } resource "aws_vpc_security_group_ingress_rule" "app_ingress_self" { security_group_id = aws_security_group.app.id referenced_security_group_id = aws_security_group.app.id ip_protocol = "tcp" from_port = 80 to_port = 80 } resource "aws_vpc_security_group_egress_rule" "app_egress" { security_group_id = aws_security_group.app.id cidr_ipv4 = "0.0.0.0/0" ip_protocol = "-1" }
NLBの設定
ターゲットグループは、デフォルトだと待ち時間が長いので、こんな感じにしてみた。あとで変えるかもしれない。
deregistration_delay
: 30shealth_check
: 10s x 2
それと、ECSではFARGATEを使うつもりなので、ターゲットグループの target_type
は ip
にしておいた。
######################################### # NLB ######################################### resource "aws_lb" "main" { name = "${local.main_name}-nlb" internal = false load_balancer_type = "network" security_groups = [aws_security_group.nlb.id] subnets = [aws_subnet.main.id] } resource "aws_lb_target_group" "main" { name = "${local.main_name}-target-group" port = 80 protocol = "TCP" vpc_id = aws_vpc.main.id target_type = "ip" deregistration_delay = 30 health_check { interval = 10 port = "traffic-port" protocol = "TCP" healthy_threshold = 2 unhealthy_threshold = 2 } } resource "aws_lb_listener" "main" { load_balancer_arn = aws_lb.main.arn port = "80" protocol = "TCP" default_action { target_group_arn = aws_lb_target_group.main.arn type = "forward" } }
ECSの設定
で、ECS。Service Connectで遊ぶつもりなのでCloudMapの設定も入れておく。
######################################### # ECS ######################################### resource "aws_ecs_cluster" "main" { name = "${local.main_name}-ecs" } resource "aws_service_discovery_http_namespace" "namespace" { name = "${local.main_name}-namespace" }
Outputを書いておく
NLBのDNS名だけ欲しいので出しておく
######################################### # Output ######################################### output "nlb_dns_name" { value = aws_lb.main.dns_name }
設定終わり
planしてapplyだ!
❯ terragrunt plan いろいろ出力される ❯ terragrunt apply いろいろ出力される Apply complete! Resources: 18 added, 0 changed, 0 destroyed. Outputs: nlb_dns_name = "<NLBのDNS名が出力される>"
でけた。
動作確認
ECSにサービスを作るのとかはOpenTofuからじゃなくてecspressoからやりたいので、今は手でゴニョゴニョ作って確認する。nginxのタスクを作って、そのサービスを作った。からの、NLBのDNSを叩くと、nginxのデフォルトページが返ってきた。
❯ curl <NLBのDNS名> <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>
ヽ(=´▽`=)ノ
destroy
やりたかったことの準備だけして満足した(えー!)ので、今日はここまででいいや。今、手で作ったECS Serviceを手で強制削除してから、destroyだー!
❯ terragrunt destroy -auto-approve
...
Destroy complete! Resources: 18 destroyed.
おしまい。次は、このファイルで apply
したら環境が立ち上がるので続きができる。あー。ECRも欲しくなりそうだな・・・。次回の自分、たのむ!ファイルはGitHubにあげといた。
楽しかった。