写在前面
博客站点已经搭好,接下来要把站点部署到云服务器上。服务器是腾讯云,管理面板用的是 1Panel。
思路很简单:在 1Panel 里创建一个静态网站,然后用 GitHub Actions 自动构建,再通过 rsync 把产物同步到服务器。之后每次推送到 main,网站就会自动更新。整条链路分四步:创建静态网站、配置 SSH 公钥认证、配置 GitHub 仓库与 Secrets、编写工作流。
在 1Panel 创建静态网站
按 1Panel 的指引新建一个静态网站即可。创建完成后,到网站设置里记下站点根目录,后面配置 DEPLOY_PATH 会用到。路径一般类似:
text
/opt/1panel/www/sites/你的域名/index
确认网站能访问到这个目录下的文件,再往下做部署。路径配错的话,后面 rsync 带 --delete 会把错误目录里的文件清掉,务必核对。
配置 SSH 公钥认证
生成密钥
先在本地生成一把专门给 GitHub Actions 用的密钥,不要复用日常登录的那把:
bash
# 生成 RSA 格式密钥(兼容性最好,避免格式报错)
ssh-keygen -t rsa -b 4096 -C "github-actions-deploy" -f ~/.ssh/id_rsa_github_actions
一路回车,不要设密码。生成的文件:
~/.ssh/id_rsa_github_actions→ 私钥(给 GitHub Actions,绝不能泄露)~/.ssh/id_rsa_github_actions.pub→ 公钥(放到服务器)
为什么选 RSA 而不是 ED25519?
burnett01/rsync-deployments对 OpenSSH 新格式(-----BEGIN OPENSSH PRIVATE KEY-----)兼容不好,常见报错是error in libcrypto或invalid format,SSH 直接失败。
ssh-keygen -t rsa生成的是传统 PEM 格式(-----BEGIN RSA PRIVATE KEY-----),这个 Action 吃得比较稳。
在服务器上添加公钥
bash
# 切换到部署用户(我用的是 root,也可以用普通用户)
sudo su -
# 创建 .ssh 目录
mkdir -p /root/.ssh
chmod 700 /root/.ssh
# 添加公钥
echo "ssh-rsa AAAAB3... github-actions-deploy" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
用 root 是因为 1Panel 默认以 root 跑,静态网站目录的属主也是 root。如果改用普通用户,把公钥写进对应用户的
~/.ssh/authorized_keys,并给网站目录授权。root 权限过大,能不用就不用。
验证免密登录
在本地执行:
bash
ssh -i ~/.ssh/id_rsa_github_actions root@你的服务器IP
能直接进去、不弹密码,说明配置成功。
配置 GitHub 仓库
仓库结构
GitHub 仓库只放源代码(Markdown、主题、配置),不放构建后的 HTML。构建在 CI 里完成,再把 dist/ 同步到服务器。
text
my-blog/
├── src/ # 源代码
├── dist/ # 构建产物(.gitignore 忽略)
├── package.json
├── astro.config.mjs # 框架配置
└── .github/
└── workflows/
└── deploy.yml # GitHub Actions 工作流
Astro 的构建产物是 dist/,不是 public/。public/ 是源码里的静态资源,要提交进仓库。.gitignore 只忽略构建产物和依赖:
text
dist/
node_modules/
配置 GitHub Secrets
进入仓库 → Settings → Secrets and variables → Actions,添加以下密钥。名称必须和工作流里引用的完全一致:
| Secret 名称 | 示例值 | 说明 |
|---|---|---|
SSH_HOST |
123.456.78.90 | 服务器 IP 或域名 |
SSH_USER |
root | SSH 登录用户名 |
DEPLOY_PATH |
/opt/1panel/www/sites/blog.example.com/index |
网站根目录(绝对路径) |
SSH_PRIVATE_KEY |
-----BEGIN RSA PRIVATE KEY-----... |
本地生成的私钥完整内容 |
SSH_PORT |
22 | 可选,默认 22 |
私钥必须是完整的 PEM,头尾和中间换行都要保留:
text
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
...(多行)...
-----END RSA PRIVATE KEY-----
复制时注意:不要漏掉 BEGIN / END,不要在首尾加空格,也不要多出空行。换行本身是私钥格式的一部分,不能压成一行。
编写 GitHub Actions 工作流
在 .github/workflows/deploy.yml 中写入:
yaml
name: Deploy
on:
push:
branches: [main]
workflow_dispatch:
concurrency:
group: deploy-production
cancel-in-progress: false
permissions:
contents: read
# Required repository secrets:
# SSH_HOST - server hostname or IP
# SSH_USER - SSH username
# SSH_PRIVATE_KEY - private key of the deploy user
# DEPLOY_PATH - remote directory, e.g. /var/www/blog/
# Optional:
# SSH_PORT - SSH port, defaults to 22
jobs:
deploy:
runs-on: ubuntu-latest
name: Build and rsync to server
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
with:
run_install: false
- name: Setup Node.js
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Rsync dist to server
uses: burnett01/rsync-deployments@4d419d1dc46d4a8a2b6ceab2f951f0f93b2da8f5 # v9.0.0
with:
switches: -avzr --delete
path: dist/
remote_path: ${{ secrets.DEPLOY_PATH }}
remote_host: ${{ secrets.SSH_HOST }}
remote_port: ${{ secrets.SSH_PORT || 22 }}
remote_user: ${{ secrets.SSH_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}
推送到 main,或在 Actions 页面手动触发 workflow_dispatch。跑通后打开网站确认页面已更新。
rsync 的 --delete 会删掉远端目录里、本地 dist/ 没有的文件。DEPLOY_PATH 一定要指向网站根目录,不要指到上一级。