一个真实的噩梦

去年,我朋友老张的公司做了一个"很正常的决定"——把代码从 GitHub Enterprise 迁移到自建 GitLab。

听起来不复杂对吧?代码搬过去,CI 改改,完事了。

直到他们发现:公司 200 多个 Go 微服务的 import path 全是以 github.com/their-org/ 开头的。

迁移代码仓库只需要一个周末。但修改所有下游服务的 import path?3 个工程师干了两个星期。改完还要回归测试,又是一周。

老张事后跟我说了一句话,我至今记得:

“如果当初用了 vanity URL,这一切只需要改一条 DNS 记录。”

这篇文章,就是写给还没踩这个坑的你。


四大隐患

隐患一:平台锁定——换 Git 服务 = 改所有 import

Go 模块的 import path 就是它的身份标识。当你写:

import "github.com/your-org/user-service"

你实际上是在说:这个模块永远住在 GitHub 上。

想迁移到 GitLab?改 import。想迁移到 Gitea?改 import。想迁移到自建 Gitea?还是改 import。

而且不只是改一个地方——是所有引用了这个模块的项目、所有下游服务、所有 go.mod 文件。对于中型团队,这可能意味着 数百个文件的批量修改 + 全量回归测试

这不是假设。Go 社区里几乎每年都有公司因为 Git 平台迁移而经历 “import path 大逃亡”。

隐患二:IP 地址和内网信息裸奔

比 GitHub 锁定更危险的是自建 Git 服务器的场景。很多公司内部直接用 IP 地址或内网域名:

import "192.168.1.100/user-service"
import "gitlab.internal.corp/platform/auth"

这些路径会出现在:

  • 开源项目的 go.mod(如果不小心公开)
  • 代码截图、技术博客、Stack Overflow 问答
  • 前员工的 GitHub 仓库(离职后没清理)

你的内网拓扑结构,就这样散落在互联网的各个角落。

隐患三:品牌缺失——看起来不够"正式"

这个听起来有点虚,但在商业场景中很实际。

比较一下这两个 import path:

import "github.com/zhang-san-ge-personal-project/lib"   // 😬
import "go.yourcompany.com/lib"                          // ✨

第一个看起来像个人实验项目。第二个看起来像一个正式的产品。

当你的 SDK 被外部客户使用时,import path 就是你的品牌门面。github.com/xxx 传递的信息是"这是一个 GitHub 仓库",而 go.yourcompany.com/xxx 传递的是"这是一个经过正式发布的产品"。

隐患四:管理混乱——10+ 模块时谁记得住

当团队只有 2-3 个私有模块时,直接用 GitHub 路径没什么问题。但当模块数量增长到 10、20、50 时:

  • 新人入职:这个 github.com/org/pkggithub.com/org/pkg-v2 哪个在用?
  • 跨团队协作:你的 github.com/org/auth 和隔壁团队的 github.com/org2/auth-lib 是什么关系?
  • 模块归属:这个 repo 还在用吗?owner 是谁?

没有一个统一的命名空间,模块管理就变成了"靠记忆 + 靠搜索 + 靠运气"。


解法:Vanity URL——Go 官方的标准答案

其实 Go 语言从一开始就预见到了这个问题。Go 的 go-import 机制(也叫 Vanity URL)就是为了解耦 import path 和实际代码仓库而设计的。

原理很简单:当 go get 请求一个 import path 时,它会先访问这个 URL 并查找一个 HTML meta 标签:

<meta name="go-import" content="go.yourcompany.com/lib git https://github.com/your-org/lib">

这个标签告诉 Go 工具链:“虽然我的 import path 是 go.yourcompany.com/lib,但实际代码在 github.com/your-org/lib。”

之后 go get 就会从 GitHub 拉取代码,但你的代码里写的 import path 永远是 go.yourcompany.com/lib

这意味着:你可以随意更换底层代码仓库,而 import path 永远不变。

代码搬到 GitLab?改一下 meta 标签就行。迁移到自建服务器?改 meta 标签。甚至从一个 repo 拆成两个?配合 subdirectory module 支持,meta 标签也能搞定。

这个机制在 go modules 文档 中有详细说明。标准库中的 golang.org/x/... 系列包就是最经典的案例——它们的代码托管在 Google 自己的基础设施上(https://cs.opensource.google/go/x/),但 import path 是 golang.org/x/


实操:一行命令搞定

知道了原理,实际操作怎么办?手写 HTML meta 标签 + 配置 HTTPS + 维护 DNS?太原始了。

gvu 是一个专门解决这个问题的工具——托管的 Go Vanity URL 服务,一行命令搞定:

# 安装
curl -fsSL https://gomodvanityurls.com/install.sh | sh

# 注册(免费,无需邮箱)
gvu auth signup

# 添加你的第一个 vanity URL
gvu add gomod.io/yourname/lib https://github.com/your-org/lib

30 秒后,你的同事就可以用 go get gomod.io/yourname/lib 来拉取代码了。

如果你有自定义域名,还能这样:

gvu add go.yourcompany.com/auth https://github.com/your-org/auth-service

通配路由、主版本自动支持(/v2/v3…)、自动 HTTPS 证书——全都开箱即用。


回到开头

老张的公司花了 3 周完成了 200 个微服务的 import path 迁移。3 个人 × 3 周 = 9 人周的成本,只因为当初"觉得直接用 GitHub 路径没什么问题"。

import path 是你代码的门牌号。

门牌号应该跟着你,不跟着房东。


如果你也在管理 Go 私有模块,不妨花 30 秒试试 gvu——免费的 gomod.io 域名、免费的自动 HTTPS、免费的主版本支持。你的 import path,应该由你说了算。