Open14

Docker Contextを使ってECSにデプロイするよ

けけずんけけずん

AWSのFargateを用いた環境でサービスを提供する際、基盤となる環境はTerraformで構築し、Fargate上で動作するコンテナについてはamazon-ecs-cliやecspressoなどのツールを用いてコンテナの展開を行なっていた。
amazon-ecs-cliとecspressoどちらもyaml形式で設定ファイルを記述し、コマンドを実行するだけで設定ファイルに記述した内容通りにサービスの作成やタスクの実行が行われるようになるため非常に便利。素敵なツールをありがとうございます。

けけずんけけずん

しかしながら上記のツールを使わずとも、2020年末ごろからDockerコマンドのみでECSへのコンテナの展開を完結できるようになっていたらしい。パッと見た感じ、Docker ComposeのCompose fileのような形式で記述でき、AWSリソースの設定箇所も x-aws- プレフィックスで記述されているため分かりやすい。

けけずんけけずん

順序よくやっていく。

ECSコンテキストの作成。
認証情報にはaws-cliで使用していたdefaultのプロファイルを使用する。

$ docker context create ecs my-ecs-context
? Create a Docker context using: An existing AWS profile
? Select AWS Profile default
Successfully created ecs context "my-ecs-context"

現在のコンテキストの切り替え。
先ほど作成したECSコンテキストにを使用する。

$ docker context use my-ecs-context
my-ecs-context

コンテキストの一覧。
コンテキストが切り替えられていることを確認する。


$ docker context ls
default             moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                                          swarm
my-ecs-context *    ecs
けけずんけけずん

Dockerの構成ファイルを作成する。
試しに最小限でnginxを用いたwebサーバを立ててみる。

docker-compose.yaml
services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
けけずんけけずん

コンテナを起動する。
コマンド実行後はリソースの作成が行われるため、少し時間がかかる。

$ docker compose up

[+] Running 14/14
 ⠿ learn-docker-context-ecs  CreateComplete                                                                               205.1s
 ⠿ LogGroup                  CreateComplete                                                                                 2.0s
 ⠿ WebTCP80TargetGroup       CreateComplete                                                                                 1.0s
 ⠿ CloudMap                  CreateComplete                                                                                47.0s
 ⠿ WebTaskExecutionRole      CreateComplete                                                                                26.0s
 ⠿ Cluster                   CreateComplete                                                                                 6.0s
 ⠿ DefaultNetwork            CreateComplete                                                                                 6.0s
 ⠿ DefaultNetworkIngress     CreateComplete                                                                                 1.0s
 ⠿ Default80Ingress          CreateComplete                                                                                 1.0s
 ⠿ LoadBalancer              CreateComplete                                                                               122.0s
 ⠿ WebTaskDefinition         CreateComplete                                                                                 3.0s
 ⠿ WebServiceDiscoveryEntry  CreateComplete                                                                                 2.0s
 ⠿ WebTCP80Listener          CreateComplete                                                                                 3.0s
 ⠿ WebService                CreateComplete                                                                                59.0s
けけずんけけずん

ECSのクラスター、サービス、タスクの生成のみを行なってくれるのかと思いきや、ロードバランサまで作られており、ECSサービスと紐づいていた。そして作成されたロードバランサのDNS名にアクセスすると、Nginxの起動時の画面が表示された。

VPCなどの設定に関してはdefaultのものが使用されているようだった。

けけずんけけずん

もはや簡略化されすぎてて逆に意味不明すぎる。
docker compose up を実行した結果、どのリソースが作成・変更されたのか分からん。

けけずんけけずん

試しにVPCだけ作成して、Docker構成ファイルにVPC ID指定した後に docker compose up を実行してみた。

docker-compose.yaml
x-aws-vpc: "[作成したVPCのID]"

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
$ docker compose up  
VPC [作成したVPCのID] should have at least 2 associated public subnets in different availability zones

異なるアベイラビリティゾーンのpublicなサブネットが2つ必要らしく、コンテナの起動に失敗する。

けけずんけけずん

アベイラビリティゾーンを二つ用意し、それぞれにpublicサブネットを配置して同様に実行してみた。

上手く起動でき、Nginxにアクセスできた。

docker compose up

[+] Running 14/14
 ⠿ learn-docker-context-ecs  CreateComplete                                                                               214.1s
 ⠿ Cluster                   CreateComplete                                                                                 6.0s
 ⠿ DefaultNetwork            CreateComplete                                                                                 6.0s
 ⠿ WebTCP80TargetGroup       CreateComplete                                                                                 2.0s
 ⠿ WebTaskExecutionRole      CreateComplete                                                                                23.0s
 ⠿ CloudMap                  CreateComplete                                                                                48.0s
 ⠿ LogGroup                  CreateComplete                                                                                 2.0s
 ⠿ LoadBalancer              CreateComplete                                                                               123.0s
 ⠿ DefaultNetworkIngress     CreateComplete                                                                                 1.0s
 ⠿ Default80Ingress          CreateComplete                                                                                 1.0s
 ⠿ WebTaskDefinition         CreateComplete                                                                                 3.1s
 ⠿ WebServiceDiscoveryEntry  CreateComplete                                                                                 2.0s
 ⠿ WebTCP80Listener          CreateComplete                                                                                 4.0s
 ⠿ WebService                CreateComplete   

このときに作成されたリソースを確認していく。

けけずんけけずん

ちなみにインターネットゲートウェイがなかったら起動失敗する。

けけずんけけずん
VPC [作成したVPCのID] has no internet gateway (Service: AmazonElasticLoadBalancing; Status Code: 400; Error Code: InvalidSubnet; Request ID: hogehoge; Proxy: null)
けけずんけけずん

CloudTrailの証跡を見て、作成されたリソースをざっと書く。
docker-compose.yaml にオプションなど書いていないため、基本的に作成されるリソースのオプションはデフォルトになってるはず。

CloudFormation

docker compose up コマンド実行時に最初に CloudFormation にテンプレートが登録される。
リソースの作成状態などは CloudFormation 上で管理されるようで、ローカルには state ファイルのようなものは作られない。また AWS に作成されるリソースの命名には、 docker compose up 実行時のカレントディレクトリ名が使用されるため、別のプロジェクトで同じフォルダ名を使用していると事故る可能性がある(多分)

ECS

  • Cluster
    • Fargate
  • Service
    • 許可されたVPC: 作成したVPC
    • 許可されたサブネット: 作成した2つのpublicサブネット
    • セキュリティグループ: docker compose up時に作成されるセキュリティグループ
    • パブリックIPの自動割り当て: ENABLED
  • TaskDefinition
    • Fargate
    • タスクサイズ
      • タスクメモリ(MiB): 512
      • タスクCPU(単位): 256
    • コンテナの定義
      • web
      • Web_ResolvConf_InitContainer
        • コマンドに謎のコマンドが指定されている
          • ["ap-northeast-1.compute.internal","learn-docker-context-ecs.local"]
  • ServiceDiscovery Service

Security group

  • Security group rule 1
    • Type: HTTP
    • Protocol: すべてのプロトコル
    • Port: 80
    • Source: 0.0.0.0/0
  • Security group rule 2
    • Type: すべてのトラフィック
    • Protocol: すべてのプロトコル
    • Port: すべてのポート
    • Source: Ingress2自身(自身のネットワーク内での通信を許可してる)

EC2

  • LoadBalancer
    • Type: application
    • Scheme: internet-facing
    • IP Type: ipv4
    • Availability zone: 作成した2つのpublicサブネット
    • Security group: docker compose up時に作成されるセキュリティグループ
  • Listener
  • TargetGroup
    • Target type: IP
    • IP address type: IPv4
    • Protocol(Port): HTTP(80)
    • Protocol version: HTTP1

CloudWatch Logs

  • Name: /docker-compose/learn-docekr-context-ecs

IAM Role

  • 以下のポリシーを持つロールが作成される
    • AmazonEC2ContainerRegistryReadOnly
    • AmazonECSTaskExecutionRolePolicy
けけずんけけずん

// TODO: TerraformでSecurity Group, RDSなど作成してDocker Context ECSを使ってみる