ようへいの日々精進XP

よかろうもん

Amazon ECS と AWS Application Load Balancer の組み合わせを試しているメモ

ども、ECS は最近全然触れていないかっぱです。

追記

AWS CLI のでも操作してみた。

inokara.hateblo.jp

tl;dr

先日、AWS Application Load Balancer がリリースされて、Amazon ECS の Dynamic Port Mapping にも対応されたとのことで、ECS と Application Load Balancer の組み合わせを試行錯誤している。内容な認識等に誤りがあるかもしれないのでご容赦下さい。

試した環境

手元の環境

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

% ecs-cli --version
ecs-cli version 0.4.2 (9159726)

ECS

  • amzn-ami-2016.03.f-amazon-ecs-optimized (ami-ed26e78c)
  • ECS のクラスタは ecs-cli で作成済み
  • マネジメントコンソールより ECS の Service を作成していく
  • Service 内で起動する Task はコンテナ自身のホスト名を返す Sinatra アプリケーション(コンテナ内で 4567 ポートで Listen する)

memo

以下、殴り書き。(後で整理するかも)

  • 事前に Application Load Balancer を作っておく(以後、Application Load Balancer を ALB と記す)
  • ALB 作成の際にはターゲットグループを一つ作る必要がある(ターゲットにインスタンスを登録する必要は無い)
  • ターゲットグループを消す時にはALB を消してからターゲットグループを削除した方が良さそう(リスナーの定義を先に消せば良い)
  • 下図のように ECS の Service 作成時に ALB を指定することが出来る(ターゲットグループが生成される)

f:id:inokara:20160813103607p:plain

f:id:inokara:20160813103618p:plain

ecsServiceRole という IAM Role が付与される。この Role には AmazonEC2ContainerServiceRole というマネジメントポリシーが適用されている。

f:id:inokara:20160813103632p:plain

f:id:inokara:20160813103648p:plain

f:id:inokara:20160813103833p:plain

  • 既存の ECS Service には追加出来ない?(Service 作成時のみ ELB 定義が出来るようだ)
  • Container Instance に付ける Security Group は ALB に付与した Security Group から Container の Dynamic Port にアクセス出来るようなルールを定義しておく
source: public-access
protocol: TCP
port range: 0-65535

f:id:inokara:20160813101535p:plain

  • ルーティングのルールはロードバランサーの一覧にて該当の ALB を指定してリスナーで修正する

f:id:inokara:20160813102651p:plain

/* としておくとクライアントのリクエストをそのままバックエンドに流すような挙動になった。

  • ALB に登録されたコンテナにアクセスしてみる
% curl -v alb-ecs-demo-xxxxxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com/hostname
*   Trying 54.xx.xxx.xxx...
* Connected to alb-ecs-demo-xxxxxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com (54.xx.xxx.xxx) port 80 (#0)
> GET /hostname HTTP/1.1
> Host: alb-ecs-demo-xxxxxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com
> User-Agent: curl/7.43.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Sat, 13 Aug 2016 01:39:01 GMT
< Content-Type: text/html;charset=utf-8
< Content-Length: 12
< Connection: keep-alive
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< * Connection #0 to host alb-ecs-demo-xxxxxxxxxxxxxx.ap-northeast-1.elb.amazonaws.com left intact
59294f32f412
  • 当たり前だのクラッカーだけど Task のスケールアウトにも追随してくれる
#
# Task を 3 つに増やした後にアクセス
#
% curl alb-ecs-demo-1930795025.ap-northeast-1.elb.amazonaws.com/hostname
79a796b62150%                                                                                                                                                                                    
% curl alb-ecs-demo-1930795025.ap-northeast-1.elb.amazonaws.com/hostname
68cb8177a441%
% curl alb-ecs-demo-1930795025.ap-northeast-1.elb.amazonaws.com/hostname                                                                               59294f32f412% 

まとまってないけどまとめ

  • まさに産業革命
  • Dynamic Port Mapping やりくりする為に HAProxy とか入れなくても良くなったのは嬉しい
  • 後で CLI でもやってみる

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 が動いていない状態でもクラスタの状態は何らかの方法で管理されている?