Skip to content
Published on

GitHub Actions 上級 CI/CD — Matrix、Cache、Self-hosted Runner

Authors
  • Name
    Twitter
GitHub Actions Advanced CI/CD

はじめに

GitHub Actionsは、今やCI/CDのデファクトスタンダードです。基本的なビルド/テストパイプラインは誰でも作れますが、Matrix StrategyCacheSelf-hosted Runnerを適切に活用すれば、ビルド時間を半分以下に短縮し、コストも大幅に削減できます。

本記事では、実務ですぐに適用できる上級CI/CDパターンを解説します。

Matrix Strategy:並列ビルドの力

基本的なMatrix構成

Matrixを使用すると、複数の環境の組み合わせを自動的に並列実行します:

name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, macos-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

この設定は3(Nodeバージョン)× 2(OS)= 6つの並列Jobを生成します。

上級Matrix:includeとexclude

特定の組み合わせのみを追加または除外できます:

strategy:
  fail-fast: false # 1つが失敗しても残りは継続実行
  max-parallel: 4 # 同時実行最大4つ
  matrix:
    node-version: [18, 20, 22]
    os: [ubuntu-latest, macos-latest, windows-latest]
    exclude:
      # Node 18 + Windowsの組み合わせを除外
      - node-version: 18
        os: windows-latest
    include:
      # 特定の組み合わせにのみ追加変数を設定
      - node-version: 22
        os: ubuntu-latest
        coverage: true

動的Matrix生成

ビルド時にMatrixを動的に決定するパターン:

jobs:
  prepare:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - uses: actions/checkout@v4
      - id: set-matrix
        run: |
          # 変更されたパッケージのみをビルド対象に設定
          CHANGED=$(git diff --name-only HEAD~1 | grep "^packages/" | cut -d/ -f2 | sort -u | jq -R . | jq -s .)
          echo "matrix={\"package\":$CHANGED}" >> $GITHUB_OUTPUT

  build:
    needs: prepare
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJson(needs.prepare.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v4
      - run: npm run build --workspace=packages/${{ matrix.package }}

Cache:ビルド速度の最適化

依存関係キャッシュの基本

- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

setup-* アクションの組み込みキャッシュ

ほとんどのsetupアクションにはキャッシュが組み込まれています:

# Node.js - 組み込みキャッシュ
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: 'npm'

# Python - pipキャッシュ
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'

# Go - モジュールキャッシュ
- uses: actions/setup-go@v5
  with:
    go-version: '1.22'
    cache: true

Dockerレイヤーキャッシュ

DockerビルドでGitHub Actions Cacheを活用すると、劇的な速度向上が可能です:

- uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: myapp:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

キャッシュ戦略の比較

キャッシュ方式メリットデメリット
actions/cache汎用、あらゆるパスをキャッシュ可能手動キー管理が必要
setup-* 組み込みキャッシュ設定が簡単、自動キー生成該当ツールのみ対応
Docker GHAキャッシュレイヤー単位のキャッシュ、非常に高速Dockerビルド専用

Self-hosted Runner:カスタム環境の構築

なぜSelf-hosted Runnerなのか?

GitHub-hosted Runnerの制限事項:

  • コスト:大規模プロジェクトでの分単位課金の負担
  • スペック制限:最大7GB RAM、14GB SSD(Standard)
  • ネットワーク:内部ネットワークへのアクセス不可
  • GPU非対応:ML/AIワークロード不可

Runnerのインストールと登録

# LinuxにRunnerをインストール
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.321.0.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.321.0/actions-runner-linux-x64-2.321.0.tar.gz
tar xzf ./actions-runner-linux-x64-2.321.0.tar.gz

# Runnerの登録
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO \
  --token YOUR_TOKEN \
  --labels gpu,linux,x64 \
  --name my-gpu-runner

# systemdサービスとして登録
sudo ./svc.sh install
sudo ./svc.sh start

WorkflowでSelf-hosted Runnerを使用

jobs:
  gpu-test:
    runs-on: [self-hosted, gpu, linux]
    steps:
      - uses: actions/checkout@v4
      - name: GPUテスト実行
        run: |
          nvidia-smi
          python -m pytest tests/gpu/ -v

  deploy:
    runs-on: [self-hosted, linux]
    needs: gpu-test
    steps:
      - name: 内部ネットワークへデプロイ
        run: |
          kubectl apply -f k8s/
          kubectl rollout status deployment/myapp

RunnerをKubernetesで運用する

Actions Runner Controller(ARC)を使用すると、RunnerをPodとして自動スケーリングできます:

# ARCのインストール(Helm)
helm install arc \
  --namespace arc-systems \
  --create-namespace \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller

# Runner Scale Setのデプロイ
helm install arc-runner-set \
  --namespace arc-runners \
  --create-namespace \
  -f values.yaml \
  oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

values.yaml の例:

githubConfigUrl: 'https://github.com/YOUR_ORG'
githubConfigSecret:
  github_token: 'ghp_xxxxx'
maxRunners: 10
minRunners: 1
template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/actions/actions-runner:latest
        resources:
          requests:
            cpu: '2'
            memory: '4Gi'
          limits:
            cpu: '4'
            memory: '8Gi'

実践統合例:モノレポCI/CD

すべての上級機能を組み合わせた実践パイプライン:

name: Monorepo CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      services: ${{ steps.changes.outputs.services }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - id: changes
        run: |
          SERVICES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} \
            | grep "^services/" | cut -d/ -f2 | sort -u \
            | jq -R . | jq -s .)
          echo "services=$SERVICES" >> $GITHUB_OUTPUT

  build-and-test:
    needs: detect-changes
    if: needs.detect-changes.outputs.services != '[]'
    runs-on: [self-hosted, linux]
    strategy:
      fail-fast: false
      matrix:
        service: ${{ fromJson(needs.detect-changes.outputs.services) }}
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Install & Build
        run: |
          npm ci
          npm run build -w services/${{ matrix.service }}

      - name: Test
        run: npm run test -w services/${{ matrix.service }}

      - name: Docker Build & Push
        if: github.ref == 'refs/heads/main'
        uses: docker/build-push-action@v6
        with:
          context: ./services/${{ matrix.service }}
          push: true
          tags: |
            ghcr.io/${{ github.repository }}/${{ matrix.service }}:${{ github.sha }}
            ghcr.io/${{ github.repository }}/${{ matrix.service }}:latest
          cache-from: type=gha,scope=${{ matrix.service }}
          cache-to: type=gha,scope=${{ matrix.service }},mode=max

  deploy:
    needs: build-and-test
    if: github.ref == 'refs/heads/main'
    runs-on: [self-hosted, linux]
    strategy:
      max-parallel: 1 # 順次デプロイ
      matrix:
        service: ${{ fromJson(needs.detect-changes.outputs.services) }}
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/${{ matrix.service }} \
            app=ghcr.io/${{ github.repository }}/${{ matrix.service }}:${{ github.sha }}
          kubectl rollout status deployment/${{ matrix.service }} --timeout=300s

セキュリティベストプラクティス

シークレット管理

# Environmentで分離
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production # 承認が必要な環境
    steps:
      - name: Deploy
        env:
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
        run: ./deploy.sh

OIDCによるクラウド認証(シークレット不要)

permissions:
  id-token: write
  contents: read

steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789:role/github-actions
      aws-region: ap-northeast-2

まとめ

GitHub Actionsの上級機能を適切に活用すれば、CI/CDパイプラインの速度、コスト、柔軟性のすべてを改善できます。Matrixで並列ビルド、Cacheで速度最適化、Self-hosted Runnerでカスタム環境まで――この3つを組み合わせれば、エンタープライズグレードのパイプラインを構築できます。

クイズ

Q1:Matrix Strategyで3つのNodeバージョン × 2つのOSの組み合わせを設定すると、何個のJobが生成されますか?

6個。Matrixはすべての組み合わせのデカルト積を生成します。3 × 2 = 6つの並列Job。

Q2:Matrixでfail-fast: falseの意味は? 1つのJobが失敗しても、残りのJobを中断せずに継続実行します。デフォルトはtrueで、1つでも失敗すると全体がキャンセルされます。

Q3:actions/cacheのkeyにhashFilesを使用する理由は? package-lock.jsonなどの依存関係ファイルのハッシュ値をキーとして使用することで、依存関係が変更された場合のみキャッシュを更新し、変更がなければ既存のキャッシュを再利用するためです。

Q4:Dockerビルドでcache-from: type=ghaは何を意味しますか? GitHub Actionsのキャッシュバックエンドを、Dockerビルドレイヤーキャッシュとして使用するという意味です。別途レジストリなしでビルドキャッシュを保存・復元できます。

Q5:Self-hosted RunnerをKubernetesで自動スケーリングするにはどのツールを使用しますか?

Actions Runner Controller(ARC)。Helmでインストールし、RunnerをPodとして管理し、ワークロードに応じて自動スケーリングします。

Q6:動的Matrixを生成するにはどのパターンを使用しますか? 最初のJobでoutputsとしてJSON配列を出力し、2番目のJobでfromJson()でパースしてmatrixに渡すパターンです。

Q7:OIDCを使用したクラウド認証のメリットは? 長期シークレット(Access Key)を保存する必要がなく、GitHub Actionsが発行する一時トークンでクラウドに認証します。シークレット漏洩のリスクがなく、自動的に期限切れになります。

Q8:モノレポで変更されたサービスのみをビルドするための核心的な技法は? git diffで変更されたファイルパスを分析して影響を受けるサービスのリストを抽出し、これを動的Matrixとして渡して該当サービスのみをビルドします。