プラットフォーム、パイプライン、デプロイ戦略を一つのシステムとして設計する

チームの役割分担は明確になっています。誰が何を構築し、誰が変更をレビューし、誰が本番インシデントに対処するかは決まっています。しかし、「実際にこのアプリケーションはどこで動かすのか?」とか「コードはプルリクエストからどうやって実行中のサービスになるのか?」と聞かれると、答えがあいまいになります。

ここで、ほとんどのデリバリー施策は頓挫します。チームはツールを選び、YAMLファイルを書き、ブルーグリーンとカナリアデプロイのどちらにするかを決め始めますが、インフラストラクチャ、自動化、リリースの仕組みの間の関連性を考慮していません。その結果、プラットフォームがパイプラインと対立し、デプロイ戦略がその両方に逆行する、断片化されたシステムが生まれます。

なぜプラットフォームエンジニアリングが最初に来るのか

新しいマイクロサービスをデプロイする必要があるチームを想像してみてください。共有プラットフォームがない場合、彼らは手動で仮想マシンをプロビジョニングし、手作業で依存関係をインストールし、チケットシステムを通じてネットワークを設定します。別のチームは同じことを別の方法で行います。6か月後、ある環境はUbuntu 20.04を使用し、別の環境は22.04を使用しています。あるチームはログをローカルに保存し、別のチームは中央のサービスにストリーミングします。インシデントが発生したとき、どの環境が本番環境のように動作するのか誰も知りません。

プラットフォームエンジニアリングは、一貫した基盤を提供することでこの問題を解決します。それは、「チームが毎回ゼロからすべてを再構築することなく、どのようにして環境を取得するのか」という問いに答えます。プラットフォームは、標準化されたリソーステンプレートを持つKubernetesクラスター、すべてのチームが使用する一連のTerraformモジュール、またはインフラストラクチャの詳細を完全に抽象化するマネージドサービスレイヤーである可能性があります。

鍵となるのは一貫性です。すべてのチームが同じ基盤にデプロイする場合、環境間の差異は縮小します。ステージング環境は、本番環境と同じプラットフォーム上で動作するため、本番環境と同様に動作します。テストで現れる問題は、本番環境でも同じ問題として現れます。構成のずれによって引き起こされる新しい問題ではありません。

共有Terraformモジュールにより、この一貫性が再現可能になります。

# modules/team-namespace/main.tf
resource "kubernetes_namespace" "team" {
  metadata {
    name = var.team_name
    labels = {
      team    = var.team_name
      env     = var.environment
      managed = "platform"
    }
  }
}

resource "kubernetes_resource_quota" "limits" {
  metadata {
    name      = "${var.team_name}-quota"
    namespace = kubernetes_namespace.team.metadata[0].name
  }
  spec {
    hard = {
      pods                = var.max_pods
      requests.cpu        = var.max_cpu
      requests.memory     = var.max_memory
      limits.cpu          = var.max_cpu
      limits.memory       = var.max_memory
      persistentvolumeclaims = var.max_pvcs
    }
  }
}

すべてのチームは独自の変数でこのモジュールを呼び出しますが、基盤となるリソース定義は同一のままです。

デリバリーの背骨としてのパイプライン

CI/CDパイプラインは、単なる自動化された一連のステップではありません。それは、開発者によって書かれたコードを、ユーザーがアプリケーションと対話する環境に接続する経路です。パイプラインのすべてのステージ(ビルド、単体テスト、統合テスト、セキュリティスキャン、デプロイ)は、変更をユーザーに届けるバリューストリームにおける一歩を表しています。

パイプラインがなければ、これらのステップは手動で行われます。誰かが自分のラップトップでアーティファクトをビルドします。別の誰かがそれをサーバーにコピーします。さらに別の人が手動でテストを実行します。手動のステップごとに遅延とリスクが生じます。依存関係の忘れ、異なるオペレーティングシステムのバージョン、またはテストのスキップは、デプロイ後にのみ現れる障害を引き起こす可能性があります。

適切に設計されたパイプラインは、すべての変更に対して同じチェックを強制します。すべてのプルリクエストは同じビルドプロセスをトリガーし、同じテストを実行し、同じ検証ゲートを通過します。チームは、グリーンパイプラインが、その変更が重要なすべてのチェックに合格したことを意味すると信頼できます。この信頼こそが、頻繁なデプロイを安全にするものです。

デプロイ戦略はテクノロジーではなくユーザーに関するもの

デプロイ戦略について話すとき、人々はしばしば技術的なパターンに焦点を当てます:ブルーグリーン、カナリア、ローリングアップデート、フィーチャーフラグ。これらは実装の詳細です。本当の問いは、「ユーザーを中断させることなく、どのように変更を届けるか」です。

答えはアプリケーションの特性に依存します。何百万ものユーザーがいる公開Webサービスの場合、トラフィックの1%を新しいバージョンにルーティングし、エラーレートを監視しながら徐々に割合を増やすカナリアリリースが必要になるかもしれません。20人しか使用しない内部ツールの場合、インスタンスを1つずつ置き換える単純なローリングアップデートで十分かもしれません。

データベースのデプロイは、さらに別の複雑さをもたらします。カラムを追加するスキーママイグレーションは、古いコードと一緒に実行しても安全です。カラムの名前を変更したり、その型を変更するマイグレーションは、アプリケーションバージョン間の慎重な調整を必要とします。デプロイ戦略は、アプリケーションコードだけでなく、これらの制約も考慮する必要があります。

ロールバック機能も戦略の一部です。何か問題が発生した場合、どのくらいの速さで以前のバージョンに戻れますか?アプリケーションをデータベースとは独立してロールバックできますか?プラットフォームは即時ロールバックをサポートしていますか、それとも再ビルドと再デプロイが必要ですか?これらの質問は、最初の本番デプロイの前に、インシデント発生時ではなく、回答されるべきです。

3つのレイヤーは一緒に設計されなければならない

プラットフォーム、パイプライン、デプロイ戦略は独立した選択肢ではありません。それらは、各レイヤーが互いに制約し、可能にするシステムを形成します。

以下の図は、各レイヤーがどのように互いに依存し、制約するかを示しています。

flowchart TD DS["デプロイ戦略<br>変更がユーザーに届く方法"] P["パイプライン<br>自動化されたデリバリー経路"] PL["プラットフォーム<br>一貫したインフラストラクチャ"] DS -->|必要とする| P P -->|実行される基盤| PL PL -->|可能にする| DS PL -->|制約する| P P -->|検証する| DS DS -->|要件を定義する| PL

プラットフォームは、パイプラインが何をできるかを決定します。プラットフォームがトラフィックスイッチングによるブルーグリーンデプロイをサポートしていない場合、パイプラインはその戦略を実装できません。プラットフォームにロールバックメカニズムがない場合、デプロイ戦略は高速な復旧に依存できません。

パイプラインは、デプロイ戦略がどのように実行されるかを決定します。パイプラインにステージング環境に対する統合テストを実行する検証ステージが含まれていない場合、カナリアリリースには意味のあるヘルスチェックがありません。パイプラインがデータベースマイグレーションの検証をスキップする場合、デプロイ戦略はスキーマ変更を安全に処理できません。

デプロイ戦略は、プラットフォームが何をサポートしなければならないかを決定します。戦略が即時ロールバックを必要とする場合、プラットフォームは以前のバージョンを実行し続け、トラフィックを即座に切り替える必要があります。戦略がフィーチャーフラグを使用する場合、プラットフォームは再デプロイなしでランタイム構成の変更をサポートする必要があります。

これらの3つのレイヤーが一緒に設計されると、結果として単一の一貫したユニットとして機能するデリバリーシステムが生まれます。チームは互換性のない部分をどのように連携させるかを考える必要がありません。プラットフォームはパイプラインが必要とするものを提供します。パイプラインは戦略が必要とするものを実行します。戦略はプラットフォームの能力を尊重します。

デリバリーシステムのための実践的なチェックリスト

プラットフォーム、パイプライン、デプロイ戦略を最終決定する前に、以下のチェックを実行してください。

  • すべてのチームが同じパイプラインテンプレートを使用してサービスをデプロイできますか?
  • プラットフォームはステージングと本番で同じ動作を提供しますか?
  • 手動介入なしでデプロイをロールバックできますか?
  • パイプラインには、デプロイ戦略に一致する検証ステップが含まれていますか?
  • プラットフォームは、回避策なしで選択したデプロイ戦略をサポートできますか?
  • データベースマイグレーションプロセスは、パイプラインに統合されており、別途処理されていませんか?
  • ユーザーを中断させる恐れなく、営業時間中にデプロイできますか?

いずれかの答えが「いいえ」の場合、レイヤー間にギャップがあります。システムをスケールさせる前に、そのギャップを修正してください。

まとめ

プラットフォーム、パイプライン、デプロイ戦略は、3つの別々の決定事項ではありません。それらは、一緒に設計されなければならない一つのシステムです。それらが整合しているとき、チームは頻繁にデプロイし、迅速に復旧し、すべての変更が同じ厳格な経路を通過することを信頼できます。それらが整合していないとき、すべてのデプロイは調整の問題になり、すべてのインシデントはプラットフォームが提供するものと戦略が必要とするものの間のギャップを明らかにします。プラットフォームから始め、その周りにパイプラインを構築し、両方がサポートできる戦略を選択してください。