AWS CodePipelineがECSへのデプロイをサポートしたのでやってみる


AWS CodePipeline が Amazon ECSへのデプロイをサポートしました。(先日発表されたばかりのFargateも)
https://aws.amazon.com/jp/about-aws/whats-new/2017/12/aws-codepipeline-adds-support-for-amazon-ecs-and-aws-fargate/
これによりコンテナ上で稼働するアプリケーションの継続的デリバリーがより簡単に実装できるようになります。

早速チャレンジしてみました。

ECSでテスト用のコンテナを起動する

ECR の設定

Elastic Container Registry に今回のテスト用のリポジトリを作成します。
後ほどリポジトリのURIを使用しますので、メモしておきます。

Dockerfileの作成

今回は公式ドキュメントのhello-worldを流用しました。
基本的にはこちらの手順を踏襲します。

FROM ubuntu:12.04

# Install dependencies
RUN apt-get update -y
RUN apt-get install -y apache2

# Install apache and write hello world message
RUN echo "Hello World!" > /var/www/index.html

# Configure apache
RUN a2enmod rewrite
RUN chown -R www-data:www-data /var/www
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2

EXPOSE 80

CMD ["/usr/sbin/apache2", "-D",  "FOREGROUND"]

イメージをビルド

Dockerfileをビルドし、ECRにプッシュします

$ docker build -t ecs-test-repo .
$ docker tag ecs-test-repo:latest 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo:latest
$ docker push 012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo:latest

ECSへのデプロイ

以下のようなタスク定義ファイルを作成します。

imagedefinitions.json
{
    "family": "ecs-hello-world",
    "containerDefinitions": [
        {
            "name": "ecs-hello-world",
            "image": "012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo:latest",
            "cpu": 10,
            "memory": 128,
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80
                }
            ],
            "entryPoint": [
                "/usr/sbin/apache2",
                "-D",
                "FOREGROUND"
            ],
            "essential": true
        }
    ]
}

AWS CLIでタスク定義の登録を行います。

$ aws ecs register-task-definition --cli-input-json ./imagedefinitions.json

コンソールでタスク定義が登録されていることを確認します。

新規サービスを作成し、登録したタスクを起動します。


クラスタインスタンスのパブリックIPにアクセスすると、Hello Worldが表示されます。
長くなりましたが、以上で事前準備が完了しました。

AWS CodeCommitの設定

DockerイメージをECRにプッシュするための buildspec.yml を作成し、CodeCommitのリポジトリに追加します。

リポジトリの作成

リポジトリ名を指定して、新規作成します。

今回、Eメール通知の設定はスキップします。

リポジトリへの接続

ローカルに作成したリポジトリをcloneします。
リポジトリへの接続には適切な権限をもったIAMユーザおよびGit認証情報が必要ですので
あらかじめ作成しておきます。

$ git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/ecs-test-repo
Cloning into 'ecs-test-repo'...
warning: You appear to have cloned an empty repository.

buildspec.ymlの作成

REPOSITORY_URIにはECRのURIを設定します。
またイメージタグにソースコードのコミットIDを追加するようにします。

buildspec.yml

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION)
      - REPOSITORY_URI=012345678910.dkr.ecr.ap-northeast-1.amazonaws.com/ecs-test-repo
      - 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:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file...
      - printf '[{"name":"ecs-hello-world","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
artifacts:
    files: imagedefinitions.json

artifacts として事前準備で作成したタスク定義ファイルを指定します。
パイプラインの中でタスク定義の新しいリビジョンが作成され、サービスが更新されます。

CodeCommitへプッシュ

Dockerfile, buildspec.yml, imagedefinitions.json をCodeCommitへプッシュします。
Dockerfileの中身を少しだけ編集しています。

RUN echo "Hello World! by CodePipeline" > /var/www/index.html
$ git add.
$ git commit -m "Adding build specification."
$ git push

Piepelineの設定

Pipelineの作成

マネージドコンソールの AWS CodePipelineから新規パイプラインを作成します。

ソースの設定でソースプロバイダを CodeCommitに設定し、先程作成したリポジトリを指定します。

ビルドの設定で以下のように、選択/入力し、ビルドプロジェクトを保存します。

  • ビルドプロパイダでAWS CodeBuildを選択
  • 新しいビルドプロジェクトを作成 を選択し、任意のプロジェクト名を入力
  • 環境イメージで AWS CodeBuildマネージド型イメージの使用を選択し、OS:Ubuntu、ランタイム:Dockerとする

デプロイの設定で、プロバイダに Amazon ECS を選択し、展開先のクラスタ名、サービス名を指定します。

AWS サービスロールを選択します。必要に応じて新規作成もできます。

レビュー画面で設定値を確認し、パイプラインを作成します。

パイプラインの実行

作成直後にパイプラインが起動します。
パイプライン作成ウィザードの中で、CodeBuildのビルドプロジェクト用のサービスロールを作成していますが、
初期の状態ではECRに対する権限が不足しているため、以下のようにビルドが失敗します。

そのためIAM管理画面で、作成したサービスロールに対し、AmazonEC2ContainerRegistryPowerUserポリシーを追加します。

Buildステージを再実行すると今度はステージングまで処理が完了します。

結果確認

タスク定義を確認すると新しいリビジョンが作成されています。
※途中メモリの設定などを誤り複数回デプロイしているため、以下の画面ショットではリビジョンが4まで増えています。。。

同様にサービスも新しいタスク定義のリビジョンで更新されています。

最後にブラウザで確認します。Hello worldのメッセージが更新されていることを確認できました!

最後に

以上の手順でCodePipelineでECSへのデプロイを行うことができました。
参考になれば幸いです。