AWS
docker
ECS

雑にAWS ECSまとめ

ECSとは

ECS(EC2 Containre Service).
Dockerコンテナに最適化したオーケストレーションサービス。

クラスターの中にDockerコンテナをいくつも立ち上げられて、
外部からのアクセスを内部の任意のコンテナに動的ポートマッピングしてくれたりする。

内部で特定のDockerコンテナが落ちたらそのコンテナには外部から接続されないようにしたり
デプロイで、一個ずつコンテナを更新する際にダウンタイムが発生しないようにうまく制御してくれる。

要はコンテナの死活監視・デプロイ・スケールなどが簡単にできます。

ECSの何が良いのか

 本来コンテナの集合を1つのサービスとして提供する際はKurbernetesなどを使って、自分らで死活監視やスケールアップ・ダウンを管理しないといけない。これがめちゃくちゃ大変な作業で、ECSを使うとここら辺を全部AWSが勝手にやってくれるので大変効率が良い。開発者はインフラレイヤの面倒を見る手間が劇的に減って、アプリケーションレイヤに集中できるようになる。

「コンテナの集合を1つのサービスとして提供する」と何が良いのか

例えば、従来のように1つのサーバーインスタンスで1つのNginxデーモンでサービスを運用するのに比べて、少ないマシンリソースで高いパフォーマンスを発揮できる。

概念

大きく分けて

  1. Cluster
  2. Task
  3. Service

Clusterはコンテナの集まりで、Taskはクラスタ状で動かすコンテナのことだと思えばOK。コンテナイメージの指定・CPU・メモリ・環境変数・ロードバランサの定義などをTask作成のステップで行う。Serviceは、ClusterとTaskの紐付ける役割ともつ。1クラスターに複数サービスを登録することももちろんできる。ロードバランサと内部コンテナのポートマッピングや、コンテナの死活監視などもServiceが担ったりしている。さらには、アプリケーションが落ちないように良い感じにスケジューリングしながらクラスタ内のコンテナ一個一個にデプロイをしてくれます。

利用の流れ

1. ECRにDocker Imageを登録
2. Taskを定義。
このとき1のDocker Imageを指定
ネットワークモードについては以下の4つあります。
default: Windowsにしか設定できない。
bridge: EC2インスタンスで起動する時はとりあえずこれを設定。
awsvpc: Fargateで起動する時はとりあえずこれを設定。
host: 基本使わない。

3. ロードバランサなどの設定
4. ECS Clusterを作成
(CLBじゃなく、出来るだけALBを使う。1ホストで複数タスクを起動する際にALBじゃ無いとポートマッピングできないらしい)
5. Serviceを作成し、TaskとClusterを紐づける
6. 作成されたEC2のパブリックIPアドレスにアクセスして、サービスが走っていることを確認する

CI / CD環境構築

circleCI公式doc
など、探せば色々なところにやり方が載っています。
例としてCircleCIのデプロイワークフローを書いてみました。

/circleci/config.yml
version: 2
jobs:
  dev-deploy:
    machine:
      enabled: true
    working_directory: ~/your-app
    steps:
      - checkout
      - run:
          name: "Install awscli"
          command: pip install "urllib3<1.23,>=1.21.1" awsebcli
      - run:
          name: "Install ecs-deploy"
          command: |
            curl https://raw.githubusercontent.com/silinternational/ecs-deploy/master/ecs-deploy | sudo tee -a /usr/bin/ecs-deploy
            sudo chmod +x /usr/bin/ecs-deploy
      - run:
          name: "jqをインストール"
          command: sudo apt-get update && sudo apt-get install -y jq
      - run:
          name: "AWS ECRにログイン"
          command: eval $(aws ecr get-login --no-include-email --region ap-northeast-1)

      - run:
          name: "ビルドとDockerImageのpush"
          command: |
            docker build -t $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/your_image_name -t $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/your_image_name:$CIRCLE_SHA1 .
            docker push $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/your_image_name:$CIRCLE_SHA1
      - run:
          name: "Seviceデプロイ"
          environment:
          command: |
            ecs-deploy --cluster lb-dev --service-name lb-dev-service \
              --region ap-northeast-1 --timeout 120 \
              --image $AWS_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/your_image_name:$CIRCLE_SHA1

workflows:
  version: 2
  test:
    jobs:
      - test
  prod-deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only:
                - master
                # masterにpushされるとデプロイが走る
            tags:
              only:
              - /^debug-.*/
              # debug-から始まるタグをpushするとデプロイが走る。
              # テストデプロイしたいときなどに使う

トラブルシューティング

サービスデプロイ時の service xxx was unable to place a task because no container instance met all of its requirements.

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/service-event-messages.html#service-event-messages-2

タスクのコンテナインスタンス上の Amazon ECS コンテナエージェントが切断されている。

コンテナインスタンスにSSHでログインしてエージェントログを調べる必要がある。
ネットワークの問題でこのエラーが出ることが多い。

対処法の参考: https://qiita.com/aikan-umetsubo/items/e4cf1c6a092d82503c63

クラスターを削除できない

主にCloud Formationで作成されたクラスターを削除しようとするときに起こることが多い。
Cloud Formation経由で作成されたクラスターを削除すると、内部で該当するCloud Formationプロジェクトを削除する。このとき依存するVPCやネットワークACL、セキュリティグループも削除しようとして、外部から依存されてる設定を削除しようとしてエラーで止まる。

その他

.envの読み込みを自動化する

  1. SSMのパラメータストアでKeyValue形式でenvの中身を設定。
  2. ECSの設定から1のパラメータストアを読み込み.envとしてアプリで読み込む

ECS インスタンスのドレイニング

クレスタから特定のインスタンスを外すときに、ドレイニングが必要になる。
ドレイニングの自動化についてはAWSの公式ドキュメント が参考になる。

タスクを削除する

タスクないの全てのリビジョンを「登録削除」すると親のタスクも自動で削除される。