AWS CI/CD for Amazon ECS ハンズオンをやってみた

AWS Fargateを触ってみたい、でも時間が...というあなたに冬休み中にできる3時間程度のハンズオンを
2020.12.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

概要

AWS CI/CD for Amazon ECS ハンズオンをやってみました。

ハンズオンは下記の3本立てです。3時間程度で終わりました。

  1. サンプルアプリのコンテナイメージ作成
  2. AWS Fargate環境構築とコンテナ実行
  3. CI/CDのパイプライン作成

ハンズオンを進めながら個人的なメモを補足した内容です。実際のハンズオン資料を見ながら作業をすすめ補足資料になればよいなと思っています。 CodeBuild時にDocker Hubのレートリミットに引っかかるとつまづくと思うのでそこの回避方法を載せています。

まず、AWS Fargateとは

ハンズオン1

Fargateでコンテナを実行するまでの前準備する内容です。

ハンズオン資料より

VPC、ALB作成

ハンズオンの手順ではVPCはCloudFormationで構築します。コードはハンズオン資料内にダウンロードリンクがあります。ALBとセキュリティグループはマネジメントコンソールから作成します。 ハンズオン終了後リソースを削除するのが手間なのですべてCloudFormationで作成しました。ハンズオンの資料の最後には作成したリソースの削除方法もまとまっているため手動でリソースを作成しても消し忘れない安心設計ではあります。

bigmuramura/cfn-cicd-ecs-handson

Dockerアプリ開発

ハンズオンの手順ではECRにリポジトリを作成します。ハンズオンはマネジメントコンソールから作成します。こちらもついでにCloudFormationで作成しました。

AWS Cloud9環境の構築

Cloud9を用意するのが手間だったのでローカルではじめました。Dockerfilesrc/index.phpを適当なディレクトリ配下に作成します。

実行環境

> docker -v
Docker version 20.10.0, build 7287ab3

Dockerfile作成と、PHPサンプルアプリ作成

> tree
.
├── Dockerfile
└── src
    └── index.php

Dockerfile

FROM php:7.4.0-apache
COPY src/ /var/www/html/

ホスト名を返すページを作成します。

index.php

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>PHP Sample</title>
    </head>
  <body>
    <?php echo gethostname(); ?>
  </body>
</html>

Dockerイメージ作成、コンテナ起動

> docker build -t php-sample .
> docker run --rm -p 8080:80 -d php-sample:latest
> docker ps
CONTAINER ID   IMAGE               COMMAND                  CREATED         STATUS         PORTS                  NAMES
a72fd7ff71f1   php-sample:latest   "docker-php-entrypoi…"   4 seconds ago   Up 3 seconds   0.0.0.0:8080->80/tcp   cool_almeida

WEBブラウザでアクセス

curl叩いた様子

 curl localhost:8080
<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>PHP Sample</title>
    </head>
  <body>
    a72fd7ff71f1  </body>
</html>

コンテナ停止

> docker stop cool_almeida

マネジメントコンソールから作成したリポジトリのプッシュコマンドの表示をクリック

表示された1番目のコマンドを実行

# ECRへログイン
> aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 1234567890.dkr.ecr.us-east-1.amazonaws.com
Login Succeeded

2番目のコマンドはハンズオン途中でイメージ作成済みのため使いません。

表示された3番目のコマンドのdocker tagの後のphp-sample:latest部分を変更して実行しました。 リポジトリ名をCloudFormationでhandson-dev-php-sample作成したためです。2番目のコマンドをそのまま使ってビルドした方がシンプルでした。

# タグ付け
> docker tag php-sample:latest 1234567890.dkr.ecr.us-east-1.amazonaws.com/handson-dev-php-sample:latest

表示された4番目のコマンドを実行

# ECRへイメージをPush
> docker push 1234567890.dkr.ecr.us-east-1.amazonaws.com/handson-dev-php-sample:latest

リポジトリにイメージが追加されました

ハンズオン2

AWS Fargate環境構築する内容です。

IAMロール作成。
ハンズオン資料通りAWSCodeDeployRoleForECSのポリシーがアタッチされたロールを作成。

クラスター作成。
ハンズオン資料時のキャプチャには表示されていないContainer Insightsがクラスター作成時に有効にできたのでチェックを入れて作成。

タスク定義作成。

イメージ名の指定のここに入力する文字列は...

ECRからリポジトリ名のコピーボタンを押すとタグも含めコピーされるので貼り付ければOKです。

サービス作成。

ターゲットグループの状態。コンテナが起動しhealthyになりました。

WEBブラウザからアクセス。

Fargateで起動しているコンテナにALB経由でアクセスすることができました。

ハンズオン3

CI/CDパイプラインの構築する内容です。

ハンズオン資料より

CodeCommitリポジトリのクローン。git-remote-codecommitを使用するたためHTTPSのクローン(GRC)のURLを使用しています。

> git clone codecommit::us-east-1://php-sample
Cloning into 'php-sample'...
warning: You appear to have cloned an empty repository.

ハンズオン1で作成したファイルをコピーしました。

> tree
.
├── Dockerfile
└── src
    └── index.php

buildspec.yml新規作成。

pre_buildフェーズ

  1. ECRリポジトリへログイン
  2. handson-dev-php-sampleリポジトリのURIを変数に設定
  3. イメージタグとしてビルドIDのPrefixを利用

buildフェーズ

  1. Dockerイメージのビルド
  2. タグ付け

post_buildフェーズ

  1. ビルドしたイメージをECRへPush

artifactsフェーズ

  1. CodePipelineで後続フェーズにタグ情報を渡すためのアウトプット

buildspec.yml

version: 0.2
phases:
  pre_build:
    commands:
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=1234567890.dkr.ecr.us-east-1.amazonaws.com/handson-dev-php-sample
      - IMAGE_TAG=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)

  build:
    commands:
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG

  post_build:
    commands:
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - printf '{"Version":"1.0","ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json

artifacts:
    files: imageDetail.json

appspec.yml新規作成。

appspec.yml

version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: "<TASK_DEFINITION>"
        LoadBalancerInfo:
            ContainerName: "php-sample-fargate"
            ContainerPort: 80

taskdef.json新規作成。

image:の項目を<IMAGE1_NAME>に修正する。

taskdef.json

# 抜粋
      "volumesFrom": [],
      "stopTimeout": null,
      "image": <IMAGE1_NAME>,
      "startTimeout": null,
      "firelensConfiguration": null,

最終的なディレクトリ構成はこちら。

> tree
.
├── Dockerfile
├── appspec.yml
├── buildspec.yml
├── src
│   └── index.php
└── taskdef.json

CodeCommitのリポジトリへPush

> git add -A
> git commit -m "first commit”
> git push origin master

パイプライン作成

資料通り進めていけばよいのですが運が悪いとCodeBuildで失敗します。

下記エラーでCodeBuild失敗!!

toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit

簡単な対処としてはハンズオンの中でNAT Gatewayを作成しているためCodeBuildをVPC内で実行しNAT GatewayのEIP経由でDoceker Hubへアクセスさせてレートリミットを回避します。

CodeBuildからビルドプロジェクトを選択し、php-sample-buildを編集します。

VPCとPrivateSubnetを選択し、セキュリティグループはアウトバウンドが空いていればよいのでハンズオンの中で作成したセキュリティグループを流用します。

手動でパイプラインを再実行させます。

今度はCodeBuild成功しました。

CodeDeployによりBlue/Greenデプロイされ、ALBのURLからアクセスしました。コンテナのプライベートIPが変わっているため新しいコンテナに切り替わったことがわかります。ハンズオンの設定ではコンテナは常時1台しか起動していません。

おわりに

一度手を動かしてみるとタスク定義やサービスなど用語だけ聞いてもピンとこないものが理解しやすくなるかと思います。ハンズオン1の準備さえ終わればすぐにFargateで動かせるので興味あれば手を動かしてみることをオススメします。

以上、網走の大村でした。

参考

AWS でデプロイの自動化を実現するベストプラクティスをグラレコで解説 - builders.flash☆ - 変化を求めるデベロッパーを応援するウェブマガジン | AWS