Tasuke Hubのロゴ

ITを中心に困っている人を助けるメディア

分かりやすく解決策を提供することで、あなたの困ったをサポート。 全ての人々がスムーズに生活できる世界を目指します。

【初心者向け】CI/CDの基本と実践:DevOpsの第一歩を踏み出そう

記事のサムネイル

CI/CDとは?初心者のための基本概念解説

CI/CD(Continuous Integration/Continuous Deployment)はモダンな開発プロセスを支える重要な概念です。シンプルに言えば、コードの統合と配信を自動化することで、開発チームの効率を高め、品質を向上させる手法です。「ソフトウェア開発の世界では、スピードが命」というスティーブ・ジョブズの言葉にあるように、CI/CDは開発サイクルを加速させる強力なツールなのです。

CI(継続的インテグレーション)は、開発者がコードを頻繁に共有リポジトリにマージし、自動テストを実行して問題を早期に発見するプラクティスです。一方、CD(継続的デリバリー/デプロイメント)は、CIを拡張して本番環境への配信まで自動化するものです。

TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者
graph LR
    A[コード変更] --> B[コミット]
    B --> C[自動ビルド]
    C --> D[自動テスト]
    D --> E[自動デプロイ]
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#bbf,stroke:#333,stroke-width:2px

CI/CDを導入することで得られる主なメリットは以下の通りです:

  1. バグの早期発見: 小さな変更ごとにテストを実行することで、問題を早期に特定できます
  2. リリースの加速: 手動プロセスを自動化し、リリースまでの時間を短縮します
  3. コードの品質向上: 自動テストと一貫した統合環境により、コード品質が向上します
  4. チームの連携強化: 全員が同じ自動化されたプロセスを共有することで、チームの連携が強化されます

CI/CDの導入は、小規模な開発チームでも大企業でも、開発サイクルを改善し、より確実に高品質なソフトウェアを提供するための重要なステップです。次のセクションでは、具体的なCI/CDツールと、それらをプロジェクトに導入する方法について解説します。

主要なCI/CDツール:初心者にもおすすめのツールを比較

現在、多くのCI/CDツールが利用可能ですが、ここでは初心者にも使いやすい主要なツールを紹介します。「最高のツールは、あなたが実際に使えるツールだ」というDevOpsの格言があるように、自分のプロジェクトやチームに合ったツールを選ぶことが重要です。

GitHub Actions

GitHubのリポジトリと直接統合されたCI/CDツールで、追加のサービスを利用する必要がなく、GitHub上で完結します。特にGitHubを既に使用しているチームにとっては、導入のハードルが最も低いでしょう。

# .github/workflows/simple-ci.yml
name: Simple CI

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

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18'
        
    - name: Install dependencies
      run: npm install
      
    - name: Run tests
      run: npm test

Jenkins

最も古くから使われているオープンソースのCI/CDツールの一つで、高度なカスタマイズが可能です。自社サーバーにインストールして利用するため、完全な制御が可能ですが、初期設定が少し複雑です。

// Jenkinsfile
pipeline {
    agent any
    
    stages {
        stage('Build') {
            steps {
                sh 'npm install'
            }
        }
        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sh 'npm run deploy'
            }
        }
    }
}

CircleCI

クラウドベースのCI/CDサービスで、シンプルな設定と高速な処理が特徴です。無料プランもあり、小規模プロジェクトから始めるのに適しています。

# .circleci/config.yml
version: 2.1
jobs:
  build:
    docker:
      - image: cimg/node:18.0.0
    steps:
      - checkout
      - run: npm install
      - run: npm test
      
workflows:
  version: 2
  build_and_test:
    jobs:
      - build

GitLab CI/CD

GitLabに組み込まれたCI/CDツールで、GitLabリポジトリを使用している場合に便利です。Docker統合が優れており、複雑なパイプラインも構築可能です。

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy

build-job:
  stage: build
  script:
    - npm install
  artifacts:
    paths:
      - node_modules/

test-job:
  stage: test
  script:
    - npm test

deploy-job:
  stage: deploy
  script:
    - npm run deploy
  only:
    - main

どのCI/CDツールを選ぶかは、以下の要素を考慮して決めることをおすすめします:

  1. 既存のインフラストラクチャとの互換性: 既に使用しているバージョン管理システムやクラウドサービスとの相性
  2. 学習曲線: チームメンバーがすぐに使いこなせるか
  3. スケーラビリティ: プロジェクトの成長に合わせて拡張できるか
  4. コスト: 無料プランの制限や、有料プランのコスト構造
  5. コミュニティサポート: 問題が発生した時に参考にできる資料や事例の充実度

次のセクションでは、これらのツールを使って実際にCI/CDパイプラインを構築する手順を、具体的な例を交えて解説します。

実践!GitHubActionsで始めるCI/CD入門

ここでは、最も導入しやすいGitHub Actionsを使って、JavaScriptプロジェクト用のCI/CDパイプラインを構築する手順を解説します。「理論より実践」というエンジニアリングの格言通り、実際に手を動かしながら学びましょう。

Step 1: リポジトリの準備

まず、GitHubのリポジトリに移動し、.github/workflowsディレクトリを作成します。ここがGitHub Actionsの設定ファイルを置く場所になります。

# ローカルでディレクトリを作成する場合
mkdir -p .github/workflows

Step 2: ワークフローファイルの作成

次に、.github/workflowsディレクトリにci-cd.ymlという名前のファイルを作成します。

# .github/workflows/ci-cd.yml
name: Node.js CI/CD

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

jobs:
  # ビルドとテストのジョブ
  build:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [16.x, 18.x]
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Node.js ${{ matrix.node-version }}のセットアップ
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    
    - name: 依存関係のインストール
      run: npm ci
    
    - name: ビルド
      run: npm run build --if-present
    
    - name: テスト実行
      run: npm test
    
    # ビルド成果物を保存
    - name: ビルド成果物のアップロード
      uses: actions/upload-artifact@v2
      with:
        name: build-files
        path: ./dist  # プロジェクトに合わせてパスを変更

  # デプロイジョブ(mainブランチへのpushのみ実行)
  deploy:
    needs: build  # buildジョブが成功した後に実行
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    
    steps:
    - name: ビルド成果物のダウンロード
      uses: actions/download-artifact@v2
      with:
        name: build-files
        path: ./dist
    
    - name: デプロイ(例:Netlifyへのデプロイ)
      run: |
        # ここに実際のデプロイコマンドを書きます
        # 例:Netlifyへのデプロイ
        # npm install -g netlify-cli
        # netlify deploy --dir=./dist --prod
      env:
        # 環境変数を設定(実際の値はGitHubのSecrets機能で管理)
        NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
        NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

Step 3: GitHub Secretsの設定

デプロイには通常、APIキーなどの機密情報が必要です。これらはGitHubのSecrets機能を使って安全に管理します。

  1. GitHubリポジトリの「Settings」タブを開く
  2. 左側のメニューから「Secrets and variables」→「Actions」を選択
  3. 「New repository secret」ボタンをクリック
  4. 必要な機密情報(例:NETLIFY_AUTH_TOKEN)を追加

Step 4: トリガーの確認

設定したワークフローは以下のタイミングで自動的に実行されます:

  • mainブランチへのpush時
  • mainブランチへのプルリクエスト作成/更新時

Step 5: 実行結果の確認

ワークフローが実行されると、GitHubリポジトリの「Actions」タブで進捗状況や結果を確認できます。失敗した場合は、ログを確認して問題を特定し修正します。

// 自動テストの例: tests/sample.test.js
const { sum } = require('../src/math');

test('1 + 2 = 3', () => {
  expect(sum(1, 2)).toBe(3);
});

test('5 + 7 = 12', () => {
  expect(sum(5, 7)).toBe(12);
});

CI/CDパイプラインでの自動テストは、開発の信頼性を高める重要な要素です。テストが通過しない限り、コードは次のステップに進めないようにすることで、品質を保証します。

このシンプルな例から始めて、徐々にプロジェクトの要件に合わせてパイプラインをカスタマイズしていくことをおすすめします。次のセクションでは、CI/CDパイプラインを最適化するためのベストプラクティスを紹介します。

CI/CDのベストプラクティス:効率的な開発フローのために

CI/CDパイプラインを効果的に活用するには、いくつかのベストプラクティスを押さえておくことが重要です。「準備と計画には時間をかけても、実行には時間をかけるな」というエンジニアリングの知恵の通り、事前の設計がスムーズな自動化につながります。

1. 小さく頻繁なコミット

CI/CDの効果を最大化するには、大きな変更を一度にコミットするのではなく、小さな変更を頻繁にコミットする習慣をつけることが重要です。

# 悪い例: 複数の機能を一度に大きくコミット
git add .
git commit -m "複数の新機能を追加、バグ修正、リファクタリングも実施"

# 良い例: 小さく分割したコミット
git add src/feature-a/
git commit -m "機能A: ログイン画面のUI改善"

git add src/bugfix/
git commit -m "修正: ナビゲーションメニューの表示バグを解消"

git add src/refactoring/
git commit -m "リファクタリング: ユーザー管理モジュールのコード整理"

小さなコミットには次のようなメリットがあります:

  • 問題が発生した場合の原因特定が容易になる
  • コードレビューがしやすくなる
  • マージコンフリクトのリスクが減少する

2. 効果的なテスト戦略

テストはCI/CDパイプラインの中核ですが、すべてのテストを毎回実行するのは時間がかかります。テストを種類別に分け、効率的に実行することが重要です。

// テストの種類別に分類する例
// 単体テスト: 高速で頻繁に実行(すべてのコミットで)
describe('Unit Tests', () => {
  test('sum function adds two numbers correctly', () => {
    expect(sum(1, 2)).toBe(3);
  });
});

// 統合テスト: やや時間がかかるが重要(PRごとに実行)
describe('Integration Tests', () => {
  test('API endpoint returns correct user data', async () => {
    const response = await api.getUser(1);
    expect(response.name).toBe('John Doe');
  });
});

// E2Eテスト: 時間がかかるため選択的に実行(mainへのマージ前など)
describe('E2E Tests', () => {
  test('User can login and navigate to dashboard', async () => {
    await page.goto('/login');
    await page.fill('#username', 'testuser');
    await page.fill('#password', 'password');
    await page.click('#login-button');
    expect(await page.isVisible('#dashboard')).toBe(true);
  });
});

テスト戦略のポイント:

  • 高速な単体テストを多用し、即座にフィードバックを得る
  • 統合テストと結合テストは、より重要なポイント(PRの作成時など)で実行
  • E2Eテストは、重要な変更や本番環境デプロイ前に実行

3. パイプラインのパフォーマンス最適化

CI/CDパイプラインが遅すぎると、開発のフィードバックループが遅くなり、効果が薄れます。パイプラインの実行時間を短縮するためのテクニックを紹介します。

# GitHub Actionsでキャッシュを活用する例
steps:
  - uses: actions/checkout@v2

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

  - name: Install Dependencies
    run: npm ci

パフォーマンス最適化のためのヒント:

  • 依存関係のキャッシュを活用する
  • 並列処理を活用して複数のジョブを同時に実行する
  • テスト実行の並列化(テストを複数のグループに分けて実行)
  • 不要なステップをスキップする条件を設定する

4. セキュリティの確保

CI/CDパイプラインはコードの品質だけでなく、セキュリティも確保する重要な場所です。以下のプラクティスを取り入れましょう。

# GitHub Actionsでセキュリティスキャンを実行する例
jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Run SAST scan
        uses: github/codeql-action/analyze@v1
        with:
          languages: javascript, typescript
      
      - name: Check for vulnerable dependencies
        run: npm audit

セキュリティ対策のポイント:

  • 静的コード解析(SAST)ツールでセキュリティの脆弱性を検出
  • 依存関係の脆弱性スキャンを定期的に実行
  • シークレット(APIキーなど)をコードに直接含めず、適切に管理
  • コンテナイメージのスキャンを実施(Dockerを使用する場合)

5. モニタリングとフィードバック

CI/CDパイプラインの効果を継続的に高めるには、パイプラインそのものをモニタリングし、改善することが重要です。

// パイプラインの実行時間を記録する簡易スクリプト例
const startTime = new Date();

// パイプラインの各ステップを実行...

const endTime = new Date();
const duration = (endTime - startTime) / 1000;
console.log(`パイプライン実行時間: ${duration}秒`);

// メトリクス記録(例:Prometheusへ送信など)
metrics.recordPipelineDuration(duration);

モニタリングのポイント:

  • パイプラインの実行時間を記録・分析し、遅延の原因を特定
  • 成功率と失敗率を監視し、不安定なテストを特定して改善
  • テストカバレッジの変化を追跡し、品質低下を防止
  • チーム全体にパイプラインの状態を可視化(ダッシュボードなど)

これらのベストプラクティスを取り入れることで、CI/CDパイプラインはより効率的かつ効果的になり、開発チームの生産性とソフトウェアの品質向上に大きく貢献します。

まとめ:CI/CDでプロジェクトの品質と生産性を向上させよう

この記事では、CI/CDの基本概念から具体的な導入方法、さらに効果的な活用のためのベストプラクティスまで解説してきました。「旅の終わりが新たな旅の始まり」というように、CI/CDの導入は継続的な改善の第一歩にすぎません。

CI/CDを始めるにあたって覚えておきたいポイントをまとめましょう:

  1. 小さく始め、段階的に拡張する:最初から完璧なパイプラインを目指すのではなく、基本的なビルドとテストから始めて、徐々に機能を追加していきましょう。

  2. 自動化の範囲を見極める:すべてを自動化することが必ずしも最適ではありません。チームの状況や、プロジェクトの要件に合わせて自動化の範囲を調整することが重要です。

  3. チーム全体の理解と協力を得る:CI/CDはツールだけでなく、開発文化の変革でもあります。チーム全体がその価値を理解し、積極的に活用することで効果が最大化します。

  4. 継続的な改善を忘れない:CI/CDパイプライン自体も定期的に見直し、より効率的で効果的なものへと改善していくことが大切です。

CI/CDの導入は一朝一夕で完了するものではありませんが、その効果は長期的に見ると非常に大きなものです。開発プロセスの自動化により、エンジニアはより創造的な作業に集中できるようになり、製品の品質と開発速度は向上していきます。

今回紹介したツールや手法をぜひ実際のプロジェクトに取り入れて、DevOpsの世界への第一歩を踏み出してみてください。自動化の力であなたのプロジェクトがさらに飛躍することを願っています!

"自動化とは、問題を解決することではなく、問題を解決する時間を作り出すことである。" - オートメーションの哲学

おすすめコンテンツ