初めてのパイプラインはツールではなく、一貫性が重要
想像してみてください。あなたのチームは何ヶ月も手動でデプロイを続けてきました。リリースのたびに、誰かがサーバーにSSHでログインし、最新コードをプルし、サービスを再起動しています。動いてはいますが、ストレスが溜まります。ある日、開発者がデプロイ前にテストを実行するのを忘れます。次の日には、別のメンバーが完全にマージされていないブランチからデプロイしてしまいます。本番環境で実際に何が動いているのか、誰も確信を持てません。
これこそが、ほとんどのチームがパイプラインを必要と感じる瞬間です。しかし、最初にツールに飛びつくのが本能です。Jenkins、GitLab CI、GitHub Actionsなど。ツール自体が問題ではありません。問題は、すべてのデプロイがユニークなイベントであり、次に何が起こるか誰も予測できないことです。
本当の解決策はツールではありません。それは再現可能なプロセスです。
まずは単一のパスから始める
何かを標準化する前に、すべての変更が通るルートを1つ決めてください。2つのルートでも、「場合による」でもありません。コードから本番環境までの1つのパスです。
ほとんどのアプリケーションでは、そのパスは次のようになります:
以下がそのゴールデンパスの視覚的表現です:
- ビルド成果物を作成する
- ユニットテストを実行する
- 開発環境にデプロイする
- 統合テストを実行する
- ステージング環境にデプロイする
- 本番環境にデプロイする
チームによっては、これらのステージを別の名前で呼ぶかもしれません。それで構いません。重要なのは、すべての変更が毎回同じ順序で同じシーケンスを通過することです。
以下は、そのゴールデンパスを最小限のGitHub Actionsワークフローで表現したものです:
name: Golden Path
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Build artifact"
unit-tests:
needs: build
runs-on: ubuntu-latest
steps:
- run: echo "Run unit tests"
deploy-dev:
needs: unit-tests
runs-on: ubuntu-latest
steps:
- run: echo "Deploy to development"
integration-tests:
needs: deploy-dev
runs-on: ubuntu-latest
steps:
- run: echo "Run integration tests"
deploy-staging:
needs: integration-tests
runs-on: ubuntu-latest
steps:
- run: echo "Deploy to staging"
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- run: echo "Deploy to production"
これがあなたのゴールデンパスです。完璧である必要はありません。ただ存在すればいいのです。存在すれば、改善できます。しかし、単一のパスがなければ、改善のベースラインすらありません。
目標はスピードではなく一貫性
パイプラインを最初に設定したとき、手動デプロイよりも遅く感じるでしょう。それは正常です。手動デプロイはステップをスキップするから速いのです。テストをスキップし、検証をスキップし、災害を防ぐ部分をスキップします。
パイプラインは短期的には速くありません。しかし、より予測可能です。そして、予測可能性こそが後で速く動くための鍵です。
一貫性がもたらすもの:
- すべての変更が同じ方法でビルドされる。「自分のマシンでは動く」はもうありません。
- すべての変更が同じ方法でテストされる。テストスイートのスキップはもうありません。
- すべての変更が同じ環境にデプロイされる。「ステージングにデプロイするのを忘れた」はもうありません。
パイプラインが一貫していれば、チームは推測するのをやめます。「誰かテストを実行した?」と尋ねる必要も、デプロイ手順をSlackで確認する必要もありません。パイプラインが自動的にそれらの質問に答えてくれます。
スピードバンプではなくリスクゲートを追加する
一貫したパイプラインは良いものです。しかし、いつ停止すべきかを知っているパイプラインはさらに優れています。
リスクゲートとは、パイプライン内で条件に基づいて一時停止または停止できるポイントのことです。目的は処理を遅くすることではなく、問題がユーザーに到達する前にキャッチすることです。
最も簡単なゲートから始めましょう:自動テスト。ユニットテストが失敗したらパイプラインは停止します。統合テストが失敗したらパイプラインは停止します。このゲートには人間の判断は不要です。テストを書き、パイプラインがそれを強制します。
2つ目のゲートは、本番環境への手動承認です。変更は開発環境とステージング環境には自動的に流れます。しかし、本番環境に到達するには、誰かが明示的に承認する必要があります。これは、自動テストがまだ十分に包括的でない場合や、本番変更のユーザー影響が大きい場合に役立ちます。承認者を慎重に選んでください。単にアクセス権がある人ではなく、アプリケーションとリスクを理解している人であるべきです。
3つ目のゲートは、基本的なセキュリティスキャンです。依存関係の既知の脆弱性をスキャンし、危険なパターンのコードをスキャンします。完璧である必要はありません。最も一般的な問題から始め、時間をかけてチェックを追加してください。
リスクゲートは壁ではない
よくある間違いは、リスクゲートを恒久的な障壁として扱うことです。些細な理由でゲートがパイプラインを止め続けると、チームはそれを回避し始めます。常に無視されるゲートはノイズになります。
リスクゲートは定期的に評価する必要があります。次の質問を自問してください:
- このゲートは実際の問題をキャッチしていますか?
- 誤警報でパイプラインを止めていませんか?
- チームはこのゲートを信頼していますか?
役に立たないゲートは、削除するか調整してください。誰も尊重しないゲートは、ゲートがないよりも悪いです。少なくともゲートがなければ、チームは安全網がないことを認識しています。
1つのパスが機能したら、拡張する
ゴールデンパスが安定し、リスクゲートが機能し始めると、パターンに気づくでしょう。あるアプリケーションで機能する同じステージが、わずかな調整で別のアプリケーションでも機能します。あるサービスで問題をキャッチする同じゲートが、別のサービスでも問題をキャッチできます。
これが、さらに標準化を進める瞬間です。すべてのチームに同じツールを強制するのではなく、共有のパイプラインテンプレートを提供します。チームは同じビルドステップ、同じテストランナー、同じデプロイスクリプトを再利用できます。アプリケーション固有の部分だけをカスタマイズします。
ここから、同じパターンをデータベース変更やインフラストラクチャ変更に拡張できます。しかし、それは別の記事のトピックです。
初めての標準化パイプラインの実践的チェックリスト
- コードから本番環境までの1つのゴールデンパスを定義する
- すべての変更が同じステージを同じ順序で通過するようにする
- 自動テストゲート(ユニット、統合)を追加する
- 必要に応じて本番環境への手動承認を追加する
- 基本的なセキュリティスキャンを追加する
- 1ヶ月後にゲートをレビューする。機能しないものは削除または調整する
まとめ
初めてのパイプラインは、適切なツールを選ぶことではありません。すべてのデプロイを予測可能にすることです。1つのパスから始め、一貫性を持たせ、実際の問題をキャッチするゲートを追加し、そのパターンを他のシステムに拡張してください。ツールは後からついてきます。プロセスが先です。