kanikoをAWS CodeBuildで使う その2

前回書いた記事では,CodeBuildのベースイメージとしてkanikoを利用した際のビルド及びECRにpushする方法を書いた.

core.cohalz.co

今回はCodeBuildのイメージを変更せずデフォルトイメージ(aws/codebuild/standard:2.0)のまま,docker runを使ってkanikoを利用する方法について書いていく.

この方法はCloudBuildでビルドする方法に近く,またgcr.io/kaniko-project/warmerを使ったベースイメージのキャッシュも利用できるようになるため,実際にCodeBuildでkanikoを利用したい場合にはこちらを利用することが一般的になると思う.

以下では動かすための手順を書いていく.

CodeBuildのロール情報をkanikoのイメージに渡す

kanikoをdocker run経由で実行するということで,そのままではECRにpushする際に必要なIAMのロール情報をコンテナに渡すことができない.

ロール情報をコンテナに渡すための方法としてはCodeBuildからhttp://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}というエンドポイントを叩き,その結果をコンテナの環境変数として渡すという方法をとれば良い.

metadata=$(curl -s http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI})

export AWS_ACCESS_KEY_ID=$(echo "${metadata}" | jq -r .AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo "${metadata}" | jq -r .SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo "${metadata}" | jq -r .Token)

mkdir .docker && echo "{\"credsStore\":\"ecr-login\"}" > .docker/config.json

docker run \
  -v $(pwd):/workspace \
  -v $(pwd)/.docker:/kaniko/.docker \
  -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
  -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
  -e AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN} \
  gcr.io/kaniko-project/executor \
  -d ${ECR_REPO}

エンドポイントや環境変数の渡し方などは以下の記事が参考になる.

qiita.com

dev.classmethod.jp

docs.aws.amazon.com

gcr.io/kaniko-project/warmerを使えるようにする

これだけでもkanikoを使ったビルドができるようになるが,これに加えてkaniko-project/warmerを使うことで,ビルドする際のベースイメージのキャッシュが効くようになり,ビルド時間の短縮を狙うことができる.

以下のように書くことで,環境変数BASE_IMAGESにキャッシュを効かせたいイメージを書くことで,ホストの/cacheディレクトリにベースメージのキャッシュを保存することができる.

images=$(echo ${BASE_IMAGES} | perl -anal -e 'print join(" ", map {"--image=" . $_ } split ",")')

docker run -v /cache:/cache gcr.io/kaniko-project/warmer --cache-dir=/cache ${images}

そしてこのディレクトリをCodeBuildがキャッシュするようにbuildspec.ymlを書き換えれば良い.

BASE_IMAGESには複数のイメージを書くことができ,例えばgolang:1.10,alpine:latestを渡すと,$imagesの内容は--image=golang:1.10 --image=alpine:latestとなる

その他の注意点

気をつけないといけないのはまず,CodeBuildのビルド環境に特権付与が必要ということ.

特権を付与しないとdocker run自体を実行することができずに,

docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?. 

と言われてしまう.

また,docker runする際に,gcr.io/kaniko-project/executorgcr.io/kaniko-project/warmerのイメージが必要になってくるため,これらのイメージ自体をキャッシュするためにはCodeBuildのレイヤキャッシュも有効にする必要がある.

つまりは基本的にCodeBuildのローカルキャッシュは全部有効にしておけば良い.

buildspecとCloudFormationテンプレート

以上を踏まえて,完成したbuildspec.ymlはこのようになった.

version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
  build:
    commands:
      - metadata=$(curl -s http://169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI})
      - export AWS_ACCESS_KEY_ID=$(echo "${metadata}" | jq -r .AccessKeyId)
      - export AWS_SECRET_ACCESS_KEY=$(echo "${metadata}" | jq -r .SecretAccessKey)
      - export AWS_SESSION_TOKEN=$(echo "${metadata}" | jq -r .Token)
      - images=$(echo ${BASE_IMAGES} | perl -anal -e 'print join(" ", map {"--image=" . $_ } split ",")')
      - mkdir .docker && echo "{\"credsStore\":\"ecr-login\"}" > .docker/config.json
      - |
        docker run \
          -v /cache:/cache \
          gcr.io/kaniko-project/warmer \
          --cache-dir=/cache \
          ${images}
      - | 
        docker run \
          -v $(pwd):/workspace \
          -v $(pwd)/.docker:/kaniko/.docker \
          -v /cache:/cache \
          -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
          -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
          -e AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN} \
          gcr.io/kaniko-project/executor \
          --cache=true \
          --cache-dir=/cache \
          --cache-repo ${ECR_REPO} \
          -d ${ECR_REPO}:${ECR_TAG}
cache:
  paths:
    - /cache/**/*
 

CodeBuildのキャッシュでカスタムキャッシュを有効にしていれば,/cache以下の内容を保持し,次回以降のビルドが高速化される.

以上の構成を動かすCloudFormationテンプレートは以下から利用できる.

github.com

以上の形で,結構複雑ではあるけれど,キャッシュを利用できるような形でkanikoを使うことができるようになった.