站群與多 IP

站群伺服器批次建站教程:多 IP 一站一 IP,Nginx 模板部署、批次 SSL 與驗證

6 個步驟 12 分鐘閱讀 73 次閱讀 0
本文目錄

一台帶有一個或多個 /24 IP 段的站群伺服器,可以承載數百個網站,每個網站使用獨立 IP。在控製麵板裡逐個點選幾百次既慢又容易出錯;用幾份模板加一個指令碼,幾分鐘就能完成同樣的工作,而且每個站點的配置完全一致。本文介紹如何規劃域名與 IP 的對應關係、防止站點串到錯誤的 IP 上(串站)、用模板生成 Nginx 虛擬主機、用 acme.sh 批次簽發 SSL 證書,以及檢查每個域名是否都在自己的 IP 上正常響應。

本文假設所有 IP 都已繫結在伺服器上(參見 Linux 繫結整段 /24 IP),並且每個域名的 A 記錄都已指向為其分配的 IP。批次跑幾百個域名之前,請先用兩三個域名測試指令碼。

步驟 1:規劃域名與 IP 對照表

用一個純文本檔案作為唯一的資料來源:每行一個站點,寫明域名和對應的 IP。其他所有配置都由它生成。

mkdir -p /root/cluster
cat > /root/cluster/sites.txt <<'EOF2'
example-a.com 203.0.113.10
example-b.com 203.0.113.11
example-c.net 203.0.113.12
EOF2

# Check that every IP in the list is actually bound on the server
awk '{print $2}' /root/cluster/sites.txt | while read ip; do ip -br addr | grep -qw "$ip" || echo "NOT BOUND: $ip"; done

步驟 2:防止站點串到其他 IP 上

Nginx 會先根據監聽地址(IP 和埠)選出 server,再匹配主機名;主機名不匹配的請求,會由該地址的預設站點(default server)響應,如果沒有專門指定,就是該地址上配置的第一個站點。在站群上,這意味著有人直接訪問裸 IP,或者把另一個站點的主機名發到這個 IP 時,會看到你的某個站點,從而把這些站點關聯起來。需要在兩處攔截這類請求:一是為尚未部署站點的 IP 設定兜底配置(見下文),二是在模板中為每個 IP 設定預設站點(步驟 3)。

cat > /etc/nginx/conf.d/00-default.conf <<'EOF2'
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}
EOF2

# Hide the Nginx version in headers and error pages
sed -i 's/^\s*#\?\s*server_tokens.*/    server_tokens off;/' /etc/nginx/nginx.conf
grep -q 'server_tokens off' /etc/nginx/nginx.conf || sed -i '/http {/a\    server_tokens off;' /etc/nginx/nginx.conf

步驟 3:建立虛擬主機模板

模板中的佔位符由指令碼按站點逐一替換。每個站點只監聽自己的 IP,該 IP 上任何其他主機名的請求都會被直接斷開。

mkdir -p /etc/nginx/sites-cluster /www/sites
cat > /root/cluster/vhost-http.tpl <<'EOF2'
# Unknown hostnames on this IP: close the connection
server {
    listen __IP__:80 default_server;
    server_name _;
    return 444;
}

server {
    listen __IP__:80;
    server_name __DOMAIN__ www.__DOMAIN__;
    root /www/sites/__DOMAIN__/public;
    index index.html index.php;
    access_log /www/sites/__DOMAIN__/logs/access.log;
    error_log  /www/sites/__DOMAIN__/logs/error.log;

    location /.well-known/acme-challenge/ { allow all; }
    location / { try_files $uri $uri/ =404; }
}
EOF2

# Load the generated files from the http block
grep -q 'sites-cluster' /etc/nginx/nginx.conf || sed -i '/http {/a\    include /etc/nginx/sites-cluster/*.conf;' /etc/nginx/nginx.conf

步驟 4:用一個指令碼生成全部站點

cat > /root/cluster/deploy.sh <<'EOF2'
#!/bin/bash
set -e
while read -r DOMAIN IP; do
  [ -z "$DOMAIN" ] && continue
  mkdir -p /www/sites/$DOMAIN/public /www/sites/$DOMAIN/logs
  [ -f /www/sites/$DOMAIN/public/index.html ] || echo "<h1>$DOMAIN</h1>" > /www/sites/$DOMAIN/public/index.html
  sed -e "s/__DOMAIN__/$DOMAIN/g" -e "s/__IP__/$IP/g" /root/cluster/vhost-http.tpl > /etc/nginx/sites-cluster/$DOMAIN.conf
done < /root/cluster/sites.txt
chown -R www-data:www-data /www/sites 2>/dev/null || chown -R nginx:nginx /www/sites
nginx -t && systemctl reload nginx
EOF2
chmod +x /root/cluster/deploy.sh && /root/cluster/deploy.sh

把各站點的內容上傳到 /www/sites/DOMAIN/public。以後新增站點時,只需在 sites.txt 中追加行,再執行一次指令碼;已有內容不會被覆蓋。

步驟 5:批次簽發 SSL 證書

安裝 acme.sh 並將 CA 設定為 Let's Encrypt(參見使用 Certbot 或 acme.sh 申請免費 SSL 證書)。下面的迴圈通過網站根目錄為每個域名簽發證書、安裝證書,並在該站點自己的 IP 上新增 HTTPS server 塊。

cat > /root/cluster/ssl.sh <<'EOF2'
#!/bin/bash
mkdir -p /etc/nginx/ssl
while read -r DOMAIN IP; do
  [ -z "$DOMAIN" ] && continue
  [ -f /etc/nginx/ssl/$DOMAIN.crt ] && continue          # already done
  ~/.acme.sh/acme.sh --issue -d $DOMAIN -d www.$DOMAIN -w /www/sites/$DOMAIN/public || { echo "FAILED: $DOMAIN"; continue; }
  ~/.acme.sh/acme.sh --install-cert -d $DOMAIN \
     --key-file /etc/nginx/ssl/$DOMAIN.key --fullchain-file /etc/nginx/ssl/$DOMAIN.crt \
     --reloadcmd "systemctl reload nginx"
  cat >> /etc/nginx/sites-cluster/$DOMAIN.conf <<EOF3
server {
    listen $IP:443 ssl default_server;
    ssl_reject_handshake on;      # unknown hostnames on this IP (Nginx 1.19.4+)
}
server {
    listen $IP:443 ssl;
    http2 on;
    server_name $DOMAIN www.$DOMAIN;
    ssl_certificate     /etc/nginx/ssl/$DOMAIN.crt;
    ssl_certificate_key /etc/nginx/ssl/$DOMAIN.key;
    root /www/sites/$DOMAIN/public;
    index index.html index.php;
    access_log /www/sites/$DOMAIN/logs/access.log;
}
EOF3
  sleep 5
done < /root/cluster/sites.txt
nginx -t && systemctl reload nginx
EOF2
chmod +x /root/cluster/ssl.sh && /root/cluster/ssl.sh

建議每批只跑幾十個域名;Let's Encrypt 限制同一帳戶在短時間內申請新證書的數量。DNS 尚未指向正確 IP 的域名會簽發失敗;修正 DNS 後重新執行指令碼即可,已完成的域名會自動跳過。證書續期由 acme.sh 的定時任務(cron)自動完成。

步驟 6:逐個驗證站點

# Each domain must answer on its own IP with HTTP 200 (or 301 if you redirect to HTTPS)
while read -r DOMAIN IP; do
  code=$(curl -s -o /dev/null -w '%{http_code}' --resolve $DOMAIN:80:$IP http://$DOMAIN/)
  dns=$(dig +short $DOMAIN A | tail -1)
  echo "$DOMAIN $IP http=$code dns=$dns $( [ "$dns" = "$IP" ] && echo OK || echo DNS-MISMATCH)"
done < /root/cluster/sites.txt

# The bare IP must not show any site (connection closed = correct)
curl -sI http://203.0.113.10/ || echo "closed as expected"
curl -skI https://203.0.113.10/ || echo "handshake rejected as expected"

常見問題

可以用寶塔面板來做嗎?

可以:寶塔面板可以在站點設定中為每個站點繫結 IP,幾十個站點時完全夠用(參見為每個網站分配獨立 IP)。站點達到幾百個時,模板加指令碼更快、更不容易出錯,資料庫和監控仍然可以交給寶塔管理。

出站流量也要走各站點自己的 IP 嗎?

如果站點需要抓取內容或呼叫 API,出站請求預設從伺服器主 IP 發出。如需讓它們使用各自站點的 IP,請參見為每個網站分配獨立 IP 中的出站 IP 部分。

站點應該如何分佈在多個 IP 段上?

對於 SEO 站群,把站點分散到多個 /24 段(不同 C 段),並讓模板、統計帳戶和託管資訊彼此區分開,比 IP 數量本身更重要。參見多 C 段 IP 的 SEO 站群伺服器。

如果按以上步驟操作後問題仍未解決,請提交工單聯絡 IMIDC 7×24 技術支援,並附上伺服器 IP、系統版本、出問題的域名和 IP,以及 nginx -t 的輸出,方便工程師快速定位。

這篇文章有幫助嗎?

相關教程