Deployment Server 架設:部署伺服器建置指南【2025】

Deployment Server 架設:部署伺服器建置指南【2025】

每次部署都要 SSH 進五台伺服器,一台一台執行指令?

Deployment Server(部署伺服器)是集中管理部署作業的中心節點。透過它,你可以一次將程式碼部署到所有目標伺服器,同時保持部署流程的一致性和可追蹤性。

這篇文章將教你從零開始架設 Deployment Server,從基礎設定到進階自動化,建立專業的部署管理系統。

什麼是 Deployment Server?

Deployment Server 是專門用來執行部署作業的伺服器,作為部署流程的控制中心。

Deployment Server 的角色

集中化管理

所有部署作業都從 Deployment Server 發起:
- 統一的部署入口點
- 一致的部署環境
- 集中的日誌記錄

安全隔離

Deployment Server 作為中介層:
- 開發者不需要直接存取生產伺服器
- 部署金鑰集中管理
- 減少暴露面

自動化執行

與 CI/CD 整合:
- 接收 Webhook 觸發部署
- 執行預定義的部署腳本
- 處理多伺服器部署

架構示意

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   ┌──────────┐                                                  │
│   │ Developer│                                                  │
│   └────┬─────┘                                                  │
│        │ git push                                               │
│        ▼                                                        │
│   ┌──────────┐     Webhook      ┌─────────────────┐            │
│   │  GitHub  │ ────────────────▶│   Deployment    │            │
│   │  GitLab  │                  │     Server      │            │
│   └──────────┘                  └────────┬────────┘            │
│                                          │                      │
│                         ┌────────────────┼────────────────┐     │
│                         │                │                │     │
│                         ▼                ▼                ▼     │
│                   ┌──────────┐    ┌──────────┐    ┌──────────┐ │
│                   │   Web    │    │   API    │    │  Worker  │ │
│                   │  Server  │    │  Server  │    │  Server  │ │
│                   └──────────┘    └──────────┘    └──────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

何時需要 Deployment Server?

情境 需要 Deployment Server
單一伺服器、小型專案 不一定需要
多台伺服器、團隊協作 建議使用
需要審計追蹤 必要
安全合規要求高 必要
複雜的部署流程 強烈建議

想了解更多部署基礎知識,請參考我們的 程式部署完整指南

Deployment Server 規格與準備

讓我們開始準備 Deployment Server 的環境。

硬體規格建議

Deployment Server 主要執行腳本和網路傳輸,不需要太強的規格:

規格 最低需求 建議規格
CPU 1 vCPU 2 vCPU
RAM 1 GB 2 GB
儲存 20 GB SSD 50 GB SSD
網路 與目標伺服器同網段 內網低延遲

作業系統選擇

推薦使用 Linux:

  • Ubuntu 22.04 LTS:最常見,社群支援好
  • Debian 12:穩定,更新週期長
  • Rocky Linux 9:RHEL 相容,企業環境常用

網路規劃

Deployment Server 的網路位置很重要:

內網部署(推薦)

Internet → Firewall → DMZ → [App Servers]
                        ↑
              Internal Network
                        ↓
            [Deployment Server]

優點:
- 與目標伺服器低延遲
- 不暴露在公網
- 安全性較高

雲端部署

Cloud VPC
├── Public Subnet
│   └── Bastion Host
└── Private Subnet
    ├── Deployment Server
    └── App Servers

初始設定

SSH 進入新建立的伺服器,執行初始設定:

# 更新系統
sudo apt update && sudo apt upgrade -y

# 設定時區
sudo timedatectl set-timezone Asia/Taipei

# 安裝基本工具
sudo apt install -y git curl wget vim htop

# 建立部署專用帳號
sudo useradd -m -s /bin/bash deployer
sudo usermod -aG sudo deployer

💡 部署架構設計需要協助?讓 VibeFix 幫你規劃

SSH 金鑰管理

SSH 金鑰是 Deployment Server 的核心,需要妥善管理。

產生部署金鑰

在 Deployment Server 上產生專用金鑰:

# 切換到 deployer 帳號
sudo su - deployer

# 產生 Ed25519 金鑰(推薦)
ssh-keygen -t ed25519 -C "deployer@deployment-server" -f ~/.ssh/deploy_key

# 或 RSA 金鑰(相容性較好)
ssh-keygen -t rsa -b 4096 -C "deployer@deployment-server" -f ~/.ssh/deploy_key

部署公鑰到目標伺服器

將公鑰複製到所有目標伺服器:

# 方法一:ssh-copy-id
ssh-copy-id -i ~/.ssh/deploy_key.pub user@target-server

# 方法二:手動複製
cat ~/.ssh/deploy_key.pub | ssh user@target-server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

SSH Config 設定

設定 ~/.ssh/config 簡化連線:

# Web Server
Host web-1
    HostName 10.0.1.10
    User deploy
    IdentityFile ~/.ssh/deploy_key
    StrictHostKeyChecking accept-new

Host web-2
    HostName 10.0.1.11
    User deploy
    IdentityFile ~/.ssh/deploy_key
    StrictHostKeyChecking accept-new

# API Server
Host api-1
    HostName 10.0.2.10
    User deploy
    IdentityFile ~/.ssh/deploy_key
    StrictHostKeyChecking accept-new

# 批次設定
Host 10.0.*.*
    User deploy
    IdentityFile ~/.ssh/deploy_key
    StrictHostKeyChecking accept-new

設定完成後,可以直接用主機名稱連線:

ssh web-1
ssh api-1

金鑰安全最佳實踐

最小權限原則

目標伺服器上的部署帳號只給予必要權限:

# 在目標伺服器上
# 建立專用部署帳號
sudo useradd -m -s /bin/bash deploy

# 只給予部署相關目錄的權限
sudo chown -R deploy:deploy /var/www/app
sudo chown -R deploy:deploy /opt/app

# 如需重啟服務,使用 sudoers 限制
echo "deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart myapp" | sudo tee /etc/sudoers.d/deploy

金鑰輪換

定期更換部署金鑰:

# 產生新金鑰
ssh-keygen -t ed25519 -f ~/.ssh/deploy_key_new

# 部署新公鑰到所有伺服器
for host in web-1 web-2 api-1; do
    ssh-copy-id -i ~/.ssh/deploy_key_new.pub $host
done

# 測試新金鑰
ssh -i ~/.ssh/deploy_key_new web-1 "echo 'New key works'"

# 替換舊金鑰
mv ~/.ssh/deploy_key ~/.ssh/deploy_key_old
mv ~/.ssh/deploy_key_new ~/.ssh/deploy_key

更多 SSH 金鑰設定,請參考我們的 GitHub Deploy Key 設定教學

防火牆與安全設定

Deployment Server 是高價值目標,必須做好安全防護。

UFW 防火牆設定

# 安裝 UFW
sudo apt install -y ufw

# 預設拒絕所有連入
sudo ufw default deny incoming
sudo ufw default allow outgoing

# 允許 SSH(限制來源 IP)
sudo ufw allow from 10.0.0.0/8 to any port 22

# 如果需要接收 Webhook
sudo ufw allow from 192.30.252.0/22 to any port 9000  # GitHub
sudo ufw allow from 185.199.108.0/22 to any port 9000  # GitHub
sudo ufw allow from 140.82.112.0/20 to any port 9000  # GitHub

# 啟用防火牆
sudo ufw enable
sudo ufw status verbose

SSH 強化

編輯 /etc/ssh/sshd_config

# 禁用密碼登入
PasswordAuthentication no
ChallengeResponseAuthentication no

# 禁用 root 登入
PermitRootLogin no

# 限制登入使用者
AllowUsers deployer admin

# 設定連線逾時
ClientAliveInterval 300
ClientAliveCountMax 2

# 限制同時連線數
MaxSessions 10
MaxStartups 10:30:60

重啟 SSH 服務:

sudo systemctl restart sshd

Fail2ban 防暴力破解

# 安裝 Fail2ban
sudo apt install -y fail2ban

# 建立設定檔
sudo tee /etc/fail2ban/jail.local << EOF
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
EOF

# 啟動服務
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

部署工具選擇與設定

有多種工具可以在 Deployment Server 上執行部署作業。

Ansible

最流行的組態管理與部署工具,使用 YAML 語法。

安裝

sudo apt install -y ansible

設定 Inventory

建立 /etc/ansible/hosts 或專案目錄下的 inventory.ini

[webservers]
web-1 ansible_host=10.0.1.10
web-2 ansible_host=10.0.1.11

[apiservers]
api-1 ansible_host=10.0.2.10
api-2 ansible_host=10.0.2.11

[workers]
worker-1 ansible_host=10.0.3.10

[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/deploy_key

建立部署 Playbook

deploy.yml

---
- name: Deploy Application
  hosts: webservers
  become: yes
  vars:
    app_path: /var/www/app
    repo_url: [email protected]:company/app.git
    branch: main

  tasks:
    - name: Pull latest code
      git:
        repo: "{{ repo_url }}"
        dest: "{{ app_path }}"
        version: "{{ branch }}"
        force: yes
      notify: Restart app

    - name: Install dependencies
      command: npm ci
      args:
        chdir: "{{ app_path }}"

    - name: Build application
      command: npm run build
      args:
        chdir: "{{ app_path }}"

  handlers:
    - name: Restart app
      systemd:
        name: myapp
        state: restarted

執行部署

# 部署到所有 webservers
ansible-playbook -i inventory.ini deploy.yml

# 只部署到特定伺服器
ansible-playbook -i inventory.ini deploy.yml --limit web-1

# 試跑(不實際執行)
ansible-playbook -i inventory.ini deploy.yml --check

💡 不確定該用哪個部署工具?讓 VibeFix 專家幫你評估

Capistrano

Ruby 生態系常用的部署工具,特別擅長管理 Release 版本。

安裝

# 安裝 Ruby
sudo apt install -y ruby ruby-dev
sudo gem install capistrano

初始化專案

cd /path/to/project
cap install

設定 config/deploy.rb

set :application, "myapp"
set :repo_url, "[email protected]:company/app.git"
set :deploy_to, "/var/www/myapp"
set :branch, "main"

set :linked_files, %w{.env}
set :linked_dirs, %w{log tmp/pids tmp/cache tmp/sockets vendor/bundle}

set :keep_releases, 5

namespace :deploy do
  desc 'Restart application'
  task :restart do
    on roles(:app) do
      execute :sudo, :systemctl, :restart, :myapp
    end
  end

  after :publishing, :restart
end

設定 config/deploy/production.rb

server "10.0.1.10", user: "deploy", roles: %w{app web}
server "10.0.1.11", user: "deploy", roles: %w{app web}

set :ssh_options, {
  keys: %w(~/.ssh/deploy_key),
  forward_agent: false
}

執行部署

cap production deploy

Capistrano 會自動管理 Release 目錄:

/var/www/myapp/
├── current -> releases/20250115120000
├── releases/
│   ├── 20250115120000/
│   ├── 20250114100000/
│   └── 20250113090000/
├── shared/
│   ├── .env
│   └── log/
└── repo/

Fabric

Python 生態系的部署工具,適合簡單任務。

安裝

pip install fabric

建立 fabfile.py

from fabric import task, Connection
from fabric import SerialGroup

SERVERS = [
    "[email protected]",
    "[email protected]",
]

@task
def deploy(c):
    """Deploy to all servers"""
    group = SerialGroup(*SERVERS, connect_kwargs={"key_filename": "~/.ssh/deploy_key"})

    with group.cd("/var/www/app"):
        group.run("git pull origin main")
        group.run("npm ci")
        group.run("npm run build")
        group.sudo("systemctl restart myapp")

@task
def rollback(c):
    """Rollback to previous version"""
    group = SerialGroup(*SERVERS, connect_kwargs={"key_filename": "~/.ssh/deploy_key"})

    with group.cd("/var/www/app"):
        group.run("git checkout HEAD~1")
        group.run("npm ci")
        group.run("npm run build")
        group.sudo("systemctl restart myapp")

@task
def status(c):
    """Check service status on all servers"""
    group = SerialGroup(*SERVERS, connect_kwargs={"key_filename": "~/.ssh/deploy_key"})
    group.sudo("systemctl status myapp")

執行

fab deploy
fab rollback
fab status

工具比較

特性 Ansible Capistrano Fabric
語言 YAML/Python Ruby Python
Agentless
Release 管理 需設定 內建 需自己實作
學習曲線 中等 中等
適合場景 基礎設施管理 Web App 部署 簡單任務
社群生態 豐富 Ruby 生態 Python 生態

Git Webhook 自動部署

讓 Deployment Server 接收 Webhook,實現 Push 後自動部署。

設定 Webhook Server

使用 webhook 工具接收 GitHub/GitLab 的 Webhook:

# 安裝 webhook
sudo apt install -y webhook

# 或使用 Go 版本
wget https://github.com/adnanh/webhook/releases/download/2.8.1/webhook-linux-amd64.tar.gz
tar -xzf webhook-linux-amd64.tar.gz
sudo mv webhook-linux-amd64/webhook /usr/local/bin/

設定 Webhook 規則

建立 /etc/webhook/hooks.json

[
  {
    "id": "deploy-app",
    "execute-command": "/opt/scripts/deploy.sh",
    "command-working-directory": "/opt/scripts",
    "response-message": "Deployment triggered",
    "trigger-rule": {
      "and": [
        {
          "match": {
            "type": "payload-hmac-sha256",
            "secret": "your-webhook-secret",
            "parameter": {
              "source": "header",
              "name": "X-Hub-Signature-256"
            }
          }
        },
        {
          "match": {
            "type": "value",
            "value": "refs/heads/main",
            "parameter": {
              "source": "payload",
              "name": "ref"
            }
          }
        }
      ]
    },
    "pass-arguments-to-command": [
      {
        "source": "payload",
        "name": "after"
      }
    ]
  }
]

建立部署腳本

/opt/scripts/deploy.sh

#!/bin/bash
set -e

COMMIT_SHA=$1
LOG_FILE="/var/log/deploy/deploy-$(date +%Y%m%d-%H%M%S).log"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"

# 記錄開始
echo "=== Deployment started at $(date) ===" | tee -a $LOG_FILE
echo "Commit: $COMMIT_SHA" | tee -a $LOG_FILE

# 通知開始
curl -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"🚀 Deployment started: $COMMIT_SHA\"}" \
    $SLACK_WEBHOOK

# 執行 Ansible 部署
cd /opt/deploy/myapp
if ansible-playbook -i inventory.ini deploy.yml 2>&1 | tee -a $LOG_FILE; then
    # 成功通知
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"✅ Deployment succeeded: $COMMIT_SHA\"}" \
        $SLACK_WEBHOOK
    echo "=== Deployment succeeded ===" | tee -a $LOG_FILE
else
    # 失敗通知
    curl -X POST -H 'Content-type: application/json' \
        --data "{\"text\":\"❌ Deployment failed: $COMMIT_SHA\"}" \
        $SLACK_WEBHOOK
    echo "=== Deployment failed ===" | tee -a $LOG_FILE
    exit 1
fi

啟動 Webhook 服務

建立 systemd 服務 /etc/systemd/system/webhook.service

[Unit]
Description=Webhook Server
After=network.target

[Service]
Type=simple
User=deployer
ExecStart=/usr/local/bin/webhook -hooks /etc/webhook/hooks.json -port 9000 -verbose
Restart=always

[Install]
WantedBy=multi-user.target

啟動服務:

sudo systemctl enable webhook
sudo systemctl start webhook

設定 GitHub Webhook

  1. 前往 GitHub Repository → Settings → Webhooks
  2. 點擊「Add webhook」
  3. 設定:
    - Payload URL:http://your-deployment-server:9000/hooks/deploy-app
    - Content type:application/json
    - Secret:與 hooks.json 中設定的相同
    - Events:選擇「Just the push event」

日誌與監控

Deployment Server 需要良好的日誌記錄,方便追蹤和除錯。

部署日誌管理

# 建立日誌目錄
sudo mkdir -p /var/log/deploy
sudo chown deployer:deployer /var/log/deploy

# 設定 logrotate
sudo tee /etc/logrotate.d/deploy << EOF
/var/log/deploy/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    create 644 deployer deployer
}
EOF

部署歷史追蹤

建立部署歷史記錄:

#!/bin/bash
# /opt/scripts/record-deploy.sh

HISTORY_FILE="/var/log/deploy/history.csv"

# 記錄格式:時間,Commit,部署者,結果
echo "$(date -Iseconds),$1,$2,$3" >> $HISTORY_FILE

監控與告警

使用 Prometheus + Alertmanager 監控部署狀態:

# prometheus/alerts/deploy.yml
groups:
  - name: deployment
    rules:
      - alert: DeploymentFailed
        expr: deployment_status{status="failed"} > 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Deployment failed"
          description: "Deployment to {{ $labels.environment }} failed"

VibeFix 部署架構服務

建立專業的 Deployment Server 需要考慮很多細節:

  • 伺服器規格與網路架構
  • SSH 金鑰管理與安全設定
  • 部署工具選擇與設定
  • 自動化流程設計
  • 監控與告警整合

VibeFix 團隊提供完整的部署架構設計與實作服務。

服務範圍:
- Deployment Server 規劃與建置
- Ansible / Capistrano 設定
- CI/CD 整合
- 安全性強化
- 監控系統建置

想了解更詳細的服務?查看 VibeFix 服務內容

結語:建立可靠的部署基礎設施

Deployment Server 是自動化部署的核心,一個好的部署伺服器可以:

  • 集中管理所有部署作業
  • 提供一致的部署環境
  • 加強安全性與存取控制
  • 完整記錄部署歷史
  • 實現自動化部署流程

透過這篇教學,你已經學會:

  • Deployment Server 的角色與架構
  • 伺服器規格與初始設定
  • SSH 金鑰管理與安全最佳實踐
  • 防火牆與安全強化
  • Ansible、Capistrano、Fabric 三種部署工具
  • Git Webhook 自動部署設定
  • 日誌與監控管理

接下來,你可以學習 CI/CD 建置與部署流程,將 Deployment Server 整合進完整的 CI/CD Pipeline。或者參考 軟體部署策略,了解藍綠部署、滾動部署等進階策略。


部署失敗?別擔心

Deployment Server 架設涉及多個技術面向,踩坑是正常的。VibeFix 團隊可以幫你快速建立可靠的部署基礎設施。

幫我部署 →

分享文章:
V

VibeFix

專門解決 AI Vibe Coding 後的疑難雜症,讓你的專案順利上線。

這篇文章有幫到你嗎?

如果還有問題,讓我們直接幫你解決!

聯繫我們