GitHub Actions 自动部署:push 之后什么都不用管
手动 rsync 上传能用,但每次改完还要敲命令,迟早会忘。用 GitHub Actions 把这件事自动化:push 到 main 就自动部署。
一、基本原理
本地 push → GitHub 收到 → 起一台虚拟机构建 → 通过 SSH 把产物推到你的服务器你需要提供三样东西:构建脚本、服务器地址、登录凭证(放 Secret,不进代码库)。
二、准备 SSH 密钥
不要用你自己的登录密钥,单独生成一对专供 CI 使用:
ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/gh_actions
# 公钥放到服务器
ssh-copy-id -i ~/.ssh/gh_actions.pub -p 2222 user@example.com
# 私钥内容填到 GitHub Secret
cat ~/.ssh/gh_actions在仓库 Settings → Secrets and variables → Actions 里加三个 Secret:
| Secret 名 | 内容 |
|---|---|
SSH_PRIVATE_KEY | gh_actions 私钥全文 |
SERVER_HOST | 服务器 IP 或域名 |
SERVER_USER | 登录用户名(如 baize) |
三、部署到自己的服务器
.github/workflows/deploy.yml:
name: deploy
on:
push:
branches: [ main ]
workflow_dispatch: # 允许手动触发,调试很有用
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 拉取代码
uses: actions/checkout@v4
- name: 部署到服务器
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 2222
source: "./*"
target: "/var/www/baize"
strip_components: 0
- name: 重载 Nginx
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 2222
script: |
sudo nginx -t && sudo systemctl reload nginx
echo "deployed at $(date)"如果服务器上有构建步骤(比如 npm build),改成 SSH 里执行:
- name: 构建并重启
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: 2222
script: |
cd /srv/app
git pull --ff-only
npm ci
npm run build
sudo systemctl restart myapp四、部署到 GitHub Pages
name: deploy-pages
on:
push:
branches: [ main ]
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/upload-pages-artifact@v3
with:
path: .
- id: deployment
uses: actions/deploy-pages@v4五、加缓存,省时间省钱
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # 自动缓存 node_modules
- run: npm ci
- run: npm run build六、只在特定路径变更时才部署
文档改了没必要重新部署站点:
on:
push:
branches: [ main ]
paths:
- "assets/**"
- "index.html"
- ".github/workflows/deploy.yml"七、失败排查
| 现象 | 排查 |
|---|---|
Permission denied (publickey) | 私钥格式不对(要含首尾行)、或公钥没放进服务器 authorized_keys |
Host key verification failed | scp-action 加 host_key 参数,或 ssh-action 加 disable_host_key_checking: true |
| 命令找不到 | CI 里的 PATH 与登录 shell 不同,用绝对路径 /usr/bin/npm |
| 部署成功但站点没变 | CDN 缓存,去 Cloudflare 清一下缓存 |
调试技巧:把敏感值打码后再 echo,确认 Secret 真的读到了:
- name: 检查 Secret
run: echo "host length: ${#SERVER_HOST}"
env:
SERVER_HOST: ${{ secrets.SERVER_HOST }}八、安全注意
- Secret 永远不要 echo 出来,日志是公开的(私有仓库也一样,日志可能被分享);
- CI 专用密钥在服务器上限制权限:只在
authorized_keys里允许它执行特定命令; - 第三方 Action 尽量锁定版本或用 commit SHA,
@v4这种 tag 是可以被改的; - fork 的 PR 默认拿不到 Secret,这个默认行为不要随便改。
