TT Lab
はじめる
学ぶ 学習パス コース

CGOA — GitOps認定アソシエイト

タグ v1 をデプロイしたのに v2 が出てきた

TT Labで続きを見る

目標

ブランチ・タグ・コミットSHAをそれぞれ追跡するArgo CDのApplicationを立て、どの名前が動き、どの名前が動かないのかを、 実際の同期の記録で確認します。そして、GitOpsでのロールバックがなぜ新しいコミットでなければならないのかを見ます。

なぜ重要なのか

OpenGitOpsの2つ目の原則は、望ましい状態が「バージョン管理され、変更されない形(versioned and immutable)」で保存されなければならないと述べています。 Gitのコミットは内容のハッシュなので変わりませんが、ブランチとタグはコミットを指すラベルにすぎず、誰でも動かせます。 targetRevision: v1は「v1という名前が今指しているもの」であって、「最初にレビューしたそのコミット」ではありません。 この違いを知らないと、レビューを通過したリリース名の下に、レビューしていない内容がデプロイされ、監査記録には依然としてv1と残ります。 ロールバックも同じ原則から導かれます。クラスターだけを古い状態に戻すと、Gitとクラスターが分かれてしまい、 Gitの履歴を消すと、何がいつデプロイされたかの証拠が消えてしまいます。

ステップ

  1. ベアリポジトリ/srv/bare/rev.gitを作成して/root/cgoa-rev/repoにクローンしてください。app/release.yamlにConfigMap release(namespaceは指定せず、data version: v1)をコミットしてmainへpushし、そのコミットにタグv1を付けてタグもpushします。そのコミットの40桁のSHAを、/root/cgoa-rev/v1.shaに1行で書いてください。
  2. /root/cgoa-rev/apps.yamlに3つのApplicationを作成して適用してください。名前と対象ネームスペースはrev-branch・rev-tag・rev-shaで、sourceはgit://gitd.gitsrv.svc.cluster.local:9418/rev.gitのappパス、targetRevisionは順にmain、v1、v1.shaに書いた40桁のSHAです。3つとも、project defaultにし、自動同期(prune・selfHeal)とCreateNamespace=trueを有効にして、3つのアプリがすべてSyncedになることを確認してください。
  3. /root/cgoa-rev/repoでapp/release.yamlのversionをv2に変えてコミットし、mainへpushしてください(タグには触れません)。3つのアプリをhard refreshしたあと、各アプリのstatus.sync.revisionを/root/cgoa-rev/after-commit.jsonに{"branch": ..., "tag": ..., "sha": ...}の形で書いてください。branchは新しいコミットへ進み、tagとshaはv1のコミットに残っている必要があります。
  4. タグv1をmainの最新コミット(v2)へ強制的に移してgit push -fし、rev-tagをhard refreshしてください。rev-tagのstatus.history[].revisionを順番に/root/cgoa-rev/tag-history.jsonに{"target": "v1", "revisions": [...]}として書きます。同じ名前v1の下に異なる2つのコミットが記録され、rev-tagネームスペースのConfigMapがv2に変わり、rev-shaはそのままv1である必要があります。
  5. まず、タグv1をv1.shaの元のコミットに戻してpushし、rev-tagが再びそのコミットに同期されるようにしてください。そのあと、/srv/bare/rev.git/hooks/pre-receiveに実行可能なフックを置き、すでにあるrefs/tags/*を変更または削除するpushは拒否し、新しいタグの作成とブランチの更新は許可するようにします。最後に、v1をもう一度移すpushを試し、拒否された出力の全体を/root/cgoa-rev/guard.txtに保存してください。
  6. mainは今v2です。履歴を消さずに、git revertでv2のコミットを打ち消す新しいコミットを作ってpushしてください。rev-branchがその新しいコミットに同期され、ConfigMapがv1に戻っている必要があります。/root/cgoa-rev/rollback.jsonに、revert_sha(新しいmainのSHA)、deployed(rev-branchネームスペースのConfigMapのversionの値)、equals_v1_sha(新しいSHAがv1.shaと同じか、ブール値)を書いてください。
  7. 自動同期を有効にしたまま、argocd app rollback rev-branch 0 --coreを実行し、拒否のメッセージを含む出力の全体を/root/cgoa-rev/rollback-refused.txtに保存してください。coreモードはkubeconfigの現在のネームスペースからArgo CDの設定を探すので、/root/cgoa-rev/kubeconfigにk3s kubeconfigをコピーして現在のネームスペースをargocdに変え、KUBECONFIGで指定して実行します。rollbackを成功させようとして自動同期を切らないでください。終わったとき、rev-branchは依然としてmainの最新コミットにSyncedである必要があります。
  8. /root/cgoa-rev/report.jsonに、mutable_refs(動いた参照の種類2つをbranch・tagとして、配列)、immutable_ref(commit-sha)、pinned_revision(今のrev-shaのstatus.sync.revision)、tag_guard(pre-receive)、rollback(git-revert)を書いてください。採点ツールは、ファイルの値とともに3つのアプリの現在の状態を、もう一度確認します。

参考

v1のコミットにタグを付けてSHAを書く

ベアリポジトリ/srv/bare/rev.gitを作成して/root/cgoa-rev/repoにクローンしてください。app/release.yamlにConfigMap release(namespaceは指定せず、data version: v1)をコミットしてmainへpushし、そのコミットにタグv1を付けてタグもpushしてください。そのコミットの40桁のSHAを、/root/cgoa-rev/v1.shaに1行で書いてください。

空のベアリポジトリはgit init --bareで作ります。gitデーモンが/srv/bareをそのまま公開するので、別途登録するものはありません。タグはブランチとは別にpushしないと、リモートにできません。SHAはgit rev-parseで得られます。

ブランチ・タグ・SHAを追跡する3つのアプリ

/root/cgoa-rev/apps.yamlに3つのApplicationを作成して適用してください。名前と対象ネームスペースはrev-branch・rev-tag・rev-shaで、sourceはgit://gitd.gitsrv.svc.cluster.local:9418/rev.gitのappパス、targetRevisionは順にmain、v1、v1.shaに書いた40桁のSHAです。3つとも、project defaultにし、自動同期(prune・selfHeal)とCreateNamespace=trueを有効にして、3つのアプリがすべてSyncedになることを確認してください。

3つのアプリは、targetRevisionの1行だけが違います。今は3つの名前がすべて同じコミットを指しているので、status.sync.revisionも同じである必要があります。

mainにv2を上げると、誰が追従するか

/root/cgoa-rev/repoでapp/release.yamlのversionをv2に変えてコミットし、mainへpushしてください(タグには触れません)。3つのアプリをhard refreshしたあと、各アプリのstatus.sync.revisionを/root/cgoa-rev/after-commit.jsonに{"branch": ..., "tag": ..., "sha": ...}の形で書いてください。branchは新しいコミットへ進み、tagとshaはv1のコミットに残っている必要があります。

refreshを要求する方法は、annotation argocd.argoproj.io/refresh=hardです。ファイルに書く値は推測せず、Applicationの状態から読み取ってください。

タグv1をデプロイしたのにv2が起動した

タグv1をmainの最新コミット(v2)へ強制的に移してgit push -fし、rev-tagをhard refreshしてください。rev-tagのstatus.history[].revisionを順番に/root/cgoa-rev/tag-history.jsonに{"target": "v1", "revisions": [...]}として書いてください。同じ名前v1の下に異なる2つのコミットが記録され、rev-tagネームスペースのConfigMapがv2に変わり、rev-shaはそのままv1である必要があります。

タグは名前にすぎず、コミットではありません。historyの各項目には、そのときのsource.targetRevisionと実際のrevisionが一緒に残ります。

すでにあるタグは動かせないようにする

まず、タグv1をv1.shaの元のコミットに戻してpushし、rev-tagが再びそのコミットに同期されるようにしてください。そのあと、/srv/bare/rev.git/hooks/pre-receiveに実行可能なフックを置き、すでにあるrefs/tags/*を変更または削除するpushは拒否し、新しいタグの作成とブランチの更新は許可するようにしてください。最後に、v1をもう一度移すpushを試し、拒否された出力の全体を/root/cgoa-rev/guard.txtに保存してください。

pre-receiveは、標準入力で옛SHA 새SHA 참조이름の形式の行を受け取ります(プレースホルダーは古いSHA、新しいSHA、参照名です)。新しく作る参照の古いSHAは、0が40個並んだものであり、削除する参照の新しいSHAもそうです。フックが0以外の値で終了すると、pushの全体が拒否されます。元に戻すpushは、フックを置く前に行います。

ロールバックは新しいコミットで行う

mainは今v2です。履歴を消さずに、git revertでv2のコミットを打ち消す新しいコミットを作ってpushしてください。rev-branchがその新しいコミットに同期され、ConfigMapがv1に戻っている必要があります。/root/cgoa-rev/rollback.jsonに、revert_sha(新しいmainのSHA)、deployed(rev-branchネームスペースのConfigMapのversionの値)、equals_v1_sha(新しいSHAがv1.shaと同じか、ブール値)を書いてください。

resetのあとの強制pushでも内容はv1になりますが、v2のコミットがmainの履歴から消えます。誰がいつ何を元に戻したのかが残るほうを選んでください。

自動同期のアプリのrollbackが拒否される理由

自動同期を有効にしたまま、argocd app rollback rev-branch 0 --coreを実行し、拒否のメッセージを含む出力の全体を/root/cgoa-rev/rollback-refused.txtに保存してください。coreモードはkubeconfigの現在のネームスペースからArgo CDの設定を探すので、/root/cgoa-rev/kubeconfigにk3s kubeconfigをコピーして現在のネームスペースをargocdに変え、KUBECONFIGで指定して実行してください。rollbackを成功させようとして自動同期を切らないでください。終わったとき、rev-branchは依然としてmainの最新コミットにSyncedである必要があります。

Argo CDは、自動同期が有効なアプリを古いrevisionに戻しません。戻しても、次の調整でGitに引き戻されるからです。kubectl config set-context --current --namespace=...は、指定したkubeconfigファイルだけを変更します。

変わる名前と変わらない名前の報告

/root/cgoa-rev/report.jsonに、mutable_refs(動いた参照の種類2つをbranch・tagとして、配列)、immutable_ref(commit-sha)、pinned_revision(今のrev-shaのstatus.sync.revision)、tag_guard(pre-receive)、rollback(git-revert)を書いてください。採点ツールは、ファイルの値とともに3つのアプリの現在の状態を、もう一度確認します。

前のステップで実際に動いたものが何だったのか、最後まで動かなかったアプリが何だったのかを思い出してください。pinned_revisionは状態から読み取ります。