背景
私が所属しているプロジェクトのリリースフローは、大まかに以下の順序で行われます。
- DBのマイグレーション専用ブランチにマージ
- CloudBuildで、マイグレーション用のシェルが実行される
- デプロイ専用ブランチにマージ
- CloudBuildで、k8sのマニフェストが適用される
また先日、DBの接続情報などをSealedSecretを用いてSecretで管理するようになりました。
そこでマイグレーションの実行もk8sのJobで実行したくなったのですが、マイグレーションの完了を待ってからデプロイするにはどうすればいいか
と疑問に思い、調査した結果、ArgoCDでうまいことやれそうだ
ということが分かったので、検証することにしました。
しかもArgoCDでGitOpsを実現しようとしていたので、ちょうどよかったです。
この記事で触れること
- ArgoCDのHookについて
説明で用いるyamlは以下のリポジトリにコミットしてあります。
istsh/secret-sample-manifests
この記事では触れないこと
- ArgoCDの導入方法
- GitOpsとはなにか
- デプロイ対象のアプリケーションについての諸々
ArgoCDのHook
Hookの定義方法
Hookはアノテーションとして定義します。
ここではJobのyamlを紹介します。
apiVersion: batch/v1
kind: Job
metadata:
name: sample-job
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
argocd.argoproj.io/sync-wave: "2"
Hookの種類
ArgoCDのHookは5種類あります。
Hook | 説明 |
---|---|
PreSync | マニフェストの適用前に実行される。 |
Sync | すべてのPreSync Hookが成功として完了した後で、マニフェストの適用と同時に実行される。 |
Skip | マニフェストの適用をスキップする。 |
PostSync | すべてのSync Hookが成功として完了し、すべてのリソースが正常な状態になった後に実行される。 |
SyncFail | PreSyncやSync, PostSyncが失敗したときに実行される。(ver1.2以降) |
Hookの削除
Hookを何らかの方法で削除するには、hook-delete-policy
をアノテーションに定義します。
Policyは3種類あります。
Policy | 説明 |
---|---|
HookSucceeded | Hookが成功した後に削除される。 |
HookFailed | Hook失敗した後に削除される。 |
BeforeHookCreation | 次の新しいHookが作成される直前に削除される。(ver1.3以降) |
同一Hookでの実行順序の指定
sync-wave
をアノテーションに定義します。
これを定義すると、その値の昇順にHookが実行されます。
ただし、注意点が2つあります。
1つ目は、値は数値ではなく文字列で定義すること。
2つ目は、公式ドキュメントにもあるように、sync-waveの初期値は0です。同一Hookでsync-waveを設定する場合に、どれかに設定漏れがあると、それが意図せず先に実行されることがあるので注意してください。
マイグレーションのJobを定義
前提として、JobはSecretから環境変数を読み取る、SecretはSealedSecretを使用している、とします。
先にSecret(SealedSecret)とJobのyamlを載せます。
使用しているイメージやその引数は、今回の説明には関係ないので無視してください。
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: sample-secret
namespace: default
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
argocd.argoproj.io/sync-wave: "1"
spec:
encryptedData:
MYSQL_HOST: AgBPhntyTvUOYPtjJrdZk0fbVyljZFQExAVKAHYjE0rtTNsqaNXygninGs5CPPALfIf/BgmONK35xepHnH7kqc+WNm2nt4MueAMHAtrHQ/8SCkzXbQGLuiyUUds2Q2xVu/rBAVwGfWaKkwi9Db8Y7u6UOLE2wjgk2hpfqnf0KCgR4+u4q936tszBlqQrzk6+ZCqn7oPkOHI3iHJIi4Myo+Xh51Cx0ItEcq18huAK04tTgPoNDJgjLZZnWt83eOJcdqs6mNuI2khsi+/sXizA/naRRDJjiZat5//v/e73ejPRYLJkLCxmbiqBkZUmzBcTgj9PNvOPd+Be/VlKV0I4oO23e4f4CTp+xsq/hxje1qo38zL5UEalbYvtWVCAQCHyA5zcacpD7Sbl0hDSn4JCmXgktrtmgp4iADw2dGDqj7yGyvgQt18VUyWwqIzdhdih9a9LLvqK+gEf9pHs6p6j+rSlvIhKWM9k+vp44h9GLv5ViXB0jFD6Ig5kuyrne6OPceJgj1kuoGQbGdwCvd6thjNytWwBuPujKo5AK4VepLUUh7q7zZwqBv0yXMwSkmFDpJMTmMuy6W32HZvmJw1m/ajaAqlltDKl2pbyDClRLJ/K1n9QDiOEgIdLX3t9C5tJL1G9b1bMTpTfqqYcQbTQnDBEJJ7du4eYQRoT9EP4vI0eyab8beuvBvwIdPvsPWwY+uf7TzuyX0ve6/8=
MYSQL_PASSWORD: AgAV4O7rOmyqL2JtPZ/XtUIMNW5GPgSMhc8zU0zU7LyaNfu5PPFfZrqmyvV4u0XjiNL0nH0rxCEnbm8ZlYtDQCDTMAZMgQkDSWVq7RGnL0txkE3mtsgzctNvIk54lvoVi7fzsBUHXLWsXu58NgQsGhc/VG25zMIq2ycEKd0nEFoa3EK1EoNsb3gKNS4qGM7hqkS5y7ufOcUR5mVuvXlihrfn9nGcucFsRsMbxsTYWUauM8x0wqxrnw7o74EkA0Gyhu2FI43n4mleubC8pzOEYvurfyfd1UmS7H6j/Cm4VuPtiX/uynvKyb2iVJX3vkeyKnmvhP6AcegfI1UYSD1nLg6taDOUFDdScCRsdI97lF0ehYauinLLp94rRQIXOFWYaSqq8VNbQzApXTDAgSzjazdx17p1IVrspEc7xCzD1Dcn3RiiiSpGnYEbG4rF4pFu8AcKH5j/MSydKN4iJgQ2utOgJ7pUXNeShgRrNf/BI67uus44fgeIXnkG8+LG7/WGhsjnYTRsECZD4g+q2vB3tYeBSFQ7ojUFVN5ChsZgTS+uziIp3jzxu3sWfSt4puGx+BlFp+dxv0Zp1YrRKuje+dBWlCkrf9Sa16SR7DCqKdX+1G0W6rwYYEyXwRW9Y0s6lUZwfpd0kOVcYZABohfH3RkWmxgKjF76xFlyFfHILVtdGJbe1nz1144wAMQeE3jvk/ypeAGy+I8lbg==
MYSQL_USER: AgDjq7qBJR1DDOWapYF4AhtQi1sQgeglSRhu6tZ+JHPTPPdNFBV+zQk3xzKKRldZeeIlXQ5ZquEdc3lMWYKqg5xvL2e21YhgPv4oWADDvsWxWdklZVLDl0SGPQT64v6K+lPVR5Gia6zIpocvv4v3GbMcCXFkFK/b5HeSU6FcxrAhrQ+vdxSWKfkezO8vrS1wJ3MYLgLSwtiRDsibjpYwVTg0sLDcpTlrDk95IEnmcY8bE6qGJ3BO8mUBPhsBnk5yLJO2MZeOofA9IbpUDIpUJSFcaaXc+PK3C+vKxxJ2Ia94Y07UshUOrl72dTrp9aJv1mdkVfzZ7EHQpHh+2k7b2U1nMFVFwK2QV193vJVSQDM21IzIttE4QOxllv0ei48FVMVw3pJ0ABJMKiYta6KAZvKQ5EJkEIVVHtVqRH6plpBgU72dAPVuJAmdlAQ3I844lj1AKwfydylFRJP/olkHegB5Lu3ScwblicPZt4Q+f+IV79YefKR+Bf8zwLB5bd5uFOVx5Jj3dkiSfTN9P60FE6uA1axxyECafqwcgEmxafODtRpP+94rbAUboWCCXTAqjL+KyclCkXg0lKKLDHBmIzhsV3+eDwlApHWfvs1/x2dyW17Rjs7Nqb6vi/xX+YXT0d67TzNEUFfYFABRN9ABN/YSCWsVxIJXvMulg4RpsGD4g5E4GG5gwj3Jjjfr0yEtsu9fS5Zn
template:
metadata:
creationTimestamp: null
name: sample-secret
namespace: default
apiVersion: batch/v1
kind: Job
metadata:
name: sample-job
annotations:
argocd.argoproj.io/hook: PreSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
argocd.argoproj.io/sync-wave: "2"
spec:
backoffLimit: 0
template:
spec:
containers:
- name: sample-job
image: istsh/secret-sample-app-job:1.0.0
args: ["success"]
envFrom:
- secretRef:
name: sample-secret
restartPolicy: Never
ここで重要なのは、Jobだけでなく、Secret(SealedSecret)にもPreSyncを定義していることです。
マイグレーションを実行する場合、SecretからDBの接続情報を取得することになると思いますが、JobだけにPreSyncを定義すると、Jobの実行時にSecretが作成/更新されていないため、実行に失敗します。
SecretにもPreSyncを定義することで、同一HookでSecretも作成されます。
しかし、Secretが作成されるよりも前にJobが実行されてしまうと、DBの接続情報が取得できずに失敗してしまいます。
ここで使えるのがsync-wave
です。Secretのsync-waveを"1"
、Jobを"2"
とすることで、Secretが必ず先に作成されます。
また、hook-delete-policy
を定義しないと、次回の実行時にHookが残ったままなので、PreSyncそのものが失敗してしまいます。必ず定義しましょう。
HookSucceeded
は失敗した場合に削除されずに残ってしまいます。HookFailed
も同様なので、BeforeHookCreation
が有用なのではないでしょうか。
Hookの状態を確認
Hookの状態は以下のコマンドで確認できます。
$ argocd app get sample-app --show-operation
Name: sample-app
Project: default
Server: https://kubernetes.default.svc
Namespace: default
URL: https://localhost/applications/sample-app
Repo: https://github.com/istsh/secret-sample-manifests.git
Target: candidate-local
Path: k8s/overlays/local
SyncWindow: Sync Allowed
Sync Policy: Automated
Sync Status: Synced to candidate-local (2d128f4)
Health Status: Healthy
Operation: Sync
Sync Revision: 2d128f4f1a2ee8a773a0505d827c34e28ac13593
Phase: Succeeded
Start: 2020-09-28 01:24:56 +0900 JST
Finished: 2020-09-28 01:25:09 +0900 JST
Duration: 13s
Message: successfully synced (all tasks run)
GROUP KIND NAMESPACE NAME STATUS HEALTH HOOK MESSAGE
bitnami.com SealedSecret default sample-secret Succeeded PreSync sample-secret created
batch Job default sample-job Succeeded PreSync job.batch/sample-job created
ConfigMap default envoy-secret-sample-server-config Synced configmap/envoy-secret-sample-server-config unchanged
ConfigMap default envoy-secret-sample-proxy-config Synced configmap/envoy-secret-sample-proxy-config unchanged
Service default secret-sample-server-preview Synced Healthy service/secret-sample-server-preview unchanged
Service default secret-sample-proxy-active Synced Healthy service/secret-sample-proxy-active unchanged
Service default secret-sample-server-active Synced Healthy service/secret-sample-server-active unchanged
Service default secret-sample-proxy-preview Synced Healthy service/secret-sample-proxy-preview unchanged
argoproj.io Rollout default secret-sample-server Synced Healthy rollout.argoproj.io/secret-sample-server unchanged
argoproj.io Rollout default secret-sample-proxy Synced Healthy rollout.argoproj.io/secret-sample-proxy unchanged
まとめ
ArgoCDのPreSyncを用いることで、アプリケーションのデプロイ前にマイグレーションを実行する
ことが可能になりました。
マイグレーションに限らず、PreSyncで「デプロイを開始します」、PostSyncで「デプロイが完了しました」、SyncFailで「デプロイが失敗しました」と通知することも可能です。
これ以外におすすめの使い方を知っている方は、コメント等いただけると幸いです。