CircleCI で CIRCLE_SHA1 を使う場合の注意

過去に自分がミスった内容と似た事例を他にも見かけたので、今更ながらメモ。

TL; DR

  • CircleCI の ECS/ECR へのデプロイに関するドキュメントで、コンテナイメージのタグに CIRCLE_SHA1 の値が設定されているが、これには要注意
  • CircleCI における CIRCLE_SHA1 の値は、最終コミットを識別するハッシュ値だよ
  • Git上のブランチやタグに依って変わるわけではないので、ブランチ・タグによって異なるものの識別には使えないよ
  • イメージタグのような識別子を作成する場合、それが必要なユニーク性を持っているか確認することが必要だよ

CircleCI で ECR/ECS にデプロイするときのコンテナイメージタグ

CircleCI 公式のドキュメントとして、以下の記事が公開されています。 (2019/12/15現在)

AWS ECR/ECS へのデプロイ - CircleCI

上記記事より引用

version: 2.1
orbs:
  aws-ecr: circleci/aws-ecr@0.0.2
  aws-ecs: circleci/aws-ecs@0.0.3
workflows:
  build-and-deploy:
    jobs:
      - aws-ecr/build_and_push_image:
          account-url: "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com"
          repo: "${AWS_RESOURCE_NAME_PREFIX}"
          region: ${AWS_DEFAULT_REGION}
          tag: "${CIRCLE_SHA1}"
      - ...

ビルドの結果出来上がる個々の コンテナイメージを識別するものとしてタグを設定します(上記例の tag )が、ここでは、タグに設定する値は "${CIRCLE_SHA1}" となっています。

(以前のドキュメントでは、普通に docker コマンドを使用して docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com:${CIRCLE_SHA1} という感じになっていたはず…)

CIRCLE_SHA1 って?

さて、この CIRCLE_SHA1 に設定される値はどのようなものでしょうか。こちらも CircleCI の公式ドキュメントで説明されています。

環境変数の使い方 - CircleCI

CIRCLE_SHA1 String 現在のビルドの最後のコミットに関する SHA1 ハッシュ。

VCS が Git であれば、 Git のコミットハッシュと同一の値になります。CircleCI でビルドを開始する際に Git リポジトリから取得した内容 (バージョン断面) を識別するものと考えていいと思います。

コンテナイメージタグに CIRCLE_SHA1 を設定すると

この CIRCLE_SHA1 をコンテナイメージタグに使うと、どうなるでしょう?

「Git リポジトリの内容が同一ならば、ビルドされるコンテナイメージも同一である」という条件が成り立つのであれば、これで問題ありません。CIRCLE_SHA1リポジトリの内容ごとにユニークであるので、そこと 1:1 に対応付けられるコンテナイメージも CIRCLE_SHA1 で識別することができます。

しかし、上記の条件が必ずしも成り立たない場合、つまりGitリポジトリが同一であっても、できあがるコンテナイメージが異なる場合がある(異なるイメージそれぞれを管理する必要がある)場合は注意が必要です。

例えば、よく見かける方式として、本番環境やステージング環境にリリースを行うために、Git 上の master から特定のブランチにマージし、それをトリガーとして CircleCI で各環境向けのビルドおよびデプロイを行う、という方式を考えます。

コンテナイメージ内に環境の情報を含める場合は、当然、リポジトリの内容が同一であっても、本番環境向けとステージング環境向けでできあがるコンテナイメージに差異が発生します。しかし、 CIRCLE_SHA1 の値は、トリガーとなったブランチに依らず。あくまでも最終コミットのハッシュ値によって決定されるため、それぞれのコンテナイメージに同一のタグが設定されることになります。

Docker および ECR の仕様上、すでに存在するタグと同一のタグを付与して Push した場合、古い方のイメージからはタグが剥がされてしまうため、例えば ステージング環境へのデプロイ後に本番環境へのデプロイを行った場合、ステージング環境の ECS クラスタにも本番環境用のコンテナイメージがデリバリされてしまうことになります。また、並行して複数環境のビルド・デプロイを行って、完了する順番が不定となるような場合、見かけ上ランダムにコンテナイメージが切り替わるため、再現検証の難しい障害となることがあります。

回避方法

この問題をするには、以下のやり方が考えられます

  • ECR リポジトリを環境ごとに用意する
  • CIRCLE_SHA1 以外の、適切な識別文字列を用意する。
  • タグにデプロイ先環境を識別する文字列を付加する

インフラ構成や実装の手間、ユニーク性の要件、費用等に合わせた方法を取るとよいでしょう。

まとめ

CIRCLE_SHA1 (に限りませんが) を何かの識別子として使用する場合、そのユニーク性が識別する対象のユニーク性と合致するかどうか、毎回きちんと検証しましょうというお話でした。