静态站点部署与持续交付
2026/10/28OpsOpsnginxdeploycicdvuepress大约 2 分钟约 721 字
占位符
| 写法 | 含义 |
|---|---|
【域名】 | 站点域名 |
【IP】 | 服务器公网 IP(SSH / rsync 目标) |
【项目目录】 | 本机文档站仓库路径 |
【构建输出】 | 如 docs/.vuepress/dist/ |
服务器用户固定 root,静态目录固定 /root/deploy/nginx/html。
先说场景
网关主站读宿主机 html/。发布 = 构建产物覆盖该目录。子域若反代其它容器,走另一条发版线。
构建产物
cd 【项目目录】
pnpm install
pnpm docs:build # 以 package.json 为准| 工具 | 【构建输出】常见值 |
|---|---|
| VuePress | docs/.vuepress/dist/ |
| VitePress | docs/.vitepress/dist/ |
| Vite SPA | dist/ |
| Hugo | public/ |
同步的是构建结果,不是整仓源码。密钥用本机 .env / CI Secrets,勿提交仓库。
方式一:本机 rsync(对照)
rsync -avz --delete \
-e "ssh -o IdentitiesOnly=yes" \
【构建输出】 \
root@【IP】:/root/deploy/nginx/html/先保证:ssh root@【IP】 能登录。密钥过多报 Too many authentication failures 时保持 -e "ssh -o IdentitiesOnly=yes",或指定 -i 私钥。
校验:curl -sSI https://【域名】/ | head
方式二:服务器上构建
# root@【IP】
cd /root
git clone git@github.com:ORG/REPO.git
cd REPO && git pull --ff-only
pnpm install --frozen-lockfile && pnpm docs:build
rsync -a --delete 【构建输出】 /root/deploy/nginx/html/方式三:GitHub Actions
Secrets:
| Secret | 填法 |
|---|---|
DEPLOY_HOST | 【IP】 |
DEPLOY_SSH_KEY | root 可用私钥 |
DEPLOY_PATH | /root/deploy/nginx/html |
# .github/workflows/deploy-docs.yml
name: Deploy docs
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile && pnpm docs:build
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts
- name: Rsync
run: |
rsync -avz --delete -e "ssh -o IdentitiesOnly=yes" \
docs/.vuepress/dist/ \
"root@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/"把 CI 公钥放进服务器 /root/.ssh/authorized_keys。
可选原子发布:rsync 到 /root/deploy/nginx/releases/时间戳,再 ln -sfn 切到 html。
一键完成(本机发布)
在文档站仓库根目录执行,只改三行:
#!/usr/bin/env bash
set -euo pipefail
# ========== 只改这里 ==========
DOMAIN='【域名】' # 如 docs.example.com
IP='【IP】' # 如 203.0.113.10
DIST='docs/.vuepress/dist' # 【构建输出】,按项目改
# SSH 私钥可选;空则用默认 ssh agent / 默认密钥
SSH_KEY='' # 如 $HOME/.ssh/id_ed25519
# ==============================
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
if [[ ! -d node_modules ]]; then
pnpm install
fi
pnpm docs:build
[[ -f "$DIST/index.html" ]] || { echo "缺少 $DIST/index.html"; exit 1; }
SSH_OPTS=(-o IdentitiesOnly=yes)
if [[ -n "$SSH_KEY" ]]; then
SSH_OPTS+=(-i "$SSH_KEY")
fi
echo "→ root@${IP}:/root/deploy/nginx/html/"
rsync -avz --delete \
-e "ssh ${SSH_OPTS[*]}" \
"${DIST}/" \
"root@${IP}:/root/deploy/nginx/html/"
echo
echo "发布完成。检查: https://${DOMAIN}/"
curl -sSI "https://${DOMAIN}/" | head -8 || true无 rsync 时的替代一键:
#!/usr/bin/env bash
set -euo pipefail
DOMAIN='【域名】'
IP='【IP】'
DIST='docs/.vuepress/dist'
pnpm docs:build
tar czf /tmp/site-dist.tar.gz -C "$DIST" .
scp /tmp/site-dist.tar.gz "root@${IP}:/tmp/"
ssh "root@${IP}" 'mkdir -p /root/deploy/nginx/html && rm -rf /root/deploy/nginx/html/* && tar xzf /tmp/site-dist.tar.gz -C /root/deploy/nginx/html'
curl -sSI "https://${DOMAIN}/" | head -8检查清单
dist/index.html存在- 路径是
/root/deploy/nginx/html,用户是root@【IP】 https://【域名】/返回 200- 改静态文件一般不用 reload Nginx;改 conf / 证书才需要
关系
本机/CI 构建 → rsync → /root/deploy/nginx/html
↓
nginx-gateway(【IP】:443)
↓
https://【域名】/