ようへいの日々精進XP

よかろうもん

Amazon ECS と AWS Application Load Balancer の組み合わせを試しているメモ(2)〜 AWS CLI で試す 〜

エブリデイ試した系ですいません。かっぱです。

tl;dr

inokara.hateblo.jp

の続き。

AWS CLI で ALB を作成して ECS の Service を作成して ALB と組み合わせてみる。尚、ALB を操作するには elbv2 というサブコマンドを利用する。

メモ

ちょっとウンチク

ここまで試してきて Amazon ECS と AWS Application Load Balancer は以下のような構成になっていると理解。

f:id:inokara:20160816092039p:plain

  • Listner で ALB がインターネットからの接続を何番ポートで Listen するか定義する
  • Path Pattern 毎に Target Group を定義する
  • Target Group には EC2 インスタンスやコンテナをぶら下げることが出来る(バックエンドへのルーティングやヘルスチェックについて定義する)

ALB の詳細については以下の Blog 記事はドキュメントと並んでとても参考になる。

dev.classmethod.jp

また、ALB についての制限事項については以下のドキュメントには目を通しておきたい。

docs.aws.amazon.com

試した環境

Amazon ECS や AWS Application Load Balancer を操作する環境としては...

% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G24b

% aws --version
aws-cli/1.10.56 Python/2.7.6 Darwin/15.6.0 botocore/1.4.46

ECS については...

  • クラスタ作成済み(コンテナインスタンスは 2 台)
  • Task Definition は ecscompose-ecs-app:22 を利用
  • Container Name は app とする
  • コンテナアプリケーションはコンテナ内で Port 4567 で Listen する
  • Task は /hostname という URL にアクセスするとコンテナのホスト名を返す

ざっくり流れ

  1. ALB ロードバランサの作成
  2. ALB ターゲットグループの作成
  3. ALB リスナーの作成
  4. ALB ターゲットグループを指定して ECS Service を作成

ALB の作成

% cat alb.json
{
    "Name": "alb-demo", 
    "Subnets": [
        "subnet-12345678",
        "subnet-abcdefgh"
    ], 
    "SecurityGroups": [
        "sg-12345678"
    ] 
}
  • run
% ELB_ARN=$(aws --debug \
  elbv2 create-load-balancer \
    --cli-input-json file://alb.json | jq -r '.LoadBalancers[].LoadBalancerArn')
  • 確認
% aws \
  elbv2 describe-load-balancers
  • 念のため削除(消す必要が無ければ叩かない)
% aws \
  elbv2 delete-load-balancer \
    --load-balancer-arn ${ELB_ARN}

ターゲットグループ作成

% cat target-group.json
{
    "Name": "default", 
    "Protocol": "HTTP", 
    "Port": 80, 
    "VpcId": "vpc-xxxxxxxx",
    "HealthCheckPath": "/hostname", 
    "HealthCheckIntervalSeconds": 30, 
    "HealthCheckTimeoutSeconds": 15, 
    "HealthyThresholdCount": 3, 
    "UnhealthyThresholdCount": 3, 
    "Matcher": {
        "HttpCode": "200"
    }
}

以下のように HealthCheckPort を指定しない場合には traffic-port という値が入る。Amazon ECS のコンテナをぶら下げる場合には Docker から払いだされたポートに対するヘルスチェックとなるので HealthCheckPort は指定しない。

% aws elbv2 describe-target-groups
{
    "TargetGroups": [
        {
            "HealthCheckPath": "/hostname", 
            "HealthCheckIntervalSeconds": 30, 
            "VpcId": "vpc-xxxxxxxxx", 
            "Protocol": "HTTP", 
            "HealthCheckTimeoutSeconds": 15, 
            "HealthCheckProtocol": "HTTP", 
            "LoadBalancerArns": [
                "arn:aws:elasticloadbalancing:ap-northeast-1:1234567890123:loadbalancer/app/alb-demo/1234567890123456"
            ], 
            "UnhealthyThresholdCount": 3, 
            "HealthyThresholdCount": 3, 
            "TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:1234567890123:targetgroup/default/1234567890123456", 
            "Matcher": {
                "HttpCode": "200"
            }, 
            "HealthCheckPort": "traffic-port", 
            "Port": 80, 
            "TargetGroupName": "default"
        }
    ]
}
  • run
% TARGET_GROUP=$(aws --debug \
  elbv2 create-target-group \
    --cli-input-json file://target-group.json | jq -r '.TargetGroups[].TargetGroupArn')
  • 確認
% echo ${TARGET_GROUP}

リスナーの作成

  • run
% aws \
  elbv2 create-listener \
    --load-balancer-arn ${ELB_ARN} \
    --protocol HTTP \
    --port 80 \
    --default-actions Type=forward,TargetGroupArn=${TARGET_GROUP}

ECS Service の作成

  • ecs-simple-service-elb.json
% cat ecs-simple-service-elb.json
{
    "serviceName": "ecs-simple-service-elb",
    "taskDefinition": "ecscompose-ecs-app:22",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxxxxxxxx:targetgroup/default/zzzzzzzzzzzzzzzz",
            "containerName": "app",
            "containerPort": 4567
        }
    ],
    "desiredCount": 2,
    "role": "ecsServiceRole"
}
  • run
aws \
  ecs create-service \
    --cluster ecs-demo \
    --service-name ecs-simple-service-elb \
    --cli-input-json file://ecs-simple-service-elb.json

ここまでで...

  • ALB とリスナー

f:id:inokara:20160813233133p:plain

  • ターゲットグループ(ヘルスチェックが適切では無かったので unhealthydraining を繰り返している... )

f:id:inokara:20160813233346p:plain

  • ECS Service

f:id:inokara:20160813233821p:plain

確認

  • run
% URL=$(aws \
  elbv2 describe-load-balancers \
    --query 'LoadBalancers[].DNSName' \
    --output text)

% echo ${URL}
% curl ${URL}
% curl ${URL}/hostname
  • output
% curl ${URL}/hostname
e80f74a9026f%
% curl ${URL}/hostname
af8647af702e%
% curl ${URL}/hostname
5963b959e447%

その他メモ

ecs-cli で ECS クラスタ作成

--subnets--azs 等の複数指定出来るパラメータの値はカンマで区切ること。スペースでは無いので注意。(aws cli はスペースで区切るという違いがある。)

% ecs-cli up \
  --keypair ${KEY_NAME} \
  --capability-iam \
  --size 1 \
  --vpc vpc-xxxxxxxx \ 
  --instance-type t2.micro \
  --subnets subnet-xxxxxxxx,subnet-zzzzzzzz \
  --azs ap-northeast-1a,ap-northeast-1c \
  --security-group sg-xxxxxxxx

ecs-agent が止まるとどうなるのか

ALB と ECS を連携させた状態で ecs-agent を止めてみると...

  • 起動している Task(コンテナ)は維持される
  • ALB にぶら下がっている Task(コンテナ)にも正常にアクセス出来る

更にこの状態で一つの Task を Stop Container してみると...

  • ALB のヘルスチェックが失敗して停止したコンテナは unhealthydraining に状態が変化する(ALB から切り離される)
  • draining に遷移したタイミングで Running tasks count が 1 つ減る

ここで疑問、ecs-agent が動いていない状態でもクラスタの状態は何らかの方法で管理されている?