モチベーション
Fargate簡単だよ系の記事は見かけるが、実際にやるといくつかパニクりポイントがあります。
公式ドキュメントは逆引き系なので使い易いんですが、単語が多い為か最初は難解、
ここでは備忘録として残します。
Fargateとは
Dockerです。
こんな感じの構成になってます
クラスタ/サービス/タスク/コンテナ
起動タイプがEC2の場合はクラスタの部分がEC2インスタンスのクラスタとなり自分で管理する必要あり。
Fargateの場合、裏側で勝手に管理してくれるのでスケールアウトがメタクソ楽になります。
その代わりお値段が約2割増です。
パニクりポイント
初めての人がALBを使ってAPIをデプロイしようとするとパニクりポイントに出会えます。
その1. TARGET GROUPについて
ターゲットグループを真っ先に作りましょう。
なぜかって?
ECSのデプロイはターゲットグループに紐づけるのではなく
ECSからターゲットグループへ接続しに行くイメージを持つと理解が早まります。
ターゲットタイプはIPです。
ポートはそのまま80でOKです、ここをいぢるとハマります。
先にALBを作成しようとコンソールを操作すると、Fargateを接続できないALBを作ってしまうハメになります。
その2. ALBのVPCについて
ALBを作りましょう。
VPCおよびサブネットはFargateで使う予定のものと完全に一致させる必要があります。
ALBに先ほど作成したターゲットグループを紐づけます。
その3. EPHEMERAL PORTについて
パニクらない様にターゲットグループのエフェメラルポート(49153-65535)について説明します。
泡のように刹那的で儚いポートという意味らしい。
ECSの場合、同じ目的のコンテナを複数立ち上げたり、スケールアウトして動的です。
普通のEC2をターゲットグループに紐づける場合は手動でも事たりますが、ECSの場合はそうもいきません。
そこで、エフェメラルポートの出番です。
ECS上の動的にスケールアウトする各コンテナにはエフェメラルポートが割り当てられ、ターゲットグループに接続されます。
エフェメラルポートの管理は全て自動的に行われます。
トラフィックポートとは別なので、エフェメラルポートの事はへぇ〜 というくらいがちょうど良いでしょう。
その4. Fargateデプロイについて
Fargateのデプロイをする時はターゲットグループを指定するとALBに繋がります。
ALBに繋がっていないターゲットグループに対してデプロイする事はできません。エラーになります。
ターゲットグループを指定すると、コンテナ指定とトラフィックポート指定が必須になります。
つまりデータ流れは
1. リクエストをALBのリスニングポート80,443で受ける
2. エフェメラルポート(49153-65535)をつかってタスクへ
3. 指定したコンテナのトラフィックポートへ
4. トラフィックポートからアプリケーションへ
て事はタスクがDockerのホストで、ホストはさらにDinDしてるって事なのかどうかは今回は触れません 汗
その5. SERVICEについて
スケールアウトがサービス内のタスクを増やす処理なので
1つのサービスには1種類のタスクだけにした方がよいでしょう。
では作ってみましょう。
VPC,Role,セキュリティグループ、ECR:コンテナは事前に作成しておきます。
必要なやつをインストール
brew update
brew install jq sed awscli amazon-ecs-cli awslogs
実行
Definitionを埋めて実行します
一気には流れないので、ブロック毎に実行してほしい。
########################################################################
### Definition
# ----------------------------------------------------------------------
# ECR
export VER=0.1
export AWS_ACCOUNT_ID=<AWSのアカウントID>
export DOCKER_IMAGE=<Dockrイメージの名前>
export DOCKER_FILE=<Dockerfileのパス>
export REPOSITORY_URI=${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${DOCKER_IMAGE}
# ----------------------------------------------------------------------
# CLUSTER
export CLUSTER_NAME_FARGATE=<新規のECSのクラスタ名>
export REGION=ap-northeast-1
export CONTAINER_NAME=<露出させるコンテナ名、docker-compose.ymlのserviceと同じにする>
# ----------------------------------------------------------------------
# CLOUD WATCH
export LOG_GROUP_NAME=<ロググループのパス docker-compose.ymlのlogging/driver/options/awslogs-groupと同じ>
# ----------------------------------------------------------------------
# ALB
export VPC=<VPCの名前>
export SUBNET_ID1=<VPCに属するサブネット名>
export SUBNET_ID2=<VPCに属するサブネット名>
export SUBNET_ID3=<VPCに属するサブネット名>
export SECURITY_GROUP=<セキュリティブループ名>
export LOAD_BALANCER_NAME=<ALBの名前>
export TARGET_GROUP_NAME=<ターゲットグループの名前>
export CERTIFICATE_ARN=<ACMのARN>
# ----------------------------------------------------------------------
# TASK
export PROJECT_NAME=<サービス名>
export COMPOSE_YML=docker-compose.yml
export PARAM_YML=docker/ecs-params-fargate.yml
export LAUNCH_TYPE=FARGATE
export APP_PORT=<露出させるコンテナのトラフィックポート、docker-compose.ymlのportsと同じにする>
export HEALTH_CHECK_PATH=<アプリケーションで準備したヘルスチェックパス>
########################################################################
#### Setup your image in ECR
#### 今回はスコープ外です。
########################################################################
### Build empty cluster in AWS ECS and configure the local ECS-CLI
### すでに同名のクラスタが存在しても問題ない。
ecs-cli up --empty --cluster $CLUSTER_NAME_FARGATE
########################################################################
### Create and config load balancer
# ----------------------------------------------------------------------
export LOAD_BALANCER_ARN=$(aws elbv2 create-load-balancer --cli-input-json \
'{
"Name": "'$LOAD_BALANCER_NAME'",
"Subnets": [
"'$SUBNET_ID1'",
"'$SUBNET_ID2'",
"'$SUBNET_ID3'"
],
"SecurityGroups": [
"'$SECURITY_GROUP'"
],
"Scheme": "internet-facing",
"Type": "application",
"IpAddressType": "ipv4"
}' | jq '.LoadBalancers[0].LoadBalancerArn' | sed -e 's/"//g')
# ----------------------------------------------------------------------
export TARGET_GROUP_ARN=$(aws elbv2 create-target-group --cli-input-json \
'{
"Name": "'$TARGET_GROUP_NAME'",
"Protocol": "HTTP",
"Port": 80,
"VpcId": "'$VPC'",
"HealthCheckProtocol": "HTTP",
"HealthCheckPort": "traffic-port",
"HealthCheckEnabled": true,
"HealthCheckPath": $HEALTH_CHECK_PATH,
"HealthCheckIntervalSeconds": 30,
"HealthCheckTimeoutSeconds": 5,
"HealthyThresholdCount": 2,
"UnhealthyThresholdCount": 2,
"Matcher": {
"HttpCode": "200"
},
"TargetType": "ip"
}'| jq '.TargetGroups[0].TargetGroupArn' | sed -e 's/"//g')
# ----------------------------------------------------------------------
aws elbv2 create-listener --cli-input-json \
'{
"LoadBalancerArn": "'$LOAD_BALANCER_ARN'",
"Protocol": "HTTPS", "Port": 443,
"SslPolicy": "ELBSecurityPolicy-2016-08",
"Certificates": [{"CertificateArn": "'$CERTIFICATE_ARN'"}],
"DefaultActions": [
{
"Type": "forward",
"TargetGroupArn": "'$TARGET_GROUP_ARN'",
"Order": 1
}
]
}'
aws elbv2 create-listener --cli-input-json \
'{
"LoadBalancerArn": "'$LOAD_BALANCER_ARN'",
"Protocol": "HTTP", "Port": 80,
"DefaultActions": [
{
"Type": "forward",
"TargetGroupArn": "'$TARGET_GROUP_ARN'",
"Order": 1
}
]
}'
########################################################################
### Create CloudWatch log group
aws logs create-log-group --log-group-name $LOG_GROUP_NAME
########################################################################
### Build Service
aws ecs delete-service --cluster $CLUSTER_NAME_FARGATE --service $PROJECT_NAME --force
ecs-cli compose -p $PROJECT_NAME -f $COMPOSE_YML --ecs-params $PARAM_YML -c $CLUSTER_NAME_FARGATE service up \
--launch-type $LAUNCH_TYPE \
--target-group-arn $TARGET_GROUP_ARN \
--container-port $APP_PORT \
--container-name $CONTAINER_NAME
########################################################################
### Scale out Service
ecs-cli compose -p $PROJECT_NAME -f $COMPOSE_YML --ecs-params $PARAM_YML -c $CLUSTER_NAME_FARGATE service scale 3
########################################################################
### Check Logs
awslogs get $LOG_GROUP_NAME -w -s 2h -G -S --timestamp