没有不值得去解决的问题,也没有不值得去学习的技术!

A Tour of Go 多语言翻译项目:从 go-tour 到 go-dev,完成 EdgeOne、Nginx 与 HTTPS 生产域名迁移

图 1:新生产域名 go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功

作者:

A Tour of Go 简体中文站正式上线后,我很快注意到了一个之前一直没有优先处理的问题:生产域名和 Tour 自身的路径出现了语义重复。

原来的正式域名是:

https://go-tour.shuijingwanwq.com

而 A Tour of Go 本身仍然沿用官方的 /tour/ 路径,因此实际课程页面会变成:

https://go-tour.shuijingwanwq.com/tour/welcome/1

也就是说,域名里的 go-tour 和路径中的 /tour/ 重复出现。

其实在正式上线前一晚,我已经专门分析过这个问题。

当时最直接的想法并不是更换域名,而是:

能不能直接把 URL 中的 /tour/ 去掉?

如果能够改成:

https://go-tour.shuijingwanwq.com/welcome/1

从表面上看,URL 会简洁很多。

但继续分析后发现,/tour/ 并不是一个可以随手删除的装饰性前缀。它已经和当前 Tour 的路由、页面路径、静态资源、模板结构、浏览器验收以及现有发布方式形成了比较深的关联。

如果为了消除这一层重复而直接去掉 /tour/,就需要重新审视和修改一整套已经完成验证的路径逻辑。

而项目当时才刚刚完成:

  • 103 个课程页面全部就绪;
  • 公共 UI 本地化;
  • 浏览器最终验收;
  • 生产发布包;
  • EdgeOne 与 Nginx 部署;
  • 远程 Go Playground 运行链路。

为了让 URL 少一个 /tour/,去重新扩大程序和验证范围,性价比并不高。

因此,当晚最终放弃了“删除 /tour/”这个方案。

到了第二天,我换了一个方向思考:

既然 /tour/ 暂时不值得动,那么能不能把域名本身从 go-tour 调整成一个更宽泛的名称?

这时 go-dev 就成为了一个比较自然的候选。

如果改成:

https://go-dev.shuijingwanwq.com/tour

原来的 go-tour.../tour/... 重复问题自然消失,同时又不需要修改已经稳定下来的 Tour 路由和页面结构。

更重要的是,这还解决了另一个长期问题。

如果 A Tour of Go 后续能够获得一定流量,我可能还会继续翻译 go.dev 中其他有价值的官方内容。

这时 go-tour 作为站点级域名就显得有些窄,而:

go-dev.shuijingwanwq.com

更适合作为一个未来可能承载多个 Go 开发文档目录的长期入口。

最终,我决定把正式生产域名从:

go-tour.shuijingwanwq.com

迁移到:

go-dev.shuijingwanwq.com

新的 A Tour of Go 正式入口变成:

https://go-dev.shuijingwanwq.com/tour

这次调整因此并不只是为了让 URL 看起来更简洁,而是在不破坏现有 /tour/ 路径结构的前提下,用更小的改动解决域名重复,同时给未来扩展留下空间。

本文记录这次从 go-tourgo-dev 的完整生产域名迁移过程,包括 Cloudflare DNS、腾讯云 EdgeOne、HTTPS、301 重定向、OneinStack、Nginx、Let’s Encrypt,以及最终的源站清理和验收。


一、迁移原则:只更换公网入口,不重构内部服务

这次迁移首先确定了一个原则:

公网域名需要调整,但已经稳定运行的内部项目和服务名称没有必要跟着修改。

迁移前:

https://go-tour.shuijingwanwq.com/tour

迁移后:

https://go-dev.shuijingwanwq.com/tour

但是下面这些内部名称全部继续保持不变:

  • GitHub 仓库:go-tour-i18n
  • Go module path:github.com/shuijingwan/go-tour-i18n
  • systemd 服务:go-tour.service
  • 服务监听地址:127.0.0.1:3999
  • 生产发布目录:/data/go-tour/

也就是说,这次真正调整的是:

公网域名 → CDN → Nginx 虚拟主机

而不是把整个项目从 go-tour 全部重命名成 go-dev

这样可以显著缩小迁移范围,同时避免为了名称统一而给已经稳定的生产服务增加风险。

图 1:新生产域名 go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功
图 1:新生产域名 go-dev.shuijingwanwq.com 已正常访问,Go 示例远程运行成功

二、先让 go-dev 在 EdgeOne 上独立运行

我没有一开始就关闭旧域名,而是先让新旧两个域名并存。

首先在腾讯云 EdgeOne 中新增:

go-dev.shuijingwanwq.com

源站继续使用现有服务器:

121.40.248.29

回源协议:

HTTPS

端口:

443

为了尽快验证新域名,最开始我暂时把 EdgeOne 的回源 Host 设置成:

go-tour.shuijingwanwq.com

也就是说,此时链路实际上是:

go-dev → EdgeOne → go-tour 源站虚拟主机 → Go Tour

这样可以直接复用前一天已经验证正常的 Nginx HTTPS 配置,不需要先改服务器,就能先确认新公网域名是否正常工作。

Cloudflare DNS 中则增加新的 CNAME:

go-dev.shuijingwanwq.com

指向:

go-dev.shuijingwanwq.com.eo.dnse2.com

代理状态保持为“仅 DNS”,由 EdgeOne 正式承担 CDN 与 HTTPS 服务。

随后在 EdgeOne 中为新域名申请免费 HTTPS 证书,并使用自动验证。

证书部署完成后,新域名很快就已经能够正常访问:

https://go-dev.shuijingwanwq.com/tour/welcome/1

中文课程正文、右侧编辑器和页面静态资源均正常显示。

我又实际点击了一次“运行”,确认生产环境仍然能够通过远程 Go Playground 执行示例代码,最终正常返回:

Plaintext
Hello, 世界
程序已退出。

期间曾出现过一次偶发超时,不过再次运行后立即成功。

因此,这个问题没有被当作域名迁移的阻塞项。后续如果远程运行超时变成高频问题,再单独排查即可。


三、旧域名不直接删除,而是永久 301 到 go-dev

虽然原来的 go-tour.shuijingwanwq.com 才刚刚正式上线不久,但我仍然没有选择直接废弃它。

原因很简单。

即使只上线了一天,也可能已经存在:

  • 浏览器历史记录;
  • 博客文章中的旧链接;
  • 搜索引擎已经发现但尚未正式收录的 URL;
  • 我自己保存或分享过的地址。

因此,更稳妥的方案仍然是:

旧域名永久 301 到新域名,并保持原路径不变。

例如:

https://go-tour.shuijingwanwq.com/tour/welcome/1

永久跳转到:

https://go-dev.shuijingwanwq.com/tour/welcome/1

而不是所有旧页面都统一跳到新站首页。

我直接在 EdgeOne 的规则引擎中创建了一条规则。

匹配条件:

HOST = go-tour.shuijingwanwq.com

操作:

  • 访问 URL 重定向;
  • 目标协议:HTTPS;
  • 目标 Hostname:go-dev.shuijingwanwq.com
  • 目标路径:跟随请求;
  • 查询参数:保留;
  • 状态码:301。
图 2:在 EdgeOne 边缘将旧 go-tour 域名永久 301 到 go-dev,同时保持原路径与查询参数
图 2:在 EdgeOne 边缘将旧 go-tour 域名永久 301 到 go-dev,同时保持原路径与查询参数

发布后立即进行了实际验证:

Plaintext
HTTP/2 301
location: https://go-dev.shuijingwanwq.com/tour/welcome/1
server: TencentEdgeOne

这说明重定向直接发生在 EdgeOne 边缘节点,不需要请求再进入源站 Nginx。

这一点也为后面彻底删除服务器上的旧 go-tour 虚拟主机创造了条件。


四、重新创建 go-dev 的独立 Nginx 虚拟主机

虽然新域名已经可以正常访问,但此时它仍然临时通过:

Host: go-tour.shuijingwanwq.com

回源到旧 Nginx 虚拟主机。

如果想真正完成迁移,服务器最终仍然应该只保留:

go-dev.shuijingwanwq.com

因此下一步是在源站创建新的独立 Nginx 虚拟主机。

我的服务器使用 OneinStack,因此这里继续遵循一个原则:

凡是 OneinStack 已经提供脚本或内置流程的操作,优先使用 OneinStack,而不是直接绕过它调用底层工具。

这次使用的是:

Bash
cd /root/oneinstack

./vhost.sh --proxy --dnsapi

其中:

  • --proxy:创建反向代理虚拟主机;
  • --dnsapi:使用 DNS API 方式签发 HTTPS 证书。

输入的新域名是:

go-dev.shuijingwanwq.com

HTTPS 使用 Let’s Encrypt。

证书密钥类型继续采用:

ec-256

DNS provider 使用:

cf

也就是 Cloudflare DNS 验证。

反向代理目标继续保持:

http://127.0.0.1:3999

这样新虚拟主机最终仍然连接现有的:

go-tour.service

并没有重新部署 Go 应用。


五、操作过程中发现旧 OneinStack 目录仍然存在

这次操作过程中,还发现了一个容易在以后继续造成混乱的问题。

服务器上同时存在:

/root/oneinstack

以及之前升级 PHP 8.5 时使用的新版:

/root/oneinstack-latest-20260804

而原来的 /root/oneinstack 实际上还是 2023 年左右留下来的旧版本。

也就是说,如果继续习惯性地:

Bash
cd /root/oneinstack

很容易再次调用到旧脚本。

既然新版 OneinStack 已经经过 PHP 8.5 等实际维护验证,我决定趁这次一起把目录规范掉。

先确认没有 cron、systemd 或 Shell 配置硬编码引用旧路径后,将旧版本暂时改名:

/root/oneinstack-old-20260812

再把:

/root/oneinstack-latest-20260804

提升为标准目录:

/root/oneinstack

完成全部生产验收后,旧的 2023 年版本最终被删除。

这样以后所有 OneinStack 运维统一从:

/root/oneinstack

进入,避免再次因为新旧脚本并存而选错版本。


六、OneinStack 自动生成的 location 再次带来静态资源 404 隐患

新的 go-dev 反向代理虚拟主机创建完成后,还有一个前一天上线时已经遇到过的问题需要再次处理。

OneinStack 自动生成的 Nginx 配置中包含额外的 location 规则。

对于普通网站来说,这些规则可能没有问题。

但 A Tour of Go 的静态资源本身是由 Go Tour 服务提供的,例如:

/tour/static/css/app.css

如果被 Nginx 的本地静态文件规则提前截获,就不会继续进入:

proxy_pass http://127.0.0.1:3999

最终就可能返回:

404

因此,新虚拟主机生成后,我再次删除了那两段会截获 Tour 静态资源的额外 location

修改后执行:

Bash
nginx -t && service nginx reload

这里还确认了一个当前服务器环境的实际情况:

Bash
service nginx configtest

在这台服务器上不可用。

它会提示:

Plaintext
service 命令只支持基础 LSB 动作

因此目前已经验证可用的方式是:

Bash
nginx -t

检查配置,通过后再:

Bash
service nginx reload

或者直接组合:

Bash
nginx -t && service nginx reload

随后再次验证静态资源:

https://go-dev.shuijingwanwq.com/tour/static/css/app.css

返回:

200

https://go-dev.shuijingwanwq.com/tour/static/lib/codemirror/lib/codemirror.css

同样返回:

200

这说明 /tour/static/ 已经重新通过主反向代理交给 Go Tour 服务处理。


七、EdgeOne 正式切换到新的 go-dev 源站

新的 go-dev Nginx 虚拟主机、HTTPS 和静态资源全部验证正常后,就可以结束最初的临时过渡配置。

EdgeOne 中新域名原来的回源 Host 是:

go-tour.shuijingwanwq.com

此时正式调整为:

go-dev.shuijingwanwq.com

最终生产链路变成:

go-dev.shuijingwanwq.com

→ EdgeOne

→ HTTPS 回源 121.40.248.29:443

Host: go-dev.shuijingwanwq.com

→ 新 Nginx 虚拟主机

127.0.0.1:3999

go-tour.service

这一步完成以后,新生产域名已经彻底不再依赖旧 go-tour Nginx 虚拟主机。


八、彻底删除服务器上的旧 go-tour

既然:

  • go-dev 页面正常;
  • HTTPS 正常;
  • 静态资源正常;
  • Go 示例运行成功;
  • EdgeOne 已正式回源到 go-dev
  • 旧域名 301 在 EdgeOne 边缘完成;

那么服务器就没有必要继续保留旧的:

go-tour.shuijingwanwq.com

Nginx 虚拟主机。

这次继续使用 OneinStack 自带的删除流程:

Bash
cd /root/oneinstack

./vhost.sh --del

只删除:

go-tour.shuijingwanwq.com

而新的:

go-dev.shuijingwanwq.com

保持不动。

随后继续清理了:

  • 旧 Nginx 虚拟主机;
  • 旧 Nginx 备份配置;
  • 旧 SSL 文件;
  • 旧 acme.sh 证书管理记录;
  • /root/.acme.sh/go-tour.shuijingwanwq.com_ecc 残留目录。

最终,和这个项目相关的 acme.sh 记录只剩:

go-dev.shuijingwanwq.com

证书仍然是:

ec-256

CA:

LetsEncrypt.org

图 4:源站完成清理:Nginx 与证书管理只保留新的 go-dev 生产入口
图 4:源站完成清理:Nginx 与证书管理只保留新的 go-dev 生产入口

九、旧 go-tour 为什么还不能从 Cloudflare 和 EdgeOne 删除

服务器里的旧 go-tour 已经彻底删除,但 Cloudflare 和 EdgeOne 中的旧域名却不能一起删除。

这是因为它现在承担着一个新的职责:

旧 URL 的 HTTPS 兼容入口和永久 301。

因此 Cloudflare 中仍然需要保留:

go-tour.shuijingwanwq.com

指向 EdgeOne 的 CNAME。

EdgeOne 中也继续保留:

go-tour.shuijingwanwq.com

以及对应的 HTTPS 证书。

否则用户访问:

https://go-tour.shuijingwanwq.com/…

时,可能在获得 301 之前就因为 HTTPS 失败而无法继续访问。

因此最终架构并不是“彻底删除旧域名”,而是:

Plaintext
go-dev.shuijingwanwq.com
→ 正式生产站
→ EdgeOne
→ go-dev Nginx
→ Go Tour

go-tour.shuijingwanwq.com
→ EdgeOne
→ HTTPS
→ 301 到 go-dev 同路径

另外,我还把旧 go-tour 在 EdgeOne 中的备用回源 Host 调整成了:

go-dev.shuijingwanwq.com

因此,即使在极端情况下没有命中 301 而发生回源,也不会再依赖已经删除的旧 Nginx 虚拟主机。


十、最终验收:200、200、301

整个迁移完成后,我整理了一组非常简单的最终验收。

新正式页面:

https://go-dev.shuijingwanwq.com/tour/welcome/1

返回:

200

新域名静态资源:

https://go-dev.shuijingwanwq.com/tour/static/css/app.css

返回:

200

旧域名:

https://go-tour.shuijingwanwq.com/tour/welcome/1

返回:

301

并且:

Location: https://go-dev.shuijingwanwq.com/tour/welcome/1

图 3:迁移最终验收:新页面和静态资源均返回 200,旧域名正确返回 301
图 3:迁移最终验收:新页面和静态资源均返回 200,旧域名正确返回 301

至此,这次生产域名迁移可以正式判定完成。


十一、顺便增加一份生产运维手册

这次迁移过程中还有一个比较明显的体会。

项目持续时间越来越长以后,只依赖历史聊天记录来记住:

  • OneinStack 当前使用哪个目录;
  • Nginx 应该怎么重载;
  • HTTPS 证书采用什么方式;
  • EdgeOne 如何回源;
  • 哪些内部名称不能因为公网域名变化而重命名;

并不够可靠。

因此,在完成域名迁移后,我又给项目新增了一份:

docs/PRODUCTION_RUNBOOK.md

专门记录当前生产环境的实际维护方式。

它和:

docs/PROJECT_STATE.md

承担不同职责。

PROJECT_STATE.md 主要记录:

项目现在做到哪里,以及为什么变成现在这样。

PRODUCTION_RUNBOOK.md 主要记录:

当前生产环境应该怎么维护。

其中固定记录了:

  • Cloudflare 与 EdgeOne 架构;
  • go-devgo-tour 的职责;
  • go-tour.service/data/go-tour/
  • Nginx 虚拟主机和证书路径;
  • /root/oneinstack 作为唯一正式维护目录;
  • OneinStack 优先原则;
  • nginx -t && service nginx reload
  • /tour/static/ 不能被本地 location 截获;
  • 最小生产验收命令;
  • 本地代理可能干扰 curl --resolve 源站直连判断等注意事项。

以后再处理生产服务器问题时,可以优先查看这份文档,而不需要重新从大量历史操作中寻找细节。


十二、为什么刚上线一天,我仍然选择立即换域名

如果这是一个已经运行几年、积累了大量外链和搜索流量的网站,我不会这么轻易更换生产域名。

但这次情况完全不同。

go-tour.shuijingwanwq.com 才刚刚正式上线。

此时迁移的成本非常低,而继续使用它,则意味着以后长期接受:

go-tour.../tour/...

这样的重复结构。

而前一天已经确认,直接删除 /tour/ 又会牵涉过多已经稳定下来的程序和验证逻辑。

所以与其为了 URL 美化重新改动 Tour 本身,不如只调整站点入口。

更重要的是,项目未来未必永远只停留在 A Tour of Go。

如果后续 Tour 能够获得一定流量,我还可能选择性翻译 go.dev 中其他值得长期维护的内容。

这时:

go-dev.shuijingwanwq.com/tour/

可以继续承载 A Tour of Go。

未来如果确实有必要,也可以自然扩展到类似:

go-dev.shuijingwanwq.com/doc/

或其他内容路径。

因此,这次更换域名并不只是一次 URL 美化。

它实际上是在项目完成第一阶段正式上线之后,对未来内容边界做的一次小幅调整:

保留已经稳定的 /tour/,把更宽泛的语义放到站点级域名上。


十三、当前最终状态

截至这次迁移完成,A Tour of Go 多语言翻译项目当前生产状态为:

正式域名:

https://go-dev.shuijingwanwq.com

A Tour of Go:

https://go-dev.shuijingwanwq.com/tour

旧域名:

https://go-tour.shuijingwanwq.com

职责:

仅保留 HTTPS 和同路径永久 301。

生产服务:

go-tour.service

监听:

127.0.0.1:3999

生产目录:

/data/go-tour/

Nginx 正式虚拟主机:

go-dev.shuijingwanwq.com

HTTPS:

Let’s Encrypt + ec-256

DNS:

Cloudflare

CDN:

腾讯云 EdgeOne

OneinStack 正式维护目录:

/root/oneinstack

最终 HTTP 验收:

  • 新课程页面:200;
  • 新静态资源:200;
  • 旧域名:301;
  • 原路径:完整保留;
  • 中文页面:正常;
  • Go 示例远程运行:正常。

从这一刻开始,go-dev.shuijingwanwq.com 就成为这个项目新的正式生产入口。

go-tour.shuijingwanwq.com 则正式退回到兼容重定向入口的角色。

接下来,我还准备给整个站点增加更明确的“非官方社区多语言翻译项目”说明,并进一步整理项目介绍和品牌边界。不过这些内容不再混入本次域名迁移,而会作为下一阶段单独处理。

需要长期技术维护或远程问题排查?

我是拥有 15+ 年经验的 PHP / Go 后端工程师,长期关注已有系统维护、Bug 修复、性能优化、服务器排查、WordPress 网站维护和小功能迭代。

如果你的项目遇到以下情况,可以先从一次小问题排查开始合作:

  • ✅ PHP / Laravel / Yii2 老项目无人维护
  • ✅ Go / Gin 后端接口需要排查或优化
  • ✅ WordPress 网站访问慢、报错或插件冲突
  • ✅ Nginx / MySQL / Redis / Linux 服务器异常
  • ✅ CDN / Cloudflare / DNS / HTTPS 配置问题
  • ✅ 需要长期远程技术支持或兼职维护

更多介绍请查看:关于我 & 合作

微信:13980074657
邮箱:shuijingwanwq@gmail.com
Telegram:@shuijingwan
GitHub:https://github.com/shuijingwan

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理