開発ワークフローを進化させる技術要素
GitHub Actionsは、CI/CDパイプラインを構築するための強力なプラットフォームです。本記事では、GitHub Actionsの主要な概念と実践的な活用方法を、技術的な観点から体系的に解説します。
目次
- ワークフローの基礎と再利用戦略
- ランナーの選択と最適化
- セキュリティとアクセス制御
- パフォーマンスとコスト最適化
- 実践的な活用パターン
1. ワークフローの基礎と再利用戦略
1.1 ワークフローの構成要素
GitHub Actionsのワークフローは、リポジトリの .github/workflows ディレクトリに配置されたYAMLファイルで定義します。ワークフローは以下の3つの基本要素で構成されます。
基本的なワークフロー例
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "依存関係のインストール"
run: npm install
- name: "テストの実行"
run: npm test
1.2 ワークフローの再利用パターン
コードの重複を避け、メンテナンス性を向上させるために、GitHub Actionsは3つの再利用メカニズムを提供します。
1.2.1 Reusable Workflows(再利用可能なワークフロー)
ワークフロー全体を再利用可能にすることで、複数のリポジトリで同じデプロイメントプロセスを標準化できます。
再利用可能なワークフローの定義
# .github/workflows/deploy.yml
name: "デプロイワークフロー"
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy_token:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- name: "デプロイの実行"
run: ./deploy.sh
env:
TOKEN: ${{ secrets.deploy_token }}
再利用可能なワークフローの呼び出し
# .github/workflows/main.yml
name: "メインワークフロー"
on: push
jobs:
deploy-staging:
uses: ./.github/workflows/deploy.yml
with:
environment: staging
secrets:
deploy_token: ${{ secrets.STAGING_TOKEN }}
1.2.2 Composite Actions(複合アクション)
複数のステップを1つのアクションにまとめることで、ワークフロー内で再利用可能なロジックを作成できます。
# .github/actions/setup-node-cache/action.yml
name: "Node.js環境のセットアップ"
description: "Node.jsをインストールし、依存関係をキャッシュします"
inputs:
node-version:
description: "Node.jsのバージョン"
required: false
default: '18'
runs:
using: "composite"
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
shell: bash
1.2.3 Workflow Templates(ワークフローテンプレート)
組織全体で標準化されたワークフローを提供し、開発者がすぐに使い始められるようにします。
1.3 再利用可能なワークフローと複合アクションの比較
| 特徴 | 再利用可能なワークフロー | 複合アクション |
|---|---|---|
| ジョブの定義 | 可能(複数ジョブ) | 不可 |
| ログの表示 | 各ジョブ・ステップを個別表示 | 1つのステップとして表示 |
| ランナーの指定 | 可能 | 不可(呼び出し元に依存) |
| 呼び出し方法 | ジョブから直接 | ステップから |
| ネストレベル | 最大10レベル | 最大10アクション |
| シークレットの使用 | 可能 | 不可 |
| マーケットプレイス | 公開不可 | 公開可能 |
2. ランナーの選択と最適化
GitHub Actionsでは、ワークフローを実行する環境(ランナー)を柔軟に選択できます。
2.1 ランナーの種類と特徴
2.2 GitHub-hosted Runners
GitHubが提供・管理する仮想マシンです。各ジョブごとに新しいVMがプロビジョニングされます。
特徴
- Ubuntu Linux、Windows、macOSに対応
- 週次でプリインストールソフトウェアが更新
- メンテナンス不要
- クリーンな実行環境
プリインストールソフトウェアの確認
ワークフローログの「Set up job」セクションを展開すると、「Runner Image」に含まれるソフトウェアの詳細へのリンクが表示されます。
jobs:
build:
runs-on: ubuntu-latest # ubuntu-24.04, ubuntu-22.04なども指定可能
steps:
- run: echo "Ubuntu環境で実行中"
2.3 Larger Runners
より多くのリソースを必要とするワークロード向けの高スペックランナーです。
主な機能
- 高RAM・CPU・ディスク容量
- 静的IPアドレス(ファイアウォール設定に対応)
- Azure Private Networking
- ランナーグループによるアクセス制御
- オートスケーリング
- GPU対応ランナー
料金体系
Larger runnersは分単位の課金で、実際にワークフローが実行されている時間のみ請求されます。ランナー自体の作成・待機時間には課金されません。
2.4 Self-hosted Runners
自社で管理する物理マシンまたは仮想マシンをランナーとして使用します。
利点
- ハードウェア構成の完全な制御
- カスタムソフトウェアのインストール
- オンプレミスリソースへのアクセス
- ランナーアプリケーションは無料
- 永続的な環境(クリーンインスタンス不要)
設置場所
- 物理マシン
- 仮想マシン
- コンテナ
- オンプレミス
- クラウド(AWS、Azure、GCPなど)
階層的な管理
2.5 Actions Runner Controller (ARC)
KubernetesクラスタでSelf-hosted runnersを管理するためのOperatorです。
アーキテクチャ
主な特徴
- ワークロード数に応じた自動スケーリング
- エフェメラル(一時的)なランナー
- コンテナベースで高速なプロビジョニング
- Helmチャートによる簡単なインストール
- JIT(Just-in-Time)構成トークン
ランナーイメージのカスタマイズ
ARCでは、独自のランナーイメージを作成できます。
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy AS build
ARG RUNNER_VERSION="2.311.0"
ARG RUNNER_ARCH="x64"
ARG RUNNER_CONTAINER_HOOKS_VERSION="0.4.0"
ENV DEBIAN_FRONTEND=noninteractive
ENV RUNNER_MANUALLY_TRAP_SIG=1
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
RUN apt update -y && apt install curl unzip -y
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
&& groupadd docker --gid 123 \
&& usermod -aG sudo runner \
&& usermod -aG docker runner
WORKDIR /home/runner
RUN curl -f -L -o runner.tar.gz \
https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-${RUNNER_ARCH}-${RUNNER_VERSION}.tar.gz \
&& tar xzf ./runner.tar.gz \
&& rm runner.tar.gz
RUN curl -f -L -o runner-container-hooks.zip \
https://github.com/actions/runner-container-hooks/releases/download/v${RUNNER_CONTAINER_HOOKS_VERSION}/actions-runner-hooks-k8s-${RUNNER_CONTAINER_HOOKS_VERSION}.zip \
&& unzip ./runner-container-hooks.zip -d ./k8s \
&& rm runner-container-hooks.zip
USER runner
2.6 ランナーグループによるアクセス制御
ランナーグループを使用すると、組織レベルでランナーへのアクセスを制御できます。
2.7 プライベートネットワーク接続
GitHub-hosted runnersをプライベートネットワークに接続する方法は3つあります。
2.7.1 OIDCを使用したAPIゲートウェイ
2.7.2 WireGuardによるオーバーレイネットワーク
jobs:
connect-private-network:
runs-on: ubuntu-latest
steps:
- name: "WireGuardのインストール"
run: |
sudo apt-get update
sudo apt-get install -y wireguard
- name: "VPN接続の確立"
run: |
echo "${{ secrets.WIREGUARD_CONFIG }}" > wg0.conf
sudo wg-quick up ./wg0.conf
2.7.3 Azure Virtual Network (VNET)
GitHub Team以上のプランでは、Azure VNETを使用してランナーをプライベートネットワークに接続できます。
3. セキュリティとアクセス制御
3.1 Secrets(シークレット)の管理
シークレットは、パスワードやトークンなどの機密情報を安全に保管するための仕組みです。
暗号化の仕組み
シークレットはLibsodium sealed boxを使用して暗号化されます。暗号化はクライアント側(ブラウザまたはREST API)で行われ、GitHubインフラストラクチャへの偶発的なログ記録リスクを最小化します。
スコープ
使用例
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: "本番環境へのデプロイ"
run: ./deploy.sh
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
認証情報の権限制限
シークレットを作成する際は、最小限の権限を付与することが重要です。
- Personal Access Token (classic)ではなく、Fine-grained Personal Access Tokenを使用
- 可能な限り読み取り専用権限を付与
- Deploy Keysを単一リポジトリへのアクセスに使用
- GitHub Appによる細かい権限制御
3.2 GITHUB_TOKENの仕組み
GITHUB_TOKENは、各ワークフローの開始時にGitHubが自動生成する一時的なトークンです。
特徴
- GitHub Appのインストールアクセストークン
- リポジトリスコープに制限
- ジョブ完了時または最大24時間で失効
-
github.tokenコンテキストでアクセス可能
権限のカスタマイズ
jobs:
read-only-job:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
再帰的なワークフロー実行の防止
GITHUB_TOKENを使用してコードをプッシュしても、新しいワークフローは実行されません(workflow_dispatchとrepository_dispatchを除く)。これにより、無限ループを防ぎます。
3.3 OpenID Connect (OIDC)
OIDCを使用すると、クラウドプロバイダーから短期間のアクセストークンを直接取得できます。
従来の方法との比較
利点
- クラウドシークレットの長期保存が不要
- 認証・認可の細かい制御
- 自動的に失効する短期間トークン
OIDCトークンの例
{
"typ": "JWT",
"alg": "RS256",
"x5t": "example-thumbprint",
"kid": "example-key-id"
}
{
"jti": "example-id",
"sub": "repo:tanaka-org/tanaka-repo:environment:prod",
"environment": "prod",
"aud": "https://github.com/tanaka-org",
"ref": "refs/heads/main",
"sha": "example-sha",
"repository": "tanaka-org/tanaka-repo",
"repository_owner": "tanaka-org",
"actor_id": "12",
"repository_visibility": "private",
"repository_id": "74",
"repository_owner_id": "65",
"run_id": "example-run-id",
"run_number": "10",
"run_attempt": "2",
"runner_environment": "github-hosted",
"actor": "tanaka-taro",
"workflow": "example-workflow",
"head_ref": "",
"base_ref": "",
"event_name": "workflow_dispatch",
"ref_type": "branch",
"job_workflow_ref": "tanaka-org/tanaka-automation/.github/workflows/oidc.yml@refs/heads/main",
"iss": "https://token.actions.githubusercontent.com",
"nbf": 1632492967,
"exp": 1632493867,
"iat": 1632493567
}
3.4 Artifact Attestations(成果物の証明)
Artifact Attestationsは、ビルド成果物の来歴と整合性を暗号的に証明する仕組みです。
証明内容
- ワークフローへのリンク
- リポジトリ、組織、環境、コミットSHA、トリガーイベント
- OIDCトークンからの追加情報
SLSA レベル
Artifact Attestations単体でSLSA v1.0 Build Level 2を達成できます。再利用可能なワークフローと組み合わせることで、Level 3も実現可能です。
検証の重要性
証明を生成するだけではセキュリティ上の利点はありません。証明を検証することで、ソフトウェアの出所を確認し、セキュリティポリシーを適用できます。
# GitHub CLIを使用した検証
gh attestation verify artifact.tar.gz \
--owner tanaka-org
3.5 Kubernetesアドミッションコントローラー
Kubernetesクラスタで、Artifact Attestationsを強制するアドミッションコントローラーを設定できます。
TrustRootとClusterImagePolicy
- TrustRoot: 公開鍵情報の信頼できる配布チャネル
- ClusterImagePolicy: イメージに対する証明強制ポリシー
3.6 スクリプトインジェクション対策
ワークフローを作成する際は、信頼できない入力が実行される可能性を常に考慮する必要があります。
脆弱な例
- name: "PRタイトルのチェック"
run: |
title="${{ github.event.pull_request.title }}"
if [[ $title =~ ^tanaka ]]; then
echo "PRタイトルは'tanaka'で始まります"
fi
攻撃者がPRタイトルに a"; ls $GITHUB_WORKSPACE" を設定すると、任意のコマンドが実行されます。
安全な実装
- name: "PRタイトルのチェック"
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "$TITLE" =~ ^tanaka ]]; then
echo "PRタイトルは'tanaka'で始まります"
fi
環境変数を使用することで、インジェクションを防ぎます。
信頼できないコンテキスト
以下のコンテキストは潜在的に信頼できない入力として扱うべきです。
github.event.issue.titlegithub.event.issue.bodygithub.event.pull_request.titlegithub.event.pull_request.bodygithub.event.comment.bodygithub.head_ref
3.7 侵害されたランナーのリスク
Self-hosted runnersを使用する場合、ランナーが侵害された際のリスクを理解する必要があります。
潜在的な攻撃
- シークレットへのアクセス: 環境変数やディスク上のスクリプトからシークレットを取得
- GITHUB_TOKENの窃取: 自動的に割り当てられるトークンを盗む
- データの外部送信: HTTPリクエストで機密情報を外部サーバーに送信
- リポジトリコンテンツの改変: 書き込み権限を持つトークンでコードを変更
リポジトリ間アクセスの制御
GITHUB_TOKENは単一リポジトリにスコープされており、他のリポジトリへのアクセスは許可されません。ただし、以下の認証情報には注意が必要です。
4. パフォーマンスとコスト最適化
4.1 VariablesとContexts
4.1.1 Variables(変数)
変数は、機密性のない設定情報を保存・再利用するための仕組みです。
定義方法
- ワークフローファイル内
env:
NODE_VERSION: '18'
BUILD_CONFIG: 'production'
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Node.js $NODE_VERSION を使用"
- 組織・リポジトリ・環境レベル
設定画面から、複数のワークフローで共有する変数を定義できます。
4.1.2 Contexts(コンテキスト)
コンテキストは、ワークフロー実行に関する情報にアクセスするための仕組みです。
主なコンテキスト
-
github: イベント情報、リポジトリ情報など -
env: 環境変数 -
job: 現在のジョブ情報 -
steps: 前のステップの出力 -
runner: ランナー情報 -
secrets: シークレット -
matrix: マトリックス戦略の値
使用例
jobs:
production-check:
if: ${{ github.ref == 'refs/heads/main' }}
runs-on: ubuntu-latest
steps:
- run: echo "ブランチ $GITHUB_REF に本番デプロイ中"
4.2 Expressions(式)
式を使用すると、環境変数をプログラム的に設定したり、条件分岐を行えます。
基本構文
${{ <expression> }}
if条件での使用
ifキーワードでは、${{}}を省略できます。
jobs:
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: echo "メインブランチへのプッシュ時のみ実行"
複雑な式の例
env:
IS_PR: ${{ github.event_name == 'pull_request' }}
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
4.3 Dependency Caching(依存関係のキャッシュ)
依存関係をキャッシュすることで、ワークフローの実行時間を大幅に短縮できます。
キャッシュとArtifactsの違い
| 用途 | キャッシュ | Artifacts |
|---|---|---|
| 目的 | ジョブ間・ワークフロー実行間の再利用 | ワークフロー完了後の成果物保存 |
| 保存対象 | 変更頻度の低いファイル(依存関係など) | ビルド成果物、ログ、テスト結果 |
| 例 | node_modules、.gradle、pip cache | ビルドされたバイナリ、カバレッジレポート |
使用例
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Node.jsのセットアップとキャッシュ"
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run build
4.4 Artifacts(成果物)
Artifactsは、ジョブで生成されたファイルをワークフロー完了後も保存する仕組みです。
一般的なArtifacts
- ログファイルとコアダンプ
- テスト結果、失敗、スクリーンショット
- バイナリまたは圧縮ファイル
- ストレステストのパフォーマンス出力
- コードカバレッジ結果
使用例
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- name: "ビルド成果物のアップロード"
uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 30
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: "ビルド成果物のダウンロード"
uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- run: ./deploy.sh
4.5 Concurrency(同時実行制御)
デフォルトでは、複数のワークフローやジョブが同時に実行されます。concurrencyキーワードを使用すると、同時実行を制御できます。
デプロイメントの直列化
name: "デプロイメント"
on: push
concurrency:
group: production-deployment
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
ブランチごとの並行実行制御
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
これにより、同じブランチでの実行中のワークフローをキャンセルし、最新のコミットのみを実行できます。
4.6 Environments(環境)
環境を使用すると、デプロイメントターゲットを記述し、承認フロー、シークレット、保護ルールを設定できます。
環境の作成と使用
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- run: ./deploy.sh
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- run: ./deploy.sh
環境保護ルール
- 承認者の指定
- 待機タイマー
- デプロイメントブランチの制限
- カスタムデプロイメント保護ルール
4.7 メトリクスと可視性
GitHub Actionsは、組織とリポジトリレベルでメトリクスを提供します。
4.7.1 Usage Metrics(使用量メトリクス)
4.7.2 Performance Metrics(パフォーマンスメトリクス)
アクセス権限
- 組織オーナーは組織レベルのメトリクスを表示可能
- 「View organization Actions metrics」権限を持つユーザーも表示可能
- リポジトリの基本ロールを持つユーザーはリポジトリレベルのメトリクスを表示可能
4.8 課金とコスト管理
無料枠
- パブリックリポジトリ: GitHub-hosted runnersは無料
- Self-hosted runners: すべてのプランで無料
プライベートリポジトリの課金
| プラン | 無料時間/月 | ストレージ |
|---|---|---|
| Free | 500分 | 500 MB |
| Pro | 3,000分 | 1 GB |
| Team | 3,000分 | 2 GB |
| Enterprise | 50,000分 | 50 GB |
Larger runnersの課金
Larger runnersは分単位の課金で、無料時間の対象外です。実行時間のみが課金され、待機時間は課金されません。
コスト最適化のポイント
- キャッシュを活用して実行時間を短縮
- 不要なワークフロー実行をフィルタリング
- concurrencyで不要な実行をキャンセル
- 適切なランナータイプを選択
5. 実践的な活用パターン
5.1 マトリックス戦略による複数環境テスト
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
5.2 段階的なデプロイメント
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
- name: "スモークテスト"
run: ./smoke-tests.sh
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
5.3 依存関係の更新とPR作成
name: "依存関係の更新"
on:
schedule:
- cron: '0 0 * * 1' # 毎週月曜日
jobs:
update-dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "依存関係の更新"
run: |
npm update
npm audit fix
- name: "変更の確認"
id: check-changes
run: |
if [[ -n $(git status --porcelain) ]]; then
echo "changed=true" >> $GITHUB_OUTPUT
fi
- name: "PRの作成"
if: steps.check-changes.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b update-dependencies-$(date +%Y%m%d)
git add package*.json
git commit -m "依存関係の更新"
git push origin HEAD
gh pr create --title "依存関係の更新" --body "自動生成されたPRです"
env:
GH_TOKEN: ${{ github.token }}
5.4 コンテナイメージのビルドと証明
name: "コンテナイメージのビルド"
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: "Docker Buildxのセットアップ"
uses: docker/setup-buildx-action@v3
- name: "GitHub Container Registryへのログイン"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: "イメージのビルドとプッシュ"
id: push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: "証明の生成"
uses: actions/attest-build-provenance@v1
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
5.5 モノレポでの変更検知とテスト
name: "モノレポCI"
on:
pull_request:
paths:
- 'packages/**'
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
backend: ${{ steps.filter.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
frontend:
- 'packages/frontend/**'
backend:
- 'packages/backend/**'
test-frontend:
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd packages/frontend && npm ci && npm test
test-backend:
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd packages/backend && npm ci && npm test
6. まとめ
GitHub Actionsは、単なるCI/CDツールを超えて、開発ワークフロー全体を自動化できる強力なプラットフォームです。
重要なポイント
- 再利用性: Reusable Workflows、Composite Actions、Templatesを活用し、重複を排除
- 柔軟なランナー選択: GitHub-hosted、Larger、Self-hosted、ARCから最適なものを選択
- セキュリティファースト: Secrets、OIDC、Artifact Attestationsで安全性を確保
- パフォーマンス最適化: キャッシュ、Concurrency、適切なメトリクス監視で効率化
- コスト管理: 使用量メトリクスを活用し、最適なリソース配分を実現
GitHub Actionsを効果的に活用することで、開発チームの生産性を大幅に向上させ、高品質なソフトウェアを迅速にデリバリーできます。継続的な改善とベストプラクティスの適用により、さらなる効率化を実現していきましょう。