はじめに
ArgoCD Image Updater の概要と導入方法について説明します。
絶賛開発中につきリリース毎に重大な変更が入る可能性があります。
最新情報は下記をご確認ください。
リファレンス
- 公式 Doc
- GitHub
概要
ArgoCD Image Updater とは
ArgoCD の Application として管理されているワークロードのイメージを更新してくれるツールです。
イメージを管理しているレジストリ (Docker Hub, GCR, etc.) を定期的にチェックし自動更新します。
そのため CI/CD ワークフローを書いてイメージタグを更新する必要がなくなります。
更新フロー
- コミットをトリガーに CI/CD が動く
- コンテナレジストリにプッシュ
- ArgoCD Image Updater が定期的にウォッチ
- 更新があればイメージタグを書き換えてプッシュ
- ArgoCD の Auto Sync により更新を取り込む
- 更新後のイメージタグを持つマニフェストで apply
更新方法
イメージ更新には、2つの方法があります。
1. ArgoCD API 経由 (デフォルト)
ArgoCD API で直接マニフェストを上書きする方法です。
下記コマンドと同等の効果を持ちます。
argocd app set --p guestbook=image=example/guestbook:latest
2. Git リポジトリ経由
Git リポジトリに変更を Push する方法です。
kustomization.yml の image
フィールドを書き換え、対象ブランチに Push する。
※ 上のフロー図は Git リポジトリ経由で更新した場合を表しています。
resources:
- ../../base
patchesStrategicMerge:
- ...
images:
- name: example/guestbook
newTag: new # ここが変わる
タグ条件
更新するイメージタグに様々な条件を付けることができます。
例えば、正規表現で特定のタグが付与されたときだけ更新する、など。(後述)
導入方法
今回はコンテナレジストリとして GCR
を利用し、Git リポジトリ経由
でイメージを更新します。
環境
- GKE (v1.21.5-gke.1802)
- ArgoCD (v2.1.7)
- Terraform (v1.0.8)
- google provider (v4.3.0)
大まかな手順
- ArgoCD Image Updater を apply
- クレデンシャルを登録
- Application に annotation を追加
手順はこちらの記事を参考にさせていただきました。公式 Doc だけだと分かりにくいので非常に助かりました。
手順1 | ArgoCD Image Updater を apply
ArgoCD Image Updater も ArgoCD の Application として適用します。
namespace
は ArgoCD が稼働している場所と一致させるのが推奨です。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argocd-image-updater
namespace: argocd
labels:
app.kubernetes.io/name: argocd-image-updater
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: 'https://argoproj.github.io/argo-helm'
targetRevision: 0.6.0
helm:
values: |
config:
logLevel: "info"
chart: argocd-image-updater
destination:
server: 'https://kubernetes.default.svc'
namespace: argocd
syncPolicy:
automated:
prune: true
適用完了すると、下記スクショのようなリソースが作成されます。
pod のログを確認すると、2分毎に次のような表示が見て取れます。
time="2021-12-27T04:38:06Z" level=info msg="Processing results: applications=0 images_considered=0 images_skipped=0 images_updated=0 errors=0"
ここには、更新対象の Application やイメージの数が表示されています。
公式 Doc には記載されていませんが、次のような意味だと認識しています。
- applications: 更新対象となるイメージを所有している Application 数
- images_considered: 更新対象となるイメージ数
- images_skipped: 更新が検知されたがスキップされたイメージ数
- images_updated: 更新が検知され正常に処理されたイメージ数
上述の例では、更新対象の Application をまだ設定していないため、全て 0
になっています。
手順2 | クレデンシャルを登録
ArgoCD Image Updater が GCR の更新状況をフェッチするためのクレデンシャルを設定します。
なお、執筆時点で Workload Identity 非対応のためサービスアカウントを直接参照する必要があります。
まず、サービスアカウントキーを発行するためのリソースを追加します。
# argocd-image-updaterがGCRへイメージ参照するためのService Account
resource "google_service_account" "argocd-image-updater" {
account_id = "argocd-image-updater-${var.env}"
display_name = "argocd-image-updater-${var.env}"
}
# GCR を参照するためのRoleを付与
resource "google_project_iam_member" "argocd-image-updater" {
project = var.project_id
role = "roles/storage.objectViewer"
member = "serviceAccount:${google_service_account.argocd-image-updater.email}"
}
# サービスアカウントキーを発行
resource "google_service_account_key" "argocd-image-updater" {
service_account_id = google_service_account.argocd-image-updater.name
}
続いて、シークレットマネージャーにクレデンシャルを登録します。
本稿では、External Secrets を利用して値を取得しています。
resource "google_secret_manager_secret" "argocd_image_updater_registry_credentials" {
secret_id = "argocd_image_updater_registry_credentials"
replication {
automatic = true
}
}
resource "google_secret_manager_secret_version" "argocd_image_updater_registry_credentials" {
secret = google_secret_manager_secret.argocd_image_updater_registry_credentials.name
secret_data = jsonencode({
auths = {
"https://asia.gcr.io" = {
username = "_json_key"
password = base64decode(google_service_account_key.argocd-image-updater.private_key)
email = google_service_account.argocd-image-updater.email
auth = base64encode("_json_key:${base64decode(google_service_account_key.argocd-image-updater.private_key)}")
}
}
})
}
手順 1 の argocd-image-updater.yml をこちらで上書きして適用します。
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argocd-image-updater
namespace: argocd
labels:
app.kubernetes.io/name: argocd-image-updater
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: 'https://argoproj.github.io/argo-helm'
targetRevision: 0.6.0
helm:
values: |
config:
logLevel: "info"
registries:
- name: Google Container Registry Asia
api_url: https://asia.gcr.io
prefix: asia.gcr.io
ping: no
credentials: pullsecret:argocd/argocd-image-updater-secrets
chart: argocd-image-updater
destination:
server: 'https://kubernetes.default.svc'
namespace: argocd
syncPolicy:
automated:
prune: true
---
apiVersion: external-secrets.io/v1alpha1
kind: SecretStore
metadata:
name: argocd-image-updater
namespace: argocd
labels:
app.kubernetes.io/name: argocd-image-updater
spec:
provider:
gcpsm:
projectID: example-gcp
---
apiVersion: external-secrets.io/v1alpha1
kind: ExternalSecret
metadata:
name: argocd-image-updater-secrets
namespace: argocd
labels:
app.kubernetes.io/name: argocd-image-updater
spec:
refreshInterval: 1m
secretStoreRef:
name: argocd-image-updater
kind: SecretStore
target:
name: argocd-image-updater-secrets
creationPolicy: Owner
data:
- secretKey: .dockerconfigjson
remoteRef:
key: argocd_image_updater_registry_credentials
helm のパラメータに registries
を追加をしています。
この追加だけで、クレデンシャルが ComfigMap にダンプされ、Pod にマウントされます。(詳細)
以上で、ArgoCD Image Updater が GCR を参照できるようになりました。
手順3 | Application に annotation を追加
最後に、更新対象とするイメージを ArgoCD Image Updater に指示します。
Application のマニフェストに annotation を付加することで設定できます。
ここでは、Kustomize で管理される Application の設定例を示します。
(その他に Helm で管理しているケースもありますがイメージ更新のニーズは少ないと思います)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: example-api
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
annotations:
argocd-image-updater.argoproj.io/image-list: external-api=asia.gcr.io/example-dev/example-dev #1
argocd-image-updater.argoproj.io/external-api.update-strategy: latest #2
argocd-image-updater.argoproj.io/external-api.ignore-tags: latest #3
argocd-image-updater.argoproj.io/external-api.allow-tags: regexp:^[0-9a-f]{5,40}$ #4
argocd-image-updater.argoproj.io/write-back-method: git:repocreds #5
argocd-image-updater.argoproj.io/git-branch: dev #6
argocd-image-updater.argoproj.io/write-back-target: kustomization:../../../example-api/overlays/dev #7
spec:
destination:
namespace: default
server: https://kubernetes.default.svc
project: default
source:
repoURL: git@github.com:example/example-infra.git
path: kubernetes/example-api/overlays/dev
targetRevision: main
syncPolicy:
automated:
prune: true
計7つの annotation を追加しました。それぞれ説明します。
#1 image-list
更新対象とするイメージのリストです。
alias を付けられます。イメージ毎の設定をするのに必要ですので付けておきましょう。
複数ある場合は <alias1>=<image1>,<alias2>=<image2>,...
のようにカンマ区切りで追加します。
#2 <alias>.update-strategy
イメージ毎のアップデート戦略を指定します。
いくつか選択肢がありますが、ここでは latest
にしています。
コンテナレジストリ内で直近に更新されたイメージへ更新してくれます。
#3 <alias>.ignore-tags
特定のタグを持つときは更新しないようにします。
新規イメージに「latest」と「コミットハッシュ値」の2つのタグを付与している場合において、
適用中のイメージがどのコミットに紐づくものか把握するためには、コミットハッシュ値の方で更新したいです。
上記でアップデート戦略を latest
にしていたため、latest タグを ignore することで、
必ずコミットハッシュ値のタグでイメージが更新されるようになります。
#4 <alias>.allow-tags
更新対象とするイメージタグを正規表現で制限できます。
ここでは、コミットハッシュ値の正規表現を設定しています。
これにより、「test」などのタグを持つイメージが Push されても除外することができます。
#5 write-back-method
イメージタグの更新方法です。
冒頭の説明にもあった通り、argocd
or git
を選択します。
ここでは、git
を選択しています。
#6 git-branch
イメージタグ更新を Push するブランチを選択します。
#7 write-back-target
更新対象のマニフェストが格納されているディレクトリパスを示します。
ここでは、example-api-apps.yml から更新対象の kustomization.yml が置いてあるディレクトリへの相対パスを示しています。
以上で全ての準備が整いました。
ArgoCD Image Updater の Pod のログを確認すると、applications と images_considered が共に 1 になっています。
time="2021-12-28T10:25:47Z" level=info msg="Processing results: applications=1 images_considered=1 images_skipped=0 images_updated=0 errors=0"
新しいイメージがコンテナレジストリに Push されると、次のような更新成功のログが確認できます。
time="2021-12-28T10:33:56Z" level=info msg="Successfully updated the live application spec" application=example-api
time="2021-12-28T10:33:56Z" level=info msg="Processing results: applications=1 images_considered=1 images_skipped=0 images_updated=1 errors=0"
これにより、Git リポジトリに更新されたタグが Push されます。
そして、ArgoCD の Sync によって実際に適用されます。
まとめ
ArgoCD の Application の概要と導入方法を説明しました。
CI/CD ワークフローを頑張らなくても、イメージ更新できるのは魅力だと感じました。
一方で、ロールバック方法には課題が残ります。Git リポジトリへのタグ更新は Push のみなので、PR の revert によるロールバックは実現できません。こちらの記事で触れられているような代替手段を検討する必要がありそうです。