Config Sync を使用した安全なロールアウト

このドキュメントでは、クラスタ運用者とプラットフォーム管理者が Config Sync を使用して複数の環境で変更を安全にロールアウトする方法について説明します。このアプローチを使用すると、すべての環境に対して同時に影響するエラーを回避できます。

Config Sync は、Git リポジトリに保存されたファイルを使用して、単一のクラスタ、マルチテナント クラスタ、マルチクラスタの Kubernetes 構成を管理できるようにします。

構成は、たとえば次のようなものを表すことができます。

Config Sync は、Google Kubernetes Engine(GKE)Enterprise エディション上に構築するプラットフォーム(セキュリティ エージェント、モニタリング エージェント、証明書マネージャーなど)を実行するために必要な構成、ポリシー、ワークロードをデプロイするのに最適です。

Config Sync を使用してユーザー向けのアプリケーションをデプロイすることはできますが、前述の管理ワークロードのリリース ライフサイクルにリリースのライフサイクルをリンクさせることはおすすめしません。代わりに、継続的デプロイツールなど、アプリケーションのデプロイ専用のツールを使用することをおすすめします。そうすることで、アプリケーション チームはリリース スケジュールを自分で管理できます。

Config Sync は、多数の要素を管理できる強力なプロダクトであり、大きな影響が発生するエラーを避けるためのガードレールが必要になります。このドキュメントでは、ガードレールを作成するいくつかの方法について説明します。最初のセクションでは段階的なロールアウトについて説明します。2 番目のセクションではテストと検証を取り上げます。3 番目のセクションでは、デプロイをモニタリングする方法について説明します。

Config Sync を使用した段階的なロールアウトの実装

GKE Enterprise ユーザーによく見られるマルチクラスタ環境では、すべてのクラスタに対して構成変更を同時適用することは推奨されません。クラスタごとの段階的なロールアウトでは、エラーの影響が小さくなるため、はるかに安全です。

Config Sync を使用して段階的なロールアウトを実装するには、いくつかの方法があります。

  • Git commit またはタグを使用して、クラスタに手動で変更を適用する。
  • Git ブランチを使用して、変更がマージされたときに変更を自動的に適用する。クラスタのグループごとに異なるブランチを使用できます。
  • ClusterSelector オブジェクトと NamespaceSelector オブジェクトを使用して、変更をクラスタまたは名前空間のサブグループに選択的に適用する。

段階的ロールアウトのすべての方法にはメリットとデメリットがあります。次の表に、同時使用が可能な方法を示します。

互換性 Git commit またはタグ Git ブランチ クラスタ セレクタ 名前空間セレクタ
Git commit またはタグ 同時使用不可 互換 互換
Git ブランチ 同時使用不可 互換 互換
クラスタ セレクタ 互換 互換 互換
名前空間セレクタ 互換 互換 互換

次のディシジョン ツリーを使用すると、どのような場合にどの段階的ロールアウト方法を使用すべきかを決定できます。

ロールアウト方法のディシジョン ツリー

Git commit またはタグを使用する

他の段階的なロールアウト方法と比較すると、Git commit またはタグを使用する方法は最も制御しやすく、最も安全な方法です。Google Cloud コンソールの Config Sync ページを使用して、複数のクラスタを同時に更新できます。この方法は、変更をクラスタごとに 1 つずつ適用し、変更のタイミングを厳密に制御する場合に使用します。

この方法では、各クラスタをリポジトリの特定のバージョン(commit またはタグ)に「固定」します。この方法は、Git commit をコンテナ イメージ タグとして使用するに似ています。この方法を実装するには、RootSync または RepoSync カスタム リソースspec.git.revision フィールドに commit、タグ、またはハッシュを指定します。

Kustomize などのツールを使用して RootSync または RepoSync カスタム リソースを管理すると、ロールアウトに必要な手作業を削減できます。このようなツールを使用すると、revision パラメータを 1 か所で変更するだけで、新しい RootSync または RepoSync カスタム リソースを、選択した順序とペースで複数のクラスタに選択的に適用できます。

また、Google Cloud コンソールを使用して、同じフリートに属する複数のクラスタの revision パラメータを同時に更新することもできます。ただし、構成を更新する自動システムを使用している場合は、Google Cloud コンソールを使用して構成を変更することはおすすめしません。

たとえば、次の RootSync の定義は、1.2.3 タグを使用するように Config Sync を構成します。

apiVersion: configsync.gke.io/v1
kind: RootSync
metadata:
  name: root-sync
  namespace: config-sync-system
spec:
  sourceType: git
  sourceFormat: unstructured
  git:
    repo: git@example.com:gke/config-sync.git
    revision: 1.2.3
    auth: ssh

この構成をクラスタに適用すると、Config Sync は example.com:gke/config-sync.git リポジトリの 1.2.3 タグを使用します。

クラスタを更新するには、spec.git.revision フィールドをクラスタの新しい値に変更します。これにより、更新するクラスタと更新のタイミングを定義できます。変更をロールバックする必要がある場合は、spec.git.revision フィールドを元の値に戻します。

次の図に、この方法のロールアウト プロセスを示します。まず Config Sync リポジトリに変更を commit してから、すべてのクラスタで RootSync 定義を更新します。

Git commit とタグのロールアウト プロセス

今回の変更に伴い、次のご対応をおすすめします。

  • タグではなく Git commit ID を使用します。Git の機能によって、Git commit ID が絶対に変更されないことが保証されます。たとえば、git push --force は、Config Sync が使用している commit を変更できません。このアプローチは、監査目的、あるいはログでどの commit を使用しているか追跡する際に有効です。また、タグとは異なり、ID を commit する特別な手順はありません。
  • Git commit ID の代わりに Git タグを使用する場合は、保護をサポートする Git ソリューションを使用してればタグを保護できます。
  • 複数のクラスタを同時に更新する場合は、Google Cloud コンソールで更新できます。複数のクラスタを一度に更新するには、それらが同じフリートの一部であること(かつ同じプロジェクトに属していること)が必要です。

Git ブランチを使用する

Git リポジトリにマージされた変更を即座にクラスタに適用する必要がある場合は、commit やタグではなく Git ブランチを使用するように Config Sync を構成します。この方法では、Git リポジトリに有効期間が長い複数のブランチを作成し、別のクラスタで Config Sync を構成してその構成を異なるブランチから読み取るようにします。

たとえば、単純なパターンは 2 つのブランチで構成されます。

  • 非本番環境クラスタ用の staging ブランチ。
  • 本番環境クラスタ用の main ブランチ。

非本番環境クラスタの場合、spec.git.branch フィールドを staging に設定して RootSync または RepoSync オブジェクトを作成します。本番環境クラスタの場合、spec.git.branch パラメータを main に設定して RootSync または RepoSync オブジェクトを作成します。

たとえば、次の RootSync の定義では、main ブランチを使用するように Config Sync を構成します。

apiVersion: configsync.gke.io/v1
kind: RootSync
metadata:
  name: root-sync
  namespace: config-sync-system
spec:
  git:
    repo: git@example.com:gke/config-sync.git
    branch: main
    auth: ssh

次の図に、この方法のロールアウト プロセスを示します。

Git ブランチのロールアウト プロセス

このパターンは、3 つ以上のブランチを使用するか、環境以外のものにマッピングされているブランチを使用して、特定のニーズに適応できます。変更をロールバックする必要がある場合は、git revert コマンドを使用して、前の commit から元に戻すような commit を同じブランチに新しく作成します。

次の対応をおすすめします。

  • 複数のクラスタを扱う場合、少なくとも 2 つの Git ブランチを使用して、本番環境クラスタと非本番環境クラスタを区別できるようにします。
  • ほとんどの Git ソリューションでは、保護されたブランチ機能を使用して、ブランチの削除や未確認の変更を防ぐことができます。詳細については、GitHubGitLabBitbucket に関するドキュメントをご覧ください。

ClusterSelector オブジェクトと NamespaceSelector オブジェクトを使用する

Git ブランチは、複数のクラスタに段階的に変更をロールアウトして、最終的にすべてのクラスタが同じポリシーを持つようにする場合に適しています。ただし、変更をクラスタのサブセットまたは Namespace のサブセットにのみロールアウトする場合は、ClusterSelector オブジェクトと NamespaceSelector オブジェクトを使用します。これらのオブジェクトの目的は類似しており、特定のラベルを持つクラスタまたは Namespace にのみオブジェクトを適用します。

例:

  • ClusterSelector オブジェクトを使用すると、コンプライアンスの運用がさまざまに異なるクラスタが置かれている国に応じて、それぞれ異なるポリシーを適用できます。
  • NamespaceSelector オブジェクトを使用すると、内部チームの名前空間と外部コントラクタの名前空間に異なるポリシーを適用できます。

ClusterSelector オブジェクトと NamespaceSelector オブジェクトを使用して、次のような高度なテストとリリースのメソッドを実装することもできます。

  • ポリシーのカナリア リリース。ポリシーの影響を調査するために長時間にわたって、少数のクラスタと名前空間に新しいポリシーをデプロイします。
  • A/B テスト。同じポリシーの異なるバージョンを異なるクラスタにデプロイしてポリシー バージョンの影響を調査し、デプロイ先に最適なものを選択します。

たとえば、複数の本番環境クラスタがある組織について考えてみましょう。プラットフォーム チームは、Cluster オブジェクトと ClusterSelector オブジェクトを使用して、canary-prodprod という 2 つの本番環境クラスタのカテゴリをすでに作成しています(ClusterSelectors を使用するを参照)。

プラットフォーム チームは、Policy Controller を使用してポリシーをロールアウトし、各 Namespace にチームラベルのプレゼンスを適用して Namespace がどのチームに属しているかを識別したいと考えています。このポリシーの 1 つのバージョンがすでにドライラン モードでロールアウトされており、今これを少数のクラスタに適用したいと考えています。ClusterSelector オブジェクトを使用して 2 つの異なる K8sRequiredLabels リソースを作成し、異なるクラスタに適用します。

  • K8sRequiredLabels リソースを、enforcementAction パラメータを dryrun に設定した prod タイプのクラスタに適用します。

    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sRequiredLabels
    metadata:
      name: ns-must-have-team
      annotations:
        configmanagement.gke.io/cluster-selector: prod
    Spec:
      enforcementAction: dryrun
      match:
        kinds:
          - apiGroups: [""]
            kinds: ["Namespace"]
      parameters:
        labels:
          - key: "team"
    
  • K8sRequiredLabels リソースを、enforcementAction パラメータがない canary-prod タイプのクラスタに適用します。つまり、これでポリシーが実際に適用されます。

    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: K8sRequiredLabels
    metadata:
      name: ns-must-have-team
      annotations:
        configmanagement.gke.io/cluster-selector: canary-prod
    spec:
      match:
        kinds:
          - apiGroups: [""]
        kinds: ["Namespace"]
      parameters:
        labels:
          - key: "team"
    

configmanagement.gke.io/cluster-selector アノテーションを使用すると、チームは canary-prod タイプのクラスタに対してのみポリシーを適用し、予期せぬ副作用が本番環境の全体に波及しないようにできます。Policy Controller のドライラン機能の詳細については、制約の作成をご覧ください。

今回の変更に伴い、次のご対応をおすすめします。

  • ClusterSelector オブジェクトと NamespaceSelector オブジェクトは、無期限または長期間にわたって、構成の変更をクラスタまたは名前空間のサブセットにのみ適用する必要がある場合に使用します。
  • セレクタを使用して変更をロールアウトする場合は、慎重に行ってください。Git commit を使用すると、変更はクラスタごとにロールアウトされるため、エラーの影響は一度に 1 つのクラスタに限定されます。ただし、Git ブランチを使用する場合、そのブランチを使用するすべてのクラスタにエラーの影響が及ぶ可能性があります。セレクタを使用すると、一度にすべてのクラスタにエラーの影響が及ぶ可能性があります。

レビュー、テスト、検証の実装

Config Sync の利点の一つは、Kubernetes リソース、クラウド リソース、ポリシーなどをすべて宣言型で管理できることです。つまり、ソース制御管理システムのファイルはリソース(Config Sync の場合は Git ファイル)を表します。この特性により、アプリケーションのソースコードにすでに使用している開発ワークフロー(レビューと自動テスト)を実装できます。

レビューを実装する

Config Sync は Git に基づいているため、任意の Git ソリューションを使用して Config Sync リポジトリをホストできます。Git ソリューションには、Config Sync リポジトリに加えられた変更を確認できるコードレビュー機能が含まれているはずです。

リポジトリへの変更を確認するおすすめの方法は、次のように通常のコードレビューと同じです。

Config Sync コードベースは影響を受けやすいため、Git ソリューションでは可能であれば次の構成を行うことをおすすめします。

これらのさまざまな機能を使用することで、コードベースに対する各変更リクエストの承認を適用できます。たとえば、各変更について、少なくともプラットフォーム チームのメンバー(複数のクラスタを管理するユーザー)とセキュリティ チームのメンバー(セキュリティ ポリシーの定義と実装を担当するメンバー)によって承認されるように構成できます。

次の対応をおすすめします。

  • リポジトリにピアレビューを適用し、クラスタが使用する Git ブランチを保護する。

自動テストを実装する

コードベースの作業で、継続的インテグレーションを実装するのは、一般的なベスト プラクティスです。つまり、変更リクエストが作成または更新されたときに実行する自動テストを構成します。自動テストでは、人間が変更リクエストを確認する前に多くのエラーを検出できます。これにより、デベロッパーのフィードバック ループが強化されます。Config Sync リポジトリでも、同じツールで同じアイデアを実装できます。

たとえば、最初のうちは新しい変更に対して自動的に nomos vet コマンドを実行することをおすすめします。このコマンドは、Config Sync リポジトリの構文が有効かどうかを検証します。このテストは、構成ファイルの検証チュートリアルに沿って Cloud Build を使用して実装できます。Cloud Build は次のオプションと統合できます。

  • ビルドトリガーを使用した Bitbucket。
  • GitHub(Google Cloud Build GitHub アプリケーションを使用して)。ビルドトリガーも GitHub で使用できますが、GitHub アプリケーションが推奨の統合方法です。

構成ファイルの検証チュートリアルに示されているように、テストはコンテナ イメージを使用して行われます。したがって、Cloud Build だけでなく、コンテナを実行する任意の継続的インテグレーション ソリューションでテストを実装できます。

フィードバック ループをさらに強化する場合は、nomos vet コマンドを Git pre-commit フックとして実行するようユーザーに求める方法があります。注意すべき点としては、一部のユーザーは Config Sync によって管理されている Kubernetes クラスタにアクセスできない可能性があり、使用しているワークステーションから完全な検証を実行できない場合があることです。nomos vet --clusters "" コマンドを実行して、検証をセマンティック チェックとシンタックス チェックに制限します。

次の対応をおすすめします。

  • 継続的インテグレーション パイプラインにテストを実装します。
  • 提案されたすべての変更で少なくとも nomos vet コマンドを実行します。

ロールアウトをモニタリングする

このドキュメントでカバーするすべてのガードレールが実装されても、エラーが発生することがあります。一般的なエラーには、次の 2 つの種類があります。

  • Config Sync 自体に問題を引き起こさないものの、ワークロードの正常な動作を妨げるエラー、たとえば制約が多すぎてワークロード コンポーネントによる通信を妨げる NetworkPolicy などによるエラー。
  • 無効な Kubernetes マニフェストや、アドミッション コントローラによって拒否されたオブジェクトなど、Config Sync がクラスタに変更を適用できないエラー。上記のエラーのほとんどは、前述の方法で解決できます。

上記の最初の項目で説明したエラーの検出には、各ワークロードの状態を理解する必要があるため、Config Sync のレベルでは検出はほぼ不可能です。そのため、このようなエラーは、アプリケーションが正常に機能していないときにアラートを送信する、既存のモニタリング システムによる検出が最適です。

上記の 2 番目の箇条書きに記述されたエラー(すべてのガードレールを実装している場合にはまれなエラー)を検出するには、特定の設定が必要です。デフォルトでは、Config Sync のログにエラーが記録されます(デフォルトでは Cloud Logging に表示されます)。エラーは Config Sync の Google Cloud コンソール ページにも表示されます。常にモニタリングしているわけではないので、通常はログやコンソールだけではエラーを十分に検出できません。エラー検出を自動化する最も簡単な方法は、nomos status コマンドを実行することです。このコマンドは、クラスタにエラーが含まれているかどうかを調べます。

エラー通知用の自動アラートを使用して、より高度なソリューションを設定することもできます。Config Sync は、Prometheus 形式で指標を公開します。詳細については、Config Sync のモニタリングをご覧ください。

モニタリング システムで Config Sync の指標が得られれば、gkeconfig_monitor_errors 指標が 0 より大きい場合に通知するアラートを作成します。詳細については、Cloud Monitoring のアラート ポリシーの管理、Prometheus のアラートルールをご覧ください。

Config Sync を使用した安全なロールアウトのメカニズムの概要

次の表は、このドキュメントで説明したさまざまなメカニズムをまとめたものです。これらのメカニズムはいずれも独占的なものではありません。これらのメカニズムの一部または全部を、異なる目的に使用することもできます。

機構 最適な用途 不得意分野 使用例
Git commit ID とタグ 特定の Git commit ID またはタグを使用して、適用するクラスタの変更を正確に制御する。 クラスタ間の長期間にわたる違いに、Git commit ID またはタグを使用しない。クラスタ セレクタを使用する。 すべてのクラスタが 12345 Git commit を適用するよう構成されている。テストする新しい commit abcdef を使用して変更する。単一のクラスタの構成を変更して、この新しい commit を使用した変更を検証する。
Git ブランチ 複数の Git ブランチを使用して、同じ変更を複数の環境に段階的にロールアウトする。 クラスタ間での長期間にわたる違いに、複数の Git ブランチを使用しない。ブランチが著しく分岐し、その後のマージが難しくなる。 まず、ステージング ブランチで変更をマージすると、ステージング クラスタが変更を受け取る。
次に、マスター ブランチで変更をマージすると、本番環境クラスタが変更を受け取る。
クラスタ セレクタと名前空間セレクタ クラスタと名前空間の間の長期間にわたる違いに、セレクタを使用する。 複数の環境にまたがる段階的なロールアウトの場合はセレクタを使用しない。変更をステージングで最初にテストし、本番環境にデプロイする場合は、別の Git ブランチを使用する。 アプリケーション チームが開発クラスタには完全アクセス権を必要する一方で本番環境クラスタには読み取り専用アクセス権でよい場合、ClusterSelector オブジェクトを使用して、関連するクラスタにのみ適切な RBAC ポリシーを適用する。
ピアレビュー ピアレビューを使用して、関連するチームが変更を承認していることを確認する。 審査担当者は、すべてのエラー(特に構文エラーなど)を検出できない。 組織は、複数のシステムに影響する構成変更の確認をセキュリティ チームに委任する。セキュリティ チームのメンバーが変更を確認するようにする。
継続的インテグレーション パイプラインの自動テスト 自動テストを使用して、提案された変更のエラーを見つける。 自動テストは審査担当者を完全に置き換えることはできない。両方を使用する。 提案されたすべての変更に対して nomos vet コマンドを実行すると、リポジトリが有効な Config Sync 構成であることを確認できる。
同期エラーを監視する Config Sync によりクラスタに実際に変更が適用されたことを確認する。 同期エラーが発生するのは、Config Sync が無効なリポジトリを適用しようとしたときや、Kubernetes API サーバーが一部のオブジェクトを拒否した場合に限られる。 ユーザーがすべてのテストとレビューをバイパスし、Config Sync リポジトリに無効な変更を commit する。この変更はクラスタに適用できない。同期エラーをモニタリングすると、エラー発生時に通知を受ける。

ロールアウト戦略の例

このセクションでは、この記事の後半で紹介したコンセプトを使用して、組織内のすべてのクラスタにまたがるエンドツーエンドのロールアウト戦略を作成できるようにします。この戦略では、開発、ステージング、本番環境用に別々のフリートがあることを前提としています(フリート例 1 - アプローチ 1 を参照)。

このシナリオでは、特定の Git commit を使用して Git リポジトリと同期するように各クラスタを構成します。特定のフリートに対する変更をデプロイするには、次の 4 つのステップを実行します。

  1. 最初に新しい commit を使用するようにフリート内の 1 つの(「カナリア」)クラスタを更新します。
  2. テストを実行し、ロールアウトをモニタリングすることで、すべてが想定どおりに動作することを検証します。
  3. そのフリートの残りのクラスタをアップデートします。
  4. すべてが想定どおりに動作することを再度検証します。

変更をすべてのクラスタにデプロイするには、フリートごとにこのプロセスを繰り返します。正確に言えばこの方法は、どのブランチのどの Git commit でも適用できます。ただし、次のプロセスを使用して、審査プロセスの早い段階で問題を特定することをおすすめします。

  1. Config Sync Git リポジトリで変更リクエストを開いたら、その変更をいずれかの開発クラスタにデプロイします。
  2. 変更リクエストが受け入れられ、メインのブランチでマージされた場合は、前述のようにすべてのフリート全体で完全なデプロイを実行します。

変更によっては特定のフリートのみを対象とすることもありますが、最終的にはすべての変更をすべてのフリートにデプロイすることをおすすめします。この戦略を採用すると、どのフリートをどの commit と同期させる必要があるか追跡するという問題がなくなります。以前のフリートでは適切なテストが不可能なため、本番環境のフリートのみをターゲットとする変更には特に注意してください。たとえば、カナリア クラスタへのデプロイと残りのクラスタへのデプロイとの間で問題が表面化するまでにより多くの時間がかかるということです。

まとめると、エンドツーエンドのデプロイ全体は次のようになります。

  1. 変更リクエストを開きます。
  2. 自動テストと検証が実行され、個別審査が行われます。
  3. ジョブを手動でトリガーして、開発フリートのカナリア クラスタに変更をデプロイします。このクラスタで自動エンドツーエンド テストが実行されます。
  4. 何も問題ない場合は、メインブランチで変更リクエストをマージします。
  5. マージによって自動ジョブがトリガーされ、開発フリートのカナリア クラスタに新しいメインブランチの先端の commit がデプロイされます。自動化されたエンドツーエンド テストがこのクラスタ内で実行されます(ほぼ同時に作成され、マージされた 2 つの変更リクエストの不適合の可能性を検知)。
  6. 次のジョブが順次実行されます(手動でトリガーするか、所定の時間が経過してからユーザーの不具合のレポートを可能にします)。
    1. 開発フリートのすべてのクラスタにデプロイします。
    2. 開発フリートのクラスタ内でテストと検証を実行します。
    3. ステージング フリートのカナリア クラスタにデプロイします。
    4. ステージング フリートのカナリア クラスタでテストと検証を行います。
    5. ステージング フリートのすべてのクラスタにデプロイします。
    6. ステージング フリートのクラスタでテストと検証を実行します。
    7. 本番環境フリートのカナリア クラスタにデプロイします。
    8. 本番環境フリートのカナリア クラスタでテストと検証を行います。
    9. 本番環境フリートのすべてのクラスタにデプロイします。
    10. 本番環境フリートのクラスタでテストと検証を行います。

完全なロールアウト プロセス。

次のステップ