Help us understand the problem. What is going on with this article?

ALB & ECS:FARGATE でAPI作る件

More than 1 year has passed since last update.

モチベーション

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 
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away