shibomb

手動デプロイはもう卒業!GitHub ActionsでCI/CDを構築しWebアプリ開発を自動化

Webアプリ開発でコードを修正した後、毎回手動でサーバーに接続し、ファイルをアップロードし、サービスを再起動する…そんな作業にうんざりしていませんか?「今回は大丈夫」と思っていても、ちょっとした手順ミスでサービスが停止してしまったり、デプロイに時間がかかって本来の開発に集中できなかったりするのは、多くの開発者が経験する悩みです。この記事では、そんな手動デプロイの課題を解決する CI/CD という仕組みを、GitHub Actions を使って導入する方法をゼロから解説します。あなたの開発プロセスを自動化し、爆速かつ安全なデプロイを実現しましょう!

CI/CDとは?Web開発における継続的インテグレーションと継続的デリバリーの重要性

CI/CD は、現代のWebアプリ開発において欠かせないプラクティス(習慣・実践方法)の一つです。これは2つの主要な概念から成り立っています。

  • CI (Continuous Integration / 継続的インテグレーション) これは、開発者が書いたコードを、頻繁にメインのリポジトリに統合(マージ)し、そのたびに自動でビルドやテストを実行するプロセスのことです。チームで開発していると、複数の人が同時に異なる部分を修正します。それらの変更をこまめに統合してテストすることで、「Aさんの修正がBさんの修正と衝突して動かなくなった」といった問題を早期に発見できます。CIは、コードの品質を常に高く保つための「番人」のような役割を果たします。

  • CD (Continuous Delivery or Deployment / 継続的デリバリー or 継続的デプロイ) これは、CIで品質が保証されたコードを、いつでも本番環境にリリースできる状態に保つ(デリバリー)、あるいは自動的にリリースする(デプロイメント)プロセスです。テストを通過したアプリケーションは、ボタン一つで、あるいは特定のブランチにマージされたタイミングで、自動的にユーザーが利用する本番環境に反映されます。

なぜこれが重要なのでしょうか?手動でのリリース作業は、手順が複雑になるほど人的ミスが起こりやすくなります。CI/CDを導入することで、ビルド、テスト、デプロイといった一連の作業が自動化されるため、ミスが劇的に減り、開発者は安心して新しい機能の開発に集中できます。結果として、より速く、より安定して価値をユーザーに届けられるようになるのです。

CI/CDパイプラインの全体像:開発から本番リリースまでの流れ

CI/CDの一連の自動化された処理の流れを パイプライン と呼びます。パイプラインは、開発者がコードをリポジトリに push した瞬間から始まります。具体的な流れを見ていきましょう。

  1. コードのPush: 開発者がローカル環境で開発したコードを、GitHubなどのバージョン管理システムのリポジトリに push します。
  2. CIのトリガー: push をきっかけに、CI/CDツール(今回はGitHub Actions)が自動的に処理を開始します。
  3. ビルド: ソースコードを、実際にサーバーで動作する形式に変換します。例えば、JavaScriptのコードを圧縮したり、TypeScriptをコンパイルしたりする処理です。
  4. テスト: ユニットテストや結合テストなど、あらかじめ用意されたテストコードを自動で実行します。ここでバグが見つかれば、パイプラインは停止し、開発者に通知が送られます。
  5. デプロイ: テストにすべて合格したら、アプリケーションが本番環境やステージング環境(本番に近いテスト環境)に自動で配置(デプロイ)されます。

このパイプラインが整備されていることで、開発者は「自分のコードがテストをパスして、問題なくデプロイされる」という安心感を得られます。もし問題があれば、すぐに検知して修正できるため、大きなトラブルに発展する前に対処できるのです。

GitHub Actionsで始めるCI/CD:基本的な設定とワークフローの作成

CI/CDを実現するツールはたくさんありますが、近年特に人気なのが GitHub Actions です。GitHubにリポジトリがあれば追加の設定なしですぐに利用でき、多くの機能を無料で始められるのが大きな魅力です。

GitHub Actionsでは、ワークフロー と呼ばれるファイルに自動化したい処理を記述します。ワークフローはYAMLという形式で記述し、リポジトリ内の .github/workflows/ というディレクトリに配置します。

まずは、GitHub Actionsの主要な構成要素を理解しましょう。

  • Workflow (ワークフロー): 自動化するプロセス全体を定義したもの。1つのリポジトリに複数のワークフローを作成できます。
  • Event (イベント): ワークフローを開始させるきっかけ。pushpull_request の作成、手動実行など、様々なイベントを指定できます。
  • Job (ジョブ): ワークフローを構成する一連のステップの集まり。複数のジョブを並列または直列に実行できます。
  • Step (ステップ): ジョブの中で実行される個別のタスク。コマンドを実行したり、特定のアクションを呼び出したりします。
  • Action (アクション): ワークフローの中で再利用できるコマンドのまとまり。例えば、リポジトリのコードをチェックアウトする (actions/checkout) や、特定のプログラミング言語環境をセットアップする (actions/setup-node) など、GitHubやコミュニティが提供する多くのアクションを利用できます。

例えば、main ブランチにコードがプッシュされたときに「Hello, World!」と表示するだけの簡単なワークフローは、以下のようになります。

# .github/workflows/hello-world.yml

name: Hello World Workflow

on:
  push:
    branches:
      - main

jobs:
  say-hello:
    runs-on: ubuntu-latest
    steps:
      - name: Greet the user
        run: echo "Hello, World!"

このファイルをリポジトリに追加して push するだけで、GitHubの「Actions」タブから実行結果を確認できます。このように、YAMLファイル一つで簡単に自動化を始められるのがGitHub Actionsの強みです。

実践!シンプルなWebアプリでCIパイプラインを構築する(テスト・ビルドの自動化)

それでは、実際にシンプルなNode.jsのWebアプリケーションを例に、CIパイプラインを構築してみましょう。ここでは、リポジトリにコードが push されるたびに、自動でテストとビルドが実行されるようにします。

前提条件

  • GitHubリポジトリにNode.jsのプロジェクトがある。
  • package.json に、依存パッケージをインストールする npm install、テストを実行する npm test、アプリケーションをビルドする npm run build のスクリプトが設定されている。

この条件を満たすプロジェクトで、.github/workflows/ci.yml というファイルを以下の内容で作成します。

# .github/workflows/ci.yml

name: Node.js CI

# mainブランチへのpush、または任意のブランチへのpull_requestでワークフローを実行
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build-and-test:
    # 実行環境として、最新のUbuntuを使用
    runs-on: ubuntu-latest

    strategy:
      matrix:
        # Node.jsのバージョン22と24でテストを実行
        node-version: [22.x, 24.x]

    steps:
      # 1. リポジトリのコードをチェックアウト
      - name: Checkout repository
        uses: actions/checkout@v4

      # 2. 指定したバージョンのNode.js環境をセットアップ
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      # 3. 依存パッケージをインストール
      - name: Install dependencies
        run: npm ci

      # 4. ビルドを実行
      - name: Build application
        run: npm run build --if-present

      # 5. テストを実行
      - name: Run tests
        run: npm test

このワークフローファイルを追加してGitHubに push すると、自動的に以下の処理が実行されます。

  1. チェックアウト: リポジトリの最新コードを取得します。
  2. Node.jsセットアップ: matrix で指定したNode.jsのバージョン(22.xと24.x)の環境をそれぞれ準備します。これにより、複数のバージョンでアプリが正しく動作するかを同時に確認できます。
  3. 依存関係のインストール: npm ci コマンドで package-lock.json に基づいて高速かつ正確にパッケージをインストールします。
  4. ビルドとテスト: npm run buildnpm test を実行し、コードに問題がないか検証します。

いずれかのステップでエラーが発生すると、パイプラインは失敗し、GitHub上で通知されます。これで、コードの品質を自動でチェックするCIパイプラインが完成しました。

CDパイプラインでデプロイを自動化する(本番環境へのリリース)

CIパイプラインで品質が確認できたら、次はいよいよデプロイの自動化、つまりCDパイプラインの構築です。ここでは、main ブランチへのマージが成功したら、VPS(仮想専用サーバー)にSSH経由で自動デプロイするシナリオを考えます。

重要:機密情報の取り扱い デプロイには、サーバーのIPアドレスやSSHの秘密鍵といった機密情報が必要です。これらの情報をワークフローファイルに直接書き込むのは非常に危険です。GitHubには Secrets という機能があり、機密情報を安全に保存してワークフローから参照できます。

リポジトリの Settings > Secrets and variables > Actions から、以下の情報を登録しておきましょう。

  • HOST: サーバーのホスト名またはIPアドレス
  • USERNAME: サーバーにログインするユーザー名
  • SSH_PRIVATE_KEY: サーバーに接続するためのSSH秘密鍵

それでは、先ほどの ci.yml にデプロイ用のジョブを追加します。

# .github/workflows/ci.yml

name: Node.js CI/CD

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

jobs:
  build-and-test:
    # (先ほどのビルドとテストのジョブ内容は同じ)
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [22.x, 24.x]
    steps:
      - uses: actions/checkout@v4
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm run build --if-present
      - run: npm test

  deploy:
    # build-and-testジョブが成功したら実行
    needs: build-and-test
    # pull_requestではなく、mainブランチへのpushの時だけ実行
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /var/www/my-app
            git pull origin main
            npm install --production
            pm2 restart app

このワークフローでは、deploy という新しいジョブを追加しました。

  • needs: build-and-test: build-and-test ジョブがすべて成功した場合にのみ、deploy ジョブが実行されるように依存関係を設定しています。
  • if: github.event_name == 'push' && github.ref == 'refs/heads/main': このジョブは、Pull Requestの時ではなく、main ブランチに直接 push(またはマージ)された時だけ実行されるように条件を指定しています。
  • appleboy/ssh-action: SSH接続を簡単に行うための人気のアクションです。with 以下に、先ほどGitHub Secretsに登録した情報を ${{ secrets.HOGE }} という形式で指定します。
  • script: サーバー上で実行したいコマンドを記述します。ここでは、アプリケーションのディレクトリに移動し、最新のコードを pull し、本番用の依存関係をインストールし、pm2(プロセス管理ツール)でアプリケーションを再起動しています。

これで、main ブランチにコードがマージされると、テストからデプロイまでが完全に自動化されるCI/CDパイプラインが完成しました!

CI/CD導入で得られる圧倒的なメリットと、さらにステップアップするためのヒント

CI/CDパイプラインを導入することで、開発プロセスは劇的に改善されます。手動デプロイの恐怖から解放され、開発者はより創造的な作業に集中できます。人的ミスが減り、品質は安定し、ユーザーへ素早く価値を届けられるようになります。これは、個人開発でもチーム開発でも、計り知れないメリットをもたらします。

今回構築したパイプラインは基本的なものですが、ここからさらに発展させることができます。

  • ブランチ戦略との連携: develop ブランチやフィーチャーブランチへのPull Request作成時にテストを必須にすることで、main ブランチの品質をさらに高められます。
  • コンテナ化: Dockerを使ってアプリケーションをコンテナ化し、GitHub ActionsでDockerイメージをビルドしてコンテナレジストリに push するようにすれば、開発環境と本番環境の差異をなくし、より安定したデプロイが可能です。
  • 高度なデプロイ戦略: 本番環境への影響を最小限に抑えるため、一部のユーザーにだけ新バージョンを公開する「カナリアリリース」や、新旧環境を瞬時に入れ替える「ブルーグリーンデプロイメント」といった高度な戦略もCI/CDパイプラインに組み込めます。

まずは小さな自動化から始めてみてください。一度CI/CDの便利さを体験すれば、もう手動デプロイの世界には戻れなくなるはずです。あなたのWebアプリ開発を、今日から変えていきましょう!

関連記事