“10.0.1.50 是什么服务?”
老张盯着屏幕上的报错信息,眉头紧锁。
build failed: cannot find module "10.0.1.50/user-service":
module lookup disabled by GOPROXY=off
他转头问旁边的新人小李:“你知道 10.0.1.50 是什么服务吗?”
小李一脸茫然。他才入职第三天。
这是老张所在公司——一家中型互联网金融公司——的日常。平台组有 50 多个 Go 微服务,全部用内网 IP 地址作为 import path。没有人觉得有什么不对——直到上周服务器搬迁。
背景:IP 地址做 import path 的"原始时代"
两年前,平台组刚开始用 Go 的时候,一切都很简单。5 个微服务,代码放在自建 GitLab 上,import path 直接写 IP:
import (
"10.0.1.50/user-service"
"10.0.1.50/order-service"
"10.0.1.51/payment-gateway"
)
“能用就行”,当时没人多想。
但两年后,微服务从 5 个增长到了 50 多个。团队从 8 人扩张到 30 人。问题开始浮现:
问题一:IP 地址毫无可读性
// 这段代码你能看懂每个依赖是什么吗?
import (
"10.0.1.50/user-service" // ✅ 还行
"10.0.1.51/pay-gw" // ❓ 支付网关?
"10.0.1.52/cfg-svc" // ❓ 配置中心?
"10.0.1.53/msg-queue" // ❓ 消息队列封装?
)
新人入职第一天就要记住"IP 地址 ↔ 服务名"的映射表。老张自己都经常搞混 10.0.1.52 和 10.0.1.53。
问题二:服务器搬迁 = import path 大逃亡
上周,运维团队通知:机房要搬迁,GitLab 服务器的 IP 会从 10.0.1.50 段变成 10.0.2.50 段。
老张当时就觉得不对。果然——
$ grep -r "10.0.1.50" --include="*.go" | wc -l
347
347 处引用。 分布在 50 多个微服务中。每个服务的 go.mod、每个源文件的 import 语句,全都要改。
老张带着 4 个工程师,花了整整一周:
- 批量替换 import path
- 修改所有
go.mod中的 require 指令 - 修改 CI/CD pipeline 中的 build 配置
- 全量回归测试
- 修复因为"漏改"导致的编译错误
一周的时间,全部浪费在"改地址"这件事上。
问题三:代码截图泄露内网信息
更让老张后怕的是——有一次,一个工程师在技术社区提问时贴了一段代码截图。截图里清清楚楚地写着:
import "10.0.1.50/internal-auth-service"
公司内网 IP 地址,就这样暴露在了公网上。
虽然没有造成安全事故,但信息安全团队找上门来:“你们能不能用个域名?不要用裸 IP。”
探索:调研各种方案
老张开始调研解决方案。
方案一:手写 go-import meta 标签
Go 官方的 vanity URL 机制很简单——在网页上放一个 meta 标签:
<meta name="go-import" content="go.company.com/user-service git http://gitlab.internal/user-service">
理论上可行。但 50 个服务要写 50 个 HTML 页面,还要配 Nginx、申请 HTTPS 证书、维护路由表。
“这不是在给自己找活干吗?“老张放弃了。
方案二:域名直接指向 GitLab
把 go.company.com 解析到 GitLab 服务器?不行——GitLab 不知道如何处理 ?go-get=1 查询参数。
方案三:使用开源工具
老张找到了几个开源方案,但要么已经停止维护,要么功能太简单不支持自定义域名。
发现:gvu
一个同事在 Hacker News 上看到了 gvu——一个专门做 Go Vanity URL 的托管服务。
老张一开始不太信任:“又一个托管服务?我们自己搭不行吗?”
但同事说:“试试呗,反正免费。不好用再自己搭。”
30 分钟后,老张的态度 180 度转变。
实施:30 分钟搞定 50 个模块
第一步:安装和注册
$ curl -fsSL gomodvanityurls.com/install.sh | bash
$ gvu auth signup
第二步:批量添加路由
老张写了一个简单的脚本,把 50 个微服务的路由批量添加:
#!/bin/bash
SERVICES=(
"user-service"
"order-service"
"payment-gateway"
"config-service"
"msg-queue"
# ... 45 more
)
for svc in "${SERVICES[@]}"; do
gvu add "go.company.com/$svc" "http://gitlab.internal/platform/$svc.git"
done
因为有 Pro 订阅,50 个路由几分钟就全部添加完毕。
第三步:DNS 配置
在公司 DNS 管理后台添加一条 CNAME 记录:
go.company.com → gomodvanityurls.com
等了几分钟 DNS 生效。
第四步:修改代码
这一步最"痛苦”——要把所有 10.0.1.50/xxx 替换成 go.company.com/xxx。但这是最后一次了。
# 批量替换
find . -name "*.go" -exec sed -i 's|10.0.1.50/|go.company.com/|g' {} +
find . -name "*.go" -exec sed -i 's|10.0.1.51/|go.company.com/|g' {} +
find . -name "*.go" -exec sed -i 's|10.0.1.52/|go.company.com/|g' {} +
第五步:验证
$ go get go.company.com/user-service
go: downloading go.company.com/user-service v1.2.0
go: added go.company.com/user-service v1.2.0
✅ 成功。
效果:三个月后的今天
新人体验
小李入职后的第一个 PR 里写着:
import (
"go.company.com/user-service"
"go.company.com/order-service"
"go.company.com/payment-gateway"
)
不需要查 IP 对照表了。import path 本身就是服务名。
服务器搬迁
上个月 GitLab 服务器又做了一次迁移(从物理机迁到 K8s)。这次老张只做了一个操作:
# 更新所有路由的 target repo
for svc in "${SERVICES[@]}"; do
gvu remove "$(gvu list --oneline | grep $svc | awk '{print $1}')"
gvu add "go.company.com/$svc" "http://gitlab.k8s.internal/platform/$svc.git"
done
代码零修改。 所有微服务的 import path 没变,编译不用动,CI/CD 不用动。
通配路由
后来老张发现了通配路由功能,一条规则替代了 50 条:
$ gvu add 'go.company.com/platform/*' 'http://gitlab.k8s.internal/platform/*.git'
新增微服务时,只要 repo 在 platform 组下,自动就能用 go.company.com/platform/xxx 来导入。零配置。
回顾
| 维度 | 之前(IP 地址) | 之后(Vanity URL) |
|---|---|---|
| 可读性 | 10.0.1.50/cfg-svc ❌ | go.company.com/config-service ✅ |
| 新人上手 | 需要 IP 对照表 | import path 即服务名 |
| 服务器迁移 | 347 处修改,一周工期 | 改 DNS/路由表,代码零修改 |
| 安全 | 内网 IP 裸奔 | 只暴露品牌域名 |
| 品牌形象 | 像内部实验 | 像正式发布的产品 |
老张现在逢人就推荐 vanity URL。他最常说的话是:
“import path 是你的代码的门牌号。你搬家的时候,门牌号应该不变。”
如果你也在用 IP 地址或裸 GitHub 路径管理 Go 私有模块,试试 gvu。30 秒注册,30 分钟搞定全公司的 vanity URL。