コミットから本番環境へ:実際のパイプラインにおけるツール間の連携

コミットをプッシュした後、何が起きますか?

CIサーバーが通知を受け取れずにデプロイが止まってしまったり、誰かが手動でアーティファクトを別の場所にコピーしなければならなかった経験があるなら、その苦痛はすでに理解しているでしょう。ツールはすべて揃っています。パイプラインも設定されています。しかし、どこかで連鎖が途切れます。誰かがサーバーにSSHでログインし、手動でコマンドを実行し、何も問題が起きないことを願うしかありません。

それがまさに、CI/CDが自動化ではなくなり、ツールで飾り立てた手作業の集まりと化す瞬間です。

パイプラインの本当の仕事は、単一のツールにあるのではありません。ツール同士がどのように接続されるかにあります。リポジトリへのコミットはCIサーバーをトリガーしなければなりません。CIサーバーは完成したアーティファクトをどこに送るべきか知っていなければなりません。アーティファクトレジストリは新しいバージョンが準備できたことをデプロイツールに通知しなければなりません。そしてデプロイツールは、新しいバージョンが起動する前または後に、データベースマイグレーションプロセスと連携しなければなりません。

このトリガーとデータフローの連鎖こそが、パイプラインを実際に機能させるものです。最初から順を追って見ていきましょう。

トリガーチェーン:すべてのツールには2つの役割がある

パイプライン内のすべてのツールには2つの役割があります。前のツールからトリガーを受け取り、後ろのツールにトリガーを送信します。いずれかの接続が切れると、パイプラインは停止します。チームが手動で介入しなければならなくなり、CI/CDが本来解決すべき問題、すなわち遅くてエラーが発生しやすい手動プロセスに逆戻りします。

連鎖はコミットから始まります。開発者がメインブランチに変更をマージするか、プルリクエストをマージします。Gitサーバーがこのイベントを検出します。このイベントはCIサーバーに到達する必要があります。配信メカニズムは、Webhook、ポーリング、イベントバスのいずれかです。方法自体よりも、CIサーバーが新しいコードが待機していることを認識できることの方が重要です。

以下のシーケンス図は、このトリガーとデータフローの連鎖を示しています。

以下は、GitHub Actionsワークフローにおけるトリガーチェーンの具体例です。

name: Build and Deploy

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build artifact
        run: docker build -t myapp:${{ github.sha }} .
      - name: Push to registry
        run: |
          docker tag myapp:${{ github.sha }} registry.example.com/myapp:${{ github.sha }}
          docker push registry.example.com/myapp:${{ github.sha }}
      - name: Trigger deployment
        run: |
          curl -X POST https://deploy.example.com/api/deploy \
            -H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d '{"artifact": "myapp", "version": "${{ github.sha }}", "env": "staging"}'
sequenceDiagram participant Dev as Developer participant Git as Git Server participant CI as CI Server participant Reg as Artifact Registry participant Dep as Deployment Tool participant DB as Database Migration participant Prod as Production Environment Dev->>Git: commit push Git->>CI: webhook trigger CI->>CI: build & test CI->>Reg: push artifact Reg->>Dep: notification Dep->>DB: migration run DB->>Dep: migration complete Dep->>Prod: deploy artifact Prod->>Dep: deployment complete

CIサーバーはパイプラインを実行します。これにはビルド、テスト、パッケージングの各ステージが含まれます。出力はアーティファクトです。そのアーティファクトは、コンパイル済みバイナリ、コンテナイメージ、設定ファイル、データベースマイグレーションパッケージなどです。それはレジストリに送られる必要があります。レジストリは単なるサーバー上のフォルダではありません。すべてのアーティファクトにバージョン、メタデータ、およびビルド方法に関する来歴情報がある、構造化されたストレージシステムです。

次に、デプロイツールは新しいバージョンが利用可能になったことを知る必要があります。一部のデプロイツールはレジストリを直接監視します。他のツールはCIサーバーからのトリガーやレジストリからのWebhookを待ちます。いずれにせよ、デプロイツールは、アーティファクトYのバージョンXが特定の環境で準備できたという情報を受け取らなければなりません。

その後、デプロイツールはターゲット環境にデプロイします。しかし、アプリケーションのデプロイが単独で行われることはほとんどありません。新しいバージョンを実行する前に、データベースに変更が必要なことがよくあります。新しいカラム、変更されたインデックス、データマイグレーションなどです。データベースマイグレーションは、別個の手動ステップとしてではなく、デプロイの一部として実行する必要があります。順序が重要です。新しいアプリケーションバージョンが起動する前にマイグレーションを実行する必要がある場合もあれば、その後で実行する必要がある場合もあります。それは変更が後方互換性があるかどうかによります。

デプロイが完了した後も、パイプラインは終わりではありません。新しいバージョンが正常に動作していることを確認する必要があります。これはヘルスチェック、スモークテスト、またはエラーの急増が見られないというオブザーバビリティシグナルです。検証に失敗した場合、自動的またはワンクリックでロールバックをトリガーする必要があります。

アーティファクトフロー:ツール間を移動するデータ

この連鎖全体を通じて、データはツール間を移動します。コミットメタデータ、アーティファクトバージョン、パイプラインステータス、テスト結果、環境設定、各ツールにアクセスするための認証情報などです。このデータは、手動介入なしにツールからツールへ渡されなければなりません。

これがアーティファクトフローです。アーティファクトフローがクリーンであればあるほど、途中で情報が失われることによるエラーは少なくなります。誰かが設定ファイルの更新を忘れたため、または間違ったアーティファクトバージョンがデプロイされたためにデプロイが失敗した場合、その根本原因はほとんどの場合、アーティファクトフローの断絶にあります。

適切に設計されたアーティファクトフローには以下が含まれます。

  • 正確なコミットとパイプライン実行にリンクする、すべてのアーティファクトの一意の識別子。
  • アーティファクト自体とは別に保存される環境固有の設定。これにより、同じアーティファクトを再ビルドすることなく、ステージングから本番環境にプロモートできます。
  • 各ツールがハードコードされたシークレットなしに次のツールに認証できるようにする認証情報管理。
  • チェーン内のすべてのツールが前のステップが成功したか失敗したかを認識できるようにするステータス伝播。

パイプラインは常に線形とは限らない

トリガーチェーンは線形に聞こえますが、実際のパイプラインはそれほど単純ではありません。1つのコミットが、アプリケーション用、インフラ用、データベース用など、複数のパイプラインを同時にトリガーすることがあります。または、デプロイをトリガーする前に複数のコミットが蓄積されることもあります。リリースブランチモデルを使用してコミットをバッチ処理し、一緒にデプロイするチームもあります。他のチームはすべてのコミットを本番環境に直接デプロイします。

トリガーとデータフローのパターンによって、パイプラインがプレッシャーのかかる状況でどのように動作するかが決まります。ツールがどのように接続されるかを理解せずにツールを選択すると、デモでは機能するが本番環境では壊れるパイプラインになってしまいます。書類上は素晴らしく見えるツールでも、アーティファクトレジストリとの統合がうまくいかないかもしれません。コンテナを美しく扱うデプロイツールでも、データベースマイグレーションをサポートしていない可能性があります。

これが、ツールの選択は個々の機能ではなく、連鎖から始めるべき理由です。コミットが本番環境で実行中のサービスになるまでの流れをマッピングします。すべての引き継ぎポイントを特定します。そして、それらの引き継ぎをどれだけうまく処理できるかに基づいてツールを評価します。

ツールの乱立リスク

各チームが調整なしに独自のツールを選択すると、結果はスムーズなパイプラインではなく、ツールの乱立になります。あるチームはJenkinsを使用し、別のチームはGitHub Actionsを使用します。データベースチームは独自のマイグレーションツールを持っています。インフラチームは異なるステートバックエンドを持つTerraformを使用します。各ツールは単独では機能しますが、それらを接続するにはカスタムスクリプト、手動ステップ、そして多くの暗黙知が必要です。

ツールの乱立はメンテナンスの問題だけではありません。信頼性の問題です。カスタム統合はすべて障害点です。手動の引き継ぎはすべて、ミスが発生する場所です。目標はすべてに同じツールを使用することではありません。目標は、選択したツール全体で一貫性のあるトリガーチェーンとアーティファクトフローを持つことです。

ツール接続のための実践的チェックリスト

ツールの選択を確定する前に、パイプラインの各引き継ぎポイントについてこのチェックリストを実行してください。

  • Gitサーバーは新しいコミットについてCIサーバーにどのように通知しますか?
  • CIサーバーはアーティファクトをレジストリにどのようにプッシュしますか?
  • デプロイツールは新しいアーティファクトバージョンをどのように知りますか?
  • デプロイツールはデータベースマイグレーションをどのようにトリガーしますか?
  • パイプラインはデプロイが成功したことをどのように検証しますか?
  • ロールバックはどのようにトリガーされ、データベースのロールバックも含まれますか?
  • ツールの各ペア間でどのようなデータが流れ、それは自動的に渡されますか?

これらの引き継ぎのいずれかで誰かが手動で何かを行う必要がある場合、遅かれ早かれ問題を引き起こすギャップを発見したことになります。

まとめ

パイプラインは、最も弱い接続と同じくらいの強さしかありません。選択するツール自体よりも、ツールがどのようにトリガーとデータを互いに渡すかの方が重要です。コミットから本番環境までの連鎖をマッピングすることから始めてください。すべての引き継ぎを特定します。そして、その連鎖に適合するツールを選択してください。単独で印象的に見えるツールではありません。最高のパイプラインとは、最も多くの機能を備えたものではありません。それは、誰も立ち止まって次に何をすべきか考えなくても、コミットが本番環境に流れていくパイプラインです。