GitHub Actions + Caddy 全自动部署动态网站(动态篇)
本教程将指导你如何为动态网站搭建一套完整的自动化部署系统,实现 “代码推送即发布” 的 DevOps 流程。我们将使用 Node.js + Express 作为示例后端,但整体架构与配置方法同样适用于 Python(Django/Flask)、Java(Spring Boot)、PHP(Laravel)等动态网站技术栈。
最终效果:本地
git push→ 自动测试 → 构建 → 部署至云服务器 → 服务热重启 → 网站即刻更新(HTTPS 自动启用)。
🎯 系统架构与核心理念
与静态网站不同,动态网站部署不仅需要同步文件,还需要:
- 安装运行时环境
- 安装项目依赖
- 可能需要数据库迁移
- 重启应用进程(如 PM2、systemd 服务)
- 配置反向代理(Caddy 作为反向代理到后端端口)
整个流程依然基于 声明式自动化,通过 GitHub Actions 实现端到端无人值守部署。
开发者本地 (Local)
↓ [git push]
GitHub 仓库 (Repository)
↓ [触发]
GitHub Actions (CI/CD 管道)
↓ [构建、测试、同步、远程执行命令]
阿里云服务器 (Alibaba Cloud ECS)
↓ [Caddy 反向代理 + HTTPS]
用户访问 (HTTPS Dynamic Website)
📦 前置准备
- 一个 GitHub 仓库(包含你的动态网站源码)
- 一台云服务器(阿里云 ECS、腾讯云 CVM 等均可)
- 推荐系统:Ubuntu 22.04 / Alibaba Cloud Linux 3
- 安全组必须开放:SSH(22)、HTTP(80)、HTTPS(443) 端口(请登录云服务商控制台检查确认)
- 一个域名(可选但推荐,用于 HTTPS)
- 根据你的技术栈安装运行时环境(本教程以 Node.js 为例,后续步骤会安装)
🚀 第一部分:服务器初始化
1.1 登录并安装基础软件
通过 SSH 登录你的云服务器(假设登录用户名为 your-user,后续步骤中请将 $USER 替换为实际用户名)。
# 更新系统包
sudo apt update && sudo apt upgrade -y
# 安装 Node.js(示例使用 Node 24)
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
# 安装 PM2(进程管理,用于保持应用运行)
sudo npm install -g pm2
# 安装 Caddy(作为反向代理和 HTTPS 终端)
sudo apt install caddy
# 创建应用目录,并将所有权交给当前登录用户(确保后续部署有写入权限)
sudo mkdir -p /var/www/my-dynamic-app
sudo chown -R $USER:$USER /var/www/my-dynamic-app
💡 提示:如果你使用其他语言(如 Python),请在此步安装对应的运行时(例如
python3-pip、virtualenv等)。
1.2 配置 Caddy 作为反向代理
假设你的应用将运行在本地 127.0.0.1:3000 端口(请根据你的项目调整端口号)。
sudo nano /etc/caddy/Caddyfile
内容如下(替换 example.com 为你的域名,并确认端口与应用端口一致):
example.com, www.example.com {
# 反向代理到本地的应用进程
reverse_proxy 127.0.0.1:3000
# 启用压缩
encode gzip zstd
}
保存并验证配置:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl status caddy
💡 提示:如果使用域名,Caddy 会在首次访问时自动申请 SSL 证书;如果使用 IP,请将地址改为
http://你的服务器IP并移除encode等指令(但无法启用 HTTPS)。
🔐 第二部分:配置 SSH 密钥对与 GitHub Secrets
自动化部署的核心是让 GitHub Actions 能安全地连接到你的服务器并执行命令。
2.1 在本地生成 SSH 密钥对
在你的本地电脑(而非服务器)上执行:
# 生成一对新的密钥,专用于自动化部署
ssh-keygen -t ed25519 -f ~/.ssh/id_github_actions_dynamic -N ""
这将生成两个文件:
- 私钥 (
~/.ssh/id_github_actions_dynamic):绝密,相当于你的“钥匙”。 - 公钥 (
~/.ssh/id_github_actions_dynamic.pub):可以公开,相当于“锁芯”。
2.2 将公钥部署到服务器
- 复制公钥内容:
cat ~/.ssh/id_github_actions_dynamic.pub - 登录你的云服务器,将公钥添加到授权列表:
# 将上一步复制的公钥内容,粘贴到引号内,然后执行整条命令 echo '你的公钥内容' >> ~/.ssh/authorized_keys # 设置正确的权限(非常重要!) chmod 600 ~/.ssh/authorized_keys chmod 700 ~/.ssh
2.3 将私钥配置为 GitHub Secrets
- 查看私钥内容:
cat ~/.ssh/id_github_actions_dynamic - 进入你的 GitHub 仓库,点击 Settings → Secrets and variables → Actions。
- 点击 New repository secret,添加以下三个密钥:
SERVER_HOST:你的云服务器公网 IP 地址。SERVER_USER:用于 SSH 登录的用户名(例如root、ubuntu或你在服务器上使用的用户名)。SSH_PRIVATE_KEY:粘贴你刚刚复制的完整私钥内容(包括-----BEGIN OPENSSH PRIVATE KEY-----和-----END OPENSSH PRIVATE KEY-----行)。
- 如有构建时需要环境变量(如数据库连接串、API密钥),请一并添加到 Secrets 中(例如
DATABASE_URL、API_KEY)。
⚙️ 第三部分:创建 GitHub Actions 工作流
在项目根目录创建文件:.github/workflows/deploy.yml
以下示例以 Node.js 项目为例,并包含构建、依赖安装、进程重启。如果你使用其他技术栈,请替换相应的命令。
name: Deploy Dynamic App to Production
on:
push:
branches: [ main ] # 可根据需要调整分支
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
# 设置 Node.js 环境(按需调整或替换为其他语言环境)
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm' # 如果使用 pnpm 或 yarn,请相应调整
# 安装依赖(示例为 npm,可根据项目修改)
- name: Install dependencies
run: npm ci
# 运行测试(可选)
- name: Run tests
run: npm test
# 构建项目(如需环境变量,通过 env 传入)
- name: Build
run: npm run build
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
API_KEY: ${{ secrets.API_KEY }}
# 同步代码到服务器(使用 rsync)
- name: Deploy to Server via Rsync
uses: burnett01/rsync-deployments@7.0.1
with:
switches: -avz --delete
path: ./ # 同步整个项目(可以调整为只同步必要目录,如 dist/、node_modules 除外)
remote_path: /var/www/my-dynamic-app/
remote_host: ${{ secrets.SERVER_HOST }}
remote_user: ${{ secrets.SERVER_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}
# 在服务器上执行远程命令(重启应用、运行迁移等)
- name: Remote execution
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/my-dynamic-app
# 安装生产依赖(如需)
npm ci --only=production
# 执行数据库迁移(如需要)
# npm run migrate # 示例命令,请根据实际调整
# 重启应用(使用 PM2,如果尚未启动则创建)
if pm2 describe my-app > /dev/null 2>&1; then
pm2 reload my-app
else
pm2 start npm --name "my-app" -- start
# 设置 PM2 开机自启(仅首次需要)
pm2 save
pm2 startup
fi
pm2 save # 确保进程列表保存
关键配置说明:
path:rsync 的源路径,可以是整个项目目录,也可以是构建后的产物目录(如dist/)。建议根据项目情况调整。remote_path:服务器上存放代码的目录,必须与 Caddy 配置无关(Caddy 只代理到端口,不关心文件位置)。- 远程脚本:根据你的项目进行定制,例如安装依赖、运行迁移、重启进程等。示例使用了 PM2,如果使用 systemd 或其他工具,请相应修改。
- 环境变量:构建时所需变量通过
env传入,并从 GitHub Secrets 读取。如果应用运行时也需要变量,建议在服务器上维护一个.env文件,或通过 PM2 的--env参数传递。
🧪 第四部分:触发首次部署与验证
4.1 提交并推送代码
将工作流配置文件添加到 Git 并推送到仓库,触发首次自动化部署。
git add .github/workflows/deploy.yml
git commit -m "feat: 添加动态网站自动化部署"
git push origin main
4.2 监控部署过程
- 进入 GitHub 仓库的 Actions 标签页。
- 你会看到名为 “Deploy Dynamic App to Production” 的工作流正在运行。
- 点击进入,实时查看每个步骤的日志。
- 当所有步骤显示绿色对勾(✅),表示部署成功。
4.3 验证服务
- 访问
https://你的域名(或http://服务器IP:应用端口)。 - 确认网站功能正常。
- 检查服务器上进程状态:
pm2 status(或其他进程管理工具)。
🔧 第五部分:高级配置与问题排查
5.1 环境变量管理
动态网站通常需要敏感配置。推荐两种方式:
- 方式一:在服务器上创建
.env文件(位于应用目录),并在部署脚本中保持不变。PM2 或应用框架会自动读取。 - 方式二:通过 GitHub Secrets 在部署时注入,如在远程脚本中创建
.env文件(注意保密)。
5.2 数据库迁移自动化
若项目使用迁移工具,可在远程脚本中加入迁移命令,例如:
npm run migrate
请确保迁移命令幂等(可重复执行安全),或仅在必要时执行。
5.3 关键问题排查清单
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| Actions 日志卡在 SSH 连接 | 1. SSH 密钥格式错误 2. 安全组未开放 22 端口 3. 服务器 sshd_config 配置限制 | 1. 检查私钥格式,确保在 GitHub Secrets 中完整、多行 2. 检查阿里云安全组入方向规则 3. 检查服务器 /etc/ssh/sshd_config 中的 PermitRootLogin 和 AllowUsers 设置 |
| 网站可以 HTTP 访问,但 HTTPS 报错 | 1. 域名 DNS 解析未生效或错误 2. 安全组未开放 443 端口 | 1. 运行 nslookup yourdomain.com 检查 DNS 解析 2. 检查安全组 443 端口规则 |
| Caddy 返回 502 Bad Gateway | 1. 后端应用未运行 2. Caddy 配置中端口错误 3. 应用绑定地址不是 127.0.0.1 | 1. 检查 pm2 status 确认应用运行 2. 核对 Caddyfile 中的端口 3. 确保应用监听 127.0.0.1 而非 0.0.0.0 |
| 应用启动失败 | 1. 依赖未安装 2. 环境变量缺失 3. 端口被占用 | 1. 登录服务器手动运行 npm ci 2. 检查 .env 文件 3. 使用 netstat -tlnp 查看端口占用 |
| 数据库连接失败 | 1. 数据库服务未启动 2. 连接字符串错误 | 1. 检查数据库状态(如 systemctl status mysql) 2. 核对 .env 中的连接串 |
5.4 查看日志
# 查看应用日志(PM2)
pm2 logs my-app
# 查看 Caddy 日志
sudo journalctl -u caddy -f
# 查看系统认证日志(SSH问题)
sudo tail -f /var/log/auth.log
📈 总结:动态网站自动化部署的核心价值
通过本教程,你已经搭建了一套全自动、可监控、易回滚的动态网站部署系统,具备以下优势:
- 一键部署:从代码推送到服务上线,全程自动化。
- 进程守护:通过 PM2(或类似工具)保持应用持续运行,崩溃自动重启。
- HTTPS 自动管理:Caddy 自动处理 SSL 证书申请与续期。
- 环境一致性:依赖在每次部署时重新安装,避免环境漂移。
- 快速回滚:如需回滚,只需
git revert并推送,Actions 会自动执行旧版本部署。
从此,你可以专注于业务开发,将构建、测试、部署、运维的复杂性交给自动化系统处理。
下一步:如果你希望实现更高级的环境隔离和可重复性,可以考虑将应用容器化(Docker)。基于本教程的自动化基础,你可以轻松扩展为 Docker 部署流程。
如果这篇文档对你有帮助,可以请我喝杯咖啡 ☕️

