Carpe Diem

備忘録。https://github.com/jun06t

TerraformでECSを動的ポートマッピングに

概要

以前ECSの記事を幾つか書きましたが、

TerraformでECS環境の構築 - Carpe Diem

TerraformでECS環境の構築【オートスケール編】 - Carpe Diem

当時のECSはインスタンス1コンテナにしないとポートが競合して同じ種類のコンテナを載せることはできませんでした。
しかし今ではALBが導入され、動的ポートマッピングといってコンテナ側のポートは80で、ホスト側にマッピングする際は動的に変えてよろしくやってくれる。ALBはその動的なポートに紐づくという機能が付いています。
これによって1インスタンスに同じ種類のコンテナがいくつもたてられるようになりました。

環境

  • terraform 0.11.0
  • terraform-provider-aws 1.3.1

成果物

今回のコードはこちら

github.com

コード

インスタンスの設定

Auto Scaling Group

resource "aws_autoscaling_group" "dev_api" {
  availability_zones        = ["ap-northeast-1a"]
  name                      = "dev-api"
  max_size                  = 4
  min_size                  = 1
  health_check_grace_period = 300
  health_check_type         = "EC2"
  desired_capacity          = 1
  force_delete              = true
  launch_configuration      = "${aws_launch_configuration.dev_api.id}"
  vpc_zone_identifier       = ["${aws_subnet.private_1a.id}"]

  enabled_metrics = [
    "GroupMinSize",
    "GroupMaxSize",
    "GroupDesiredCapacity",
    "GroupInServiceInstances",
    "GroupPendingInstances",
    "GroupStandbyInstances",
    "GroupTerminatingInstances",
    "GroupTotalInstances",
  ]
...

ポイント

  • ASG自体にLB(load_balancerstarget_group_arns)を紐付けないこと

です。でないとコンテナとマッピングされたポートとは別で、紐付けたポートがくっついてしまいます。コンテナとはつながっていないので当然unhealthyのままです。

ECS

Service

resource "aws_ecs_service" "api_service" {
  name                               = "api-service"
  cluster                            = "${aws_ecs_cluster.api_cluster.id}"
  task_definition                    = "${aws_ecs_task_definition.api.arn}"
  desired_count                      = 1
  deployment_minimum_healthy_percent = 50
  deployment_maximum_percent         = 100
  iam_role                           = "${aws_iam_role.ecs_service_role.arn}"

  load_balancer {
    target_group_arn = "${aws_alb_target_group.dev_api.arn}"
    container_name   = "nginx"
    container_port   = 80
  }
}

resource "aws_ecs_task_definition" "api" {
  family                = "api"
  container_definitions = "${file("task-definitions/api.json")}"
}

ポイント

load_balancer

  • ALBのtarget group
  • コンテナ名
  • コンテナ側のポート

を設定します。

task definition

[
  {
    "name": "nginx",
    "image": "nginx",
    "cpu": 10,
    "memory": 200,
    "portMappings": [
      {
        "containerPort": 80,
        "hostPort": 0
      }
    ]
  }
]

ポイント

  • portMappingsで、hostPort0に指定

こうするとAWS側で1024以降の空いているポートを動的に割り当ててくれます。

ALB

resource "aws_alb_target_group" "dev_api" {
  name                 = "dev-api"
  port                 = 80
  protocol             = "HTTP"
  vpc_id               = "${aws_vpc.vpc.id}"
  deregistration_delay = 30

  health_check {
    interval            = 30
    path                = "/"
    protocol            = "HTTP"
    timeout             = 5
    healthy_threshold   = 2
    unhealthy_threshold = 4
    matcher             = 200
  }
...

ポイント

  • health_checkではportは指定しない

動的にポートを割り振られるため、固定したportを指定することはできません。
指定しない場合、デフォルトのtraffic-portになるため、疎通できるportに対して行うため問題ありません。

動作確認

f:id:quoll00:20171125115300p:plain

こんな感じに空いているポートに紐付ける形で、1つのインスタンス上に複数のコンテナを起動できるようになります。

まとめ

通常のECSの設定から動的ポートマッピングに対応させるためには

  • ASGではLBの指定をしない
  • hostPort0
  • health_checkではportは指定しない

とすれば問題なく利用できます。

ソース