Open3

ECS, Fargate

nabetsunabetsu

ECS(Elastic Container Services)

AWSにおけるコンテナサービス
以下のような多様な要素から成り立っている

  • ECR
  • ECS cluster
  • ECS container instance
  • ECS agent
  • ECS task definition
  • ECS task
  • ECS service

AWS-What is Amazon Elastic Container Service?

参考にした資料

  • Docker on Amazon Web Services

by Justin Menga
Published by Packt Publishing, 2018

ECSまとめ

  • ECS clusterはECS container instanceを集めたもの

  • ECS Task Definitionはコンテナの定義を行うもの(nginx等のイメージから、環境変数、CPU/Memory Resourseなど)

  • ECS ServiceはECS ClusterとTask Definitionを元に作成することができる。ECS Serviceを作成することでTaskが作成され、アプリケーションが初めてインスタンス化される

  • Task Definitionはimmutableなので、別のTaskを定義するかRevisionをあげるしかできない(同じバージョンで内容は変更できない)

  • Dynamic Port Mappingは複数のコンテナを起動させるためには重要だが、Load Balancerと組み合わせないと使い物にならない?

For dynamic port mapping to be useful, you need to associate your ECS services with an application load balancer, which will automatically detect the dynamic port mapping of each ECS service instance and load balance incoming requests to a static port defined on the load balancer to each ECS service instance.

  • 一度きりの作業を実行するにはECS Taskを利用する(実行させるコマンドや環境変数の設定もできる)

基本的な流れ

  • Network, IAM等の作成
  • Clusterの作成
  • Task Definitionの作成
  • ECS Serviceの作成

ECSサービスを作成すると実際のアプリケーション環境が作成される

ECS Cluster

  • Clusterとして以下の3つを選べる

    • Fargate(Serverless)
    • EC2 + Linux
    • EC2 + Windows
  • Clusterはtask または serviceを集めたもの

  • ClusterはRegionごとに作るもの

キャパシティプランニングの更新

ECSにおけるタスク実行のインフラをより柔軟に設定する新しい仕組み
EC2とFargateの両方に対応している

Capacity Providerとは?ECSの次世代スケーリング戦略を解説する #reinvent #cmregrowth

Task Definition

Docker ContainerをECSで動かす上で必須。
Task DefinitionではデプロイするContainerの定義(Blueprint for application)を行う。

一部の項目はTask レベルで行うが、ほとんどはContainer単位で設定する

  • Docker Image
  • CPUとメモリ
  • Launch Type(EC2 or Fargate)
  • Docker Netoworking Mode(Fargateではawsvpcがデフォルト)
  • Logging configuration
  • 完了時、エラー時の挙動
  • Data Volumeの設定
  • その他たくさん...

Task Definitionはimmutableなので、新しいアプリをデプロイする際は新しいバージョンのTask Definitionを発行する

Task Defintion作成の流れ

  • Task Role
  • Network Mode
  • Task Execution Role
  • Task Size
  • Container Definition
  1. タイプの選択
    以下のようにFargateかEC2を選ぶ

  1. タスクの定義
    タスクが他のAWSサービスを呼び出すためのIAMロールが必要

  1. Containerの定義

Serviceの作成

Task Definitionを使って具体的にどのような構成でサービスを作るか定義する。

具体的には必要なタスク数を定義することで、自動で必要なタスク数になるまで自動でタスクの起動を行ってくれる

コンソールからだとCluster選択後のサービスタブから作成できる

設定としては以下の項目のほか、デプロイの方法(ローリングアップデート or Blue/Green)や、
ネットワーク、サービスメッシュの使用有無などが選べる

サービスの作成が完了すると以下の通り新しいタスクができる

Taskの実行

一時的なタスク(デプロイ用スクリプトの実行やデータベースマイグレーションの実行、スケジュールでのバッチ処理の実行)を行うにはECS tasksを利用する

以下の表の通りECS TasksはECS Serviceとはかなり異なった振る舞いをする。

Task作成の流れ

  1. clusterからタスクを選択し、新しいタスクの実行をクリックする

  1. タスクの実行タブで起動タイプはEC2でこれまでに作成したTask Definitonを選ぶ

また、Advanced Optionからコンテナの上書きができるので、コマンドの上書きで実行させたいコマンドを指定する。

※コマンドの指定は必ずコンマ区切りで入力する点に注意

ECRまとめ

fully-Managed private Docker registry provided by AWS

  • Repositories
  • Permissions
    • IAMと組み合わせでリポジトリごとに権限の設定ができる
  • Lifecycle policy
    • 古くなったバージョンのImageの扱いなど、それぞれのレポジトリごとに設定が可能
  • Authentication sevice

immutable

immutableにするとイメージの上書きができなくなる。上書きしようとすると以下のエラーがでてpushに失敗する。
ERROR: tag invalid: The image tag 'app-latest' already exists in the 'text-api' repository and cannot be overwritten because the repository is immutable.

また、immutableのチェックを外してPUSHすると、もともとあったイメージはuntaggedになる。
Uploading file..._6g33e1l0h

  • scan

リポジトリの作成時にスキャンを有効にすると以下のようにImage内の脆弱性を検査してくれる

Repositoryの作成

作成には以下3つの方法がある。

  • コンソール
  • CLI
  • CloudFormation

CloudFormationの例

AWSTemplateFormatVersion: "2010-09-09"

Description: ECR Repositories

Resources:
  TodobackendRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: docker-in-aws/todobackend
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 10,
                "description": "Untagged images",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 7
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }

上記のテンプレートファイルを作成し、以下のコマンドを実行

$ aws cloudformation deploy --template-file ecr.yml --stack-name ecr-repositories

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - ecr-repositories

すでに同じ名前のリポジトリが存在する場合には、deployを実行しても内容は上書きされない?

Imageの発行(Single Image)の手順

ImageをBuildしてECRにPublishする。
そのためには以下の対応が必要

  • ECRへのログインを行う(Dockerクライアントの認証)
  • 発行先のECR RepositoryのURIでDocker Imageがビルドする
  • その上で、ECRにDocker ImageをPushする

以下のプッシュコマンドを表示より、ImageをPublishするまでの手順が確認できる

  1. 認証トークンを取得し、レジストリに対して Docker クライアントを認証する
# 
% aws ecr get-login --no-include-email
docker login -u AWS -p eyJwYXlsb2FkIjoibXFHaGZXdFFqOFh5WDlIUHJDakFYTEphWHdPdEdodlg3cW1NMnBZWk0zcFlhVHFJd1RwY1BZMk9vRTdBWGhDMVhadnJ3T2dzZjJFOUVLTWdKMW9PdHVvL1g2bWplUFV
...


# 自動でログインまで
% $(aws ecr get-login --no-include-email)
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded

Docker ImageのBuild

  • ビルドからタグ付まで
    ビルド時に-tオプションでタグをつける。
    タグの内容はECRのURI + バージョン名
# 1回でビルドからタグ付まで
% docker build -t xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest .
...
Successfully built 9c6c55c424e6
Successfully tagged xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest

# 2回に分けるやり方(AWSに書いてあるのはこっち)
$ docker build -t docker-in-aws/todobackend .
$ docker tag docker-in-aws/todobackend:latest xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest
  • Push
    docker push <タグ名>でプッシュが行える
% docker push xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazo
naws.com/docker-in-aws/todobackend:latest
The push refers to repository [455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend]
6c0d36bac6d0: Pushed 
ff07f0a70215: Pushed 
ab92eaccd0cd: Pushed 
20bd9404ca0d: Pushed 
ef47db0b2220: Pushed 
f227d9a2d5b2: Pushed 
cf50c2fe3e68: Pushed 
7fd45620b4c6: Pushed 
5216338b40a7: Pushed 
latest: digest: sha256:bf3825df219e07fd5784b139e626b41b9754d315f431be13f955143b1b305c7d size: 2202

上記のコマンドを実行するとECRに対象のImageが作成されていることが確認できる。

リポジトリに保存されるときには圧縮されるのが実際のイメージサイズよりは小さくなる。
課金はストレージの容量と転送量で決まるので注意

発行したイメージの利用

  • Task Definitionの作成
    Task Definitionの作成でコンテナの定義でイメージとしてECRにプッシュしたものを選択する

  • サービスの作成
    サービスの作成で上記で作成したTask Definitionを選択する

Deploy

ECS Rolling Deployments

ECSにおけるDeployの特徴はRolling Deploymentにあり、
ロードバランサーをつかって既存のタスクと新しいタスクを切り替えることで
ダウンタイムゼロ、エンドユーザーへの影響なしを実現する。
以下の図でその

  • TaskDefinitionの変更がトリガーとなりデプロイが行われ
  • DeploymentConfigurationで設定した内容に応じてデプロイが行われる

  • Cloudformation deploy

CI/CD

第1回 AWS Fargate かんたんデプロイ選手権

AmazonECS / Fargate 本番運用のための構築とデプロイ方法まとめ

GitHub: CloudFormation Templates for AWS Fargate deployments

全体の流れ

  • CodeBuildでテストの実行およびECRへの最新版のImageの発行

Continuou Integration

Custom CodeBuild Containerの作成

CodeBuildがデフォルトで提供しているイメージはaws cli, make, docker-compose等、
ビルドに必要なツールがインストールされていない。

pre-build stepでこれらのツールをインストールすることも可能だが、
ビルドのたびに毎回これらのインストールが発生するのでビルドに時間がかかってしまう。

Code Buildはカスタムイメージの使用もサポートしているので、
必要なライブラリをインストールしたイメージを自分で作成してみる。

  • Docker in Docker(DinD)
    Dockerの中でDockerを使うのには色々議論があるが、ビルドの用途であれば問題はない

ImageのBuild

# CodeBuild用コンテナのイメージ作成(codebuildという名前で)
$ docker build -t codebuild -f Dockerfile.codebuild .

リポジトリの作成およびイメージのPublish

アプリケーション用とは別のECRを作成する。
テンプレートファイルに定義を追加して、CloudFormationでデプロイする。

(ecr.yml)

Resources:
  CodebuildRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: fargate-nginx-flask/codebuild
      RepositoryPolicyText:
        Version: '2008-10-17'
        Statement:
          - Sid: CodeBuildAccess
            Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action:
              - ecr:GetDownloadUrlForLayer
              - ecr:BatchGetImage
              - ecr:BatchCheckLayerAvailability
# ECRへのPublishのため作成したImageの名前を変更
$ docker tag codebuild 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/fargate-nginx-flask/codebuild
$ make login
# ECRにPush
$ docker push 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/fargate-nginx-flask/codebuild

CodeBuildの作成

version: 0.2

phases:
  pre_build:
    commands:
      - nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --storage-driver=overlay&
      - timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
      - export BUILD_ID=$(echo $CODEBUILD_BUILD_ID | sed 's/^[^:]*://g')
      - export APP_VERSION=$CODEBUILD_RESOLVED_SOURCE_VERSION.$BUILD_ID
      - make login
  build:
    commands:
      - make build
      - make publish
  post_build:
    commands:
      - make clean
      - make logout

以下2点の設定を行う。

  • カスタムイメージの設定
  • 特権付与

Build Stageの作成

ECRへのアクセス権限がなくてエラーになるので、CodeBuildのロールにポリシー(ContainerRegistoryPowerUser)をアタッチする。(CloudFormationの設定を変えれば解決する?)


また、ECRリポジトリ自体にも以下のようなPermissionの設定がないとエラーになるので注意。

以下が上記の設定をしていない場合に出力されるエラーメッセージ

Continuous Delivery

  • non-production environmentへのリリース

ステージ間での情報の受け渡し

Buildステージで作成したタグ情報にDeployステージでアクセスするなど、ステージ間での情報の受け渡しが行える。

CodeDeployの設定

CloudFormationでのリリースを実施する場合、ロールとして必要なのはCloudFormationを信頼するentityとして設定したものなので注意。

間違えたentityを設定している場合以下のエラーが出るので注意。

Role arn:aws:iam::111111111:role/CodePipeline-AccessRole is invalid or cannot be assumed (Service: AmazonCloudFormation; Status Code: 400; Error Code: ValidationError; Request ID: 022bff38-4cdf-487d-86a0-8dac8d5a374a)

Fargate

Fargateについてのまとめは以下参照
AWS Fargate

AWSのECS上にflaskでできたアプリを公開する際の、Nginxの置き場所

nabetsunabetsu

実例(Udemy)

First

VPCの作成

参考リポジトリ
03-Fargate-and-ECS-Fundamentals

上記リポジトリのREADMEに記載がある通り以下のリソースを作成する

  • VPCの作成
  • Subnet
  • IGW
  • Route Table(外への通信をIGWに流す)

Clusterの作成

Fargate Clusterの作成

クラスターの作成から以下の通り入力してfargate clusterを作成する

作成が完了してもECS Instancesには何も作成されない(Serverlessなので)

EC2 + Linux Clusterの作成

インスタンスのタイプやネットワークの設定を行い、clusterを作成する

作成が完了するとTaskは何もないがEC2は指定した数だけ作成される?

ECR

イメージをビルドして、ローカルで稼働確認を行う。
稼働確認が問題なければECRにプッシュを行う

$ docker build -t 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/aws-ecr-nginx:1.0.0 .
$ docker run --name aws-ecr-nginx -p 80:80 --rm -d 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/aws-ecr-nginx:1.0.0

$ docker push 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/aws-ecr-nginx:1.0.0

Load Balancing & Auto Scaling

2つのnginx containerを作成する
ロードバランサーは高いので1つのLBで複数のアプリケーションをロードバランシングできるようにする?

Docker imageの作成

.
├── Application1
│   ├── Dockerfile
│   └── app1
│       └── index.html
└── Application2
    ├── Dockerfile
    └── app2
        └── index.html

ロードバランサーの作成

Task Definitionの作成

  • stacksimplify/nginxapp1:latest
    Fargateで作成

  • stacksimplify/nginxapp2:latest
    EC2で作成。
    ネットワークはデフォルトで。

サービスの作成

ロードバランサーを使い、ヘルスチェックの設定を加える

CI/CD

  • staging-ecs-cicd-nginx-svc
  • prod-ecs-cicd-nginx-svc

buildspec.ymlの作成

version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18       
  pre_build:
    commands:
      - echo Logging in to Amazon ECR.....
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=180789647333.dkr.ecr.ap-south-1.amazonaws.com/ecs-cicd-nginx
      - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:$IMAGE_TAG .
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file...
      - printf '[{"name":"ecs-cicd-nginx","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
    files: imagedefinitions.json

Code Pipelineの作成

デプロイプロバイダーとしてECSを選び、イメージファイル名を指定する

CodeBuildにAmazonEC2ContainerRegistryFullAccessのポリシーを付与する

An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:sts::xxxxxxxxxxx:assumed-role/MyCodeBuildRole/AWSCodeBuild-39e590a0-eb1b-4bcd-bca9-1ac9acfae1f0 is not authorized to perform: ecr:GetAuthorizationToken on resource: *

nabetsunabetsu

実例(Docker on AWS)

ECS clusterの作成

  • ECSのコンソールからクラスターの作成をクリック
  • EC2 Linux & ネットワーキングを選択

ECS Container Instances

ECS Clusterから紐づくInstance(今回の場合EC2)が確認できる

ユーザデータの変更・表示を選ぶとEC2の起動時に作成したclusterとの紐付けが行われていることが確認できる

IAMロールは添付のようにecs用のものが設定されている

ECS container instanceへの接続

通常のEC2にアクセスするのと同様にSecurity Groupの設定を変えればSSHでアクセスできる

% ssh -i docker.pem ec2-user@52.198.242.187

   __|  __|  __|
   _|  (   \__ \   Amazon Linux 2 (ECS Optimized)
 ____|\___|____/

For documentation, visit http://aws.amazon.com/documentation/ecs
1 package(s) needed for security, out of 6 available
Run "sudo yum update" to apply all updates.

接続先のEC2ではdocker engineが起動している

$ docker info
Containers: 1
 Running: 1
 Paused: 0
 Stopped: 0
Images: 2
Server Version: 18.09.9-ce
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
...

ecs agentが起動している

$ docker ps
CONTAINER ID        IMAGE                            COMMAND             CREATED             STATUS                  PORTS               NAMES
a8e634eab5c7        amazon/amazon-ecs-agent:latest   "/agent"            14 hours ago        Up 14 hours (healthy)                       ecs-agent

inspect ECS agent

$ docker container inspect ecs-agent --format '{{json .HostConfig.Binds}}' | jq
[
  "/var/run:/var/run",
  "/var/log/ecs:/log",
  "/var/lib/ecs/data:/data",
  "/etc/ecs:/etc/ecs",
  "/var/cache/ecs:/var/cache/ecs",
  "/sys/fs/cgroup:/sys/fs/cgroup",
  "/var/lib/ecs:/var/lib/ecs",
  "/etc/pki:/etc/pki:ro",
  "/run/docker/plugins:/run/docker/plugins:ro",
  "/etc/docker/plugins:/etc/docker/plugins:ro",
  "/usr/lib/docker/plugins:/usr/lib/docker/plugins:ro",
  "/proc:/host/proc:ro",
  "/usr/lib:/usr/lib:ro",
  "/lib:/lib:ro",
  "/usr/lib64:/usr/lib64:ro",
  "/lib64:/lib64:ro",
  "/sbin:/host/sbin:ro",
  "/etc/alternatives:/etc/alternatives:ro",
  "/usr/sbin:/usr/sbin:ro"
]

Notice that the /var/run folder is mapped from the host to the agent, which provides access to the Docker Engine socket located at /var/run/docker.sock, allowing the ECS agent to manage the Docker Engine. You can also see that ECS agent logs will be written to /var/log/ecs on the Docker Engine host file system.

ECS agentはport 51678で起動し、3つのエンドポイントを提供している

> curl -s localhost:51678 | jq
{
  "AvailableCommands": [
    "/v1/metadata",
    "/v1/tasks",
    "/license"
  ]
}
> curl -s localhost:51678/v1/metadata | jq
{
  "Cluster": "test-cluster",
  "ContainerInstanceArn": "arn:aws:ecs:us-east-1:385605022855:container-instance/f67cbfbd-1497-47c0-b56c-a910c923ba70",
  "Version": "Amazon ECS Agent - v1.16.2 (998c9b5)"
}

Log

以下の2つの場所にログが出力され、ECS Agent logsは2種類に分かれている

  • Docker Engine logs: Located at /var/log/docker

  • ECS Agent logs: Located at /var/log/ecs

  • Init logs: Located at /var/log/ecs/ecs-init.log, these logs provide output related to the ecs-init service, which is an Upstart service that ensures the ECS agent is running on container instance startup.

  • Agent logs: Located at /var/log/ecs/ecs-agent.log.*, these logs provide output related to ECS agent operation. These logs are the most common logs you will inspect for any ECS agent related issues.

ECS Task Definition

デプロイしたいアプリケーションの定義を行う

Nginxの定義

サンプルとしてNginxだけのContainerを定義する

  • まず左側のメニューからタスク定義の作成をクリックして、EC2を選ぶ

  • 次の画面でコンテナの定義を行う

ECS service definition

作成したECS task definitionをECS ClusterにデプロイするためにECS serviceを定義する。

  • Instance(ECS tasks)の数

ECS clusterよりサービスの作成をクリックし、作成を行う。
サービスの作成時にはこれまでに作成したECS clusterとTask Definitionを選択する
(Task Definitionの選択時にファミリーとリビジョンを選択する。これがアプリケーションを更新するときに重要になる)

作成が完了してからEC2のパブリックIPでアクセスを行うとNginxが正常に動作していることが確認できる。

Deploying to ECS Services

新しいバージョンのcontainer applicationをどうやってデプロイするか

  • task definitionはimmutable
  • ECS task definitionのバージョンはfmaily:revisionの形式で表される(my-task-definitonというアプリケーションのバージョン3ならmy-task-definition:3)
  1. 新しいrevisionのECS Task Definitonを作成する
  2. ECS Serviceで紐づくTask Definitionを1で作成したものに更新する

デプロイの実行

まず、Task Definitionの新しいバージョンを作成する

  1. ECSのコンソールから既存のTask Definitonを選択し、新しいリビジョンの作成をクリックする

  1. コンテナの定義でnginxを選択し、ホストのポートを削除してStatic Port MappingからDynamic Port Mappingに変更する(the Docker Engine will assign a dynamic port from the ephemeral port range on underlying ECS container instances. For the Amazon Linux AMI we are using, this port range is between 32768 and 60999.)

  2. 最後にページしたの作成をクリックすると新しいリビジョンが作成される

次にECS Serviceを更新して、新しく作成したRevisionを適用する

  1. 対象のclusterを選択して更新をクリックする

  1. タスク定義で新しいリビジョンを選択し、その他の設定は変えずに作成を行う

  2. 作成が完了するとデプロイメントタブからデプロイの様子が確認できる

作成直後は既存のサービスと新しいサービスの2つが存在し、新しいサービスの実行中は0になっている

少し時間が経つと新しいサービスの実行中が1になる

さらにもう少し経つと古いサービスが存在しなくなり、新しいサービスに切り替わったことが確認できる

  • デプロイ結果の確認
    タスクから実際にどのポートが使用されているか確認できる。

ECS Tasks

上記のようなフローは長期的に使われるアプリケーション(いわゆる一般的なアプリケーション)で使える方法。

一時的なタスク(デプロイ用スクリプトの実行やデータベースマイグレーションの実行、スケジュールでのバッチ処理の実行)を行うにはECS tasksを利用する

以下の表の通りECS TasksはECS Serviceとはかなり異なった振る舞いをする。

ECS Tasksの使用

  1. clusterからタスクを選択し、新しいタスクの実行をクリックする

  1. タスクの実行タブで起動タイプはEC2でこれまでに作成したTask Definitonを選ぶ

また、Advanced Optionからコンテナの上書きができるので、コマンドの上書きで実行させたいコマンドを指定する。

※コマンドの指定は必ずコンマ区切りで入力する点に注意

タスクを停止した時の動作の確認

すべてを停止をクリックしてすべてのタスクを停止させた時の動作を確認する。

停止を押すとすべたのタスクが画面から消えるが、ECS Serviceで定義しているタスクは一定時間経過後に自動で再作成される(必要なタスク数と一致するように自動で再作成が行われる)

ECS Clusterの削除

ECS CLI

  • ECS CLIはgolangで書かれている(AWS CLIはPython)

公式ドキュメント

GitHub

ECR(Elastic Container Registory)

fully-Managed private Docker registry provided by AWS

  • Repositories
  • Permissions
  • Lifecycle policy
    • 古くなったバージョンのImageの扱いなど、それぞれのレポジトリごとに設定が可能
  • Authentication sevice

Amazon Elastic Container Registry の料金

ECR Repositoryの作成

サンプルとして作成したtodobackendアプリケーションをECS Clusterで実行するにはECRにimageを保存する

Consoleからの作成

ECRのコンソールを表示して、新しいリポジトリを作成する

CLIからの操作

$ aws ecr describe-repositories
{
    "repositories": [
        {
            "repositoryArn": "arn:aws:ecr:ap-northeast-1:455176667242:repository/docker-in-aws/todobackend",
            "registryId": "455176667242",
            "repositoryName": "docker-in-aws/todobackend",
            "repositoryUri": "455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend",
            "createdAt": 1584848494.0,
            "imageTagMutability": "MUTABLE",
            "imageScanningConfiguration": {
                "scanOnPush": false
            }
        }
    ]
}

# レポジトリの削除
$ aws ecr delete-repository --repository-name docker-in-aws/todobackend

# レポジトリの作成
$ aws ecr create-repository --repository-name docker-in-aws/todobackend

CloudFormationでの操作

As a general rule of thumb, given the critical nature of ECR repositories as a distribution mechanism for your Docker images, I typically recommend the various ECR repositories for your account and region are defined in a single, shared CloudFormation stack dedicated solely to the creation and management of ECR repositories.

(ecr.yml)

AWSTemplateFormatVersion: "2010-09-09"

Description: ECR Repositories

Resources:
  TodobackendRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: docker-in-aws/todobackend

上記のtemplateファイルを作成し、cloudformationでデプロイする

% aws cloudformation deploy --template-file ecr.yml --stack-name ecr-repositories

Imageの発行(Single Image)

ImageをBuildしてECRにPublishする。
そのためには以下の対応が必要

  • ECRへのログインが行う(Dockerクライアントの認証済み)
  • 発行先のECR RepositoryのURIでDocker Imageがビルドする
  • その上で、ECRにDocker ImageをPushする

ECRの認証

以下のプッシュコマンドを表示より、ImageをPublishするまでの手順が確認できる

  1. 認証トークンを取得し、レジストリに対して Docker クライアントを認証する
# 
% aws ecr get-login --no-include-email
docker login -u AWS -p eyJwYXlsb2FkIjoibXFHaGZXdFFqOFh5WDlIUHJDakFYTEphWHdPdEdodlg3cW1NMnBZWk0zcFlhVHFJd1RwY1BZMk9vRTdBWGhDMVhadnJ3T2dzZjJFOUVLTWdKMW9PdHVvL1g2bWplUFV
...




% $(aws ecr get-login --no-include-email)
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded

Docker ImageのBuild

  • ビルドからタグ付まで
# 1回でビルドからタグ付まで
% docker build -t xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest .
...
Successfully built 9c6c55c424e6
Successfully tagged xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest

# 2回に分けるやり方(AWSに書いてあるのはこっち)
$ docker build -t docker-in-aws/todobackend .
$ docker tag docker-in-aws/todobackend:latest xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest
  • Push
% docker push xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazo
naws.com/docker-in-aws/todobackend:latest
The push refers to repository [455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend]
6c0d36bac6d0: Pushed 
ff07f0a70215: Pushed 
ab92eaccd0cd: Pushed 
20bd9404ca0d: Pushed 
ef47db0b2220: Pushed 
f227d9a2d5b2: Pushed 
cf50c2fe3e68: Pushed 
7fd45620b4c6: Pushed 
5216338b40a7: Pushed 
latest: digest: sha256:bf3825df219e07fd5784b139e626b41b9754d315f431be13f955143b1b305c7d size: 2202

上記のコマンドを実行するとECRに対象のImageが作成されていることが確認できる。

リポジトリに保存されるときには圧縮されるのが実際のイメージサイズよりは小さくなる。
課金はストレージの容量と転送量で決まるので注意

Imageの発行(Multi Container Image)

docker-composeファイルの変更

version: '2.4'

services:
  web:
    image: nginx

上記がDocker-composeでイメージを指定する方法だが、buildとimageを組み合わせるときにはもう少し設定が必要になる

  • releaseとappの2つのserviceでimageとbuildの両方を指定

  • Dockerはimageとbuildの両方のプロパティを設定するとImageのBuildを実行し、作成されたイメージにimageで指定した名前をタグ付けしてくれる

  • appのタグに${APP_VERSION}をつけているのはMakefileでつけたアプリのバージョンを利用するため

version: '2.4'

volumes:
  public:
    driver: local

services:
  test:
    build:
      context: .
      dockerfile: Dockerfile
      target: test
  release:
    image: 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:latest
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      DJANGO_SETTINGS_MODULE: todobackend.settings_release
      MYSQL_HOST: db
      MYSQL_USER: todo
      MYSQL_PASSWORD: password  
  app:
    image: 455176667242.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend:${APP_VERSION}
    extends:

Docker-composeを使ったImageのPublish

  • docker-compose build releaseでECR Repositoryの名前がタグ付されたImageをbuildできる

  • docker-compose push releaseでDocker Composeファイルに定義されたリポジトリにimageをPushできる

# 不要なImageの削除
% docker rmi xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/docker-in-aws/todobackend


% docker-compose build release
WARNING: The APP_VERSION variable is not set. Defaulting to a blank string.
Building release

% docker-compose push release
WARNING: The APP_VERSION variable is not set. Defaulting to a blank string.
Pushing release 
...
latest: digest: sha256:1e5366aebd9ac1c3bdedf0931de07f3ab041e4f4047c13edd044cf00f91f91c6 size: 2202

Publish Workflowの自動化

LoginとLogoutの自動化

.PHONY: test release clean version login logout
...
login:
	$$(aws ecr get-login --no-include-email)

logout:
	docker logout https://455176667242.dkr.ecr.ap-northeast-1.amazonaws.com
> make logout
docker logout https://385605022855.dkr.ecr.us-east-1.amazonaws.com
Removing login credentials for 385605022855.dkr.ecr.us-east-1.amazonaws.com
> make login
$(aws ecr get-login --no-include-email)WARNING! Using --password via the CLI is insecure. Use --password-stdin.Login Succeeded

Publishingの自動化

docker-compose pushコマンドでreleaseとappの2つを指定する

.PHONY: test release clean version login logout publish
...
publish:
	docker-compose push release app

% git commit -m "automate publishing workflow"
[master b979578] automate publishing workflow
 2 files changed, 13 insertions(+), 2 deletions(-)

$ make login
$ make test $$ make relase

$ make publish
docker-compose push release app
Pushing release...
Pushing app...
...
1.amazonaws.com/docker-in-aws/todobackend:b979578)...
...
b979578: digest: sha256:1e5366aebd9ac1c3bdedf0931de07f3ab041e4f4047c13edd044cf00f91f91c6 size: 2202

上記のワークフローを実行した結果、appとreleaseの2つのimageがPushされていることが確認できた。

ECRのImageを取得する方法

ECS container instanceでImageを取得(同一アカウント)

EC2(container instance)のIAM Roleで制御されている
自動で作成されるEC2にデフォルトで付与されているマネージドポリシーAmazonEC2ContainerServiceforEC2Roleの内容は以下の通りであり、ECRへのアクセス権限が設定されている

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecs:CreateCluster",
        "ecs:DeregisterContainerInstance",
        "ecs:DiscoverPollEndpoint",
        "ecs:Poll",
        "ecs:RegisterContainerInstance",
        "ecs:StartTelemetrySession",
        "ecs:Submit*",
        "ecr:GetAuthorizationToken",
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}

それぞれのポリシーの詳細は以下の通りであり、権限としては問題ないがすべてのリソースが対象になっているため、

  • ecr:GetAuthorizationToken: Permits retrieval of an authentication token that is valid for 12 hours and can be used to log in to ECR using the Docker CLI.
  • ecr:BatchCheckLayerAvailability: Checks the availability of multiple image layers in a given repository.
  • ecr:GetDownloadUrlForLayer: Retrieves a pre-signed S3 download URL for a given layer in a Docker image.
  • ecr:BatchGetImage: Retries detailed information for Docker images in a given repository.

ECS container instanceでImageを取得(別アカウント)

ECR Repository policiesの設定

リポジトリのポリシーを変更して別アカウントからのアクセスを許可する

他のサービスからのアクセス

例えばCodeBuildなど、その他のレポジトリからのアクセスを許可する方法

Lifecycle Policyの設定

以下のようにタグ付されていない(これから使われることがない)Imageがあると煩雑なだけでなく、料金も余計にかかってしまう

Consoleからの操作

実際にライフサイクルを適用する前にテストルールを作成して、適用された場合の結果を事前に確認できる。

CloudFormation

AWSTemplateFormatVersion: "2010-09-09"

Description: ECR Repositories

Resources:
  TodobackendRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: docker-in-aws/todobackend
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 10,
                "description": "Untagged images",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 7
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }

ECSのCloudFormationの定義

ECS Clusterの定義

AWSTemplateFormatVersion: "2010-09-09"

Description: Fargate Sample

Resources:
  ApplicationCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: fargate-sample-cluster

deploy

$ aws cloudformation deploy --template-file stack.yml --stack-name fargate-sample

Building Custom ECS Container Instances

ECS-Optimized AMIをベースに自分で機能を加えたカスタムイメージを使ってContainer Instanceを作成する

lthough the ECS-Optimized AMI is great for getting up and running quickly, you may want to add additional features to your ECS container instances for your production environments, such as adding logging agents or including support for HTTP proxies so you can place your ECS clusters in private subnets.

カスタムイメージの必要性

  • Custom Storage Configuration
    The default ECS-optimized AMIはOSのための8GBのパーティションとDocker imageとcontainer filesystemのための22GB、合計30GBのボリュームを持つ。
    デフォルトではDocker volumesはOSのための8GBの領域に保存されるが、これはProductionでは避けるべき。

  • Installation of additional packages and tools

CloudWatchのログへの出力やCloudformationのツール?はデフォルトのAMIに含まれていない

  • 初回起動時のスクリプトを実行するため

Packerを使ったカスタムイメージの作成

PackerはAWSに限定せずマシンイメージを作成するためのツール

Pakcer公式

インストールはbrewを使って行える

brew install packer

ECSの作成

ECS Clusterの作成

(stack.yml)

AWSTemplateFormatVersion: "2010-09-09"

Description: Todobackend Application
Resources:
  ApplicationCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: todobackend-cluster

$ aws cloudformation deploy --template-file stack.yml --stack-name todobackend

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - todobackend