【実践】Claude Code GitHub Actions on Self-hosted Runners環境構築ガイド - Xserver VPS編

## 📖 はじめに

このガイドでは、Xserver VPSを使って**Claude Code**を**GitHub Actions**で動かすための環境を構築します。実際の構築例を基に、初心者の方でも環境を作れるよう解説します。

### 用語解説
- **Self-hosted Runners**: GitHubのクラウドではなく、自分のサーバーでGitHub Actionsを実行する仕組み
    - About self-hosted runners - GitHub Docs
　　URL: https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners
　
- **Claude Code**: AIアシスタントClaudeをGitHubで使えるようにするツール
    - Claude Code 概要 - Anthropic
    　URL: https://docs.anthropic.com/ja/docs/claude-code/overview
　
- **PM2**: Node.jsアプリケーションを永続的に動かすためのプロセスマネージャー
    - PM2 - Home
    　　URL: https://pm2.keymetrics.io/
　
## 🔧 前提条件

### 必要な環境
- **VPS**: Xserver VPS（Ubuntu 22.04.4 LTS）
- **メモリ**: 2GB以上
- **ストレージ**: 20GB以上の空き容量
- **GitHubアカウント**: リポジトリの管理者権限

### 今回の環境情報
```
OS: Ubuntu 22.04.4 LTS
```

## 📝 構築手順

### Step 1: VPSへの接続

```bash
# MacやLinuxの場合
ssh -i /path/to/your-key.pem root@xxx.xx.xx.xxx

# Windowsの場合はPuTTYなどを使用
```

### Step 2: 専用ユーザーの作成

セキュリティのため、GitHub Actionsランナー専用のユーザーを作成します。

```bash
# runnerユーザーを作成
sudo useradd -m -s /bin/bash runner

# パスワードを設定
sudo passwd runner
# パスワードを2回入力
```

**ポイント**: rootユーザーでランナーを実行することはできません（セキュリティ上の制限）

### Step 3: 必要なソフトウェアのインストール

```bash
# システムを最新に更新
sudo apt update

# 必要なツールを一括インストール
sudo apt install -y curl git nodejs npm unzip jq

# Node.jsのバージョン確認（v16以上が必要）
node --version  # v20.19.1
npm --version   # 10.8.2
```

### Step 4: GitHub Actionsランナーのダウンロード

```bash
# ランナー用ディレクトリを作成
mkdir /root/actions-runner && cd /root/actions-runner

# GitHubから最新のランナーをダウンロード
curl -o actions-runner-linux-x64-2.324.0.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.324.0/actions-runner-linux-x64-2.324.0.tar.gz

# ファイルの整合性を確認
echo "e8e24a3477da17040b4d6fa6d34c6ecb9a2879e800aa532518ec21e49e21d7b4  actions-runner-linux-x64-2.324.0.tar.gz" | shasum -a 256 -c

# 解凍
tar xzf ./actions-runner-linux-x64-2.324.0.tar.gz
```

### Step 5: ランナーをrunnerユーザーの場所に移動

```bash
# runnerユーザーのホームディレクトリに移動
mv /root/actions-runner /home/runner/

# 所有権を変更
chown -R runner:runner /home/runner/actions-runner

# runnerユーザーに切り替え
su - runner
cd ~/actions-runner
```

### Step 6: GitHubでランナーを登録

1. GitHubリポジトリの **Settings** → **Actions** → **Runners** を開く
2. **New self-hosted runner** をクリック
3. 表示されるトークンをコピー

### Step 7: ランナーの設定

```bash
# GitHubから提供されるコマンドを実行（トークンは自分のものを使用）
./config.sh --url https://github.com/YOUR-USERNAME/YOUR-REPO --token YOUR-TOKEN

# 設定時の質問と回答例：
# Runner group: [Enter]を押す（デフォルト）
# Runner name: xxxx-runner（わかりやすい名前を入力）
# Additional labels: [Enter]を押す（スキップ）
# Work folder: [Enter]を押す（デフォルトの_work）
```

### Step 8: PM2でランナーを永続化

**なぜPM2を使うのか？**
- systemdより簡単に設定できる
- Node.jsアプリケーションの管理に特化
- ログの確認が簡単

```bash
# PM2をグローバルにインストール（まだの場合）
npm install pm2@latest -g

# ランナーをPM2で起動
pm2 start ./run.sh --name "xxxx-runner"

# 状態を確認
pm2 status

# ログを確認
pm2 logs xxxx-runner

# 設定を保存（再起動後も自動で起動）
pm2 save

# 自動起動の設定
pm2 startup
# 表示されたコマンドをrootユーザーで実行する必要があります
```

### Step 9: Bunのインストール（Claude Code用）

「error: unzip is required to install bun」というエラーが出たため、まずunzipをインストール：

```bash
# rootユーザーに戻る
exit

# unzipをインストール
sudo apt install -y unzip

# runnerユーザーに戻る
su - runner

# Bunをインストール
curl -fsSL https://bun.sh/install | bash

# パスを通す（既に自動で追加されているが確認のため）
echo 'export BUN_INSTALL="$HOME/.bun"' >> ~/.bashrc
echo 'export PATH="$BUN_INSTALL/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

# バージョン確認
bun --version  # 1.2.15
```

### Step 10: jqのインストール（Claude Code Action用）

```bash
# rootユーザーで実行
exit
sudo apt install -y jq

# バージョン確認
jq --version  # jq-1.6
```

### Step 11: PM2でランナーを再起動

```bash
# runnerユーザーで実行
su - runner
pm2 restart xxxx-runner
```

## 🔍 動作確認

### GitHubでの確認
1. **Settings** → **Actions** → **Runners** を開く
2. **Idle**（待機中）状態になっていることを確認
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435233/12e276a6-c6e3-4307-803c-6ed892460770.png)


### VPSでの確認
```bash
# PM2でプロセスを確認
sudo -u runner pm2 list

# 出力例：
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
│ 0  │ xxxx-runner        │ fork     │ 2    │ online    │ 0%       │ 3.5mb    │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
```

## 📄 GitHub Actionsワークフローの設定

`.github/workflows/claude.yml`を作成：

```yaml
name: Claude Code

on:
    issue_comment:
        types: [created]
    pull_request_review_comment:
        types: [created]
    issues:
        types: [opened, assigned]
    pull_request_review:
        types: [submitted]

jobs:
    claude:
        if: |
            (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
            (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
            (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
            (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
        
        # ここが重要！セルフホストランナーを指定
        runs-on: self-hosted
        
        permissions:
            contents: write
            pull-requests: write
            issues: write
            id-token: write
        
        steps:
            - name: Checkout repository
              uses: actions/checkout@v4
              with:
                  fetch-depth: 1

            - name: Run Claude Code
              id: claude
              uses: anthropics/claude-code-action@beta
              with:
                  anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
                  allowed_tools: |
                    mcp__github__create_issue,
                    mcp__github__create_pull_request,
                    Edit,Replace,View
```

## ⚠️ よくあるトラブルと解決方法

### 1. "Must not run with sudo"エラー
- **原因**: rootユーザーで設定しようとした
- **解決**: runnerユーザーに切り替えて実行

### 2. "unzip is required"エラー
- **原因**: unzipがインストールされていない
- **解決**: `sudo apt install -y unzip`

### 3. "jq: not found"エラー
- **原因**: jqがインストールされていない
- **解決**: `sudo apt install -y jq`

### 4. ランナーがOffline
- **原因**: PM2プロセスが停止している
- **解決**: `pm2 restart xxxx-runner`

## 🔒 セキュリティのベストプラクティス

1. **プライベートリポジトリでのみ使用**
   - パブリックリポジトリでは悪意のあるコードが実行される可能性

2. **Bashコマンドの制限**
   ```yaml
   allowed_tools: |
     Bash(npm install),
     Bash(npm test),
     Bash(git status)
   ```

3. **定期的なアップデート**
   ```bash
   sudo apt update && sudo apt upgrade -y
   ```

## 📊 リソース監視

```bash
# CPU/メモリ使用状況
htop

# ディスク容量
df -h

# PM2のログ
pm2 logs

# 全体のステータス
pm2 monit
```

## 🎯 PM2 vs systemd

今回はPM2を使用しましたが、それぞれの特徴：

| PM2 | systemd |
|-----|---------|
| Node.js向けに最適化 | Linuxの標準サービス管理 |
| 設定が簡単 | より細かい制御が可能 |
| ログ管理が便利 | システム統合が強い |
| `pm2 status`で確認 | `systemctl status`で確認 |

## まとめ

これで、Xserver VPSでClaude Code GitHub Actions Self-hosted Runnersの環境が完成しました！

**次のステップ**:
1. mainブランチにワークフローファイルをpush
2. IssueやPRで`@claude`をメンション
3. 自分のVPSでClaude Codeが動作することを確認

**メリット**:
- ✅ GitHub Actionsの無料枠を消費しない
- ✅ 完全に自分でコントロール可能
- ✅ 他のアプリケーション（Difyなど）と共存可能

質問があれば、GitHubのIssueで聞いてください！