AWS ECS / Fargate 検証時につまずいた点について

Adways Advent Calendar 2020 14 日目の記事です。


 

どうも皆さんこんにちは。マーケティングテクノロジー Div の遠藤です。
フルリモートワークになり、早幾月。
特に食生活が変わったわけでもないのに着々と体重が増え始めました。

「通勤、おまえだったのか。体重の増加を食止めていたのは……」

結構運動になっていたのですね。意外な側面に気づかされました。

はじめに

私のチームで運用しているサービスの Web アプリケーションは AWS の EC2 上で動作させています。
現在、これを ECS Fargate へ移行するための検証を進めています。
本日は、ECS Fargate をこれから使おうとしている or 使い始めている方向けに
自分が検証前、検証中につまずいた点をその時の心理描写を交えつついくつか共有できればと思います。

現状の構成

利用している AWS サービス

  • EC2(Rails、Nginx)
  • RDS(MySQL)
  • ElastiCache(Redis)
  • ALB

※ Nginx 、Rails 間は Socket 通信

サービスのインフラ構成図

簡略化してありますが、概ね次のような構成をとっています。

f:id:AdwaysEngineerBlog:20201217211452p:plain

今回は、VPC、セキュリティグループについては既存のものを利用し、
EC2 のみ ECS Fargate へ切り替える想定で進めました。

利用したツール

構築には主に AWS CLI / ECS CLI を使用しました。
バージョンは下記になります。

$ aws --version
aws-cli/2.0.51 Python/3.7.3

$ ecs-cli --version
ecs-cli version 1.20.0

つまずいた点

Rails と Nginx コンテナとタスクの構成について

まず最初につまずいた点は、Fargate のタスクとコンテナについてです。

既存の構成で Nginx 、Rails 間は
Socket 通信しており、今回はその構成を踏襲する形にしようと考えていました。

私は、当初、1 つのタスクで動かせるコンテナは 1 つだと思い込んでおり、
それぞれのコンテナを別タスクとして作成していました。
すると、ボリュームが共有設定ができず、Nginx、Puma 間の socket 通信ができいないことに気付きました。

「うわー、ボリュームを共有するような構成ってとれないんかー。つらいわー。」

と一瞬絶望しましたが、一度冷静になりドキュメントを読み直してみると、

1 つのタスク定義に複数のコンテナを定義でき、
同じタスク内ならボリュームの共有ができることが判明し事なきを得ました。

最終的な構成は下記のような形になりました。

f:id:AdwaysEngineerBlog:20201217211432p:plain

スケジュールベースの Auto Scaling 設定

次につまずいた点についてですが、
既存システムでは、スケジュールベースの Auto Scaling 設定をいれているので、
ECS Fargate にもそのまま設定をいれればいいかととりあえず
AWS Management Console からは設定箇所を探してみました。
が、見つからず。。。

AWS Management Console 上ですと、

スケーリングポリシータイプ

  • ターゲットの追跡
  • ステップスケーリング

の2タイプしか選べず、スケジュールベースがありませんでした。

「うわー、スケジュールベースで設定できないんかー。つらいわー。」

と一瞬絶望しましたが、一度冷静になりドキュメントを読み直してみると、

AWS CLI を使って設定することが可能なことが判明し事なきを得ました。

以下、設定コマンドになります。

# ECS のサービスにスケーラブルターゲットを登録
aws application-autoscaling register-scalable-target \
    --service-namespace ecs \
    --scalable-dimension ecs:service:DesiredCount \
    --resource-id service/[クラスター名]]/[サービス名] \
    --min-capacity 1 \
    --max-capacity 3 \
    --region [リージョン] \
    --profile [プロファイル名]
# 登録されたか確認
$ aws application-autoscaling describe-scalable-targets \ 
    --service-namespace ecs --profile [プロファイル名]

{
    "ScalableTargets": [
        {
            "ServiceNamespace": "ecs",
            "ResourceId": "service/[クラスター名]]/[サービス名]",
            "ScalableDimension": "ecs:service:DesiredCount",
            "MinCapacity": 1,
            "MaxCapacity": 3,
            "RoleARN": "[作成されたARN]",
            "CreationTime": "[作成日時]",
            "SuspendedState": {
                "DynamicScalingInSuspended": false,
                "DynamicScalingOutSuspended": false,
                "ScheduledScalingSuspended": false
            }
        }
    ]
}
# スケールアウト設定
# 設定時間は UTC であることに注意
# 下記は、毎日 9 時に 3 タスクにスケールアウトし、10 時に 1 タスクにスケールインする設定
$ aws application-autoscaling put-scheduled-action \
    --service-namespace ecs \
    --resource-id service/[クラスター名]]/[サービス名] \
    --scheduled-action-name [スケジュール名] \
    --schedule "cron(0 9 * * ? *)" \
    --scalable-dimension ecs:service:DesiredCount \
    --scalable-target-action MinCapacity=3,MaxCapacity=3 \
    --region [リージョン] \
    --profile [プロファイル名]

# スケールイン設定
$ aws application-autoscaling put-scheduled-action \
    --service-namespace ecs \
    --resource-id service/[クラスター名]]/[サービス名] \
    --scheduled-action-name [スケジュール名] \
    --schedule "cron(0 10 * * ? *)" \
    --scalable-dimension ecs:service:DesiredCount \
    --scalable-target-action MinCapacity=1,MaxCapacity=1 \
    --region [リージョン] \
    --profile [プロファイル名]
# 設定確認
$ aws application-autoscaling describe-scheduled-actions \
    --service-namespace ecs \
    --profile [プロファイル名]

{
    "ScheduledActions": [
        {
            "ScheduledActionName": "[スケジュール名]",
            "ScheduledActionARN": "[作成されたARN]",
            "ServiceNamespace": "ecs",
            "Schedule": "cron(0 10 * * ? *)",
            "ResourceId": "service/[クラスター名]]/[サービス名]",
            "ScalableDimension": "ecs:service:DesiredCount",
            "ScalableTargetAction": {
                "MinCapacity": 1,
                "MaxCapacity": 1
            },
            "CreationTime": "[作成日時]"
        },
        {
            "ScheduledActionName": "[スケジュール名]",
            "ScheduledActionARN": "作成されたARN",
            "ServiceNamespace": "ecs",
            "Schedule": "cron(0 9 * * ? *)",
            "ResourceId": "service/[クラスター名]]/[サービス名]",
            "ScalableDimension": "ecs:service:DesiredCount",
            "ScalableTargetAction": {
                "MinCapacity": 3,
                "MaxCapacity": 3
            },
            "CreationTime": "[作成日時]"
        }
    ]
}

キャパシティプロバイダーで FARGATE_SPOT を設定

最後はキャパシティプロバイダーで FARGATE_SPOT を利用する設定方法についてです。
当初の私は、

「あー、キャパシティプロバイダーを使うと、Auto Scaling Group を細かく設定できるのね。」

ぐらいの理解度で、

「現状必要ない機能だから設定しなくていいか。スケジュールベースで AutoScaling 設定できたしな。」

考えるのを放棄しました。

しかし。。。FARGATE_SPOT の存在を知り、

「FARGATE_SPOT お値段最大 70% OFF!!これは試しておきたい!!」

使う方法を軽く調べたところキャパシティプロバイダーから設定できることが判明し、
再びキャパシティプロバイダーについて考えることになりました。

ドキュメントによると、

FARGATE キャパシティープロバイダーと FARGATE_SPOT キャパシティープロバイダーは予約されており、作成しなくても使用できます。

「なるほどー、作成しなくても使えるのかー。親切だなー」

と思い、どのような形で設定できるか知りたかった私は AWS Management Console から設定を試みたのですが、
FARGATE_SPOT を選択できる箇所が存在しません。

「あ、これは画面からは設定できないパターンのやつだな」

と早々に察せれれば良かったのですが、画面内を小一時間彷徨ってしまいました。

「うわー、ドキュメントに書いてあるのに設定できないー。つらいわー。」

と一瞬絶望しましたが、一度冷静になりドキュメントを読み直してみると、

Fargate および Fargate Spot キャパシティープロバイダーを作成する必要はありません。
これらのアカウントはすべてのアカウントで使用でき、クラスターに関連付けるだけで使用できるようになります。

作る必要はないけれど、クラスターに関連づける必要はあることが判明し事なきを得ました。

# 既存の ECS Cluster に Fargate Spot を適用させる
aws ecs put-cluster-capacity-providers \
     --cluster [クラスター名] \
     --capacity-providers FARGATE FARGATE_SPOT \
     --default-capacity-provider-strategy capacityProvider=FARGATE,weight=0,base=1 capacityProvider=FARGATE_SPOT,weight=1 \
     --profile [プロファイル名]
  • base:必ず動いてほしいタスク数
    この場合は FARGATE が必ず 1 タスクは動く設定になります。

  • weight:タスクが増える際にどの比率で増やしていくかの対比
    この場合は FARGATE は 0 なので、今後増えるタスクは FARGATE_SPOT のみになります。

さらに、このままでは既存サービスには設定が反映されていないので、別途設定してあげる必要があります。

$ aws ecs update-service \
     --capacity-provider-strategy capacityProvider=FARGATE,weight=0,base=1 capacityProvider=FARGATE_SPOT,weight=1 \
     --cluster [クラスター名] \
     --service [サービス名] \
     --desired-count 2 \
     --force-new-deployment \
     --profile [プロファイル名]

検証をしてみて所感

検証を始める前は、

「ECS Fargate でコンテナ動かすだけだし、そんなに難しくないだろう」

と高を括っていたのですが、
ECS、Fargate の特有のクラスター、キャパシティープロバイダー、サービス、タスク
といったそれぞれの機能の理解が浅かったこともあり、想像以上に検証は難航してしまいました。

しかしながら、公式ドキュメント は非常に充実しているので、
困ったことがあった場合には、ドキュメントに目を通せば解決方法が判明する感じはありがたかったです。

他にもつまずいた点は多々あるのですが、これ以上紹介すると自分がいかにドキュメントを読まずに
見切り発車で進めているかバレてしまうのでこの辺にしておきます。


 

次はまっちゃんの記事です。