ECS

AWS Container

  • ECSは2種類
    • EC2起動タイプ
    • Fargate起動タイプ

ECSクラスタの定義

次で簡単にECSクラスターは定義可能

resource "aws_ecs_cluster" "example" {
  name = "example"
}

ECSタスクの定義

  • コンテナの実行単位をタスクと呼ぶ
  • 例) Djangoアプリの前段階にNGINXを配置する場合は、一つのタスクの中でDjangoコンテナとNGINXが実行される
  • タスクはタスク定義から生成される。タスク定義はクラスでタスクはインスタンス
resource "aws_ecs_task_definition" "example" {
  family                   = "example" <== タスク定義名のプレフィックス。この場合はexample:1となる。リビジョン番号はタスク定義更新時にインクリされる
  cpu                      = "256" <===== CPUとMeomoryは組み合わせがあるので注意
  memory                   = "512"
  network_mode             = "awsvpc" <======== FARGATEの場合は、AWSVPCにする
  requires_compatibilities = ["FARGATE"] <======= 起動タイプはFargateにする
  container_definitions    = file("./container_definitions.json") <============= 実行するコンテナを定義する
}

実行するコンテナを定義(コンテナ定義)は次のような(container_definitions.json)感じ。

[
  {
    "name": "example", <============ コンテナ名
    "image": "nginx:latest", <======== イメージ名
    "essential": true, <============ タスク実行に必須かどうか
    "portMappings": [ <============ マッピングするコンテナのポート番号
      {
        "protocol": "tcp",
        "containerPort": 80
      }
    ]
  }
]

ECSサービスの定義

  • 通常、コンテナを起動しても、処理が完了したら、終了する。
  • それは困るので、ECSサービスを利用する。
  • ECSサービスは起動するタスクを定義でき、指定した数のタスクを維持する。
  • 何らかの理由でタスクが終了してしまった場合は、自動的にタスクが起動する
  • また、ECSサービスはALBとの橋渡し役にもなる
  • インターネットからのリクエストをALBで受け、そのリクエストをコンテナにフォワードする
resource "aws_ecs_service" "example" {
  name                              = "example"
  cluster                           = aws_ecs_cluster.example.arn
  task_definition                   = aws_ecs_task_definition.example.arn <====== さっき作成したタスクを指定する
  desired_count                     = 2 <======= 維持するタスクの数
  launch_type                       = "FARGATE" <======= 起動タイプ
  platform_version                  = "1.3.0" <========== LATESTは最新じゃない場合もあるので、明示的に指定
  health_check_grace_period_seconds = 60 <========== タスク起動時のヘルスチェックの猶予期間(s)。タスクの起動に時間がかかる場合は、猶予がないと、ヘルスチェックに引っかかり、タスクの起動と終了が無限に続いてしまうため。

  network_configuration { <============== ネットワークの設定
    assign_public_ip = false <========プライベートで起動させるので、false
    security_groups  = [module.nginx_sg.security_group_id]

    subnets = [ <=============== 起動させたいサブネットを指定
      aws_subnet.private_0.id,
      aws_subnet.private_1.id,
    ]
  }

  load_balancer { <=========== ロードバランサ
    target_group_arn = aws_lb_target_group.example.arn <======== ターゲットグループの指定
    container_name   = "example" <======== コンテナ定義(container_definitions.json)のname。複数コンテナ定義がある場合は、最初にロードバランサーからリクエストを受けるコンテナを指定する
    container_port   = 80 <======= コンテナ定義(container_definitions.json)のportmappings.containerport
  }

  lifecycle { <================== Fargateの場合は、デプロイの対日にタスクが更新され、plan時に差分がでるので、変更を無視する。
    ignore_changes = [task_definition]
  }
}

module "nginx_sg" {  <===================== moduleでsgを定義
  source      = "./security_group"
  name        = "nginx-sg"
  vpc_id      = aws_vpc.example.id
  port        = 80
  cidr_blocks = [aws_vpc.example.cidr_block]
}

ECSのロギング

  • cloudwatch logsを使う
resource "aws_cloudwatch_log_group" "for_ecs" {
  name              = "/ecs/example"
  retention_in_days = 180 <================ ログ保存機関
}

ECSタスク実行IAMロール

ECSに権限を付与するための、ECSタスク実行IAMロールを作成する

data "aws_iam_policy" "ecs_task_execution_role_policy" {
  arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"  <== CloudWachやECRの操作権限を持っているPolicy
}

ポリシードキュメント

data "aws_iam_policy_document" "ecs_task_execution" {
  source_json = data.aws_iam_policy.ecs_task_execution_role_policy.policy <==== source_jsonを使うと、既存のポリシーを継承できる。

  statement { <============= 継承しているので、追加となる
    effect    = "Allow"
    actions   = ["ssm:GetParameters", "kms:Decrypt"]
    resources = ["*"]
  }
}

IAMロール

module "ecs_task_execution_role" {
  source     = "./iam_role"
  name       = "ecs-task-execution"
  identifier = "ecs-tasks.amazonaws.com"
  policy     = data.aws_iam_policy_document.ecs_task_execution.json
}

Dockerコンテナのロギングのためのタスク定義の修正

  • DockerコンテナがCloudWatch Logsにログを投げられるようにする
  • もともとのタスク定義にexecution_role_arnを追加する
resource "aws_ecs_task_definition" "example" {
  family                   = "example"
  cpu                      = "256"
  memory                   = "512"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  container_definitions    = file("./container_definitions.json")
  execution_role_arn       = module.ecs_task_execution_role.iam_role_arn <============== 追加分
}

コンテナ定義の修正

[
  {
    "name": "example",
    "image": "nginx:latest",
    "essential": true,
    "logConfiguration": { <=================== これが追加された。ログを投げるための設定
      "logDriver": "awslogs",
      "options": {
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "nginx",
        "awslogs-group": "/ecs/example"
      }
    },
    "portMappings": [
      {
        "protocol": "tcp",
        "containerPort": 80
      }
    ]
  }
]