
我用 VuePress、GitHub Actions 和阿里云搭建了 AI 测研社
我用 VuePress、GitHub Actions 和阿里云搭建了 AI 测研社
本文记录我从 WordPress 转向 VuePress,并使用 GitHub Actions 将网站自动部署到阿里云服务器的完整过程。最终实现的效果是:本地修改 Markdown,推送到 GitHub,网站自动构建并发布,全程不需要手动上传文件。
一、为什么要搭建 AI 测研社
我希望建立一个长期更新的个人技术网站,用来记录和分享以下内容:
- AI 测试工程化
- Claude Code、Codex、MCP 和 Skills 实战
- n8n AI 自动化工作流
- PPT 与视频自动化
- 测试工程师的成长与转型
网站不只是一个博客,也是我后续沉淀项目案例、建立个人品牌和验证商业化方向的基础。
我最初尝试过 WordPress。WordPress 的后台编辑和插件生态很方便,但对于以 Markdown、代码和技术文档为主的网站,我更希望获得以下能力:
- 文章直接以 Markdown 文件保存
- 可以使用 Git 管理所有修改记录
- 页面是静态文件,服务器压力更小
- 本地可以使用 Obsidian、Typora、VS Code 或 Claude Code 写作
- 推送代码后能够自动构建和部署
因此,我最终选择了:
VuePress + GitHub + GitHub Actions + 阿里云 + 1Panel/OpenResty
二、最终技术架构
整个发布流程如下:
flowchart LR
A["本地 Markdown"] --> B["GitHub 仓库"]
B --> C["GitHub Actions 构建"]
C --> D["阿里云静态网站"]各部分的作用如下:
| 组件 | 作用 |
|---|---|
| VuePress 2 | 将 Markdown、主题和配置构建为静态网站 |
| Plume 主题 | 提供首页、导航、侧边栏、搜索和文章样式 |
| GitHub | 保存源码、文章和历史版本 |
| GitHub Actions | 自动安装依赖、构建网站并上传构建结果 |
| 阿里云服务器 | 托管最终生成的静态文件 |
| 1Panel | 管理网站、OpenResty、HTTPS 证书和服务器文件 |
| OpenResty | 对外提供网站访问服务 |
最终访问域名为:
https://ceyanshe.com三、准备工作
开始之前,需要准备:
- 一个 GitHub 账号和代码仓库
- 一台阿里云 Linux 服务器
- 一个已经解析到服务器的域名
- 本地安装 Git 和 Node.js
- 一个可以正常构建的 VuePress 项目
本文使用的主要环境:
服务器系统:Ubuntu 22.04
服务器面板:1Panel
Web 服务:OpenResty
Node.js:20
包管理器:npm
默认分支:mainNode.js 和 VuePress 相关版本会持续更新。实际使用时,应以项目
package.json和锁文件中已经验证通过的版本为准,不要在部署时随意升级。
四、本地创建和运行 VuePress 项目
如果项目已经创建,可以跳过本节。
1. 初始化项目
在本地创建项目目录:
mkdir ai-test-blog
cd ai-test-blog
npm init -y安装 VuePress 和需要的主题:
npm install -D vuepress如果使用 VuePress Plume 主题,建议优先按照主题官方脚手架创建项目,避免手动安装时出现版本不匹配。
2. 配置运行命令
在 package.json 中确认存在类似脚本:
{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}本地启动:
npm run docs:dev构建正式版本:
npm run docs:build构建成功后,静态文件通常位于:
docs/.vuepress/dist/不同项目的文档目录可能是 docs/、src/ 或其他名称,应以项目现有配置为准。
五、将项目上传到 GitHub
在项目根目录执行:
git init
git add .
git commit -m "feat: initialize AI testing blog"
git branch -M main
git remote add origin https://github.com/你的用户名/你的仓库名.git
git push -u origin main以后每次修改文章或网站配置,通常只需要:
git add .
git commit -m "docs: add a new article"
git push origin main这三条命令分别表示:
| 命令 | 作用 |
|---|---|
git add . | 把当前修改加入本次待提交内容 |
git commit -m "说明" | 在本地保存一个带说明的版本 |
git push origin main | 把本地版本推送到 GitHub 的 main 分支 |
推送完成后,GitHub Actions 就会自动开始后续构建和部署。
六、在阿里云创建网站目录
我使用 1Panel 管理服务器,网站最终目录为:
/opt/1panel/www/sites/ceyanshe.com/index/同时预留两个目录:
/opt/1panel/www/sites/ceyanshe.com/index-staging/
/opt/1panel/www/sites/ceyanshe.com/backups/它们分别用于:
index/:当前线上版本index-staging/:新版本临时上传目录backups/:切换版本前保存上一版
首次创建目录:
mkdir -p /opt/1panel/www/sites/ceyanshe.com/index
mkdir -p /opt/1panel/www/sites/ceyanshe.com/index-staging
mkdir -p /opt/1panel/www/sites/ceyanshe.com/backups如果 GitHub Actions 使用的不是 root 用户,还需要保证部署用户对以上目录具有读写权限。不要为了省事直接把目录设置为 777。
七、配置 OpenResty 静态网站
在 1Panel 中创建网站后,将网站根目录指向:
/opt/1panel/www/sites/ceyanshe.com/index核心 OpenResty/Nginx 配置可参考:
server {
listen 80;
listen 443 ssl;
server_name ceyanshe.com www.ceyanshe.com;
root /opt/1panel/www/sites/ceyanshe.com/index;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}实际使用 1Panel 时,面板还会自动加入证书、日志和其他配置,不要直接删除这些已有内容。通常只需要确认:
server_name是自己的域名root指向正确的静态目录index包含index.html- HTTPS 证书已经签发并启用
修改配置后先检查语法:
docker exec 你的OpenResty容器名 openresty -t语法检查成功后,再在 1Panel 中重新加载 OpenResty。
八、为自动部署创建 SSH 密钥
GitHub Actions 需要通过 SSH 连接阿里云服务器。推荐使用专门的部署密钥,不要把服务器登录密码写进工作流。
1. 在本地生成密钥
ssh-keygen -t ed25519 -C "github-actions-deploy"建议单独保存,例如:
~/.ssh/ai-test-blog-deploy
~/.ssh/ai-test-blog-deploy.pub其中:
- 没有
.pub后缀的是私钥,只能保存在安全位置 - 带
.pub后缀的是公钥,可以放到服务器
2. 把公钥加入服务器
将公钥内容追加到服务器部署用户的:
~/.ssh/authorized_keys然后设置权限:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys3. 先在本地验证
ssh -i ~/.ssh/ai-test-blog-deploy 用户名@服务器IP只有本地能够通过该密钥正常登录后,再配置 GitHub Actions。
九、配置 GitHub Actions Secrets
进入 GitHub 仓库:
Settings
→ Secrets and variables
→ Actions
→ New repository secret创建以下 Secrets:
| Secret 名称 | 内容 |
|---|---|
SERVER_HOST | 阿里云公网 IP 或已经解析的域名 |
SERVER_PORT | SSH 端口,默认一般为 22 |
SERVER_USER | 服务器部署用户名 |
SSH_PRIVATE_KEY | 上一步生成的完整私钥内容 |
粘贴私钥时,应包含开头和结尾:
-----BEGIN OPENSSH PRIVATE KEY-----
……
-----END OPENSSH PRIVATE KEY-----注意:
- 不要把私钥直接写入
.yml文件 - 不要把服务器密码提交到 GitHub
- 不要把
.env、私钥文件或真实密钥放进文章 - Secrets 创建后无法再次直接查看明文
十、创建 GitHub Actions 自动部署工作流
在项目根目录创建:
.github/workflows/deploy.yml参考配置如下:
name: Build and Deploy
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: production-deploy
cancel-in-progress: true
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Build
run: npm run docs:build
- name: Configure SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SERVER_HOST: ${{ secrets.SERVER_HOST }}
SERVER_PORT: ${{ secrets.SERVER_PORT }}
run: |
install -m 700 -d ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p "$SERVER_PORT" -H "$SERVER_HOST" >> ~/.ssh/known_hosts
- name: Prepare staging directory
env:
SERVER_HOST: ${{ secrets.SERVER_HOST }}
SERVER_PORT: ${{ secrets.SERVER_PORT }}
SERVER_USER: ${{ secrets.SERVER_USER }}
run: |
ssh -i ~/.ssh/deploy_key -p "$SERVER_PORT" \
"$SERVER_USER@$SERVER_HOST" \
'mkdir -p /opt/1panel/www/sites/ceyanshe.com/index-staging &&
find /opt/1panel/www/sites/ceyanshe.com/index-staging \
-mindepth 1 -maxdepth 1 -exec rm -rf -- {} +'
- name: Upload build files
env:
SERVER_HOST: ${{ secrets.SERVER_HOST }}
SERVER_PORT: ${{ secrets.SERVER_PORT }}
SERVER_USER: ${{ secrets.SERVER_USER }}
run: |
rsync -az --delete \
-e "ssh -i ~/.ssh/deploy_key -p $SERVER_PORT" \
docs/.vuepress/dist/ \
"$SERVER_USER@$SERVER_HOST:/opt/1panel/www/sites/ceyanshe.com/index-staging/"
- name: Switch production directory
env:
SERVER_HOST: ${{ secrets.SERVER_HOST }}
SERVER_PORT: ${{ secrets.SERVER_PORT }}
SERVER_USER: ${{ secrets.SERVER_USER }}
run: |
ssh -i ~/.ssh/deploy_key -p "$SERVER_PORT" \
"$SERVER_USER@$SERVER_HOST" <<'REMOTE'
set -e
site_root="/opt/1panel/www/sites/ceyanshe.com"
release_time="$(date +%Y%m%d-%H%M%S)"
test -f "$site_root/index-staging/index.html"
mkdir -p "$site_root/backups"
if [ -d "$site_root/index" ]; then
mv "$site_root/index" \
"$site_root/backups/index-$release_time"
fi
mv "$site_root/index-staging" "$site_root/index"
mkdir -p "$site_root/index-staging"
find "$site_root/backups" \
-mindepth 1 -maxdepth 1 -type d -mtime +7 \
-exec rm -rf -- {} +
REMOTE这份工作流做了什么
- 监听
main分支的推送 - 拉取最新代码
- 使用 Node.js 20 安装依赖
- 执行 VuePress 构建
- 通过 SSH 连接阿里云
- 使用
rsync上传到index-staging - 确认新版本存在
index.html - 备份当前线上目录
- 将新版本切换为正式目录
- 清理超过 7 天的旧备份
workflow_dispatch 表示也可以在 GitHub Actions 页面手动触发工作流。
如果你的构建目录不是
docs/.vuepress/dist/,需要修改rsync命令中的本地路径。如果服务器目录或域名不同,也必须同步替换工作流中的路径。
十一、第一次触发自动部署
提交工作流:
git add .github/workflows/deploy.yml
git commit -m "ci: add automatic deployment workflow"
git push origin main推送后进入 GitHub 仓库的 Actions 页面,即可看到 Build and Deploy。
绿色对勾表示部署成功,红色叉号表示某一步执行失败。点击某一次运行记录,可以查看具体是哪一步报错。
十二、我实际遇到的依赖版本冲突
部署过程中,我遇到过一次典型的 npm ci 失败:
vuepress@2.0.0-rc.31
@vuepress/plugin-slimsearch@2.0.0-rc.131搜索插件要求的 VuePress 版本与项目安装版本不一致,GitHub Actions 因此停止构建。
这类问题不能只在 Actions 页面反复点击重试,因为依赖关系没有变化,重试仍会失败。
正确处理思路是:
- 阅读 Actions 中的具体错误信息
- 找出冲突的包和版本要求
- 在本地调整
package.json - 重新生成并提交
package-lock.json - 本地构建成功后再推送
当时采用的处理方式是把 VuePress 调整到兼容版本:
npm install -D vuepress@2.0.0-rc.30
npm run docs:build确认构建成功后:
git add package.json package-lock.json
git commit -m "fix: align VuePress dependency versions"
git push origin main这里不建议使用:
npm ci --force或:
npm ci --legacy-peer-deps它们可能暂时跳过依赖检查,却把兼容性风险留到后面。更稳妥的做法是让核心包、主题和插件使用真正兼容的版本。
十三、日常写作和发布流程
自动化部署跑通后,我的日常发布流程变得很简单。
1. 在本地写文章
可以使用:
- Obsidian
- Typora
- VS Code
- Claude Code
- Codex
文章以 .md 文件保存在 VuePress 对应目录。
2. 本地预览
npm run docs:dev重点检查:
- 标题和目录是否正常
- 图片是否能够显示
- 代码块格式是否正确
- 内部链接是否有效
- 桌面端和手机端是否溢出
3. 正式构建
npm run docs:build本地构建通过,可以提前发现大部分路径、依赖和 Markdown 语法问题。
4. 提交和推送
git status
git add .
git commit -m "docs: add VuePress deployment guide"
git push origin main5. 查看自动部署结果
进入:
GitHub 仓库 → Actions → Build and Deploy等待工作流显示绿色成功,再打开正式网站检查新文章。
完整流程可以概括为:
本地写 Markdown → 本地预览和构建 → Git 提交 → 推送 GitHub → Actions 自动构建 → 上传阿里云 → 网站更新
十四、常见问题排查
1. npm ci 失败
优先检查:
package.json和package-lock.json是否同步- Node.js 版本是否一致
- VuePress、主题和插件版本是否兼容
- 本地是否真的执行过
npm run docs:build
不要一开始就使用 --force 掩盖问题。
2. SSH 连接失败
检查:
SERVER_HOST、SERVER_PORT和SERVER_USER是否正确- 私钥是否完整复制到
SSH_PRIVATE_KEY - 对应公钥是否已经加入服务器
- 阿里云安全组是否放行 SSH 端口
- 服务器防火墙是否允许该端口
3. Permission denied
说明部署用户没有目标目录的写权限。应给部署用户设置合理的目录所有权或权限,不建议使用 chmod -R 777。
4. Actions 成功,但网站没有变化
检查:
- OpenResty 的
root是否指向正式index/目录 - 工作流上传路径是否正确
- 浏览器是否命中了旧缓存
- 构建输出目录是否写错
index/index.html的修改时间是否已经更新
可以先使用无痕窗口或强制刷新:
Ctrl + F55. 图片在本地正常,线上不显示
重点检查:
- 图片是否已经提交到 GitHub
- Markdown 图片路径是否区分大小写
- 是否误用了本地绝对路径
- 图片是否放在 VuePress 可公开访问的目录
- 图床链接是否允许公网访问
6. OpenResty 配置修改后网站打不开
先测试配置:
docker exec 你的OpenResty容器名 openresty -t只有出现 syntax is ok 和 test is successful 后,才重新加载服务。
十五、安全与稳定性建议
为了避免后续出现安全和部署问题,我做了以下约束:
- 服务器密码、IP 和私钥全部通过 GitHub Secrets 管理
- 仓库中不保存真实密码和密钥
- 使用
npm ci和锁文件保证依赖可复现 - 新版本先上传到临时目录
- 确认
index.html存在后再切换 - 切换前备份上一版网站
- 定期清理历史备份,避免磁盘被占满
- 每次推送前先在本地完成构建
- 部署失败时保留当前线上版本
如果以后对可用性要求更高,可以继续升级为“版本目录+软链接切换”的发布方式,实现更完整的原子发布和一键回滚。
十六、最终效果
目前,AI 测研社已经跑通了完整的内容发布链路:
- 本地使用 Markdown 写文章
- GitHub 保存项目和历史版本
- GitHub Actions 自动构建
- 自动上传阿里云服务器
- OpenResty 提供 HTTPS 访问
- 推送
main分支后自动更新网站
这套方案最大的价值,不是“搭出了一个网页”,而是建立了一条可以长期使用的内容生产与发布流水线。
过去发布一篇文章,需要进入后台编辑、手动上传图片和调整格式;现在只需要专注于 Markdown 内容,剩下的构建和部署工作由自动化流程完成。
十七、下一步计划
网站基础设施完成后,我接下来会把主要精力放在真实项目和连续内容上:
- 完善 AI Testing Agent 实战专题
- 整理 Claude Code + Playwright MCP 系列教程
- 发布 n8n AI 自动化工作流案例
- 完善 PPT 与视频自动化专题
- 将博客内容同步改编为公众号文章和短视频
如果你也希望搭建一个以 Markdown 为核心、可以自动部署的技术网站,这套方案很适合个人博客、项目文档和小型知识库。
附录:发布前检查清单
每次推送前可以快速确认:
附录:图片放置说明
为了让本文中的截图正常显示,请将 GitHub Actions 截图保存为:
images/github-actions-deploy.png并让它与本文 Markdown 文件保持如下相对结构:
文章目录/
├── 我用VuePress-GitHub-Actions和阿里云搭建了AI测研社.md
└── images/
└── github-actions-deploy.png如果文章要发布到 VuePress 网站,也可以先把图片上传到腾讯云 COS,再把相对路径替换为图片的公网 URL。