argocd-image-updaterを利用してPodに最新のイメージをデプロイする

僕が今ペパボで運用管理しているクラスタはArgoCDを利用してアプリケーションをデプロイしています。ArgoCDをざっくり説明するならば、Gitリポジトリの更新を検知して、更新された内容でマニフェストを自動で適用してくれるいわゆるGitOpsと呼ばれる技術を実現するソフトウェアです。

そんなArgoCDを利用するときに悩むのが、イメージビルド後のデプロイ戦略です。ArgoCD自体はプラグインを開発せずに利用するならば、Gitリポジトリの最新の資産で、マニフェストのアプライしか実行してくれません。それが何故問題かというと、普通にContainer Deliveryをやろうと思うと、下記のような手順になると思います。

  1. CIツールで自動テストを実行
  2. コンテナイメージをビルド
  3. コンテナイメージの自動テスト
  4. コンテナリポジトリにコンテナイメージをプッシュ

この場合、GitOpsを利用すると、1が始まるタイミングは多くの場合、マージブランチにマージされたタイミングだと思います。(etc: default or master)

その場合に、何が起こるかというと、ArgoCDが最新の資産を利用してマニフェストをアプライするときに、本当にデプロイしたいイメージがまだビルドされていないという問題が起こります。

これを解決するために、コンテナイメージのプッシュ後に例えばビルド時刻などをGitリポジトリにコミット、プッシュして、強制的にリポジトリに差分を起こしたり、マニフェストに変更がない場合、KubernetesはPodの入れ替えを実行しないので、ダミーラベルなどにタイムスタンプを書き込み、強制的にPodを入れ替えるような事が必要になります。これはこれで動くのでいいっちゃいいのですが、もっとスマートなやり方はないだろうかと探していたら、argocd-image-updater を見つけました。argocd-image-updaterを利用すると、指定したコンテナイメージの最新のタグで実行されているPodのイメージタグを書き換えてくれます。今動いているのが hoge:latest イメージで、hoge:xxxxx が新たにプッシュされたらPodのイメージを hoge:xxxx に書き換えてくれます。

先に注意事項を述べておくとまだ開発途中物で意図しない挙動や、今後破壊的な変更が入る可能性があるのでこの記事を読んで利用する場合はその点を注意してください。

インストール手順は、ドキュメントがしっかりと整備されています。インストールができれば、あとはArgoCDのApplicationリソースにAnnotationを書くだけで利用することができます。

argocd-image-updater.argoproj.io/image-list: example=repo.example.com/foo/bar
argocd-image-updater.argoproj.io/example.update-strategy: latest
argocd-image-updater.argoproj.io/example.allow-tags: regexp:^[0-9a-f]{5,40}$
argocd-image-updater.argoproj.io/example.ignore-tags: "latest"

僕が実際に利用しているのは上記のような定義です。まず argocd-image-updater.argoproj.io/image-list: example=repo.example.com/foo/bar のようにイメージに別名を付けます。(repo.example.com/foo/barに別名exampleをつける)これをすることで独自イメージリポジトリを利用している場合に、アノテーション文字列のバリデーションエラーを避けることができるのと、単純に以降の定義が短くなるので便利です。

argocd-image-updater.argoproj.io/example.update-strategy: latest この定義はイメージの更新の戦略を選ぶことができ、常に最新を選ぶ latest やセマンティックバージョニングを選択することができます。僕は本番用のイメージをGitのコミットハッシュのSHA1をタグに付与してビルドしているので latest を僕は利用しています。OSSなどでセマンティックバージョニングを利用しているならば、セマンティックバージョニングで良いと思います。

argocd-image-updater.argoproj.io/example.allow-tags: regexp:^[0-9a-f]{5,40}$ この行は、更新戦略に latest を選んでいるので、例えば、テスト用のイメージを repo.example.com/foo/bar:test のようにビルドした際も、それが最新のイメージだと認識されてしまいます。それを避けるために、GitのコミットハッシュであるSHA1を正規表現で定義しており、本番用にビルドしたイメージだけを更新の対象としています。

argocd-image-updater.argoproj.io/example.ignore-tags: "latest" こちらのタグは latest を付与したタグを更新から除外するために付与しています。この理由はDeploymentsのimageの定義に repo.example.com/foo/bar:latest と書きたいから、入れています。それはなぜかというと、下記のようなビルドを行っているからです。

  1. イメージをビルドする際に repo.example.com/foo/bar:SHA1 でイメージを作成
  2. repo.example.com/foo/bar:SHA1repo.example.com/foo/bar:latest というタグを付与する

この場合、repo.example.com/foo/bar:latestrepo.example.com/foo/bar:SHA1 は同じイメージを指すタグです。このとき、最新のイメージが repo.example.com/foo/bar:latest と判断されてしまうと、マニフェストに記載されている定義と同じになるため、Deploymentsの書き換えが発生せず、最新のコンテナイメージがデプロイされないからです。よって、 latest タグを除外することで SHA1 のほうのタグが優先して選択されるようにしています。さらに、argocd-image-updater は動作しているPodのマニフェストを書き換えるだけで、Gitリポジトリに定義されているマニフェストを書き換えるわけではないため、あくまでマニフェストファイルに latest タグを書きたいがためにこのようにしています。

最後に

まだ運用し始めたばかりで、大きなハマりや問題も起きていないのですが、なにか問題が起きたり、知見を得られたらまた共有します。