ALB & ECS:FARGATE でAPI作る件

# モチベーション
Fargate簡単だよ系の記事は見かけるが、実際にやるといくつかパニクりポイントがあります。
公式ドキュメントは逆引き系なので使い易いんですが、単語が多い為か最初は難解、
ここでは備忘録として残します。

# Fargateとは
Dockerです。
こんな感じの構成になってます
クラスタ/サービス/タスク/コンテナ　

起動タイプがEC2の場合はクラスタの部分がEC2インスタンスのクラスタとなり自分で管理する必要あり。
Fargateの場合、裏側で勝手に管理してくれるのでスケールアウトがメタクソ楽になります。
その代わりお値段が約2割増です。

# パニクりポイント
初めての人がALBを使ってAPIをデプロイしようとするとパニクりポイントに出会えます。

### その１. TARGET　GROUPについて

```
ターゲットグループを真っ先に作りましょう。  

なぜかって？　
ECSのデプロイはターゲットグループに紐づけるのではなく
ECSからターゲットグループへ接続しに行くイメージを持つと理解が早まります。

ターゲットタイプはIPです。 
ポートはそのまま80でOKです、ここをいぢるとハマります。
先にALBを作成しようとコンソールを操作すると、Fargateを接続できないALBを作ってしまうハメになります。
```

### その2. ALBのVPCについて

```
ALBを作りましょう。
VPCおよびサブネットはFargateで使う予定のものと完全に一致させる必要があります。
ALBに先ほど作成したターゲットグループを紐づけます。
```

### その３.　EPHEMERAL PORTについて
```
パニクらない様にターゲットグループのエフェメラルポート（49153-65535）について説明します。

泡のように刹那的で儚いポートという意味らしい。
ECSの場合、同じ目的のコンテナを複数立ち上げたり、スケールアウトして動的です。
普通のEC2をターゲットグループに紐づける場合は手動でも事たりますが、ECSの場合はそうもいきません。
そこで、エフェメラルポートの出番です。
ECS上の動的にスケールアウトする各コンテナにはエフェメラルポートが割り当てられ、ターゲットグループに接続されます。
エフェメラルポートの管理は全て自動的に行われます。
トラフィックポートとは別なので、エフェメラルポートの事はへぇ〜　というくらいがちょうど良いでしょう。
```

### その4. Fargateデプロイについて
```
Fargateのデプロイをする時はターゲットグループを指定するとALBに繋がります。
ALBに繋がっていないターゲットグループに対してデプロイする事はできません。エラーになります。
ターゲットグループを指定すると、コンテナ指定とトラフィックポート指定が必須になります。

つまりデータ流れは
1. リクエストをALBのリスニングポート80,443で受ける
2. エフェメラルポート（49153-65535）をつかってタスクへ
3. 指定したコンテナのトラフィックポートへ
4. トラフィックポートからアプリケーションへ
```

て事はタスクがDockerのホストで、ホストはさらにDinDしてるって事なのかどうかは今回は触れません 汗

### その５. SERVICEについて
```
スケールアウトがサービス内のタスクを増やす処理なので
１つのサービスには１種類のタスクだけにした方がよいでしょう。
```

# では作ってみましょう。
VPC,Role,セキュリティグループ、ECR:コンテナは事前に作成しておきます。

### 必要なやつをインストール

```shell
brew update
brew install jq sed　awscli　amazon-ecs-cli　awslogs
```

### 実行

Definitionを埋めて実行します  
一気には流れないので、ブロック毎に実行してほしい。

```shell

########################################################################
### 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 
```