はじめに
インフラエンジニアとしてTerraform運用を行っているのですが、 TerraformやFargateもだいぶ浸透してきて、導入している企業も増えてきているように感じます。 そのようなケースのサンプルとして公開したいと思います。
ファイル構成
ファイル構成は以下としています。
├── logs │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf ├── buckets │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf ├── ecr │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── provider.tf │ └── variables.tf └── fargate ├── backend.tf ├── files │ └── container_definition.json ├── main.tf ├── outputs.tf ├── provider.tf └── variables.tf
ポイントとしては、logとFargateのtfファイルの階層を分けている事です。 分ける理由としては、同階層にしてしまうとTerraform destoryした時にログまで消えてしまって後悔。。。というケースが起こるかなと思い階層を分けています。 同様の理由としてS3bucket、ECRも階層を分けています。
また、files以下にコンテナ定義のjsonファイル等を入れています。
Fargate
まずはクラスターから
resource "aws_ecs_cluster" "default" { name = local.service_name }
タスク定義は以下のようになっています。 定義の内容をjsonファイルに記載しておいて、ECRのARNなどはdataソースを使って記入しています。
data "template_file" "default" { template = file("files/container_definition.json") vars = { ECR_ARN = data.terraform_remote_state.ecr.outputs.default["repository_url"] SERVICE_NAME = local.service_name } } resource "aws_ecs_task_definition" "default" { family = local.service_name container_definitions = data.template_file.default.rendered task_role_arn = aws_iam_role.default.arn network_mode = "awsvpc" execution_role_arn = aws_iam_role.default.arn cpu = 512 memory = 1024 requires_compatibilities = ["FARGATE"] }
タスク定義のjsonファイルは以下のようになっています。
[ { "name": "${SERVICE_NAME}", "image": "${ECR_ARN}", "essential": true, "portMappings": [ { "containerPort": 80, "hostPort": 80 } ], "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-region": "ap-northeast-1", "awslogs-group": "/ecs/${SERVICE_NAME}", "awslogs-stream-prefix": "${SERVICE_NAME}" } } } ]
Ecsのサービスは以下のようになっています。
resource "aws_ecs_service" "default" { name = local.service_name cluster = aws_ecs_cluster.default.id task_definition = aws_ecs_task_definition.default.arn desired_count = 2 launch_type = "FARGATE" load_balancer { target_group_arn = aws_lb_target_group.default.arn container_name = local.service_name container_port = 80 } network_configuration { subnets = [ data.terraform_remote_state.subnet.outputs.publib_a["id"], data.terraform_remote_state.subnet.outputs.publib_c["id"], ] security_groups = [ aws_security_group.default.id ] assign_public_ip = true } }
LB
resource "aws_lb" "default" { name = local.service_name internal = false load_balancer_type = "application" subnets = [ data.terraform_remote_state.subnet.outputs.publib_a["id"], data.terraform_remote_state.subnet.outputs.publib_c["id"], ] security_groups = [ aws_security_group.default.id, ] enable_deletion_protection = true access_logs { bucket = data.terraform_remote_state.buckets.outputs.default["id"] enabled = true } } resource "aws_lb_target_group" "default" { name = local.service_name port = 80 protocol = "HTTP" vpc_id = data.terraform_remote_state.subnet.outputs.default["id"], target_type = "ip" }
VPCとサブネットはバックエンドから参照しています。
また、security groupは別で作成しておいてください。
bucketについては、前述の通り階層を分けているためバックエンドから参照してください。
ターゲットグループのtarget_typeはFargateと連携するためにip
に指定しておいてください。
LBへSSL証明書の適用 Certificate Managerに登録してあるSSL証明書を参照しています。
resource "aws_lb_listener" "default" { load_balancer_arn = aws_lb.default.arn protocol = "HTTPS" port = "443" ssl_policy = "ELB_SecurityPolicy-TLS-1-2-2017-01" certificate_arn = data.terraform_remote_state.certificate.outputs.default["arn"] default_action { type = "forward" target_group_arn = aws_lb_target_group.default.arn } }
80ポートへのアクセスはリダイレクトするようにします。
resource "aws_lb_listener" "redirect_https" { load_balancer_arn = aws_lb.default.arn port = "80" protocol = "HTTP" default_action { type = "redirect" redirect { port = "443" protocol = "HTTPS" status_code = "HTTP_301" } } }
variables
最後に、variablesは以下のようになっています。
variable "environment" { default = "development" } locals { service_name = "${var.environment}-service-name" }
ここでlocalsを使っているのは、環境名などを変数に埋め込むためです。 特に必要ないかもしれないです。
最後に
これからTerraformを導入していきたい、Fargateを使ってみたいという方に少しでも参考になれば。。。