デプロイとリリースの違い:新しいコードがなぜユーザーに届かないのか
デプロイが完了しました。パイプラインはグリーン、アーティファクトは本番サーバーに配置され、チームは完了と叫びたい気分です。しかし、アプリケーションを確認すると、ユーザーにはまだ古いバージョンが表示されています。ユーザー側では何も変わっていません。
この瞬間、多くのチームが混乱します。すべて正しく行ったはずです。コードはそこにあります。サーバーは新しいバイナリを実行しています。では、なぜユーザーにアップデートが届かないのでしょうか?
答えはシンプルです。デプロイとリリースは別物です。そして、その違いを理解することで、ソフトウェアの出荷に対する考え方が変わります。
デプロイとはコードがサーバー上にあること
デプロイとは、アプリケーションの新しいバージョンをサーバーに配置する行為です。アーティファクトがレジストリから取り出され、ターゲット環境にコピーされ、サーバーがそれを実行し始めます。本番環境にデプロイした場合、新しいバージョンは本番サーバー上に物理的に存在します。
それだけです。デプロイは技術的なアクションです。ユーザーが新しいバージョンを実際に使えるかどうかについては、何も語っていません。
例えて言うなら、映画館のプロジェクターに新しい映画をセットしたようなものです。フィルムは通され、リールは準備完了、プロジェクターの電源は入っています。しかし、映画はまだ上映されていません。観客はまだ前の作品を見ています。
次の図は、デプロイ、リリース、カナリアリリースにおける新旧バージョンのタイムラインとトラフィックフローを示しています。
リリースとはユーザーが新しいバージョンを利用できること
リリースとは、新しいバージョンが実際のユーザートラフィックを処理し始める瞬間です。トラフィックとは、ユーザーからアプリケーションへのリクエストを指します。トラフィックがまだ旧バージョンに送られている限り、ユーザーはサーバー上にある新しいコードの影響を一切受けません。
新しいバージョンはそこにあります。実行されています。しかし、アイドル状態です。誰もアクセスしていません。
リリースとは、「近日公開」の看板を「現在上映中」に切り替えるようなものです。プロジェクターが回り始め、観客は新しい映画を目にします。
なぜわざわざ分けるのか?
デプロイとリリースを同時に行えるのであれば、なぜ分ける必要があるのでしょうか?分けることで、コントロールが効くようになるからです。
デプロイとリリースが同じアクションである場合、すべてのデプロイがギャンブルになります。コードをプッシュすると、ユーザーがすぐにそれを目にし、何かが壊れた場合、全員が問題を経験します。「そこに置いた」と「ユーザーが見る」の間にバッファがありません。
両者を分けると、猶予期間が生まれます。以下のことが可能になります。
- 新しいバージョンをデプロイして、そのままにしておく。
- ユーザーに影響を与えずに、その動作を監視する。
- 正しく起動し、データベースに接続し、内部リクエストを処理できることを確認する。
- ユーザーが気付く前に問題を修正する。
この猶予期間がセーフティネットです。これにより、デプロイはハイリスクなイベントから日常的な作業へと変わります。
デプロイとリリースを分離する方法
最もシンプルな方法は、ロードバランサーまたはリバースプロキシを使用することです。仕組みは以下の通りです。
- 新しいバージョンを旧バージョンと並行してサーバーにデプロイします。
- ロードバランサーを設定し、すべてのユーザートラフィックを旧バージョンに送信します。
- 新しいバージョンは起動しますが、外部からのリクエストはゼロです。
- 準備ができたら、ロードバランサーの設定を更新し、トラフィックを新しいバージョンにルーティングします。
この設定変更がリリースです。デプロイの数秒後に行うことも、数時間後に行うこともできます。タイミングはあなた次第です。
以下は、架空のロードバランサーCLIを使用してトラフィックを移行する実践的な例です。
# 新しいバージョンを旧バージョンと並行してデプロイ
# (両方がすでにサーバー上で実行されていることを前提とします)
# 現在のトラフィック配分を確認
trafficctl get-weight myapp
# 出力: myapp-v1: 100%, myapp-v2: 0%
# トラフィックの10%を新しいバージョンに移行(カナリア)
trafficctl set-weight myapp-v2 10%
# 監視後、すべてのトラフィックを新しいバージョンに移行
trafficctl set-weight myapp-v2 100%
# 必要に応じてロールバック:即座にすべてのトラフィックを旧バージョンに戻す
trafficctl set-weight myapp-v1 100%
カナリアリリース:段階的なロールアウト
より洗練されたアプローチがカナリアリリースです。一度にすべてのトラフィックを切り替えるのではなく、まず新しいバージョンにユーザーのごく一部を送ります。
例えば、1000人のユーザーがいる場合、まず50人を新しいバージョンにルーティングします。5分後に問題がなければ、200人に増やします。次に500人。そして全員にします。
このアプローチは爆発半径を制限します。新しいバージョンにバグがあった場合、1000人ではなく50人だけが影響を受けます。被害を最小限に抑え、早期に問題を発見できます。
カナリアリリースはフィーチャーフラグとも相性が良いです。フラグの背後に隠されたコードをデプロイし、小規模なグループに対して有効にし、結果を観察し、徐々に対象を拡大できます。
再デプロイ不要のロールバック
分離により、ロールバックも容易になります。悪いバージョンをリリースした場合、旧バージョンを再デプロイする必要はありません。旧バージョンはまだサーバー上にあり、実行中で、トラフィックを処理できます。
ロードバランサーの設定を戻すだけです。トラフィックは旧バージョンに移行します。ユーザーは数秒以内に安定した状態に戻ります。
これに対し、デプロイとリリースが一体化したアプローチでは、ロールバックに以下が必要です。
- 旧アーティファクトを見つける。
- 再デプロイする。
- サーバーの再起動を待つ。
- ロールバック自体が問題を引き起こさないことを願う。
このプロセスは、良くても数分かかり、それ以上かかることもよくあります。その間、ユーザーは壊れたアプリケーションにアクセスし続けます。
誰がリリースのタイミングを決めるのか?
デプロイは技術的な判断です。CI/CDパイプラインで自動化できます。しかし、リリースには多くの場合、プロダクトやビジネスのステークホルダーが関与します。
プロダクトチームは、その機能がユーザーにとって準備ができているかどうかを把握しています。まず社内でテストしたい、A/Bテストを実行したい、マーケティング上の理由でリリースを遅らせたい、といった場合があります。彼らはデプロイパイプラインでは理解できない方法で、ユーザーへの影響を理解しています。
これは、すべてのリリースにミーティングが必要だという意味ではありません。定期的なアップデートであれば、短い観察期間の後にリリースを自動化できます。しかし、重要な変更については、ビジネスコンテキストを理解している人がリリースの判断に関与すべきです。
次のリリースのための実践的チェックリスト
新しいバージョンをユーザーにリリースする前に、以下のチェックを実行してください。
- 新しいバージョンは、エラーなく本番環境で少なくとも数分間実行されていますか?
- ログは正常な動作を示していますか?
- 必要に応じて、トラフィックを旧バージョンに戻す方法はありますか?
- プロダクトの観点から、機能の準備ができていることを誰かが確認しましたか?
- リリースの時間帯は、ユーザーへの影響を最小限に抑えるように選ばれていますか?
- リリース後に問題が発生した場合、誰に連絡すればよいか分かっていますか?
このリストは意図的に短くしています。リリースプロセスを複雑にしすぎると、ステップが省略されます。シンプルに保ち、毎回実際に実行しましょう。
本当に重要なこと
デプロイとリリースは同じではありません。デプロイはサーバーにコードを配置することです。リリースはユーザーがそれに触れることを許可することです。これらを別々のアクションとして扱うことで、リスク、ロールバックの速度、ユーザー体験をコントロールできるようになります。
次回、パイプラインがグリーンになったら、自問してみてください。デプロイしただけですか?それとも実際にリリースしましたか?その答えが、ユーザーがアップデートを受け取っているのか、それともまだ映画の開始を待っているのかを決定します。