ArgoCD Image Updaterを導入しました(導入方法、メリット、デメリット)
ArgoCD Image Updater とは
- ArgoCDによって管理されているkubernetesのワークロードのイメージを自動的に更新してくれるツール
- 新しいイメージの更新を自動的に検知して、kubernetesのワークロードに適応してくれる
- kubernetesのyamlをいちいち更新する必要なし
- ArgoCDによって管理さているのApplicationリソースに annotation を付与することで細かい設定を行う
-
Kustomize
、Helm
によって構築されているApplicationに対してのみ利用可能
導入方法
詳しい導入方法は公式ドキュメントを参考にしてください。
ここでは僕の行った導入例を紹介します。
僕の環境では以下のような構成で管理しています
- Kubernetesクラスタは
GKE(v1.18)
- Kubernetesのリソースは
ArgoCD(v2.0.0)
+Kustomize
- dockerイメージは
GCR
- GCPリソースは
Terraform
- Kubernetesのsercetは
Google Secret Manager
とExternal Secret
導入手順は主に以下の3ステップです
- 1,argocd-image-updaterのmanifestをapplyする
- 2,GCRへのアクセス権限を付与する
- 3,ArgoCDのapplicationにannotationを追記する
①manifestをapplyする
以下の2つのファイルを作成し、kustomizeで行いました。
- some_dir/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- github.com/argoproj-labs/argocd-image-updater/manifests/base?ref=v0.9.2
patches:
- config_map.yaml
- some_dir/config_map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-image-updater-config
data:
log.level: debug
あとは以下のコマンドを叩けば、kubernetesにインストールされます
kustomize -n argocd build some_dir | kubectl apply -f -
kustomizeのremote targetsという機能を使うことで、外部のGitリポジトリのURLをbaseに指定することができます。
あとは変更したい箇所でpatchを当てればよいので、kubernetesにツールをインストールする時のバージョン管理、パラメータ管理を楽に行うことができます。
実際はArgoCDのApplicationとして管理していますが、ここでは簡略化しています。
また、argocd-image-updaterはArgoCDのApplicationと同じnamespaceであるargocd
にデプロイしています。それが公式にも推奨されていますし、そうできればややこしい設定をほぼする必要がなくなるからです。
②Container Registriesへのアクセス権限を付与する
ここがちょっとめんどくさかったです。
GCRへアクセスするためのsecretを作成し、argocd-image-updaterのconfigmapに記載してあげる必要があります。
何パターンが実装方法があり、詳しくはこちらをみてください。
今回僕は.dockerconfigjson
フィールドを持つsecretを作成する方法にしました。(GCRを使う場合はおそらくこの方法しかない?)
やるべきことは以下の4つです
- 1, GCRへのアクセス権限を持ったServiceAccountを作成する
- 2, ServiceAccountのkeyを発行する(xxx.json)
- 3,
.dockerconfigjson
フィールドを持つsecretを作成する - 4,
argocd-image-updater-config
にregistries.conf
フィールドを追記する
まずは①と②について、以下のようにTerraformで行いました。
# GCRを使うと自動的に作成されるstorage
# すでに作成されている場合は、以下のようなimportコマンドを実行する必要がある
# terraform import google_storage_bucket.gcr_storage <バケット名>
resource "google_storage_bucket" "gcr_storage" {
name = "asia.artifacts.${var.gcp_project[terraform.workspace]}.appspot.com"
location = "ASIA"
}
# サービスアカウントを作成する
resource "google_service_account" "argocd_image_updater" {
account_id = "argocd-image-updater"
display_name = "argocd-image-updater account"
}
# オブジェクトレベルで参照権限を付与する
resource "google_storage_bucket_iam_binding" "gcr_storage_binding" {
bucket = google_storage_bucket.gcr_storage.name
role = "roles/storage.objectViewer"
members = ["serviceAccount:${google_service_account.argocd_image_updater.email}"]
}
# サービスアカウントのKeyを生成する
resource "google_service_account_key" "argocd_image_updater" {
service_account_id = google_service_account.argocd_image_updater.name
}
③のsercetの生成について、kubectlコマンドを使う場合は
kubectl create secret docker-registry hoge --docker-server="https://asia.gcr.io" --docker-username=_json_key --docker-email=test-serviceaccount@some-gcp-project.iam.gserviceaccount.com --docker-password="$(cat xxxx.json)"
のように行うこともできます。
しかし、手運用はしたくなかったので、TerraformでGoogleSecretManagerに必要な機密情報を登録し、ExternalSecretを使ってKubernetesのSecretリソースを作成しました。
Terraformでは以下のようにして、GoogleSecretManagerに機密情報を作成しました。
resource "google_secret_manager_secret" "argocd_image_updater_secret_for_docker_registry" {
secret_id = "argocd_image_updater_secret_for_docker_registry"
replication {
automatic = true
}
}
resource "google_secret_manager_secret_version" "argocd_image_updater_secret_for_docker_registry" {
secret = google_secret_manager_secret.argocd_image_updater_secret_for_docker_registry.id
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)}")
}
}
})
}
ExternalSecretは次のように定義しました。
apiVersion: kubernetes-client.io/v1
kind: ExternalSecret
metadata:
name: argocd-image-updater-docker-registry-secret
namespace: argocd
spec:
backendType: gcpSecretsManager
projectId: xxxxxxx
template:
type: kubernetes.io/dockerconfigjson
data:
- name: .dockerconfigjson
key: argocd_image_updater_secret_for_docker_registry
version: latest
④のargocd-image-updater-config
にregistries.conf
フィールドを追記は以下のようにして行いました
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-image-updater-config
data:
log.level: debug
registries.conf: |
registries:
- name: Google Container Registry Asia
api_url: https://asia.gcr.io
prefix: asia.gcr.io
ping: no
credentials: pullsecret:argocd/argocd-image-updater-docker-registry-secret
これでargocd-image-updaterがGCRにアクセスする準備が整いました。
③ArgoCDのapplicationにannotationを追記する
僕が追記したAnnotationは以下になります
- 開発環境
argocd-image-updater.argoproj.io/write-back-method: git
argocd-image-updater.argoproj.io/image-list: my-image=asia.gcr.io/some-gcp-project/some-image-name
argocd-image-updater.argoproj.io/my-image.update-strategy: latest
argocd-image-updater.argoproj.io/my-image.allow-tags: 'regexp:^[0-9a-f]{5,40}$'
argocd-image-updater.argoproj.io/my-image.ignore-tags: latest
- 本番環境
argocd-image-updater.argoproj.io/write-back-method: git
argocd-image-updater.argoproj.io/image-list: my-image=asia.gcr.io/some-gcp-project/some-image-name
argocd-image-updater.argoproj.io/my-image.update-strategy: semver
argocd-image-updater.argoproj.io/my-image.ignore-tags: latest
argocd-image-updater.argoproj.io/write-back-method: git
write-back-method
はargocd
かgit
を指定できます。git
を指定すると、対象のgitリポジトリに適応中のimageタグを自動でコミットしてくれます。
GitOpsとして、Gitリポジトリを見れば現在のインフラの状態がわかる、という状況を作るためには、write-back-method
はgit
にすべきです。(GitOpsしたいならgit一択です)
argocd-image-updater.argoproj.io/image-list: my-image=asia.gcr.io/some-gcp-project/some-image-name
argocd-image-updaterが取得すべき対象のimage名の一覧を登録します。
=
を使うと、それぞれのimage名にaliasを作成することもできるので、しておいた方が良いでしょう。
argocd-image-updater.argoproj.io/my-image.update-strategy: latest
imageのアップデート戦略を指定することができます。
単純に最新のものをイメージタグを使う場合はlatest
、セマンティックバージョニングで最新のバージョンを使う場合はsemver
を指定します。
開発環境では、コミット毎にCOMMIT_SHA
のイメージタグを作成しているので、作成するたびにデプロイしてもらいます。
本番環境では、タグを打つとタグ名のイメージタグを作成しているので、最新のバージョンをデプロイしてほしいもらいます。
argocd-image-updater.argoproj.io/my-image.allow-tags: 'regexp:^[0-9a-f]{5,40}$'
開発環境では、update戦略がlatest
の指定になっているので、たとえばtest
というイメージタグを作成した場合にも最新のイメージタグと認識されデプロイされてしまいます。それを避けるために、GitのコミットハッシュであるSHA1を正規表現で定義してあります。
argocd-image-updater.argoproj.io/my-image.ignore-tags: latest
イメージをビルドする際、COMMIT_SHA
のイメージタグと同時にlatest
というイメージタグも同時に作成しています。argocd-image-updaterがlatest
を最新と認識してしまい、新しいイメージをビルドしてもlatest
と認識してしまうと、Deploymentの書き換えが発生しないので最新のイメージがデプロイされなくなってしまいます。
それを避けるために、latest
タグは無視するようにしてます。
Kustomize使ってる人のハマりポイント
Kustomizeのイメージの上書き機能を使っている場合、argocd-image-updaterによるイメージタグの上書きが正しく行われません!
なので、DeploymentなどでKustomizeのイメージの上書き機能は使わず、イメージ名を直接書いてあげる必要がありますので、ご注意ください!
導入してみて
デプロイフローの変化
argocd-image-updaterを導入する前には以下の図のようにデプロイしていました。
- 各サービスのリポジトリでプッシュ
- CIでテスト、イメージのビルド、ArgoCDのアプリケーションを管理しているリポジトリへプッシュ
- ArgoCDが自動でデプロイ
argocd-image-updaterを導入する後は以下の図のようになり、CIからのプッシュがなくなりました。
- 各サービスのリポジトリでプッシュ
- CIでテスト、イメージのビルドのみ
- ArgoCDが自動でデプロイ
よかったこと
デプロイフローがシンプルになったことです。他リポジトリへのコミットは、やや複雑なシェルを書く必要がありましたし、アクセス権限を管理する必要があったのですが、それらを捨てることができたのでかなりすっきりしました。
よくなかったこと
デプロイをプルリクエストで管理できなくなりました。
ArgoCDのアプリケーションを管理しているGitリポジトリで、新しいイメージタグに変更するプルリクエストを作成し、それをマージすることでデプロイを管理するという方法はできません。
なので、慎重なデプロイが求められる開発環境下ではargocd-image-updaterを導入しない方がよいでしょう。
古いバージョンのイメージをデプロイできなくなりました。
古いバージョンのイメージを指定してあげたとしても、argocd-image-updaterが自動で最新のものに書き換えてしまうからです。
なので、「最新バージョンをリリースしたけど、バグってしまった!前のバージョンにロールバックするぞ!」っといったことが、ArgoCDのアプリケーションを管理しているGitリポジトリ上でできなくなります。
それを行う方法としては、以下のパターンが考えられます
- ArgoCD-UI上から、もしくは、ArgoCD-CLIからRollback機能使う
- annotationを消すなどして、一時的にargocd-image-updaterを利用しないようにする
- 最新のイメージを消してから、ArgoCDのアプリケーションを管理しているGitリポジトリにコミットする
いずれの方法も様々なデメリットがあり、行いたくないですが、リリースミスってしまった場合には妥協するしかなさそうです😅
最後に
導入してみて、一番難しかったのでは、docker registryへのアクセス権限を付与する部分でした。
またKustomizeのイメージの上書き機能によるargocd-image-updaterが機能しないハマりポイントにはわりと苦しめられました。
リリースをミスった時のロールバックをいい感じにできないというデメリットは大きいですが、CIがすっきりしたメリットも大きいと感じているので、いったんこのまま運用してみます。
まだargocd-image-updaterを使った運用はそんなにしてませんが、今後運用していく中でハマりポイントなど発見したら、紹介できればなと思います!
Discussion