LabHub

ブログ

GitHub Actions 上級 CI/CD ワークフロー — マトリクスビルド、再利用ワークフロー、セルフホストランナー

한국어English日本語

GitHub Actions Advanced CI/CD Workflows

はじめに

GitHub Actionsは2019年の正式リリース以降、CI/CD市場で最も速く成長したプラットフォームだ。2025年時点でGitHubプロジェクトの68%以上がActionsを使っており、単純なビルドやテストを超えて、セキュリティスキャン、インフラのプロビジョニング、本番デプロイまでソフトウェアライフサイクル全体をカバーする自動化エンジンとして定着した。

しかし実務でGitHub Actionsをきちんと使いこなすには、基本的なワークフローの記述を超えた高度な技法が必要になる。マトリクス戦略で複数環境のビルドを並列処理し、再利用ワークフローで組織全体のパイプラインを標準化し、セルフホストランナーでコストと性能を最適化することが、現場で求められる能力だ。

本記事では、GitHub Actionsのアーキテクチャからマトリクスビルド、再利用ワークフロー、セルフホストランナー、キャッシング戦略、シークレット管理、そして障害事例と復旧手順まで、実務で必要になる高度なトピックをすべて扱う。

GitHub Actionsのアーキテクチャとランナーの種類

アーキテクチャの概要

GitHub Actionsの実行フローは次のとおりだ。

  1. イベントトリガー: push、pull_request、schedule、workflow_dispatchなどのイベントがワークフローを開始する。
  2. ワークフローのキューイング: GitHubクラウドのコントロールプレーン(Control Plane)がワークフローYAMLをパースし、ジョブをキューに登録する。
  3. ランナーの割り当て: 利用可能なランナーがキューからジョブを取り出して実行する。
  4. ステップの実行: 各ジョブ内のステップが順に実行され、アクション(Action)やシェルコマンドが実行される。
  5. 結果の報告: 実行結果がGitHubに報告され、ログ、アーティファクト、チェックのステータスが更新される。

ランナー種別の比較

項目GitHubホストランナーセルフホストランナー
管理主体GitHubユーザー(自分で管理)
利用できるOSUbuntu、Windows、macOSすべてのOS(Linux、Windows、macOS、ARMなど)
環境の分離ジョブごとに新しいVMユーザーの設定次第
ネットワークアクセスパブリックインターネットのみプライベートネットワーク、VPNが可能
GPU/特殊HW限定的自由に構成できる
コスト分単位の課金 (2026年: +$0.002/分のプラットフォーム料金)インフラコスト + 2026年3月から$0.002/分のプラットフォーム料金
最大実行時間6時間 (またはプランごとに異なる)ユーザーの設定
セキュリティ水準ジョブごとに環境を初期化--ephemeral フラグを推奨

2026年の価格変更

2026年1月からGitHubホストランナーの価格が約40%引き下げられ、すべてのランナー種別に分あたり$0.002のクラウドプラットフォーム料金が新たに課される。2026年3月からはセルフホストランナーにも同じプラットフォーム料金が適用される。ただしパブリックリポジトリでのActions実行は引き続き無料であり、GitHub Enterprise Serverの利用者にはこの変更は適用されない。

マトリクス戦略(Matrix Strategy)の活用

基本のマトリクス構成

マトリクス戦略は、ひとつのジョブ定義で複数の環境の組み合わせを並列実行する機能だ。ビルド時間を最大80%まで短縮できる。

name: Multi-Environment CI

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

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [18, 20, 22]
        include:
          - os: ubuntu-latest
            node-version: 22
            coverage: true
        exclude:
          - os: macos-latest
            node-version: 18
      fail-fast: false
      max-parallel: 6

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test

      - name: Upload coverage
        if: matrix.coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

主な設定項目を整理すると次のとおりだ。

マトリクスの動的な生成

先行するジョブの出力をもとに、マトリクスを動的に組み立てられる。モノレポで変更のあったパッケージだけをビルドしたり、特定の条件に応じてテスト対象を決めたりするのに使う。

name: Dynamic Matrix Build

on:
  push:
    branches: [main]

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set-matrix.outputs.matrix }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Detect changed packages
        id: set-matrix
        run: |
          CHANGED=$(git diff --name-only HEAD~1 HEAD | grep '^packages/' | cut -d'/' -f2 | sort -u)
          if [ -z "$CHANGED" ]; then
            echo "matrix={\"package\":[\"core\"]}" >> $GITHUB_OUTPUT
          else
            PACKAGES=$(echo "$CHANGED" | jq -R -s -c 'split("\n") | map(select(. != ""))')
            echo "matrix={\"package\":$PACKAGES}" >> $GITHUB_OUTPUT
          fi

  build:
    needs: detect-changes
    runs-on: ubuntu-latest
    strategy:
      matrix: ${{ fromJSON(needs.detect-changes.outputs.matrix) }}
    steps:
      - uses: actions/checkout@v4

      - name: Build ${{ matrix.package }}
        run: |
          echo "Building package: ${{ matrix.package }}"
          cd packages/${{ matrix.package }}
          npm ci && npm run build

fromJSON() 関数は、GitHub Actionsの式(expression)でJSON文字列をオブジェクトに変換する中心的な関数だ。これによって前のジョブの出力値をマトリクス定義に動的に注入できる。

再利用ワークフロー(Reusable Workflows)の設計

再利用ワークフローが必要な理由

組織に10個以上のマイクロサービスがあり、それぞれにCI/CDパイプラインが必要なら、同じYAMLをコピーして貼り付けるのは保守の悪夢になる。再利用ワークフローはプログラミングの関数のように入力(inputs)と出力(outputs)を定義し、複数のワークフローから呼び出せる。

2025年11月のアップデートでネストした再利用ワークフローが最大10段階ワークフロー呼び出し全体が最大50回まで拡大され、より複雑なパイプライン構成が可能になった。

再利用ワークフローの定義

# .github/workflows/reusable-docker-build.yml
name: Reusable Docker Build

on:
  workflow_call:
    inputs:
      image-name:
        required: true
        type: string
        description: 'Dockerイメージ名'
      dockerfile-path:
        required: false
        type: string
        default: './Dockerfile'
        description: 'Dockerfileのパス'
      build-args:
        required: false
        type: string
        default: ''
        description: 'Dockerのビルド引数'
      push-image:
        required: false
        type: boolean
        default: true
    secrets:
      REGISTRY_USERNAME:
        required: true
      REGISTRY_PASSWORD:
        required: true
    outputs:
      image-tag:
        description: 'ビルドされたイメージのタグ'
        value: ${{ jobs.build.outputs.tag }}
      image-digest:
        description: 'イメージのダイジェスト'
        value: ${{ jobs.build.outputs.digest }}

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      tag: ${{ steps.meta.outputs.tags }}
      digest: ${{ steps.build-push.outputs.digest }}
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Container Registry
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ inputs.image-name }}
          tags: |
            type=sha,prefix=
            type=ref,event=branch
            type=semver,pattern={{version}}

      - name: Build and push
        id: build-push
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ${{ inputs.dockerfile-path }}
          push: ${{ inputs.push-image }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: ${{ inputs.build-args }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

再利用ワークフローの呼び出し

# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main]

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run lint && npm test

  build-api:
    needs: lint-and-test
    uses: ./.github/workflows/reusable-docker-build.yml
    with:
      image-name: ghcr.io/my-org/api-server
      dockerfile-path: ./services/api/Dockerfile
      build-args: NODE_ENV=production
    secrets:
      REGISTRY_USERNAME: ${{ secrets.GHCR_USERNAME }}
      REGISTRY_PASSWORD: ${{ secrets.GHCR_TOKEN }}

  build-web:
    needs: lint-and-test
    uses: ./.github/workflows/reusable-docker-build.yml
    with:
      image-name: ghcr.io/my-org/web-frontend
      dockerfile-path: ./services/web/Dockerfile
    secrets:
      REGISTRY_USERNAME: ${{ secrets.GHCR_USERNAME }}
      REGISTRY_PASSWORD: ${{ secrets.GHCR_TOKEN }}

  deploy:
    needs: [build-api, build-web]
    runs-on: ubuntu-latest
    steps:
      - name: Deploy with new images
        run: |
          echo "API image: ${{ needs.build-api.outputs.image-tag }}"
          echo "Web image: ${{ needs.build-web.outputs.image-tag }}"
          # kubectl set image や Helm upgrade などを実行

再利用ワークフローの設計原則

  1. 単一責任の原則: ひとつの再利用ワークフローはひとつの役割だけを担う。Dockerビルド、Terraformの適用、テスト実行などに分割する。
  2. バージョンの固定: 本番では必ずコミットSHAまたはタグで固定する。@main 参照は開発環境でのみ使う。
  3. 入力値の検証: requiredフィールドを活用し、defaultの値を適切に設定して呼び出し側の負担を減らす。
  4. シークレットの受け渡し: secrets: inherit を使えばすべてのシークレットを自動的に渡せるが、明示的に渡すほうがセキュリティ上は望ましい。

セルフホストランナー(Self-hosted Runner)の構成とセキュリティ

セルフホストランナーのインストールと登録

#!/bin/bash
# セルフホストランナーのインストールスクリプト (Ubuntu)

# 1. 専用ユーザーの作成
sudo useradd -m -s /bin/bash github-runner
sudo usermod -aG docker github-runner

# 2. ランナーディレクトリの作成とダウンロード
sudo -u github-runner mkdir -p /home/github-runner/actions-runner
cd /home/github-runner/actions-runner

# 3. 最新のランナーパッケージをダウンロード (v2.329.0以上が必須)
RUNNER_VERSION="2.322.0"
curl -o actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz \
  -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz

tar xzf actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz

# 4. ランナーの登録 (Ephemeralモードを推奨)
./config.sh \
  --url https://github.com/YOUR_ORG \
  --token YOUR_REGISTRATION_TOKEN \
  --name "prod-runner-01" \
  --labels "self-hosted,linux,x64,production" \
  --runnergroup "production-runners" \
  --ephemeral \
  --disableupdate

# 5. systemdサービスの登録
sudo ./svc.sh install github-runner
sudo ./svc.sh start
sudo ./svc.sh status

セキュリティのガイドライン

セルフホストランナーのセキュリティは、組織のコードとインフラを守るうえで中心を占める。次の原則は必ず守らなければならない。

絶対にやってはいけないこと:

必ずやるべきこと:

ランナーのオートスケーリング構成

Kubernetes環境では actions-runner-controller(ARC) を使ってランナーを自動的にスケーリングできる。

# runner-deployment.yaml (ARC v0.27+)
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
  name: production-runners
  namespace: github-runners
spec:
  replicas: 2
  template:
    spec:
      repository: my-org/my-repo
      labels:
        - self-hosted
        - linux
        - production
      ephemeral: true
      dockerEnabled: true
      resources:
        limits:
          cpu: '4'
          memory: '8Gi'
        requests:
          cpu: '2'
          memory: '4Gi'
---
apiVersion: actions.summerwind.dev/v1alpha1
kind: HorizontalRunnerAutoscaler
metadata:
  name: production-runners-autoscaler
  namespace: github-runners
spec:
  scaleTargetRef:
    kind: RunnerDeployment
    name: production-runners
  minReplicas: 1
  maxReplicas: 10
  scaleUpTriggers:
    - githubEvent:
        workflowJob: {}
      duration: '30m'
  scaleDownDelaySecondsAfterScaleOut: 300

キャッシング戦略とアーティファクト管理

キャッシュとアーティファクトの違い

項目キャッシュ(Cache)アーティファクト(Artifact)
目的依存関係の再インストールを防ぎ、ビルドを高速化するビルド成果物の保存と共有
寿命7日(デフォルト)、最大値はポリシーで設定できる90日(デフォルト)、最大400日
サイズ制限リポジトリあたり10GB以上 (2025年11月に拡大)アーティファクトあたりの最大サイズはプランごとに異なる
ジョブ間の共有同一ワークフロー内、ブランチをまたいだ復元が可能同一ワークフロー内、ダウンロードが可能
代表的な用途node_modules、pipパッケージ、Goモジュールテストレポート、ビルドバイナリ、カバレッジ

高度なキャッシング戦略

name: Optimized Caching Pipeline

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # npmキャッシュ: package-lock.jsonのハッシュを基準にする
      - name: Cache npm dependencies
        uses: actions/cache@v4
        id: npm-cache
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-npm-

      # Next.jsのビルドキャッシュ
      - name: Cache Next.js build
        uses: actions/cache@v4
        with:
          path: .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
            ${{ runner.os }}-nextjs-

      # Dockerレイヤーキャッシュ (Buildx)
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build Docker image with cache
        uses: docker/build-push-action@v6
        with:
          context: .
          push: false
          tags: my-app:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

      # アーティファクトのアップロード (テスト結果)
      - name: Run tests
        run: npm test -- --coverage

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ github.sha }}
          path: |
            coverage/
            test-results/
          retention-days: 30

キャッシング最適化のヒント

シークレット管理と環境変数のセキュリティ

シークレットの階層構造

GitHub Actionsは3段階のシークレットスコープを提供する。

  1. Organization Secrets: 組織全体、または選んだリポジトリで共有する。
  2. Repository Secrets: 特定のリポジトリでのみ使う。
  3. Environment Secrets: 特定の環境(staging、productionなど)でのみ使い、承認ワークフローを紐づけられる。

OIDCを活用したシークレットレス(Secretless)認証

長期の資格情報をシークレットに保存する代わりにOIDC(OpenID Connect)を活用すると、ワークフローの実行時に短命なトークンを自動的に発行してもらい、クラウドリソースにアクセスできる。

name: Deploy to AWS with OIDC

on:
  push:
    branches: [main]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      # OIDCによるAWS認証 (長期キーは不要)
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
          role-session-name: github-actions-deploy
          aws-region: ap-northeast-2

      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster production \
            --service my-api \
            --force-new-deployment

シークレットセキュリティのベストプラクティス

GitHub Actions vs Jenkins vs GitLab CI の比較

項目GitHub ActionsJenkinsGitLab CI
ホスティングSaaS (セルフホストランナーが可能)セルフホスト専用SaaS + セルフホスト
設定方式YAML (.github/workflows/)Groovy (Jenkinsfile)YAML (.gitlab-ci.yml)
マーケットプレイス20,000+のアクション1,800+のプラグインテンプレートカタログ
学習曲線低い高い中程度
無料プラン2,000分/月 (パブリックは無制限)無料(OSS)400分/月
マトリクスビルドネイティブ対応プラグインが必要parallel キーワード
再利用性workflow_call、composite actionsShared Librariesinclude、extends
シークレット管理内蔵 (Org/Repo/Envの階層)Credentials PluginCI/CD Variables
OIDC対応ネイティブプラグインが必要ネイティブ
コンテナ対応container キーワードDocker Pipelineプラグインネイティブ (services)
市場シェア(2025)OSS 68%Fortune 500 80%前年比 +34%の成長
おすすめの対象GitHubを使う組織、小〜中規模のチーム大規模エンタープライズ、複雑なカスタムパイプラインDevSecOps、オールインワンのプラットフォームを好むチーム

選択のガイド

障害事例と復旧手順

よく起きる障害の類型

1. シークレット漏洩インシデント

2025年3月、tj-actions/changed-files アクションが乗っ取られ、ランナーのメモリからシークレットをスキャンしてビルドログに出力する悪意あるコードが仕込まれた。これにより23,000以上のリポジトリが影響を受けた。

復旧手順:

2. キャッシュ汚染(Cache Poisoning)

誤ったキャッシュキーの設定で古い依存関係が復元されたり、悪意を持って改ざんされたキャッシュが使われたりすることがある。

復旧手順:

3. セルフホストランナーの環境汚染

非Ephemeralなランナーでは、前のジョブの残存ファイルが次のジョブに影響を与えることがある。

復旧手順:

4. 同時実行(Concurrency)の衝突

同じブランチに短い間隔で連続してプッシュすると、複数のワークフローが同時に実行されてデプロイの衝突が起きることがある。

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true

上の設定によって、同じグループの前の実行を自動的にキャンセルし、最新の実行だけを進める。

デバッグの技法

コスト最適化の戦略

コスト削減チェックリスト

  1. キャッシングの最大化: npm、pip、Goモジュール、Dockerレイヤーなど、キャッシュできる依存関係はすべてキャッシュしてインストール時間と実行時間を減らす。
  2. マトリクスの最適化: exclude で不要な組み合わせを取り除き、max-parallel で同時実行数を制限する。
  3. 条件付き実行: paths フィルターや if 条件で、変更がない場合はワークフローをスキップする。
  4. タイムアウトの設定: timeout-minutes を適切に設定し、無限ループしたジョブによるコストの流出を防ぐ。
  5. Ubuntuランナーを優先して使う: Windowsランナーは2倍、macOSランナーは10倍の分あたり料金が課される。
  6. セルフホストランナーの検討: 月あたりの実行時間が多い組織では、セルフホストランナーのほうが経済的なことがある(2026年のプラットフォーム料金を考慮すること)。
  7. concurrencyの設定: 不要な重複実行をキャンセルして、分あたり料金の無駄を防ぐ。

分あたりコスト比較表 (2026年時点)

ランナー種別分あたりコストプラットフォーム料金合計
Ubuntu (2 vCPU)$0.008$0.002$0.010
Windows (2 vCPU)$0.016$0.002$0.018
macOS (3 vCPU)$0.080$0.002$0.082
Ubuntu Large (4 vCPU)$0.016$0.002$0.018
セルフホストインフラコストは別途$0.002インフラ + $0.002

運用上の注意事項とチェックリスト

ワークフロー設計チェックリスト

セキュリティチェックリスト

モニタリングチェックリスト

まとめ

GitHub Actionsは単なるCI/CDツールを超えて、ソフトウェア開発のライフサイクル全体を自動化するプラットフォームだ。マトリクス戦略でビルド時間を大幅に短縮し、再利用ワークフローで組織全体のパイプラインを標準化し、セルフホストランナーで特殊な環境とコストを管理できる。

ただし機能が強力なぶん、セキュリティとコスト管理にも細やかな注意が必要だ。サードパーティ製アクションを狙ったサプライチェーン攻撃、シークレットの漏洩、キャッシュ汚染といった脅威に備えなければならないし、2026年から変わった価格ポリシーを踏まえてコスト最適化の戦略を立てる必要がある。

本記事で扱ったマトリクスビルド、再利用ワークフロー、セルフホストランナー、キャッシング戦略、シークレット管理、そして障害事例と復旧手順を実務に適用し、安定して効率のよいCI/CDパイプラインを構築してほしい。

参考資料

コメント

まだコメントはありません。

ログインするとコメントできます