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
}
]
}
]