デプロイで実際に何が起きているのか:環境へのアーティファクト配置

アプリケーションをビルドし、テストを実行し、検証済みのアーティファクトを保存しました。ここからが誰もが注目する瞬間、デプロイです。新しいバージョンが実際の環境で動き始めます。ステージングサーバーでの内部テストでも、本番環境で実際のユーザーが使う場合でも同様です。

デプロイとは、アーティファクトをターゲット環境に配置し、稼働状態にする行為です。しかし、デプロイを単なるサーバーへのファイルコピーと考えているなら、驚くことになるでしょう。デプロイの方法は、何をデプロイするかによって完全に異なります。アプリケーションコード、データベース変更、インフラ構成のそれぞれに、独自の仕組み、リスク、戦略があります。

デプロイは一律ではない

アプリケーションをデプロイするときは、稼働中のバージョンを新しいものに置き換えます。これは、新しいコンテナイメージをKubernetesに送信したり、サーバー上のバイナリファイルを交換したり、サービスを再起動したりすることを意味します。目標は単純です。古いバージョンを停止し、新しいバージョンを最小限の中断で起動することです。

データベースのデプロイは別物です。ファイルを置き換えるのではなく、スキーマを変更したりデータを変換したりするマイグレーションスクリプトを実行します。データベースは状態を保持します。ユーザーレコード、注文、設定など、変更の前後で一貫性を保つ必要があります。JARファイルを置き換えるようにデータベースを「上書き」することはできません。カラムを追加するマイグレーションは安全かもしれませんが、テーブル名を変更するものは実行中のすべてのクエリを壊す可能性があります。また、アプリケーションコードと異なり、データベースの変更は多くの場合、元に戻せないか、注意深いロールバックスクリプトが必要です。

インフラストラクチャのデプロイはさらに別のレイヤーを追加します。ここでは、クラウドプロバイダーやTerraform、Pulumi、Ansibleなどのプロビジョニングツールに設定を適用します。デプロイによって、仮想マシン、ロードバランサー、データベース、ネットワークルールなどのリソースを作成、変更、または破棄します。インフラデプロイのミスは、本番データベースを削除したり、機密データをインターネットにさらしたりする可能性があります。リスクは高く、フィードバックループはアプリケーションデプロイよりも遅くなります。

次のフローチャートは、3つのデプロイフローを比較しています。

flowchart TD subgraph App["アプリケーションデプロイ"] A1["インスタンスを置き換え"] --> A2["ローリング / Blue-Green / Canary"] A2 --> A3["最小限の中断"] end subgraph DB["データベースデプロイ"] D1["マイグレーションスクリプトを実行"] --> D2["スキーマ変更 / データ変換"] D2 --> D3["オールオアナッシング; ロールバック必須"] end subgraph Infra["インフラデプロイ"] I1["クラウドに設定を適用"] --> I2["リソースの作成/変更/削除"] I2 --> I3["フィードバックが遅い; リスクが高い"] end

アーティファクトの種類に応じた異なる戦略

アーティファクトの種類ごとに動作が異なるため、あるものに有効なデプロイ戦略が別のものには通用しない場合があります。

アプリケーションには、いくつかのよく知られた戦略があります。

例えば、ローリングアップデート用のKubernetes Deploymentマニフェストは次のようになります。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 1
  template:
    spec:
      containers:
      - name: app
        image: my-app:v2.1.0
        ports:
        - containerPort: 8080
  • ローリングアップデート: インスタンスを1つずつ置き換えます。古いバージョンから新しいバージョンへ徐々に移行します。ダウンタイムはありませんが、新旧のバージョンが一時的に共存します。
  • Blue-Green: 現在の環境(Blue)と並行して、完全に新しい環境(Green)を立ち上げます。Green環境の準備と検証が完了したら、すべてのトラフィックをGreenに切り替えます。瞬時に切り替わりますが、切り替え中はインフラコストが2倍になります。
  • Canary: 新しいバージョンを最初に一部のユーザーにのみ送信します。エラーやパフォーマンス低下を監視します。問題がなければ徐々にトラフィックを増やし、問題があれば大多数のユーザーに影響を与えずにカナリアをロールバックします。

これらの戦略が機能するのは、アプリケーションインスタンスがステートレスであるか、グレースフルにドレインできるためです。データを破損することなく、複数のバージョンを同時に実行できます。

データベースにはそのような余裕はありません。スキーマの2つのバージョンを同時に実行して、一貫した動作を期待することはできません。データベースマイグレーションのローリングアップデートは、スキーマ変更がグローバルであるため、ほとんど不可能です。すべてのクエリが同じ構造を参照します。データベースのカナリアデプロイも同様に難しいです。レプリカでマイグレーションを実行することはできますが、プライマリに昇格させた瞬間、すべてのユーザーに影響が出ます。データベースの変更は通常、オールオアナッシングです。マイグレーションを適用して検証し、問題が発生したらロールバックマイグレーションを実行します。

インフラストラクチャのデプロイはその中間に位置します。インフラでは、並列のリソースセットをプロビジョニングし、DNSやロードバランサーのターゲットを切り替えることでBlue-Greenを使用できます。しかし、インフラの変更には依存関係が伴うことがよくあります。新しいデータベースインスタンスを作成するには、それを指すアプリケーション設定も更新する必要があります。また、インフラの変更は遅くなる可能性があります。新しいサーバークラスターのプロビジョニングには数分かかる場合があり、数秒ではありません。

譲れない2つの原則

何をデプロイするか、どの戦略を選ぶかに関わらず、すべてのデプロイに適用される2つの原則があります。

第一に、デプロイは再現可能でなければなりません。 同じパイプラインを同じアーティファクトで2回実行した場合、同じ結果が得られるはずです。つまり、デプロイ時にソースから再ビルドするのではなく、すでに検証済みのアーティファクトをデプロイする必要があります。再ビルドは不確実性をもたらします。ビルドサーバーが異なるライブラリバージョンを持っていたり、ネットワークが遅かったり、コンパイラの最適化が異なったりする可能性があります。テストに合格したのとまったく同じバイナリ、コンテナイメージ、またはパッケージを使用してください。

再現可能性には、ターゲット環境が既知の状態であることも必要です。何がすでに実行されているか保証できなければ、デプロイ時に何が起こるかを予測できません。Infrastructure-as-Codeはここで役立ちます。環境の望ましい状態を定義するため、デプロイは何を期待すべきかを認識できます。

第二に、すべてのデプロイは記録されなければなりません。 何がデプロイされたか、どのバージョンか、どのコミットか、どの環境か、正確なタイムスタンプ、誰がまたは何がトリガーしたかをログに記録します。これはコンプライアンス監査のための書類ではありません。問題が発生したときの最初の防御線です。デプロイ後にユーザーがエラーを報告し始めたとき、デプロイログは何が変更されたかを正確に示します。ログがなければ、推測するしかありません。コード変更なのか?設定更新なのか?データベースマイグレーションなのか?インフラ変更なのか?適切なデプロイログがあれば、すぐに調査範囲を絞り込めます。

アーティファクトを配置してもデプロイは完了しない

よくある間違いがあります。アーティファクトが環境に配置された瞬間に、パイプラインがデプロイを成功とマークすることです。しかし、アーティファクトの配置は仕事の半分に過ぎません。新しいバージョンが実際に正しく動作していることを確認する必要があります。

デプロイ後の検証は別のステージです。サービスがヘルスチェックに応答すること、データベースマイグレーションがエラーなく完了したこと、インフラリソースが望ましい状態であることを確認します。一部のチームはスモークテスト(重要なユーザージャーニーのクイックセット)を実行して、デプロイが明らかなものを壊していないことを確認します。

検証が合格するまで、デプロイは完了していません。検証が失敗した場合、パイプラインはロールバックをトリガーするか、すぐにチームに警告する必要があります。何時間も後に誰かが問題に気付くのを待つのは、自動化の目的を損なうことになります。

次回のデプロイ前の実践的チェックリスト

デプロイボタンを押す前、またはパイプラインを実行させる前に、この短いチェックリストを確認してください。

  • アーティファクトはすべてのテストに合格したものと同じですか?(デプロイ時の再ビルドは避ける)
  • ターゲット環境は既知の状態ですか?(競合する可能性のある手動変更がないこと)
  • デプロイ戦略はアーティファクトの種類に適していますか?(アプリはローリング、データベースはマイグレーション、インフラはプロビジョニング)
  • ロールバック計画はありますか?(変更を迅速に元に戻せますか?データベースの場合、ロールバックマイグレーションスクリプトは準備されていますか?)
  • デプロイは自動的にログに記録されますか?(バージョン、コミット、タイムスタンプ、トリガー)
  • デプロイ後の検証ステップはありますか?(ヘルスチェック、スモークテスト、または監視アラート)

まとめ

デプロイは、あなたの作業が現実と向き合う瞬間です。ファイルコピー操作ではありません。パイプラインと実行中のシステムとの間の、注意深く計画された引き継ぎです。アーティファクトの種類が戦略を決定し、環境の状態がリスクを決定し、デプロイログが障害からの回復速度を決定します。コードを書くのと同じ厳格さでデプロイを扱ってください。悪いデプロイは、数週間の良い作業を数秒で台無しにする可能性があるからです。