把 Waline 评论系统从本地 Docker 免费迁移到 Vercel + TiDB
之前我的博客评论系统 Waline 一直跑在家里 Ubuntu 的 Docker 里,用的是 SQLite。这套方案能用,但依赖家里的机器和网络一直在线。这次把它迁到了 Vercel,数据库换成 TiDB Cloud,全程免费,这里记录一下过程和踩到的坑。
先回答两个问题
Waline 算动态网页,要收钱吗?
Waline 是动态服务(评论的读写要走后端 API),但它是 Serverless 架构,可以跑在 Vercel 上。Vercel 的 Hobby 计划永久免费,但只允许个人、非商业用途,个人博客的评论系统没问题。另外 Hobby 有用量上限,超限后是停止服务而不是扣费,普通博客基本用不到。具体额度以 Vercel 官网为准。
为什么必须换数据库?
Vercel 的文件系统是只读、无状态的,SQLite 这种本地文件数据库没法用。所以需要一个云数据库。Waline 支持 LeanCloud、MySQL、MongoDB、PostgreSQL 等,我选了 TiDB Cloud Serverless(兼容 MySQL,有免费额度,Waline 官方文档有专门教程)。
选 MySQL 系而不选 PostgreSQL 的原因是:从 SQLite 导出的 INSERT 语句基本能直接导入 MySQL,而 PostgreSQL 要额外处理列名大小写和类型差异。
几个方案的对比
迁移前我也对比过几个方向:
| 方案 | 结论 |
|---|---|
| Vercel + TiDB | Waline 官方默认方案,文档齐全,最稳 |
| Cloudflare Workers + D1 | D1 是免费数据库,但 Waline 官方不支持,后端也要改造,不推荐 |
| Render 免费档 | 免费服务会休眠,唤醒要几十秒,评论区加载体验差 |
| Cloudflare Tunnel + 现有 Docker | 不用迁移数据,最省事,但依赖机器常开 |
如果机器 24 小时开着,Cloudflare Tunnel 是最省力的选择;想摆脱本地机器,就走 Vercel + TiDB。
迁移步骤
1. 导出 SQLite 数据
先确认数据文件和数据量:
cd ~/docker/walinels -la datasqlite3 data/waline.sqlite ".tables"sqlite3 data/waline.sqlite "select count(*) from wl_Comment; select count(*) from wl_Users; select count(*) from wl_Counter;"迁移前先备份一份:
cp -r data data_backup然后把三张表导出成带列名的 INSERT 语句:
cd datafor t in wl_Users wl_Comment wl_Counter; do sqlite3 waline.sqlite ".headers on" ".mode insert $t" "select * from $t;" > $t.sqldone.headers on 会让 INSERT 带上列名,避免列顺序不一致。
导出的文件里包含管理员的密码哈希和访客 IP 等信息,这些文件不要上传到公开仓库。
2. 在 TiDB Cloud 建库建表
- 注册 TiDB Cloud,创建一个 Serverless 集群,记下 Host、Port(4000)、用户名、密码。
- 创建数据库
waline。 - 使用 Waline 官方提供的 MySQL 建表脚本,创建
wl_Comment、wl_Counter、wl_Users三张表。
3. 导入数据(这里有个坑)
我直接在 TiDB 网页的 SQL Editor 里粘贴导出的 SQL 执行,结果报了语法错误:
Parsing sql failed ... near ""like",ua,url,createdAt,updatedAt) ...原因是 SQLite 导出的列名用的是双引号("like"、"2fa"),而 MySQL/TiDB 里双引号会被当成字符串,列名要用反引号。like 还是 MySQL 的保留字,必须加反引号。
所以导入前需要替换:
sed -i 's/"like"/`like`/g' wl_Comment.sqlsed -i 's/"2fa"/`2fa`/g' wl_Users.sql用网页编辑器的话,直接手动把这两处改成反引号也行。wl_Counter 里没有这类列名,可以原样导入。
如果用命令行客户端导入,建议先执行一句,避免评论里的反斜杠被 MySQL 当成转义符:
SET SESSION sql_mode = CONCAT(@@sql_mode, ',NO_BACKSLASH_ESCAPES');导入后核对条数是否和 SQLite 一致:
SELECT COUNT(*) FROM wl_Users;SELECT COUNT(*) FROM wl_Comment;SELECT COUNT(*) FROM wl_Counter;4. 部署 Waline 到 Vercel(第二个坑)
我一开始直接 fork 了 Waline 的整个 GitHub 仓库,然后在 Vercel 里导入。Vercel 把它识别成了 docs(文档站)、server(Container)和 app(前端)三个子项目,没有一个是能直接部署的评论服务。
正确做法是使用 Waline 官方文档「Vercel 部署」页面里的 Deploy 按钮。它会基于一个很小的模板,在你的 GitHub 下创建新仓库,模板里引用的是已发布的服务端包,部署后就是可用的评论服务。之前 fork 的仓库可以删掉。
第一次部署因为还没配数据库,打不开或报错是正常的。
5. 配置环境变量(第三个、第四个坑)
在 Vercel 项目的 Settings → Environment Variables 里添加变量。
数据库变量要用 Waline 官方 TiDB 教程里的 TIDB_* 这一组,而不是通用的 MYSQL_*:
| 变量 | 值 |
|---|---|
TIDB_HOST | TiDB 集群的 Host |
TIDB_PORT | 4000 |
TIDB_DB | waline |
TIDB_USER | 用户名(Serverless 的用户名带有前缀,要完整复制) |
TIDB_PASSWORD | 密码 |
JWT_TOKEN | 随机字符串,建议新生成,不要沿用旧的 |
SITE_NAME | 站点名称 |
SITE_URL | 博客地址 |
AUTHOR_EMAIL | 博主邮箱 |
几点说明:
- 变量类型:密码和
JWT_TOKEN选 Secret(保存后不可回看);Host、端口、库名等选 Config,方便以后核对。用户名带前缀容易漏,建议选 Config。 - 环境选 Production 即可。
- 我原来在 Docker 里设置了
TZ,在 Vercel 上加这个变量会提示 reserved(系统保留名),不能设置,直接跳过。评论时间在前端会按浏览器本地时区显示,不影响使用。 - 改环境变量不会自动触发部署,要到 Deployments 里对最新一次部署点 Redeploy 才生效。
6. 验证
部署完成后:
- 打开
https://你的vercel域名/ui,用博主账号登录,说明用户表导入成功。 - 在评论管理里能看到旧评论,说明评论表导入成功。
- 打开服务根路径时评论区是空的,这是正常的,因为 Waline 的评论按页面路径存储,示例页的路径是
/,旧评论属于你博客里的各个页面路径。
评论时间在管理页里会比数据库里的值多 9 个小时,这是因为数据库存 UTC,页面按东京时区显示,不是数据错误。
7. 收尾
- 把博客前端 Waline 配置里的
serverURL改成新的地址,本地预览确认旧评论和浏览量都在。 - 在 Vercel 项目的 Settings → Domains 绑定自己的子域名(DNS 加 CNAME),前端改用这个域名。
*.vercel.app在国内访问不太稳定,绑自己的域名也方便以后更换部署而不改前端。 - 确认一切正常后,再添加
SECURE_DOMAINS环境变量做安全域名白名单,多个域名用英文逗号分隔,不带https://,然后 Redeploy。先测通再加白名单,出问题时好定位。 - 最后
docker compose down停掉本地容器,data备份先保留一段时间。
关于后续更新
Vercel 关联了 GitHub 仓库,每次 push 到生产分支都会自动拉取代码并重新部署,不需要手动操作。例外是改环境变量,必须手动 Redeploy。
总结
整个迁移的核心其实就两件事:把 SQLite 数据搬到云端 MySQL 兼容数据库,以及用官方模板部署服务端并配好环境变量。踩的坑集中在三处:
- SQLite 导出的双引号列名在 MySQL 里要换成反引号;
- 不能直接 fork 整个 Waline 仓库部署,要用官方的一键部署模板;
- TiDB 要用
TIDB_*变量,TZ在 Vercel 上是保留名不能设置。
迁完之后评论服务不再依赖家里的机器,Vercel 和 TiDB 的免费额度对个人博客也足够了。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
记忆转储