エンドポイントを使用してプライベートサブネットでECSを使用する

こんばんわ、札幌のヨシエです。

ECSは多方面のワークロードで使いやすい反面、クラスター内の情報通知やECRからコンテナイメージを取得するためにECS/ECRサービスへ接続する必要があることからインターネットへ接続できる必要があると考えておりました。
※既存のプライベートサブネットからS3などにアクセスするイメージです。

今回はPrivatelinkにてエンドポイントが用意されていたECS/ECRのエンドポイントを利用して、プライベートサブネットでECSを使用する方法を纏めてみます。

困っていたこと

上記で記載したようにPublicSubnetでECSクラスターを展開することに抵抗感がある事とNATGatewayを経由して接続するといったことをやりたくありませんでした。

結論

結果として、PrivateSubnetでECSを利用するためにはそれぞれのエンドポイントを作る必要がありました。
EC2とFargateのそれぞれで必要なエンドポイントは以下のようになりますので、参考になると嬉しいです。

ECSエンドポイント

エンドポイント名 EC2 Fargate
ecs-agent 必要 不要
ecs-telemetry 必要 不要
ecs 必要 不要

ECRエンドポイント

エンドポイント名 EC2 Fargate
ecr.dkr 必要 必要
ecr.api 必要 不要
s3 必要 必要
logs 使途によって追加 必要

構成説明

具体的なイメージとして2Tier(ALBとEC2を分離)構成を例として書いてみます。

この構成は採用されやすい構成でHTTP/HTTPS通信をALB経由でPrivateSubnetに配置されているEC2にリクエストが転送されます。
EC2はPrivateSubnetに配置されている点からインターネット経由でアクセスが出来ません。

上記のEC2構成をECSに置き換えてみます。

EC2がECSに置き換えられて、PublicSubnetにNATGatewayが追加配置しております。

これはコンテナスケーリングやコンテナイメージを取得するため、インターネットを経由して各サービス(ECS/ECR)へ接続する必要があるため必要になったNATGatewayです。

この構成で厳しいと考えられる点はNATGatewayがAZ障害等で利用不可となった際にECS/ECRへの通信が出来なくなる懸念がありました。

今回試した構成はNATGatewayを排除して、PrivateLinkを使用してECSとECRへ接続する方法を確認しました。

ECS/ECR VPCエンドポイントについて

ECS VPCエンドポイントについて

ECSを使用するためには3種類のVPCエンドポイントが必要となります。

各名称にておおよその推測はつくと思いますが、ECSクラスターのEC2で起動しているECS-Agentが通信をECSと通信を行うために使用されるエンドポイントと考えられます。

Fargateに関してはこちらのエンドポイントは不要となります、Fargate Onlyの環境ではコストが発生する観点からエンドポイントは作らない方針が良いと思われます。

  • ecs-agent
  • ecs-telemetry
  • ecs

各エンドポイントの説明文章が見当たらないため、EC2(コンテナホスト)とエンドポイント(ENI)の通信制御を行うSecurityGroupを一時的に外すことで、想定どおりECS-Agentが通信できない状態に移行したことが確認できました。

ECR VPCエンドポイント

次にECRのVPCエンドポイントを整理してみます。

ECRはECSのコンテナホスト(EC2/Fargate)によって使用エンドポイントが異なりますので注意してください。

起動タイプに「EC2」を利用している時のエンドポイント

  • ecr.dkr
    • ECRへdocker pulldocker pushを実行する時に利用されるエンドポイント
  • ecr.api
    • ECRのAPIを呼び出すためのエンドポイント
  • s3
    • コンテナイメージを取得するためのエンドポイント
    • ECRにはイメージのマニフェスト情報が配置されており、実際のコンテナレイヤー情報はS3に配置されている観点から必要

こちらも挙動を確認するためにテスト用のhttpdコンテナを起動しておき、起動中のコンテナとホストに保存されているコンテナイメージを削除します。

コンテナ状態
[root@ip-10-0-11-68 ~]# docker ps ; docker images
CONTAINER ID        IMAGE                                                                   COMMAND              CREATED             STATUS                 PORTS               NAMES
2462616cfeee        xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd:latest   "httpd-foreground"   54 seconds ago      Up 53 seconds                              ecs-web-task-5-apache-8cadcc9e9481b1e83000
7612d4931cb1        amazon/amazon-ecs-pause:0.1.0                                           "./pause"            55 seconds ago      Up 54 seconds                              ecs-web-task-5-internalecspause-c0b7d585b4e8fceee201
a57b9635ce59        amazon/amazon-ecs-agent:latest                                          "/agent"             3 hours ago         Up 3 hours (healthy)                       ecs-agent
REPOSITORY                                                       TAG                 IMAGE ID            CREATED             SIZE
amazon/amazon-ecs-agent                                          latest              0d40082075b2        2 weeks ago         63.4MB
amazon/amazon-ecs-pause                                          0.1.0               71cca41e8259        2 weeks ago         954kB
xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd   latest              96ba2182ff01        5 months ago        231MB
コンテナイメージ削除/コンテナ停止
[root@ip-10-0-11-68 ~]# docker rmi --force xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd ; docker kill 2462616cfeee
Untagged: xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd:latest
Untagged: xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd@sha256:4be2621192c24242d6c312c25c320eb898a754635ac0413dd229ea6005d8f27a
2462616cfeee

ECS-Agentがコンテナが終了したことを検知して、コンテナイメージから起動し直そうとしますがコンテナイメージも削除されているのでECRからイメージを取得します。

ECSエンドポイントと同じようにECR向けのセキュリティグループを一時的に外して挙動を確認しました。

ECS-Agentログ(コンテナ停止直後)
①コンテナの停止をECS-Agentが検知
2019-12-02T05:57:21Z [INFO] Managed task [arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxxx:task/8c0af069-2d07-490a-b3b2-8f7de87ee3ec]: sending container change event [apache]: arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxxx:task/8c0af069-2d07-490a-b3b2-8f7de87ee3ec apache -> STOPPED, Exit 137, , Known Sent: RUNNING

②docker pullに失敗
2019-12-02T06:02:28Z [WARN] DockerGoClient: failed to pull image xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd:latest: Error response from daemon: Get https://xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/v2/: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

上記のようにdocker pullが失敗していることがわかります。

セキュリティグループを再設定したところ以下の内容が出力されました。

2019-12-02T06:46:33Z [INFO] Task engine [arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxxx:task/a2bea75c-4d1c-4a7f-86fe-6d2d3b1ee56c]: pulling image xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd:latest for container apache concurrently
2019-12-02T06:46:33Z [INFO] Task engine [arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxxx:task/a2bea75c-4d1c-4a7f-86fe-6d2d3b1ee56c]: recording timestamp for starting image pulltime: 2019-12-02 06:46:33.632175896 +0000 UTC m=+15536.708536559
2019-12-02T06:46:33Z [INFO] Task engine [arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxxx:task/a2bea75c-4d1c-4a7f-86fe-6d2d3b1ee56c]: finished pulling image xxxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/yoshie-httpd:latest for container apache in 114.016015ms

起動タイプに「Fargate」を利用している時のエンドポイント

  • ecr.dkr
    • EC2と同様にdockerコマンドを実行するためのエンドポイント
  • s3
    • EC2と同様にコンテナイメージを取得するためのエンドポイント
  • logs
    • ログ情報をCloudWatchLogsへ送信するために必要なエンドポイント
    • awslogs(CloudwatchLogs)を使用しないコンテナではエンドポイントは未作成で起動することが可能
    • awslogsへログ出力されるタスク定義の場合、このエンドポイントが存在しないとタイムアウトで コンテナ起動が出来なくなる

Fargateではコンテナホストのログは見えない分、コンテナに関するログ出力はawslogsログドライバーを利用する形になるため構築時には注意した方が良さそうです。

最後に

ECS/ECRのエンドポイントについてようやく整理が出来ました。 今回特に気にしたのはECRで利用されるCloudWatchLogsへのログ転送用エンドポイントlogsとコンテナイメージ取得用エンドポイントのS3がなぜ必要なのかが整理出来たことはポイントとして大きいです。 ウェブサーバーの例で今回は書きましたが、外部に晒したくない処理を行わせるためにもコンテナは非常に重宝されるのでぜひお試し頂ければと思います。