AWS CodeBuildでDockerビルドしてECRへプッシュする
以前AWS CodeBuildでPythonアプリケーションをビルドしてS3へアップロードする方法を紹介しました。
今回はDockerイメージをビルドして、ECRにプッシュする方法について見ていきたいと思います。
サンプル
サンプルとして以前作成したPythonアプリケーションを使います。これに簡単なDockerfileを追加します。
FROM python:3.8 WORKDIR /root RUN pip install pipenv==2018.11.26 COPY ./Pipfile Pipfile COPY ./Pipfile.lock Pipfile.lock RUN pipenv install --system COPY . /root
buildspec.yml
CodeBuildのパイプラインはbuildspec.ymlで定義します。各フェーズで以下の処理を行います。
- pre_buildでテスト
- buildでイメージビルド
- post_buildでECRへプッシュ
version: 0.2 phases: install: runtime-versions: python: 3.8 commands: - pip install pipenv==2018.11.26 pre_build: commands: - pipenv install --dev - pipenv run flake8 - pipenv run pytest build: commands: - docker build -t codebuild-test . post_build: commands: - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com - docker tag codebuild-test:latest 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest - docker push 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest
AWS CodeBuildの設定
次にAWSコンソール画面からCodeBuildのビルドプロジェクトを設定をしていきます。前回とほぼ同じ手続きでOKですが、2点ばかり変更が必要です。
- Dockerビルドするので、特権 (privileged) を有効にする必要があります
- ECRへにログインおよびプッシュするので、サービスロールにECRのポリシーを付与する必要があります
ビルドを実行するとこんな感じのログが出力され、ECRへプッシュされます。
[Container] 2020/09/26 05:44:49 Waiting for agent ping [Container] 2020/09/26 05:44:51 Waiting for DOWNLOAD_SOURCE [Container] 2020/09/26 05:44:54 Phase is DOWNLOAD_SOURCE [Container] 2020/09/26 05:44:54 CODEBUILD_SRC_DIR=/codebuild/output/src553119189/src/github.com/ohke/codebuild-test [Container] 2020/09/26 05:44:54 YAML location is /codebuild/output/src553119189/src/github.com/ohke/codebuild-test/buildspec.yml [Container] 2020/09/26 05:44:54 Processing environment variables [Container] 2020/09/26 05:44:54 No runtime version selected in buildspec. [Container] 2020/09/26 05:44:54 Moving to directory /codebuild/output/src553119189/src/github.com/ohke/codebuild-test [Container] 2020/09/26 05:44:54 Registering with agent [Container] 2020/09/26 05:44:54 Phases found in YAML: 1 [Container] 2020/09/26 05:44:54 BUILD: 1 commands [Container] 2020/09/26 05:44:54 Phase complete: DOWNLOAD_SOURCE State: SUCCEEDED [Container] 2020/09/26 05:44:54 Phase context status code: Message: [Container] 2020/09/26 05:44:54 Entering phase INSTALL [Container] 2020/09/26 05:44:54 Phase complete: INSTALL State: SUCCEEDED [Container] 2020/09/26 05:44:54 Phase context status code: Message: [Container] 2020/09/26 05:44:54 Entering phase PRE_BUILD [Container] 2020/09/26 05:44:54 Phase complete: PRE_BUILD State: SUCCEEDED [Container] 2020/09/26 05:44:54 Phase context status code: Message: [Container] 2020/09/26 05:44:54 Entering phase BUILD [Container] 2020/09/26 05:44:54 Running command docker build . Sending build context to Docker daemon 43.01kB Step 1/7 : FROM python:3.8 3.8: Pulling from library/python ... Status: Downloaded newer image for python:3.8 ---> bbf31371d67d Step 2/7 : WORKDIR /root ---> Running in 8bf52f1e4fe6 Removing intermediate container 8bf52f1e4fe6 ---> d0abc177c2e9 Step 3/7 : RUN pip install pipenv==2018.11.26 ---> Running in 995508830aa0 Collecting pipenv==2018.11.26 Downloading pipenv-2018.11.26-py3-none-any.whl (5.2 MB) ... Installing collected packages: virtualenv-clone, certifi, distlib, appdirs, six, filelock, virtualenv, pipenv Successfully installed appdirs-1.4.4 certifi-2020.6.20 distlib-0.3.1 filelock-3.0.12 pipenv-2018.11.26 six-1.15.0 virtualenv-20.0.31 virtualenv-clone-0.5.4 Removing intermediate container 995508830aa0 ---> 5bb947b68efe Step 4/7 : COPY ./Pipfile Pipfile ---> 539480213459 Step 5/7 : COPY ./Pipfile.lock Pipfile.lock ---> 314848853e56 Step 6/7 : RUN pipenv install --system ---> Running in 2d266779cfb6 Installing dependencies from Pipfile.lock (731881)… Removing intermediate container 2d266779cfb6 ---> 997f1bf10f60 Step 7/7 : COPY . /root ---> 7e6aea040923 Successfully built 7e6aea040923 [Container] 2020/09/26 05:45:31 Phase complete: BUILD State: SUCCEEDED [Container] 2020/09/26 05:45:31 Phase context status code: Message: [Container] 2020/09/26 05:45:31 Entering phase POST_BUILD
イメージのレイヤキャッシュ
上の設定では常にフルビルドが走ります。これでは実行のたびに時間がかかってしまいますので、Dockerのレイヤキャッシュを行います。
CodeBuildのドキュメントではローカルキャッシュの一機能としてレイヤキャッシュもサポートされているとのことだったのですが、設定を有効にしても私の環境ではキャッシュしてくれませんでした。
代替策として、ビルド前にECRからイメージをpullし、--cache-from
オプションでpullしたイメージを指定することができます。リモートレポジトリから都度pullしてくるのでその分の通信時間はかかりますが、永続化されたイメージをキャッシュとして利用できるため、ビルドの間隔が空いてもキャッシュの恩恵を受けることができます。 (ローカルキャッシュは一定時間で揮発するので、間隔が空くとフルビルドが走ってしまう。)
version: 0.2 phases: install: runtime-versions: python: 3.8 commands: - pip install pipenv==2018.11.26 pre_build: commands: - pipenv install --dev - pipenv run flake8 - pipenv run pytest build: commands: - $(aws ecr get-login --no-include-email --region ap-northeast-1) - docker pull 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest || true - docker build --cache-from 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest --tag 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest . post_build: commands: - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com - docker push 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest
main.pyを修正してCodeBuildを実行すると、ログにStep 6/7までキャッシュが利用されていることが確認できます。
Sending build context to Docker daemon 67.07kB Step 1/7 : FROM python:3.8 3.8: Pulling from library/python 57df1a1f1ad8: Already exists 71e126169501: Already exists 1af28a55c3f3: Already exists 03f1c9932170: Already exists 65b3db15f518: Already exists 3e3b8947ed83: Already exists f156949921a1: Already exists 1c1931013093: Already exists 51fff639b6bf: Already exists Digest: sha256:1a126607adde46a706e76357c910f36b9f5529fb575d4d86a639a4997daceba7 Status: Downloaded newer image for python:3.8 ---> bbf31371d67d Step 2/7 : WORKDIR /root ---> Using cache ---> eb39a705a351 Step 3/7 : RUN pip install pipenv==2018.11.26 ---> Using cache ---> c39e0cb776bd Step 4/7 : COPY ./Pipfile Pipfile ---> Using cache ---> 5fef8209ac4d Step 5/7 : COPY ./Pipfile.lock Pipfile.lock ---> Using cache ---> dea18f792eea Step 6/7 : RUN pipenv install --system ---> Using cache ---> d590683b4da4 Step 7/7 : COPY . /root ---> be6b7aed2b45 Successfully built be6b7aed2b45 Successfully tagged 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest
(参考) ビルド時に変数を渡す
Dockerビルドに限った話ではないのですが、CodeBuildの実行時に変数 (例えばイメージタグなど) を外部から渡したい場合があります。これは環境変数にセットすることでコントロールできます。
コンソールから実行する場合は以下のようにセットします。
- AWS CLIの場合は start-build の
--environment-variables-override
オプションで指定できます (参考)
buildspec.ymlは${IMAGE_TAG}
で参照します。これで、latestに加えてIMAGE_TAGで指定した値 (ここではstg) のタグでプッシュされます。
build: commands: - $(aws ecr get-login --no-include-email --region ap-northeast-1) - docker pull 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest || true - docker build --cache-from 279213333729.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest --tag 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest --tag 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:${IMAGE_TAG} . post_build: commands: - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com - docker push 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:latest - docker push 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/codebuild-test:${IMAGE_TAG}
まとめ
AWS CodeBuildを使ってDockerビルドを実行する方法について紹介しました。