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

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

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

作者:

在

A Tour of Go 多语言翻译项目

图3:访问过去的 Go Tour 简体中文站 tour.go-zh.org 时,当前已经无法正常建立连接。

(1) 从「A Tour of Go 中文版」这个搜索词开始:我决定做一个持续维护的 Go Tour 中文版

本地运行的 A Tour of Go「Methods continued」课程页面,左侧为课程说明,右侧为 Go 代码编辑器及运行结果。

(2) A Tour of Go 中文版项目设计冻结:从 101 页到 103 页,从 Gin 转向 Cobra CLI

A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

(3) A Tour of Go 中文版开发实战:英文基线、在线运行与 101 页上游同步体系

go-tour-i18n 项目完成首个简体中文课程页面 welcome/1 的整页翻译、结构校验和本地预览。

(4) A Tour of Go 多语言翻译项目实录:完成首个 zh-CN 页面翻译闭环

图 1:DeepSeek 官方更新日志显示,本次只更新 DeepSeek-V4-Flash,DeepSeek-V4-Pro API 与 APP、Web 模型均未更新

(5) A Tour of Go 中文翻译项目进展:完成前 8 页、修复 present 语法,并暂缓 DeepSeek 对比

图 1:generics/1 简体中文页面本地预览

(6) 一个页面重试五次:使用 GLM-5.2 翻译 A Tour of Go 时遇到的 Token 与 Present 结构问题

图 1:methods/16 简体中文页面浏览器预览

(7) A Tour of Go 中文翻译实录:如何只翻译教学代码注释,而不破坏 Go 代码

图 2:A Tour of Go methods/20「练习:错误」中文候选页面最终渲染效果

(8) A Tour of Go 多语言翻译:当正确的中文语序被受保护标记顺序校验误判

图 3:methods/24 中文页面,静态代码、Bounds 行内代码和链接内的 image.Rectangle 均正确渲染

(9) A Tour of Go 中文翻译完成 7 个代表页校准:最后 3 页又发现了哪些真实问题

图 2:首批 10 个普通页面经过试跑与流程校准后全部进入 ready

(10) A Tour of Go 多语言翻译项目:首批 10 个普通页面试跑,从 2 个 blocked 到全部 ready

图 2:flowcontrol/6 最终中文页面,return 与 v 为适应自然中文语序发生整体换位

(11) A Tour of Go 中文翻译第二批实跑:从 Inline Code 顺序误判到历史响应重新验证

图 1:flowcontrol/6 首次原始输入实验直接通过统一自动校验

(12) A Tour of Go 多语言翻译项目:从 swap 问题重新审视 Token 保护,原始输入与最小保护的一次真实实验

图 7:moretypes/1 最终中文页面预览。左侧课程正文、静态代码块、行内代码和教学注释均正常渲染,右侧官方 Go 示例继续保持原样。

(13) A Tour of Go 多语言翻译实战:第三批 10 页全部 Ready,继续校准 Protected Token 的结构角色

图 4:上游源码确认共有 103 个课程页面,zh-CN 最终达到 ready=103、pending=0、blocked=0

(14) A Tour of Go 多语言翻译项目:zh-CN 103 个课程页面全部 ready,课程正文翻译阶段完成

图 4:GitHub 与 ChatGPT 已成功连接

(15) ChatGPT 连接 GitHub 实测:让 AI 直接读取 go-tour-i18n 仓库参与译文审核

图 3:中文 Go 术语校准完成后,将确定的译法写入 zh-CN glossary

(16) 为什么 channel 最终选择“通道”:A Tour of Go 中文术语统一的一次实践

图 1:技术含义基本正确,但“返回一个返回……”已经明显影响阅读,因此仍被列入 C 类修订

(17) 103 页逐页审核后,只返修 16 页:A Tour of Go 中文正文发布前质量审计

图 2:中文课程页面以及已经完成本地化的编辑器控制区

(18) A Tour of Go 多语言翻译项目:公共 UI 本地化完成,从课程译文走向完整中文界面

图 2:完整 zh-CN 正式投影成功生成,103 个课程页面被重新组装为 7 个 .article

(19) 从 103/103 ready 到完整站点验收:A Tour of Go 中文版补齐正式投影与课程元数据本地化

图 3:从 production bundle 独立启动的 A Tour of Go 简体中文页面,课程正文、公共 UI 和官方 Go 示例均已进入最终发布形态。

(20) A Tour of Go 多语言翻译项目:103 页全部完成后,我终于生成了可独立部署的 zh-CN Production Bundle

图 1:A Tour of Go 简体中文版正式生产页面,远程运行已经成功输出“Hello, 世界”。

(21) A Tour of Go 多语言翻译项目:zh-CN 正式上线,从生产发布到 go.dev Playground 的完整部署记录

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

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

图 1:A Tour of Go 多语言翻译项目正式生产首页

(23) A Tour of Go 多语言翻译项目正式上线:从项目首页、公共 Footer 到生产发布元数据的完整收尾

图 3:GA4 g/collect 请求返回 HTTP 204,并携带当前课程页面地址,确认 Google Analytics 已产生真实事件上报

(24) A Tour of Go 多语言翻译项目:接入 Google Analytics 与百度统计,从 systemd 注入到 EdgeOne 与真实上报验证

图 1:生产环境 robots.txt 与 sitemap.xml 已正确对外提供,Sitemap 共包含 104 个规范 URL。

(25) A Tour of Go 中文站上线后:补齐 robots.txt、Sitemap,并完成五大搜索引擎提交

图 4:发布生产并刷新 EdgeOne 缓存后,再次通过真实手机访问,顶栏项目名称已经完整保持在一行,最终移动端验收通过。

(26) A Tour of Go 多语言翻译项目:修复手机端首页顶栏标题换行,并完成真机验证

图 3:生产自动部署脚本第一次真实运行时,systemd 已经显示 active,但首次 localhost 探测仍然得到 HTTP 000;随后连续 3 次同时满足 active + HTTP 200 后,脚本才判定部署成功,并继续完成公网 HTTP 200 验收。这次真实运行验证了连续应用层健康检查的必要性。

(27) A Tour of Go 多语言翻译项目:从一次 203/EXEC 故障到生产自动部署脚本落地

图 1:阿里云生产环境访问 go.dev Playground 时出现 TLS 握手超时

(28) A Tour of Go 多语言翻译项目:为 Playground 搭建独立的 ZgoCloud 执行代理

图 3:清除 /tour/script.js 缓存后,Run 已直接请求 ZgoCloud /compile 并返回 HTTP 200

(29) A Tour of Go 多语言翻译项目:将生产 Run / Format 切换到 ZgoCloud 执行节点

【图 1:微信中运行 Go 示例时出现 Error communicating with remote server.】

(30) A Tour of Go 中文版手机端运行与格式化偶发失败排查:从间歇性报错到补强 Nginx 可观测性

【图 4:向 golang-dev 公开邮件列表发送 Playground 使用告知邮件并投递成功】

(31) A Tour of Go 多语言项目:为 Playground 代理增加唯一 User-Agent,并主动告知 Go 官方

【图 3:百度剩余 9 个核心 URL 提交成功,remain 归零】

(32) A Tour of Go 搜索引擎收录实测:从 Google 自然索引到百度主动提交与 Bing IndexNow

【图 1:当前正式中文 methods/24 页面,右侧示例代码运行成功】

(33) A Tour of Go 中文翻译完成后,我为什么重新评估 minimal-protect 翻译模式

【图 3:5 个页面 Request SHA256 完全一致,但 Response SHA256 全部不同】

(34) A Tour of Go 翻译实验:更多原始上下文为什么没有更好?从 157 个保护标记到 30 次重复盲评

图 3:3 个代表页 × 4 个评审模型,共 12 次匿名评审的完整排名与第一名票数。

(35) A Tour of Go 翻译质量再评估:minimal-protect 未达预期后,我开始比较 ChatGPT、Codex 与 GLM-5.2

图 2:GitHub 中的 chatgpt-zh-CN-008 重译批次。一个 Batch 同时保存 inputs、raw-responses、candidates、validation 和 retry 记录,GitHub 成为 ChatGPT 与本地翻译流水线之间的数据交接层。

(36) 没有 OpenAI API,我如何用 ChatGPT + GitHub 完成 A Tour of Go 103 页全量重译

图 1:103 个正式课程页面完成最终语义质量审核,结果为 A=103、B/C/D=0,同时不存在缺失、重复或额外页面。A/B/C/D 是本轮项目内部语义质量分级,并不是自动结构 validator 的结果。

(37) 103 页翻完还不能发布:A Tour of Go ChatGPT 译文的语义审核与 Canonical Promotion

图 3:7 个 article endpoint 的 production origin 与 EdgeOne 公网响应全部 exact match,同时新的 ChatGPT 译文标记存在,旧版译文标记已经消失。

(38) 103/103 与 7/7 Exact Match:A Tour of Go ChatGPT 新译文的最终线上验收

【图 2:upstream source preview】

(39) A Tour of Go 多语言翻译项目首次实现 upstream 同步机制:从一次性翻译到长期维护

图 2:A/B/C/D 质量评级规则,展示正式质量 rubric 与 Promotion gate。

(40) A Tour of Go 多语言翻译项目引入 Translation Quality Review:从自动验证到 AI 翻译质量控制

图 4:翻译后 Playground Example,展示中文注释与保持不变的 Go 代码结构。

(41) A Tour of Go 多语言翻译项目实现 Playground Example 本地化:从代码注释翻译到完整学习体验

图 5:公网页面验证广告脚本输出

(42) A Tour of Go 多语言翻译项目接入广告系统:从 AdSense 专用配置到通用 HTML 注入架构

图 3:百度统计单页应用设置

(43) go-dev 单页网站接入统计与 Google AdSense Auto Ads 验证记录

图 1:GitHub 仓库中的多语言翻译项目结构

(44) A Tour of Go 多语言翻译项目:选择日语作为第二语言并启动翻译验证

图 5:模型身份描述不一致

(45) A Tour of Go 多语言翻译项目中的 AI 协作异常排查:一次长期工程会话失效分析

【图 1:ChatGPT GitHub 写入流程调整说明截图】

(46) go-tour-i18n 翻译流程调整:从 ChatGPT GitHub 写入到本地 artifact 导入

图 4 展示了本次异常状态。

(47) go-tour-i18n 翻译自动化流程复盘:已验证的 ChatGPT 翻译能力在新任务中的稳定性问题

【图 6:全部评审结束以后才读取 secret key,完成最终揭盲】

(48) A Tour of Go 翻译质量再评估:Codex High 已接近 ChatGPT High,我决定调整默认翻译引擎

图 4:ja-JP 生产课程页实际运行 Go 示例

(49) A Tour of Go 日语版正式上线:第二门语言 ja-JP 完成生产发布

图 1:ja-JP 上线以后,先提交“新增语言标准化流程”

(50) A Tour of Go 多语言扩展复盘:为第三门语言建立标准化流程

图 1:课程正文结束以后,页面左侧仍然存在大块视觉空白,但 AdSense 并没有在这里插入展示广告。

(51) A Tour of Go 明明有大片空白,为什么 AdSense 就是不显示展示广告?

图 2:golang/website 当前的 _content/tour/。课程内容、静态资源和模板都在这个上游目录中持续维护。

(52) 为了 AdSense,要不要放弃 SPA?A Tour of Go 页面架构的一次取舍

图 4:最终的手动课程广告真实展示在课程内容区域内,而不是 footer 之后。

(53) 保留 SPA 以后,广告应该放在哪里?从 AdSense 预览到方案 B

图 1:Google Search Console 中,多个不同的 A Tour of Go 课程 URL 被判断为“重复网页,用户未选定规范网页”。

(54) SPA 的 SEO 老问题:为什么最后还是给 103 个 A Tour of Go 课程页生成了 Prerender HTML

图 2:方案 B 生产环境中,源码已经正常,但课程主体高度异常缩短,footer 提前进入第一屏。

(55) 从源码空白到 AdSense 高度污染:A Tour of Go 方案 B 的生产收尾

图 1:A Tour of Go 德语版 de-DE 已正式运行在生产环境

(56) A Tour of Go de-DE 上线复盘:第三门语言用了约 16 小时,下一门如何压到 8 小时?

图 1:fr-FR 首次 production 验收完成后,我没有立即开始 ko-KR,而是先连续完成新增语言与首次生产流程的优化

(57) 法语版上线后,我没有马上开始韩语:A Tour of Go 新增语言流程的一轮完善与简化

图 1:根据 ko-KR 期间 76 次 Git 提交估算实际工作时间

(58) 原本预计 6 小时,最后用了约 16~18 小时:A Tour of Go 韩语版上线复盘

图 2:英文 source 与当时发生 validation failure 的 ko-KR candidate 对比

(59) Validator 报错,到底该改译文还是改规则?一次 A Tour of Go 韩语翻译实战

图 2:法语 A Tour of Go 仓库目前已经归档,页面仍保留 553 Commits 和 61 Contributors 的历史

(60) 从法语 553 次提交到韩语两代离线:A Tour of Go 社区翻译为什么难以长期维护

【图 4:es-ES 首次 Production 收口后连续完成的流程优化 commit】

(61) 从 es-ES 首次上线复盘:我把 A Tour of Go 新增语言的 Production 流程又收敛了一轮

图 1:旧公网验收链路连续出现 curl 28 超时,最终导致 public-machine 阶段失败

(62) A Tour of Go Production 公网验收踩坑:从跨境 SOCKS 链路改为 zgocloud direct

图 1:两次 Production Machine Acceptance 都已经通过,但 Headless Chrome 分别在 / 和 /tour/ 判定页面没有完成渲染

(63) 页面明明能打开,为什么 Headless Chrome 仍然判定 Production 失败?

图 3:Bing 从 Google Search Console 中识别到新的 it-IT 站点,并同时发现已有的 1 个 sitemap

(64) GSC → Bing Webmaster Tools:多语言站点如何减少重复验证

图 3:英文站 3 小时内出现 565 个 Cloudflare 来源 URL

(65) IndexNow 实战:手工提交长期不显示,Cloudflare Crawler Hints 却很快出现在 Bing Webmaster Tools

【图 1:A Tour of Go 项目首页目前的语言版本列表】

(66) 从 nl-NL 到 tr-TR:A Tour of Go 连续上线三门语言后,我又优化了哪些流程

【图 4:Go local 页面中已经出现 French、German、Korean、Simplified Chinese】

(67) 从邮件无人回复到进入 Go 官方 Go local:我的 4 个 A Tour of Go 翻译站终于被收录

图8:最终只保留一把启用的新 API Key

(68) 腾讯云 EdgeOne API 自动化:从 CAM 子用户到最小权限 API Key 的完整配置

【图 7:Production Release Batch 最终汇总】

(69) 从同步 Go 官方上游到一个命令发布 10 个语言站:A Tour of Go Production 流程的再次收敛

【图 3:Cloudflare Smart Tiered Cache 已启用】

(70) Cloudflare 缓存 HIT 很快,MISS 却不稳定:回源阿里云杭州的一次真实优化

【图 3:简体中文站 Support 页面】

(71) 广告之外,社区项目如何长期活下去?我给 A Tour of Go 多语言项目加了 Support

图5:评论成功发布

(72) 从 Go Weekly 到 Reddit、DEV 和 Go Forum:第一次系统推广 A Tour of Go 多语言项目

【图 4:RTL 桌面课程布局修复后的效果】

(73) 第一次把 A Tour of Go 做成 RTL:阿拉伯语版本上线,以及我踩过的几个坑

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-tour 到 go-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-dev 与 go-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 则正式退回到兼容重定向入口的角色。

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

A Tour of Go 多语言翻译项目:zh-CN 正式上线,从生产发布到 go.dev Playground 的完整部署记录 A Tour of Go 多语言翻译项目正式上线:从项目首页、公共 Footer 到生产发布元数据的完整收尾

A Tour of Go 多语言翻译项目

本系列完整记录 A Tour of Go 多语言翻译项目从架构设计、整页翻译、结构保护、自动校验,到生产发布与后续维护的实际开发过程。

项目入口:
✅ Brazilian Portuguese — Português (Brasil)
✅ Dutch — Nederlands
✅ French — Français
✅ German — Deutsch
✅ Italian — Italiano
✅ Japanese — 日本語
✅ Korean — 한국어
✅ Simplified Chinese — 简体中文
✅ Spanish — Español
✅ Turkish — Türkçe
✅ 项目源码:GitHub:shuijingwan/go-tour-i18n

项目正在持续扩展更多语言版本,并长期维护翻译质量、生产发布与后续更新。项目属于非官方社区多语言翻译项目,与 Go 官方无隶属或授权关系。