ビルド成果物はどこへ行く?コードと本番環境の間にある欠けているピース

アプリケーションのビルドが完了しました。ビルドは成功し、テストもパスし、新しい成果物がラップトップのフォルダにできあがっています。さて、次はどうしますか?

初めてパイプラインを構築するチームの多くは、次のステップは明白だと考えます。デプロイです。しかし、ここには見えにくい問題が潜んでいます。ラップトップ上の成果物は、データセンターやクラウドリージョンにある本番サーバーにとっては無価値です。サーバーがあなたのローカルフォルダにアクセスしてファイルを取得することはできません。たとえビルドが専用のCIマシンで実行されたとしても、デプロイ先はそのマシンのローカルディスクにあるファイルにアクセスできません。

ここが、多くのパイプライン設計が最初に直面する壁です。ビルドプロセスとデプロイ先の両方がアクセスできる、成果物を置く場所が必要なのです。

共有ストレージ問題

パーティーのために料理を作り終えた場面を想像してください。料理をキッチンに置いたまま、ゲストが自分で取りに来ることを期待するでしょうか?そんなことはしません。みんなが手を伸ばせるテーブルに料理を置きます。

成果物も同じです。ビルドプロセスが成果物を作成し、デプロイプロセスがそれを取得する必要があります。これらのプロセスは、異なるマシン、異なるネットワーク、異なるタイミングで実行される可能性があります。両方がネットワーク経由でアクセスできる共有の場所が必要です。

この共有の場所をアーティファクトレジストリまたはアーティファクトリポジトリと呼びます。その役割はシンプルです。成果物を保存し、取得する手段を提供することです。ビルドが完了するたびに、成果物をこのレジストリにプッシュします。その後、デプロイが開始されると、ターゲットサーバーがレジストリから成果物をプルして実行します。

以下の図は基本的な流れを示しています。

flowchart TD A[ビルドマシン] -->|プッシュ| B[アーティファクトレジストリ] B -->|プル| C[デプロイ先] D[異なるネットワーク] -.-> A E[異なるネットワーク] -.-> C F[異なるタイミング] -.-> A G[異なるタイミング] -.-> C

以下は、ビルド後に成果物をプッシュし、デプロイ先でプルする方法の例です。

# ビルドマシン上: レジストリに成果物をプッシュ
curl -X POST \
  -F "file=@myapp-v1.2.3.jar" \
  https://registry.example.com/upload

# デプロイ先: レジストリから成果物をプル
curl -O https://registry.example.com/artifacts/myapp-v1.2.3.jar

単なるファイルサーバーではない

レジストリはファイルを保持するだけではありません。各成果物に関するメタデータ(バージョン番号、作成タイムスタンプ、そして多くの場合、それを生成したGitコミットハッシュ)も保持します。このメタデータは、本番環境で問題が発生したときに重要になります。

バージョン2.3.1をデプロイしたところ、ユーザーからエラーが報告されたとします。その成果物にどのようなコード変更が含まれているかを正確に把握する必要があります。成果物をソースコミットにリンクするメタデータがなければ、推測するしかありません。メタデータがあれば、差分を確認し、問題を特定し、ロールバックするか修正を進めるかを判断できます。

一部のレジストリはラベルやタグもサポートしています。統合テストに合格した成果物に「staging-validated」、手動承認後に「production-ready」といったタグを付けることができます。これらのタグは、どの成果物をどの環境にデプロイするかの自動化に役立ちます。

接続性の落とし穴

多くのチームが陥るミスがあります。クラウドにレジストリを設置したものの、本番サーバーがパブリックインターネットにアクセスできないプライベートネットワークで動作しているケースです。ビルドは成果物を正常にプッシュしますが、デプロイがプルしようとすると接続タイムアウトで失敗します。

レジストリは、デプロイが必要なすべてのサーバーからアクセス可能でなければなりません。本番環境が隔離されている場合、レジストリはその同じネットワーク内にあるか、ネットワーク境界を越えて成果物を同期する仕組みが必要です。一部のチームは、中央レジストリから成果物をキャッシュするローカルプロキシやミラーを実行します。他のチームは、プライベートネットワーキングエンドポイントをサポートするレジストリを使用します。

読んでいると当たり前に聞こえますが、パイプラインをエンドツーエンドで動かすことに集中していると見落としがちです。サーバーがアクセスできないレジストリを中心にデプロイプロセス全体を構築する前に、接続性を確認してください。

イミュータビリティの重要性

優れたレジストリは、保存された成果物が後から変更されるのを防ぎます。この特性を**イミュータビリティ(不変性)**と呼びます。つまり、今日保存した成果物は、6か月後に取得する成果物と同一であることが保証されます。

なぜこれが重要なのでしょうか?イミュータビリティがなければ、デプロイするものを信頼できません。テストに合格した後に誰かが成果物を変更する可能性があります。ステージングで発見されたバグが、環境間で成果物が変わったために本番で再発するかもしれません。本番で実行されている成果物がテストされたものと一致するか確信が持てず、デバッグは悪夢と化します。

イミュータビリティはクリーンなワークフローを強制します。すべての変更は新しいバージョンの新しい成果物を生成します。成果物の「その場での更新」はありません。何かを修正する必要がある場合は、再ビルドして新しいバージョンを作成します。この規律により、デプロイは予測可能になり、ロールバックは簡単になります。

ビルドとデプロイの分離

レジストリを導入することで、ビルドとデプロイは独立して実行できる2つの別々のプロセスになります。ビルドが完了し、成果物をプッシュして終了します。デプロイは数分後、数時間後、あるいは数日後に行うことができます。成果物はレジストリに安全に保存され、取得されるのを待ちます。

この分離は強力です。朝に成果物をビルドしてテストし、午後にシニアエンジニアがレビューし、夜間のトラフィックが少ない時間帯に本番にデプロイする、といったことが可能です。各ステップは独自のスケジュールで進行しますが、すべて同じ不変の成果物を参照します。

また、同じ成果物を再ビルドして複数の環境に再デプロイすることも意味します。ステージングテストに合格した成果物は、本番に送られる成果物とまったく同じものです。再コンパイルも、環境固有のビルドも、「自分のマシンでは動いた」という驚きもありません。

実践的なクイックチェックリスト

アーティファクトレジストリを設定する際は、以下のポイントを確認してください。

  • アクセス可能性: デプロイが必要なすべてのサーバーがネットワーク経由でレジストリに到達できますか?
  • イミュータビリティ: レジストリは保存された成果物の変更を防止しますか?
  • メタデータ: 各成果物にバージョン、タイムスタンプ、ソースコミット情報が含まれていますか?
  • 保持期間: 古い成果物をどのくらい保持しますか?クリーンアップポリシーはありますか?
  • 認証: 誰が成果物をプッシュできますか?誰がプルできますか?認証情報は定期的にローテーションされていますか?

まとめ

ビルドの出力には、ビルドプロセスとデプロイ先の両方がアクセスできる「居場所」が必要です。アーティファクトレジストリは、その共有ストレージを提供し、トレーサビリティのためのメタデータを保持し、デプロイするものを信頼できるようにイミュータビリティを強制し、ビルドとデプロイを分離して各ステップが独自のタイムラインで実行できるようにします。これがなければ、パイプラインは誰もアクセスできないマシン上にファイルを置いて終わるだけのビルドに過ぎません。