1688
Article
将 Astro 博客从 Cloudflare 迁移到雨云 VPS

最终结构#

  • anpier.cn:主域名。
  • www.anpier.cnblog.anpier.cn:301 跳转到主域名。
  • Nginx:提供 Astro 静态文件,并把 /api/ 转发到 Node.js。
  • Node.js:提供浏览量和点赞接口,只监听 127.0.0.1:8787
  • SQLite:保存统计数据,文件为 /var/lib/anpier/stats.db
  • systemd:守护统计服务。
  • GitHub Actions:推送 main 后自动构建、上传和切换版本。
  • Cloudflare:只负责 DNS,不再使用 Pages、Functions 和 D1。

服务器购买于雨云,系统镜像为 Debian 12 + 宝塔 LNMP,运行 Node.js 22。所有 SERVER_IP 都要替换成服务器公网 IP。

一、准备服务器#

root 登录服务器:

Terminal window
apt update
apt install -y ca-certificates curl git openssl rsync sqlite3 sudo
curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
bash /tmp/nodesource_setup.sh
apt install -y nodejs
node -v
sqlite3 --version

创建部署用户、服务用户和目录:

Terminal window
adduser --disabled-password --gecos "" deploy
useradd --system --user-group --no-create-home \
--home-dir /opt/anpier-api --shell /usr/sbin/nologin anpier
install -d -o deploy -g deploy -m 0755 /var/www/anpier
install -d -o deploy -g deploy -m 0755 /var/www/anpier/releases
install -d -o deploy -g deploy -m 0755 /opt/anpier-api
install -d -o deploy -g deploy -m 0755 /opt/anpier-api/releases
install -d -o anpier -g anpier -m 0750 /var/lib/anpier
install -d -o root -g anpier -m 0750 /etc/anpier
install -d -o root -g root -m 0750 /var/backups/anpier
install -d -o deploy -g deploy -m 0700 /home/deploy/.ssh

二、配置自动部署 SSH#

在 Windows PowerShell 生成 GitHub Actions 专用密钥:

Terminal window
$ServerIp = "SERVER_IP"
ssh-keygen -t ed25519 -C "anpier-github-actions" `
-f "$env:USERPROFILE\.ssh\anpier_github_actions"

公钥可以放到服务器,私钥不能上传到仓库或服务器:

Terminal window
Get-Content "$env:USERPROFILE\.ssh\anpier_github_actions.pub" | `
ssh root@$ServerIp "cat >> /home/deploy/.ssh/authorized_keys"

回到服务器设置权限:

Terminal window
chown deploy:deploy /home/deploy/.ssh/authorized_keys
chmod 600 /home/deploy/.ssh/authorized_keys

测试部署账户:

Terminal window
ssh -i "$env:USERPROFILE\.ssh\anpier_github_actions" deploy@$ServerIp

在服务器查看主机密钥指纹:

Terminal window
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

在 Windows 获取同一指纹并核对:

Terminal window
ssh-keyscan -t ed25519 $ServerIp 2>$null | ssh-keygen -lf -

三、安装统计服务#

仓库内已经包含以下文件:

deploy/anpier-api.service
deploy/api.env.example
server/
scripts/activate-release.sh
.github/workflows/deploy.yml

在仓库目录上传 systemd 配置:

Terminal window
scp .\deploy\anpier-api.service root@${ServerIp}:/tmp/anpier-api.service

在服务器安装服务并创建环境变量:

Terminal window
install -o root -g root -m 0644 \
/tmp/anpier-api.service /etc/systemd/system/anpier-api.service
install -o root -g anpier -m 0640 /dev/null /etc/anpier/api.env
hash_secret="$(openssl rand -hex 32)"
printf '%s\n' \
'ANPIER_HOST=127.0.0.1' \
'ANPIER_PORT=8787' \
'ANPIER_DB_PATH=/var/lib/anpier/stats.db' \
"ANPIER_HASH_SECRET=$hash_secret" \
'ANPIER_ALLOWED_ORIGINS=https://anpier.cn,https://www.anpier.cn,https://blog.anpier.cn' \
> /etc/anpier/api.env
unset hash_secret
systemctl daemon-reload
systemctl enable anpier-api.service

只允许 deploy 重启统计服务:

Terminal window
printf '%s\n' \
'deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart anpier-api.service' \
> /etc/sudoers.d/anpier-deploy
chmod 0440 /etc/sudoers.d/anpier-deploy
visudo -cf /etc/sudoers.d/anpier-deploy

四、配置 GitHub Actions#

打开 GitHub 仓库:

Settings -> Environments -> New environment -> production

production 的 Environment secrets 中添加:

名称
DEPLOY_HOST服务器公网 IP
DEPLOY_PORT22
DEPLOY_USERdeploy
DEPLOY_SSH_KEYanpier_github_actions 私钥的完整内容
SSH_KNOWN_HOSTS服务器的 ED25519 主机密钥行

在 Windows 把私钥送入剪贴板,再粘贴到 DEPLOY_SSH_KEY

Terminal window
Get-Content -Raw "$env:USERPROFILE\.ssh\anpier_github_actions" | Set-Clipboard

把主机密钥送入剪贴板,再粘贴到 SSH_KNOWN_HOSTS

Terminal window
ssh-keyscan -t ed25519 $ServerIp 2>$null | Set-Clipboard

推送一次代码,首次部署会自动创建 current 软链接并启动 API:

Terminal window
git push origin main

确认 GitHub Actions 中以下任务全部通过:

Deploy to VPS
Build and Check
Code quality

服务器验证:

Terminal window
systemctl status anpier-api.service --no-pager
curl --fail http://127.0.0.1:8787/api/healthz
ls -l /var/www/anpier/current /opt/anpier-api/current

五、配置 Nginx 和 HTTPS#

在宝塔中新建站点,添加三个域名:

anpier.cn
www.anpier.cn
blog.anpier.cn

网站目录设为:

/var/www/anpier/current

不需要创建 MySQL 数据库,也不需要 PHP。

在站点配置的 server 块中加入:

if ($host != 'anpier.cn') {
return 301 https://anpier.cn$request_uri;
}
location ^~ /api/ {
proxy_pass http://127.0.0.1:8787;
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_connect_timeout 5s;
proxy_read_timeout 15s;
}

在宝塔的 SSL 页面选择 Let’s Encrypt,同时申请以下三个域名的免费证书,然后开启强制 HTTPS:

anpier.cn
www.anpier.cn
blog.anpier.cn

不需要购买商业 SSL 证书。证书必须包含三个域名,否则别名在跳转前会先出现证书错误。

检查并重载 Nginx:

Terminal window
/www/server/nginx/sbin/nginx -t
/etc/init.d/nginx reload

六、切换 DNS#

域名在阿里云购买,但 NS 已指向 Cloudflare,因此 DNS 记录在 Cloudflare 修改。

添加以下记录,代理状态先设为 DNS only

类型名称内容
A@服务器公网 IP
CNAMEwwwanpier.cn
CNAMEbloganpier.cn

验证:

Terminal window
Resolve-DnsName anpier.cn
curl.exe -I http://anpier.cn/about/
curl.exe -I https://www.anpier.cn/about/
curl.exe -I https://blog.anpier.cn/about/
curl.exe https://anpier.cn/api/healthz

预期结果:HTTP 跳转 HTTPS,wwwblog 跳转 anpier.cn,健康接口返回 {"status":"ok"}

七、迁移浏览量和点赞#

第一次部署后数据库会自动创建。迁移前先停止 API 并备份:

Terminal window
systemctl stop anpier-api.service
backup_file="/var/backups/anpier/stats-before-import-$(date +%Y%m%d-%H%M%S).db"
sqlite3 /var/lib/anpier/stats.db ".backup '$backup_file'"
chmod 600 "$backup_file"

本次迁移使用浏览量 1203、点赞 280

Terminal window
sudo -u anpier sqlite3 /var/lib/anpier/stats.db "
BEGIN IMMEDIATE;
INSERT INTO counters (name, value) VALUES ('site_total', 1203)
ON CONFLICT(name) DO UPDATE SET value = excluded.value;
INSERT INTO counters (name, value) VALUES ('site_like', 280)
ON CONFLICT(name) DO UPDATE SET value = excluded.value;
DELETE FROM action_locks;
COMMIT;
"
systemctl start anpier-api.service
curl --fail http://127.0.0.1:8787/api/healthz
sqlite3 /var/lib/anpier/stats.db \
"SELECT name, value FROM counters ORDER BY name;"

浏览量按访客 12 小时去重;点赞每次点击都会增加,后端限制每个 IP 每小时最多 120 次。

八、配置数据库备份#

创建备份脚本:

tee /usr/local/sbin/backup-anpier-db >/dev/null <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
backup_dir=/var/backups/anpier
database=/var/lib/anpier/stats.db
timestamp=$(date +%Y%m%d-%H%M%S)
umask 077
sqlite3 "$database" ".backup '$backup_dir/stats-$timestamp.db'"
find "$backup_dir" -type f -name 'stats-*.db' -mtime +30 -delete
EOF
chmod 0750 /usr/local/sbin/backup-anpier-db

每天 03:30 备份:

Terminal window
printf '%s\n' \
'30 3 * * * root /usr/local/sbin/backup-anpier-db' \
> /etc/cron.d/anpier-backup
chmod 0644 /etc/cron.d/anpier-backup
/usr/local/sbin/backup-anpier-db
ls -lh /var/backups/anpier

九、配置管理账户#

生成管理员密钥:

Terminal window
ssh-keygen -t ed25519 -C "anpier-admin" `
-f "$env:USERPROFILE\.ssh\anpier_admin"

服务器创建管理员账户:

Terminal window
adduser adrian
usermod -aG sudo adrian
install -d -o adrian -g adrian -m 0700 /home/adrian/.ssh

从 Windows 安装管理员公钥:

Terminal window
Get-Content "$env:USERPROFILE\.ssh\anpier_admin.pub" | `
ssh root@$ServerIp "cat >> /home/adrian/.ssh/authorized_keys"

服务器修正权限:

Terminal window
chown adrian:adrian /home/adrian/.ssh/authorized_keys
chmod 600 /home/adrian/.ssh/authorized_keys

先保持原 SSH 会话,另开窗口测试:

Terminal window
ssh -i "$env:USERPROFILE\.ssh\anpier_admin" adrian@$ServerIp

确认密钥登录和 sudo 正常后,再关闭 root 和密码远程登录:

Terminal window
printf '%s\n' \
'PermitRootLogin no' \
'PasswordAuthentication no' \
'PubkeyAuthentication yes' \
> /etc/ssh/sshd_config.d/00-anpier.conf
sshd -t
systemctl reload ssh
sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication) '

十、日常发布#

修改文章或代码后,在 Windows 仓库目录执行:

Terminal window
pnpm.cmd check
pnpm.cmd api:test
pnpm.cmd build
git status
git add .
git commit -m "更新博客"
git push origin main

推送完成后不需要手动上传文件。GitHub Actions 会构建静态站点、测试 API、上传新版本、切换 current 软链接并重启服务;健康检查失败时会自动恢复上一个版本。

十一、换电脑后的操作#

先在旧电脑或加密备份中保存管理员私钥:

C:\Users\用户名\.ssh\anpier_admin
C:\Users\用户名\.ssh\anpier_admin.pub

GitHub Actions 的部署私钥已经保存在 GitHub,新电脑发布文章不需要该私钥。不要把任何私钥提交到仓库。

新电脑安装 Git、Node.js 22、pnpm 9.14.4 和 VS Code,然后执行:

Terminal window
git config --global user.name "你的 GitHub 用户名"
git config --global user.email "你的 GitHub 邮箱"
corepack enable
corepack prepare pnpm@9.14.4 --activate
Set-Location C:\Blog
git clone https://github.com/Adrian-Pierre/anpier_blog.git
Set-Location C:\Blog\anpier_blog
pnpm.cmd install --frozen-lockfile
pnpm.cmd check
pnpm.cmd build

把备份的 anpier_adminanpier_admin.pub 放回新电脑的 .ssh 目录,即可管理服务器:

Terminal window
ssh -i "$env:USERPROFILE\.ssh\anpier_admin" adrian@SERVER_IP

如果管理员私钥已经丢失,通过雨云控制台的远程连接进入服务器,生成一对新密钥,并把新公钥加入:

/home/adrian/.ssh/authorized_keys

十二、故障检查#

Terminal window
# API 状态和日志
systemctl status anpier-api.service --no-pager
journalctl -u anpier-api.service -n 100 --no-pager
# 本机 API
curl --fail http://127.0.0.1:8787/api/healthz
curl http://127.0.0.1:8787/api/site-total
curl http://127.0.0.1:8787/api/site-like
# Nginx
/www/server/nginx/sbin/nginx -t
tail -n 100 /www/wwwlogs/anpier.cn.error.log
# 当前版本
readlink /var/www/anpier/current
readlink /opt/anpier-api/current
# 数据库
sqlite3 /var/lib/anpier/stats.db \
"SELECT name, value FROM counters ORDER BY name;"

不要使用 POST /api/site-like 做普通检查,因为每次 POST 都会真实增加点赞数。

将 Astro 博客从 Cloudflare 迁移到雨云 VPS
https://anpier.cn/posts/blogcloudflaretovps/
作者
Adrian Pierre
发布于
2026-07-30
许可协议
CC BY-NC-SA 4.0
体温电台 · 1/3
I Really Want to Stay at Your House
Rosa Walton & Hallie Coggins