mcpField
S
server-deploy

server-deploy

github.com/majiayu000クラウド・DevOps28862 回インストール今日更新
リポジトリ

server-deploy について

server-deployは、ローカルのプロジェクトをリモートのLinuxサーバーへ配置するデプロイスキルです。ファイルからプロジェクト種別を判別し、環境構築、ビルド、プロセス管理、Webサーバー設定の手順を選びます。

主な機能

  1. 01Node.js、Python、Rust、Go、Docker、静的サイトの判別
  2. 02リモートサーバーの環境とリソースの確認
  3. 03SSHとrsyncによるプロジェクト転送
  4. 04種別に応じた依存関係の導入、ビルド、起動
  5. 05Nginxのリバースプロキシまたは静的ホスティング設定
  6. 06ドメインとSSLの設定手順

活用シーン

  1. 01ローカルアプリのリモートLinuxサーバーへのデプロイ
  2. 02種別に応じたビルドとプロセス管理方法の選択
  3. 03デプロイ先でのドメイン、Nginx、SSLの設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/majiayu000/spellbook --skill server-deploy

インストール後、Claude Code で/server-deployを入力して読み込まれたか確認します。

同梱ファイルreference/extended.md

詳細

汎用サーバーのデプロイ

ローカルプロジェクトをリモート Linux サーバーにワンクリックでデプロイします。プロジェクトの種類を自動識別し、適切なビルド、実行、プロセス管理の方法を適用します。


第零ステップ:プロジェクトの識別と情報収集

0a. プロジェクトの種類を自動識別

プロジェクトのルートディレクトリをスキャンし、以下のルールに従って種類を判定します。

目印となるファイルプロジェクトの種類ランタイムプロセス管理
package.jsonNode.jsnodePM2
Cargo.tomlRustコンパイル済み成果物systemd
go.modGoコンパイル済み成果物systemd
pyproject.toml / requirements.txt / setup.pyPythonpython3systemd / gunicorn
Dockerfile / docker-compose.ymlDockerdockerdocker compose
index.html(ほかに目印となるファイルがない場合)静的サイトなしNginx で直接ホスティング

複数の目印となるファイル(例:package.json と Dockerfile)が検出された場合は、AskUserQuestion を使ってユーザーにデプロイ方法を選択してもらいます。

0b. 情報を収集

AskUserQuestion を使って以下の情報を収集します(コンテキストにすでにある情報は省略します)。

  1. SSH 接続(header: "サーバー")

    • コンテキストに基づいて選択肢を動的に生成します(例:以前に使用したサーバー IP)
    • 予備の選択肢として「その他のサーバー」
  2. プロジェクトのパス(header: "プロジェクト")

    • 現在の作業ディレクトリから自動的に推定します
    • 不明な場合はユーザーに尋ねます
  3. ドメイン(header: "ドメイン")

    • ドメインがある場合(ユーザーに入力してもらいます)
    • IP のみでアクセスする場合(ドメインと SSL の設定を省略します)
  4. 実行ポート(header: "ポート")

    • 自動検出:
      • Node.js:package.json の scripts または .env から抽出
      • Python:main.py / app.py / manage.py から抽出
      • Rust/Go:main.rs / main.go または設定ファイルから抽出
    • 検出できない場合は、ユーザーに指定してもらいます
    • 3000, 3001, 4000, 5000, 5173, 8000, 8080, 8888 の使用は禁止です
0c. 変数の定義
変数説明
$SSH_TARGETSSH 接続文字列(例:[email protected])
$PROJECT_DIRローカルプロジェクトの絶対パス
$PROJECT_NAMEプロジェクト名
$PROJECT_TYPEプロジェクトの種類:nodejs / python / rust / go / docker / static
$REMOTE_DIRリモートのデプロイ先ディレクトリ(デフォルトは /opt/$PROJECT_NAME)
$PORTアプリケーションの実行ポート
$DOMAINドメイン(任意)

第一步:SSH 接続と鍵の設定

1a. ローカルの SSH 公開鍵を確認
cat ~/.ssh/id_rsa.pub 2>/dev/null || cat ~/.ssh/id_ed25519.pub 2>/dev/null

公開鍵がない場合は、次のコマンドで生成します:ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519

1b. パスワードなしでログインできるかテスト
ssh -o ConnectTimeout=5 -o BatchMode=yes $SSH_TARGET "echo ok" 2>/dev/null

失敗した場合は、パスワードでログインして公開鍵を追加します。

sshpass -p '<PASSWORD>' ssh -o StrictHostKeyChecking=no $SSH_TARGET \
  "mkdir -p ~/.ssh && chmod 700 ~/.ssh && echo '<PUBLIC_KEY>' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
1c. パスワードなしでログインできることを確認
ssh -o ConnectTimeout=5 $SSH_TARGET "echo '免密登录成功' && uname -a"

第二步:サーバー環境の確認

1つのコマンドですべての情報を取得します:

ssh $SSH_TARGET "echo '=== 系统 ===' && cat /etc/os-release | grep PRETTY_NAME && uname -m && echo '=== CPU ===' && nproc && echo '=== 内存 ===' && free -h && echo '=== 磁盘 ===' && df -h / && echo '=== 包管理器 ===' && which apt yum dnf pacman 2>/dev/null && echo '=== Node.js ===' && node -v 2>/dev/null || echo '未安装' && echo '=== Python ===' && python3 --version 2>/dev/null || echo '未安装' && echo '=== Rust ===' && rustc --version 2>/dev/null || echo '未安装' && echo '=== Go ===' && go version 2>/dev/null || echo '未安装' && echo '=== Docker ===' && docker --version 2>/dev/null || echo '未安装' && echo '=== Nginx ===' && nginx -v 2>&1 || echo '未安装'"

確認ポイント:

  • ディスク容量が十分か
  • メモリ:Node.js は 512MB 以上、Rust のコンパイルは 1GB 以上(またはローカルでクロスコンパイル)
  • アーキテクチャ:x86_64 / aarch64(Rust/Go はコンパイルターゲットを一致させる必要があります)
  • 不足しているコンポーネントを記録し、後でまとめてインストールします

第三步:ランタイム環境のインストール

$PROJECT_TYPE に応じて、該当するランタイムをインストールします。不足しているコンポーネントのみをインストールしてください。

Node.js プロジェクト
# 安装 Node.js 20(如未安装)
ssh $SSH_TARGET "curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs"
# 安装 PM2
ssh $SSH_TARGET "npm install -g pm2"
Python プロジェクト
# 安装 Python3 + pip + venv(如未安装)
ssh $SSH_TARGET "apt-get install -y python3 python3-pip python3-venv"
Rust プロジェクト

サーバーのリソースに応じて、次の 2 つの方法から選択します。

方法 A:サーバー上でコンパイル(メモリ 1GB 以上)

ssh $SSH_TARGET "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source ~/.cargo/env"

方法 B:ローカルでクロスコンパイル(低スペックサーバーに推奨)

# 本地编译(需安装对应 target)
rustup target add x86_64-unknown-linux-gnu  # 或 aarch64-unknown-linux-gnu
cargo build --release --target x86_64-unknown-linux-gnu
# 仅上传编译产物
Go プロジェクト

同様に、次の 2 つの方法があります。

方法 A:サーバー上でコンパイル

ssh $SSH_TARGET "wget -q https://go.dev/dl/go1.22.0.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz && echo 'export PATH=\$PATH:/usr/local/go/bin' >> ~/.bashrc"

方法 B:ローカルでクロスコンパイル(推奨)

GOOS=linux GOARCH=amd64 go build -o $PROJECT_NAME .
# 仅上传二进制文件
Docker プロジェクト
# 安装 Docker(如未安装)
ssh $SSH_TARGET "curl -fsSL https://get.docker.com | sh && systemctl enable docker && systemctl start docker"
Nginx(ドメインがある場合は必須)
ssh $SSH_TARGET "apt-get install -y nginx && systemctl enable nginx && systemctl start nginx"

第四步:プロジェクトをアップロード

4a. 除外するもの(プロジェクトの種類別)
プロジェクトの種類除外するディレクトリ/ファイル
Node.jsnode_modules/, .next/, dist/, .git/
Python__pycache__/, .venv/, venv/, *.pyc, .git/
Rusttarget/, .git/(方法 B では target/release/$BINARY のみをアップロード)
Govendor/(ある場合), .git/(方法 B ではバイナリのみをアップロード)
Docker.git/(Dockerfile のアップロードが必要です)
静的サイト.git/, node_modules/
4b. rsync でアップロード

注意:大きなファイルを転送する際に SSH 接続が切断されることがあります。接続維持用のパラメーターを使用してください。

rsync -avz --timeout=300 \
  -e "ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=10" \
  --exclude='.git' --exclude='<TYPE_SPECIFIC_EXCLUDES>' \
  $PROJECT_DIR/ $SSH_TARGET:$REMOTE_DIR/

ファイルが非常に大きい場合(>500MB):まずコードをアップロードし、大きなファイルは個別にアップロードします。

SSH 接続が頻繁に切断される場合:サーバーの SSH 設定を調整します。

ssh $SSH_TARGET "sed -i 's/#ClientAliveInterval.*/ClientAliveInterval 60/' /etc/ssh/sshd_config && sed -i 's/#ClientAliveCountMax.*/ClientAliveCountMax 120/' /etc/ssh/sshd_config && systemctl reload ssh || systemctl reload sshd"
4c. 依存関係のインストールとビルド

接続が切断されても中断されないよう、nohup を使ってバックグラウンドで実行します。

プロジェクトの種類コマンド
Node.jsnohup bash -c 'npm install && npm run build' > /tmp/build.log 2>&1 &
Pythonnohup bash -c 'python3 -m venv .venv && .venv/bin/pip install -r requirements.txt' > /tmp/build.log 2>&1 &
Rust(方法 A)nohup bash -c 'source ~/.cargo/env && cargo build --release' > /tmp/build.log 2>&1 &
Go(方法 A)nohup bash -c 'go build -o $PROJECT_NAME .' > /tmp/build.log 2>&1 &
Dockernohup bash -c 'docker compose build' > /tmp/build.log 2>&1 &
静的サイトビルド不要

完了するまで待ち、ビルド結果を確認します:ssh $SSH_TARGET "tail -20 /tmp/build.log"


第五步:アプリケーションの起動とプロセス管理

プロジェクトの種類に応じて、適切なプロセス管理方法を選択します。

Node.js → PM2
ssh $SSH_TARGET "cd $REMOTE_DIR && pm2 start npm --name $PROJECT_NAME -- start && pm2 startup && pm2 save"

確認:ssh $SSH_TARGET "pm2 list && curl -s -o /dev/null -w '%{http_code}' http://localhost:$PORT"

Python → systemd
ssh $SSH_TARGET "cat > /etc/systemd/system/$PROJECT_NAME.service << EOF
[Unit]
Description=$PROJECT_NAME
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=$REMOTE_DIR
ExecStart=$REMOTE_DIR/.venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port $PORT
# 或 gunicorn: ExecStart=$REMOTE_DIR/.venv/bin/gunicorn -w 4 -b 0.0.0.0:$PORT main:app
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable $PROJECT_NAME && systemctl start $PROJECT_NAME"

注意:ExecStart はフレームワークに応じて調整してください。

  • FastAPI/Starlette:uvicorn main:app
  • Flask:gunicorn -w 4 main:app
  • Django:gunicorn -w 4 project.wsgi:application
  • 通常のスクリプト:python main.py

確認:ssh $SSH_TARGET "systemctl status $PROJECT_NAME && curl -s -o /dev/null -w '%{http_code}' http://localhost:$PORT"

Rust / Go → systemd
ssh $SSH_TARGET "cat > /etc/systemd/system/$PROJECT_NAME.service << EOF
[Unit]
Description=$PROJECT_NAME
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=$REMOTE_DIR
ExecStart=$REMOTE_DIR/$BINARY_NAME
Restart=on-failure
RestartSec=5
Environment=PORT=$PORT

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable $PROJECT_NAME && systemctl start $PROJECT_NAME"

Rust バイナリのパス:$REMOTE_DIR/target/release/$PROJECT_NAME(方式 A)または $REMOTE_DIR/$PROJECT_NAME(方式 B) Go バイナリのパス:$REMOTE_DIR/$PROJECT_NAME

確認:ssh $SSH_TARGET "systemctl status $PROJECT_NAME && curl -s -o /dev/null -w '%{http_code}' http://localhost:$PORT"

Docker → docker compose
ssh $SSH_TARGET "cd $REMOTE_DIR && docker compose up -d"

起動時の自動起動を設定:Docker サービスはデフォルトで systemd とともに起動するため、docker compose では restart: unless-stopped を設定するだけで十分です。

確認:ssh $SSH_TARGET "docker compose ps && curl -s -o /dev/null -w '%{http_code}' http://localhost:$PORT"

静的サイト → Nginx で直接ホスティング

プロセス管理は不要です。Nginx から静的ファイルのディレクトリを直接参照します(第六步を参照)。


第六步:Nginx リバースプロキシ

ユーザーが「IP のみでアクセス」を選択した場合、この手順と以降の SSL 手順はスキップします。

6a. 動的アプリ(HTTP リバースプロキシ)
ssh $SSH_TARGET "cat > /etc/nginx/sites-available/$PROJECT_NAME << 'NGINX'
server {
    listen 80;
    server_name $DOMAIN www.$DOMAIN;

    client_max_body_size 50M;

    location / {
        proxy_pass http://127.0.0.1:$PORT;
        proxy_http_version 1.1;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection \"upgrade\";
    }
}
NGINX
ln -sf /etc/nginx/sites-available/$PROJECT_NAME /etc/nginx/sites-enabled/$PROJECT_NAME
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx"
6b. 静的サイト(直接ホスティング)
ssh $SSH_TARGET "cat > /etc/nginx/sites-available/$PROJECT_NAME << 'NGINX'
server {
    listen 80;
    server_name $DOMAIN www.$DOMAIN;

    root $REMOTE_DIR;
    index index.html;

    location / {
        try_files \$uri \$uri/ /index.html;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 30d;
        add_header Cache-Control \"public, immutable\";
    }
}
NGINX
ln -sf /etc/nginx/sites-available/$PROJECT_NAME /etc/nginx/sites-enabled/$PROJECT_NAME
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx"
6c. HTTPS バージョン(SSL 証明書がある場合)

該当する server ブロックをベースにして、次のようにします。

  • ポート 80 の server に return 301 https://$host$request_uri; を追加します。
  • ポート 443 の server ブロックを新規追加し、ssl_certificate とセキュリティヘッダーを設定します。
server {
    listen 443 ssl;
    server_name $DOMAIN www.$DOMAIN;

    ssl_certificate /etc/ssl/cloudflare/fullchain.pem;
    ssl_certificate_key /etc/ssl/cloudflare/key.pem;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # ... location 块同上 ...
}
6d. Nginx のセキュリティ強化
ssh $SSH_TARGET "sed -i 's/# server_tokens off;/server_tokens off;/' /etc/nginx/nginx.conf && nginx -t && systemctl reload nginx"

第七步:DNS と SSL の設定

ユーザーがドメインを所有している場合にのみ実行します。

7a. DNS 設定の案内

ユーザーに、ドメイン登録事業者の管理画面で DNS レコードを追加するよう案内します。

レコードタイプ名前内容
A@<サーバーIP>
Awww<サーバーIP>

Cloudflare を使用する場合は、オレンジ色の雲(プロキシモード)を有効にすることをおすすめします。

7b. SSL 方式の選択

AskUserQuestion を使って、次の選択肢を提示します。

  1. Cloudflare Origin Certificate(推奨) — DNS が Cloudflare でホスティングされている場合に利用でき、有効期間は 15 年です。
  2. Let's Encrypt — 無料のパブリック証明書です。定期的な更新が必要です。
  3. SSL を設定しない
Cloudflare Origin Certificate の手順:
  1. ユーザーに次の操作を案内します:Cloudflare → SSL/TLS → オリジンサーバー → 証明書を作成

    • ホスト名に $DOMAIN と *.$DOMAIN が含まれていることを確認します。
    • 秘密鍵の種類:RSA、有効期間:15 年
  2. ユーザーから証明書と秘密鍵を受け取った後、サーバーに書き込み、証明書チェーンを連結します。

ssh $SSH_TARGET "mkdir -p /etc/ssl/cloudflare"
# 写入 cert.pem 和 key.pem(chmod 600)
# 下载 Origin CA 根证书并拼接
ssh $SSH_TARGET "curl -s -o /etc/ssl/cloudflare/origin-ca-rsa-root.pem https://developers.cloudflare.com/ssl/static/origin_ca_rsa_root.pem && cat /etc/ssl/cloudflare/cert.pem /etc/ssl/cloudflare/origin-ca-rsa-root.pem > /etc/ssl/cloudflare/fullchain.pem"
  1. 証明書を必ず検証します:
ssh $SSH_TARGET "openssl x509 -in /etc/ssl/cloudflare/cert.pem -noout -text | grep -E 'DNS:|Subject:|Issuer:|Extended'"
  • DNS: にドメインが含まれていること
  • Extended Key Usage に TLS Web Server Authentication が含まれていること
  • 含まれていない場合は、ユーザーに再作成するよう案内します。
  1. Cloudflare の SSL モードは、まず Full (Strict) に設定します。526 エラーが発生した場合は Full に変更します。
Let's Encrypt の手順:
ssh $SSH_TARGET "apt-get install -y certbot python3-certbot-nginx && certbot --nginx -d $DOMAIN -d www.$DOMAIN --non-interactive --agree-tos --email <EMAIL>"

Cloudflare のプロキシを一時的に無効(オレンジからグレー)にする必要があります。完了後は再度有効にできます。


拡張リファレンス

このスキルを簡潔に保つため、## 第八步:安全加固 以降の詳細資料は reference/extended.md に移動しました。移動したサンプル、コマンド一覧、チェックリスト、プラットフォームの詳細、実装テンプレートが必要な場合は、このリファレンスを読み込んでください。

コメント