この記事で分かること
- CI/CD(継続的インテグレーション/継続的デリバリー)が解決する3つの課題
- GitHub Actionsのワークフロー定義(YAML)の書き方
- テスト → ビルド → デプロイを段階的に自動化する手順
- キャッシュやマトリクスビルドによるパイプラインの高速化
- 本番運用で必ず押さえるべきセキュリティと承認フローの設定
はじめに——3つの手作業、まだ続けますか?
開発の現場で、こんな作業を手動で繰り返していませんか?
- テスト: 「pushする前にテスト通しておいてね」→ 忘れる
- ビルド: 「本番用のビルドコマンド、どれだっけ…」→ 手順書を探す
- デプロイ: 「SSHで入って、docker pullして…」→ 手順を間違える
これらはすべて「人間がやるから失敗する」作業です。機械に任せれば、同じ手順を何百回でも正確に繰り返してくれます。
たとえるなら、毎朝手動で目覚まし時計をセットし直すか、一度アラームを設定して毎日自動で鳴らすかの違いです。CI/CDパイプラインは、開発作業における「自動アラーム」のようなものです。
前提知識
CI/CDとは
CI/CD は2つの概念の組み合わせです。
| 用語 | 正式名称 | 一言で言うと |
|---|---|---|
| CI | Continuous Integration(継続的インテグレーション) | コード変更のたびに自動でテスト・ビルドし、問題を早期発見する仕組み |
| CD | Continuous Delivery / Deployment(継続的デリバリー/デプロイ) | CIを通過したコードを自動でステージング・本番環境に届ける仕組み |
GitHub Actionsとは
GitHubに標準搭載されたCI/CDサービスです。リポジトリに .github/workflows/ ディレクトリを作り、YAMLファイルを1つ置くだけで動きます。別途CIサーバを用意する必要はありません。
ワークフローYAMLの基本構造
GitHub Actionsのワークフローは、4つの階層で構成されています。
-
Workflow:
.github/workflows/に置くYAMLファイル1つが1ワークフロー - on: トリガー条件(pushしたとき、PRが作られたとき、等)
- jobs: 実行するジョブの集まり。ジョブ同士は並列実行される
- steps: ジョブ内の手順。上から順番に実行される
ハンズオン: 3つの手作業を段階的に自動化する
フェーズ1: テストを自動化する
まずは最も効果の高い「テストの自動化」から始めます。
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: コードをチェックアウト
uses: actions/checkout@v4
- name: Node.js をセットアップ
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm' # node_modulesをキャッシュして高速化
- name: 依存パッケージをインストール
run: npm ci
- name: Lint(コード品質チェック)
run: npm run lint
- name: 型チェック
run: npx tsc --noEmit
- name: ユニットテスト
run: npm test
各ステップの解説:
| ステップ | 何をしているか | なぜ必要か |
|---|---|---|
actions/checkout@v4 |
リポジトリのコードをランナー(実行マシン)にダウンロード | コードがなければ何もできない |
actions/setup-node@v4 |
Node.jsをインストールし、npmキャッシュを有効化 |
cache: 'npm' で2回目以降のインストールが高速になる |
npm ci |
package-lock.json に基づいて依存パッケージを厳密にインストール |
npm install と違い、lockファイルを更新しないので再現性が高い |
npm run lint |
ESLint等でコードの品質・スタイルをチェック | 人間がレビューする前に機械的な問題を排除する |
tsc --noEmit |
TypeScriptの型エラーを検出(JSファイルは生成しない) | 型の不整合を早期に発見する |
npm test |
ユニットテストを実行 | コードが期待通りに動作するか確認する |
フェーズ2: ビルドを自動化する
テストが通ったら、本番用のDockerイメージを自動でビルドします。フェーズ1のCIワークフローにビルドジョブを追加しましょう。
# ↑ 上記のtestジョブの下に追加
build:
needs: test # testジョブの成功後に実行
runs-on: ubuntu-latest
steps:
- name: コードをチェックアウト
uses: actions/checkout@v4
- name: Docker イメージをビルド
run: |
docker build \
-t myapp:${{ github.sha }} \
-t myapp:latest \
.
- name: ビルド成果物を確認
run: docker images myapp
needs: test がポイントです。これにより、テストが成功した場合だけビルドが実行されます。
フェーズ3: デプロイを自動化する
最後に、ビルド済みイメージを本番環境にデプロイするCDワークフローを作ります。CIとファイルを分けると管理しやすくなります。
name: CD Pipeline
on:
workflow_run:
workflows: [CI Pipeline]
types: [completed]
jobs:
deploy:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.event == 'push'
runs-on: ubuntu-latest
environment: production # 手動承認を要求
steps:
- name: コードをチェックアウト
uses: actions/checkout@v4
- name: レジストリにログイン
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
docker login -u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin
- name: イメージをビルド&プッシュ
run: |
docker build -t registry.example.com/myapp:${{ github.sha }} .
docker push registry.example.com/myapp:${{ github.sha }}
- name: 本番環境にデプロイ
run: |
ssh -o StrictHostKeyChecking=no \
deploy@${{ secrets.DEPLOY_HOST }} \
"docker pull registry.example.com/myapp:${{ github.sha }} && \
docker compose up -d"
ポイント:
-
workflow_run: CIワークフローの完了をトリガーにします。CI成功時だけCDが起動する安全な仕組みです -
environment: production: このジョブに手動承認を要求します(設定方法は後述) -
${{ github.sha }}: コミットハッシュをタグに使い、「どのコミットがデプロイされたか」を追跡可能にします -
${{ secrets.* }}: 認証情報はGitHub Secretsで管理します。YAMLに直書きは絶対にNGです
完成形: push一発で回るパイプライン
3つのフェーズを組み合わせると、以下のパイプラインが完成します。
実践Tips: パイプラインを高速化する
キャッシュを活用する
npm ci は毎回すべてのパッケージをダウンロードします。actions/setup-node の cache オプションを使えば、node_modules のダウンロード結果をキャッシュして大幅に高速化できます。
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm' # これだけでキャッシュが有効になる
マトリクスビルドで複数バージョンをテストする
ライブラリ開発など、複数のNode.jsバージョンで動作確認が必要な場合は、マトリクスビルド(strategy.matrix)を使います。
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
3つのNode.jsバージョンのテストが並列で実行されます。
安全に運用するための設定
シークレットの管理
認証情報は必ずGitHub Secretsに登録します。
設定手順:
- リポジトリの Settings → Secrets and variables → Actions
- New repository secret をクリック
- 名前と値を入力して保存
本番環境の認証情報は Environment secrets(production 環境専用)に保存し、開発ブランチからは参照できないようにするのがベストプラクティスです。
手動承認の設定
本番デプロイの前に人間の承認を必須にします。
-
Settings → Environments → New environment で
productionを作成 - Required reviewers を有効にし、承認者を追加
- ワークフロー側で
environment: productionを指定
permissions を最小限にする
GitHub Actionsのデフォルト権限は広すぎることがあります。ワークフローに明示的な permissions を設定しましょう。
permissions:
contents: read # リポジトリの読み取りのみ
packages: write # パッケージの書き込み(イメージのプッシュ用)
よくある落とし穴
1. アクションのバージョンを固定しない
# NG: mainブランチを参照(予告なく動作が変わる)
uses: actions/checkout@main
# OK: メジャーバージョンを固定
uses: actions/checkout@v4
サードパーティのアクションでは、SHA固定がさらに安全です。
2. テストなしでCDを組む
テストが自動化されていない状態でCDだけ導入すると、バグ入りのコードが自動で本番にデプロイされてしまいます。必ずCIから始めてください。
3. mainブランチへの直pushを許可したまま
CI/CDを整備しても、mainに直接pushできてしまうとテストを迂回されます。Branch Protection Rules で以下を有効にしましょう。
- Require a pull request before merging(マージ前にPR必須)
- Require status checks to pass before merging(ステータスチェック必須)
4. npm install を使ってしまう
CI環境では npm install ではなく npm ci を使ってください。npm install は package-lock.json を更新してしまう場合があり、ビルドの再現性が損なわれます。
まとめ
この記事では、3つの手作業を段階的に自動化しました。
| フェーズ | 手作業 | 自動化後 |
|---|---|---|
| テスト | 「push前にテスト通しておいて」 | pushするたびに自動でLint、型チェック、テスト実行 |
| ビルド | 「本番ビルドのコマンドどれだっけ」 | テスト成功後にDockerイメージを自動ビルド |
| デプロイ | 「SSHで入ってdocker pullして…」 | 承認ボタン一つで本番環境に自動デプロイ |
最初からすべてを自動化する必要はありません。まずはフェーズ1のテスト自動化だけから始めてみてください。git push のたびにテストが自動で走る体験は、一度味わうと手動には戻れなくなります。