T-CREATOR

GitHub Actions で CI/CD パイプラインを構築する方法

GitHub Actions で CI/CD パイプラインを構築する方法

現代のソフトウェア開発において、継続的インテグレーション(CI)と継続的デプロイメント(CD)は、開発効率と品質向上のために不可欠な手法となっています。GitHub Actions を使用することで、これらのプロセスを自動化し、チーム開発をより効率的に進めることができます。

この記事では、GitHub Actions を使って CI/CD パイプラインを一から構築する方法を、初心者の方にもわかりやすく解説いたします。実際の Node.js アプリケーションを例に、基本的な設定から本番環境への自動デプロイまで、段階的に学んでいきましょう。

背景

GitHub Actions とは何か

GitHub Actions は、GitHub が提供する CI/CD プラットフォームです。リポジトリ内で発生するイベント(プッシュ、プルリクエスト作成など)をトリガーとして、自動的にワークフローを実行できます。

従来の外部 CI/CD サービスとは異なり、GitHub 内で完結するため設定が簡単で、GitHub との統合も非常にスムーズです。無料プランでも月 2,000 分の実行時間が提供されており、個人開発や小規模チームには十分な容量となっています。

GitHub Actions の基本構成要素を図で確認してみましょう。

mermaidflowchart LR
    event[イベント発生] -->|プッシュ/PR| workflow[ワークフロー実行]
    workflow --> job1[Job1: テスト]
    workflow --> job2[Job2: ビルド]
    job1 --> step1[ステップ1]
    job1 --> step2[ステップ2]
    job2 --> step3[ステップ3]
    step1 --> action1[アクション実行]
    step2 --> action2[アクション実行]
    step3 --> action3[アクション実行]

図で理解できる要点:

  • イベントトリガーからワークフロー実行までの流れ
  • 複数のジョブが並列実行される仕組み
  • 各ジョブ内でステップとアクションが順次実行される構造

CI/CD パイプラインの重要性

CI/CD パイプラインは、開発からデプロイまでの一連のプロセスを自動化するシステムです。これにより以下のメリットが得られます。

項目メリット具体的効果
1品質向上自動テストにより、バグの早期発見が可能
2開発速度向上手動作業の削減により、開発により集中できる
3デプロイリスク軽減自動化により、人的ミスを防止
4フィードバック高速化問題の即座な検出と通知

継続的インテグレーション(CI)では、コードの変更を頻繁にメインブランチに統合し、自動テストを実行します。継続的デプロイメント(CD)では、テストに合格したコードを自動的に本番環境にデプロイします。

従来の手動デプロイの課題

多くの開発チームが経験する手動デプロイの問題を見てみましょう。開発者がローカル環境でテストを行い、手動でサーバーにデプロイする従来の方法では、様々な課題が発生します。

mermaidflowchart TD
    dev[開発者] -->|手動アップロード| server[本番サーバー]
    dev -->|ローカルテスト| local[ローカル環境]
    server -->|エラー発生| error[緊急対応]
    error -->|手動修正| server
    local -->|環境差異| problem[予期しない問題]
    problem -->|デバッグ作業| dev

手動デプロイでは環境差異や人的ミスにより、予期しない問題が発生しやすくなります。

課題

手動デプロイの問題点

手動でのデプロイ作業には、以下のような深刻な問題があります。

環境の不整合 開発環境と本番環境の差異により、「ローカルでは動作するが本番で動作しない」という問題が頻繁に発生します。Node.js のバージョンや依存関係の違いが主な原因となります。

人的ミス 手動作業では、ファイルのアップロード忘れやコマンドの入力ミスなどが起こりやすく、サービス停止につながる可能性があります。

作業の属人化 特定の担当者しかデプロイ方法を知らない状況では、その人が不在の際にリリースができなくなってしまいます。

品質保証の困難さ

手動プロセスでは、一貫した品質保証が困難です。

テストの実行忘れ リリース前のテスト実行が属人的になり、重要なテストケースが実行されないことがあります。

コードレビューの抜け漏れ 急ぎのリリースでコードレビューをスキップしてしまい、後でバグが発見されるケースが多発します。

リグレッションテストの負担 新機能追加時に既存機能への影響を手動で確認するのは、時間もかかり見落としも発生しやすくなります。

チーム開発での課題

複数人での開発では、さらに複雑な問題が発生します。

マージ競合の頻発 各開発者が独立して作業し、最後にマージする際に大きな競合が発生することがあります。

デプロイタイミングの調整 複数の機能が同時に完成した場合、どの順番でデプロイするかの調整が必要になります。

責任範囲の曖昧さ 問題が発生した際に、誰がどの部分を担当していたかが不明確になりがちです。

これらの課題を解決するために、GitHub Actions を活用した CI/CD パイプラインの構築が重要になります。

解決策

GitHub Actions の基本設定

GitHub Actions を使用して CI/CD パイプラインを構築するための基本的な設定方法を説明します。

リポジトリの準備 まず、GitHub 上にリポジトリを作成し、ローカルに複製します。

bashgit clone https://github.com/your-username/your-project.git
cd your-project

ディレクトリ構造の確認 プロジェクトのルートディレクトリに以下の構造を作成します。

cssyour-project/
├── .github/
│   └── workflows/
│       └── ci-cd.yml
├── src/
├── package.json
└── README.md

GitHub Actions の設定ファイルは、.github​/​workflows​/​ ディレクトリ内に YAML 形式で記述します。

ワークフロー(.github/workflows)の作成

ワークフローファイルの基本構造を理解しましょう。

基本的なワークフローファイルの作成 .github​/​workflows​/​ci-cd.yml ファイルを作成し、以下の基本構造から始めます。

yaml# ワークフローの名前
name: CI/CD Pipeline

# トリガーとなるイベントを指定
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

イベントトリガーの設定 どのような場合にワークフローを実行するかを定義します。

yamlon:
  # メインブランチへのプッシュ時
  push:
    branches: [main]

  # プルリクエスト作成時
  pull_request:
    branches: [main]

  # 手動実行を許可
  workflow_dispatch:

  # スケジュール実行(毎日午前2時)
  schedule:
    - cron: '0 2 * * *'

実行環境の指定 ワークフローが実行される仮想環境を指定します。

yamljobs:
  build:
    # Ubuntu最新版を使用
    runs-on: ubuntu-latest

    strategy:
      matrix:
        # 複数のNode.jsバージョンでテスト
        node-version: [16.x, 18.x, 20.x]

基本的な CI/CD ステップの定義

CI/CD パイプラインの各ステップを段階的に定義していきます。

チェックアウトステップ まず、リポジトリのソースコードを取得します。

yamlsteps:
  # ソースコードのチェックアウト
  - name: Checkout code
    uses: actions/checkout@v4
    with:
      # 全履歴を取得(バージョンタグ使用時に必要)
      fetch-depth: 0

Node.js 環境のセットアップ 指定したバージョンの Node.js 環境を構築します。

yaml# Node.js環境のセットアップ
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: ${{ matrix.node-version }}
    # パッケージマネージャーのキャッシュを有効化
    cache: 'yarn'

依存関係のインストール プロジェクトの依存関係をインストールします。

yaml# 依存関係のインストール
- name: Install dependencies
  run: yarn install --frozen-lockfile

--frozen-lockfileオプションにより、yarn.lock ファイルを変更せずに正確な依存関係をインストールできます。

CI/CD パイプラインの全体的な流れを図で確認してみましょう。

mermaidflowchart TD
    start[コード変更] --> push[Git Push]
    push --> trigger[ワークフロー起動]
    trigger --> checkout[コードチェックアウト]
    checkout --> setup[環境セットアップ]
    setup --> install[依存関係インストール]
    install --> test[テスト実行]
    test --> build[ビルド実行]
    build --> deploy{デプロイ判定}
    deploy -->|成功| prod[本番デプロイ]
    deploy -->|失敗| notify[エラー通知]
    prod --> complete[完了]
    notify --> fix[修正作業]

図で理解できる要点:

  • コード変更から本番デプロイまでの自動化された流れ
  • 各ステップでの成功・失敗判定
  • エラー時の通知とフィードバックループ

具体例

Node.js アプリケーションの CI 設定

実際の Node.js アプリケーションを例に、具体的な CI 設定を実装してみましょう。

プロジェクトの初期設定 まず、サンプルの Node.js プロジェクトを準備します。

json{
  "name": "sample-node-app",
  "version": "1.0.0",
  "scripts": {
    "start": "node src/index.js",
    "test": "jest",
    "lint": "eslint src/",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "jest": "^29.5.0",
    "eslint": "^8.41.0",
    "webpack": "^5.82.1"
  }
}

基本的な Express アプリケーション シンプルな Web アプリケーションを作成します。

javascript// src/index.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;

// ヘルスチェック用エンドポイント
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'OK',
    timestamp: new Date().toISOString(),
  });
});

// メインエンドポイント
app.get('/', (req, res) => {
  res.json({
    message: 'Hello, GitHub Actions!',
    version: process.env.npm_package_version || '1.0.0',
  });
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

module.exports = app;

完全な CI ワークフロー設定 .github​/​workflows​/​ci.yml ファイルを作成し、包括的な CI 設定を実装します。

yamlname: Continuous Integration

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

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16.x, 18.x, 20.x]

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

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

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Run linter
        run: yarn lint

      - name: Run tests
        run: yarn test --coverage

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info

このワークフローでは、複数の Node.js バージョンでテストを実行し、コードカバレッジも取得します。

テスト自動化の実装

品質保証のためのテスト自動化を詳しく実装します。

Jest テストの設定 jest.config.js ファイルでテスト環境を設定します。

javascript// jest.config.js
module.exports = {
  // テスト環境の指定
  testEnvironment: 'node',

  // カバレッジレポートの設定
  collectCoverage: true,
  coverageDirectory: 'coverage',
  coverageReporters: ['text', 'lcov', 'html'],

  // テストファイルのパターン
  testMatch: [
    '**/__tests__/**/*.js',
    '**/?(*.)+(spec|test).js',
  ],

  // カバレッジ対象ファイル
  collectCoverageFrom: [
    'src/**/*.js',
    '!src/index.js', // エントリーポイントは除外
  ],

  // カバレッジの閾値設定
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};

ユニットテストの作成 src​/​__tests__​/​app.test.js ファイルで API エンドポイントをテストします。

javascript// src/__tests__/app.test.js
const request = require('supertest');
const app = require('../index');

describe('API Endpoints', () => {
  // ヘルスチェックエンドポイントのテスト
  test('GET /health should return OK status', async () => {
    const response = await request(app)
      .get('/health')
      .expect(200);

    expect(response.body).toHaveProperty('status', 'OK');
    expect(response.body).toHaveProperty('timestamp');
  });

  // メインエンドポイントのテスト
  test('GET / should return welcome message', async () => {
    const response = await request(app)
      .get('/')
      .expect(200);

    expect(response.body).toHaveProperty(
      'message',
      'Hello, GitHub Actions!'
    );
    expect(response.body).toHaveProperty('version');
  });

  // 存在しないエンドポイントのテスト
  test('GET /nonexistent should return 404', async () => {
    await request(app).get('/nonexistent').expect(404);
  });
});

統合テストの追加 より実際の使用状況に近い統合テストも実装できます。

javascript// src/__tests__/integration.test.js
const request = require('supertest');
const app = require('../index');

describe('Integration Tests', () => {
  test('Application should start and respond correctly', async () => {
    // 複数のエンドポイントを順次テスト
    await request(app).get('/health').expect(200);
    await request(app).get('/').expect(200);

    // レスポンス時間のテスト(100ms以内)
    const start = Date.now();
    await request(app).get('/');
    const responseTime = Date.now() - start;

    expect(responseTime).toBeLessThan(100);
  });
});

本番環境への自動デプロイ設定

テストに合格したコードを自動的に本番環境にデプロイする設定を実装します。

デプロイワークフローの作成 .github​/​workflows​/​deploy.yml ファイルでデプロイプロセスを定義します。

yamlname: Deploy to Production

on:
  push:
    branches: [main]
  workflow_run:
    workflows: ['Continuous Integration']
    types:
      - completed

jobs:
  deploy:
    runs-on: ubuntu-latest
    # CIワークフローが成功した場合のみ実行
    if: ${{ github.event.workflow_run.conclusion == 'success' }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18.x'
          cache: 'yarn'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Build application
        run: yarn build
        env:
          NODE_ENV: production

AWS S3 へのデプロイ設定 静的サイトや SPA の場合、AWS S3 への自動デプロイを設定できます。

yaml- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ap-northeast-1

- name: Deploy to S3
  run: |
    aws s3 sync ./dist s3://${{ secrets.S3_BUCKET_NAME }} --delete
    aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"

Docker を使用したデプロイ コンテナ化されたアプリケーションのデプロイも可能です。

yaml- name: Build Docker image
  run: |
    docker build -t ${{ secrets.DOCKER_REGISTRY }}/my-app:${{ github.sha }} .
    docker build -t ${{ secrets.DOCKER_REGISTRY }}/my-app:latest .

- name: Push to registry
  run: |
    echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
    docker push ${{ secrets.DOCKER_REGISTRY }}/my-app:${{ github.sha }}
    docker push ${{ secrets.DOCKER_REGISTRY }}/my-app:latest

デプロイ後の動作確認 デプロイが完了した後、実際にサービスが正常に動作しているかを確認します。

yaml- name: Health check
  run: |
    # デプロイ完了を待つ
    sleep 30

    # ヘルスチェックエンドポイントを確認
    curl -f ${{ secrets.PRODUCTION_URL }}/health || exit 1

    # レスポンス内容の確認
    response=$(curl -s ${{ secrets.PRODUCTION_URL }}/)
    echo "Deployment successful: $response"

- name: Notify deployment success
  if: success()
  uses: 8398a7/action-slack@v3
  with:
    status: success
    text: 'Production deployment completed successfully!'
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

デプロイプロセスの詳細な流れを図で確認してみましょう。

mermaidsequenceDiagram
    participant Dev as 開発者
    participant GitHub as GitHub
    participant Actions as GitHub Actions
    participant AWS as AWS S3/CloudFront
    participant Slack as Slack通知

    Dev->>GitHub: git push main
    GitHub->>Actions: ワークフロー起動
    Actions->>Actions: CI実行(テスト・ビルド)
    Actions->>Actions: CI成功確認
    Actions->>AWS: アプリケーションデプロイ
    AWS->>Actions: デプロイ完了
    Actions->>AWS: ヘルスチェック実行
    AWS->>Actions: 正常レスポンス
    Actions->>Slack: 成功通知送信
    Slack->>Dev: デプロイ完了通知

図で理解できる要点:

  • 開発者のコミットから本番反映までの自動化フロー
  • 各ステップでの成功確認と次ステップへの進行
  • デプロイ完了後の通知システム

これらの設定により、コードの変更から本番環境への反映まで、完全に自動化された CI/CD パイプラインが構築できます。

まとめ

GitHub Actions を使用した CI/CD パイプラインの構築について、基本的な概念から実際の実装まで詳しく解説してまいりました。

重要なポイントの再確認

自動化の効果 手動デプロイの課題を解決し、開発効率と品質の両方を大幅に向上させることができます。特に、人的ミスの削減と一貫した品質保証は、プロダクトの安定性向上に直結します。

段階的な導入 いきなり完全なパイプラインを構築するのではなく、まずは基本的なテスト自動化から始めて、段階的に機能を追加していくことが重要です。

チーム開発での価値 個人開発でも効果的ですが、複数人でのチーム開発においてその真価を発揮します。コードレビューの自動化、マージ競合の早期発見、責任範囲の明確化など、多くのメリットがあります。

CI/CD パイプライン構築のベストプラクティス

項目ベストプラクティス注意点
1小さく始める最初から完璧を目指さず、基本機能から開始
2テストファーストデプロイ前に必ずテストを実行する設計
3セキュリティ重視Secrets や Environments を適切に使用
4監視・通知失敗時の迅速な検知と対応体制の構築
5継続的改善定期的な見直しと最適化の実施

次のステップ

基本的な CI/CD パイプラインが構築できたら、以下のような発展的な機能の実装を検討してみてください。

高度な機能

  • 環境別デプロイ(staging、production)
  • ブルーグリーンデプロイメント
  • カナリアリリース
  • 自動ロールバック機能

監視・運用

  • パフォーマンスモニタリング
  • エラー追跡とアラート
  • ログ集約と分析
  • メトリクス収集と可視化

セキュリティ強化

  • 脆弱性スキャンの自動化
  • 依存関係の定期更新
  • セキュリティポリシーの適用
  • コンプライアンスチェック

GitHub Actions の豊富な機能を活用することで、開発チームの生産性を大幅に向上させることができます。まずは小さな自動化から始めて、徐々に範囲を拡大していくことをおすすめいたします。

継続的な改善と学習により、より効率的で安定した開発プロセスを構築していきましょう。

関連リンク

公式ドキュメント

学習リソース

コミュニティ