本文提供一种方案解决在离线的内网环境下搭建相同域名私有"github.com"服务,用于解决内网离线场景下 Go 语言编译构建依赖问题, 让您的内网 DevOps 实践更加完善。
对于其他语言的依赖一般可以通过修改配置文件的的方式调整为内部私有的仓库,但是Go语言比较特殊,Go 项目依赖通过 import 直接从网络导入。
go
import "github.com/google/uuid"
对于可以访问互联网的开发/CI环境还可以通过配置 GOPROXY='https://goproxy.cn,direct' 来加速国内访问,但是对于内网离线环境则网络不通。
内网离线Go开发常见的解决方案:
- 通过 vendor 模式进行缓存。但对于离线环境的项目开发过程中,会动态的新增很多新的功能和依赖,引入的依赖包还可能再依赖。这种情况下想缓存到 vendor 耗时很长,研发效率极低,无法进行自动化CI/CD。并且 vendor 目录会使项目体积变得非常大,因为包含了所有依赖的源代码。
- 修改 go.mod 文件,使用 replace 进行替换。但是依赖的开源项目还会有新依赖,go mod 动态更新的 mod 文件导致维护难度极大,效率也极低。
期望有一种解决方案能够实现如下能力:
- 由运维团队统一搭建Go基础设施和服务,研发人员和CI/CD环境不需要关注服务提供的细节
- 运维人员从互联网上进行定期的同步更新,增量离线同步到公司内网,保持代码的时效性
- 研发人员和CI/CD环境只需要几行配置即可实现和互联网一致的开发体验,甚至性能和延迟更好
本文提供一种解决方案,主要有以下几个步骤:
- 搭建私有代码仓库,定时增量同步源仓库
- 制作github.com域名证书
- 配置github.com nginx服务
- 配置DNS解析
- 修改客户端配置忽略证书告警
- 验证代码拉取
- 验证go mod 依赖拉取
- 解决非github.com域名重定向的代码仓库
1.搭建私有代码仓库
搭建私有的代码仓库服务是提供github.com的基础,它提供了核心的代码同步、存储、下载的功能,你可以选择任意你熟悉的私有代码仓库软件,比如 Gitlab、Gitea、Gogs 等。
本文以 Gitea 为例,使用 Docker Compose 来部署
yaml
services:
gitea:
image: harbor.cncfstack.com/docker.io/gitea/gitea:1.21.1
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=1.2.3.4:5432
- GITEA__database__NAME=aaa
- GITEA__database__USER=bbb
- GITEA__database__PASSWD=ccc
- TLSMinVersion=TLSv1.2
restart: always
volumes:
- /data/gitea-data/git:/data/git
- /data/gitea-data/gitea:/data/gitea
- /data/gitea-data/ssh:/data/ssh
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ./app.ini:/data/gitea/conf/app.ini:ro
ports:
- "3000:3000"
- "22:10022"
对于运行依赖的数据库也可以使用 docker-compose 来部署,对于 app.ini 配置文件可以参考官网的配置或者不挂载使用默认的配置也可以。
2.制作github.com域名证书
在上一篇文章 《一张图了解HTTPS证书的CA、签发、校验和数据加密流程。详解OpenSSL自签证书,并封装自动化脚本》 中详细介绍了如何制作自签名证书。
使用文章中提供的自动化脚本 make_https_crt.sh 来创建自签名证书,需要特别注意的是需要制作 github.com 的证书。
在整理上一篇文章时,我已经制作好了github.com的证书。
bash
#./make_https_crt.sh github.com
域名私钥: /tmp/aa/github.com/github.com.key
域名公钥: /tmp/aa/github.com/github.com.pem
域名证书请求文件: /tmp/aa/github.com/github.com.csr
域名证书: /tmp/aa/github.com/github.com.crt
......
3.配置github.com nginx服务
Gitea 启动后不直接对外提供服务,而是通过一个 Nginx 来进行代理。这样做主要是为了更好的控制一些配置,比如:证书、跨域等问题。
这样的话可以添加 github.com 的虚拟主机以及对应的自签名证书。
Nginx 最简配置如下:
bash
server {
listen 443 ssl;
server_name github.com;
ssl_certificate /tmp/aa/github.com/github.com.crt;
ssl_certificate_key /tmp/aa/github.com/github.com.key;
location / {
proxy_pass http://gitea:3000;
}
}
4.配置DNS解析
在搭建完成私有代码仓库服务后,需要将 github.com 添加到 DNS 解析中,这样私有的 "github.com" 才能提供服务。
对于公司级别的服务,建议在公司或者机房的统一DNS服务器中添加解析。
对于少量机器或个人设备可以添加到 hosts 文件中
- 对于Windows设备修改文件路径
bash
C:\Windows\System32\drivers\etc\hosts
对于 Linux/Mac 修改文件路径
bash
/etc/hosts
如下是藏云阁的DNS配置,可以作为国内代码拉取的配置,合法证书的入口地址为 gitea.cncfstack.com。如果缺少对应的代码仓库,可创建自动同步的仓库。
120.55.240.206 github.com
5.修改客户端配置忽略证书告警
在上面步骤中,已经搭建好了私有 "github.com" 的服务,但由于 https 的证书是自签名的,所以需要配置忽略证书验证。
提示:如果有运维团队维护企业私有的CA自签的证书,该CA可预置到所有的节点,那么可以忽略当前步骤。
以下提供了3种方法来忽略证书,内网离线环境推荐使用方法3配置全局忽略验证。
- 方法1: 对于一次性的克隆操作,你可以选择临时忽略证书验证:
使用环境变量,在克隆命令前设置 GIT_SSL_NO_VERIFY 环境变量。
bash
GIT_SSL_NO_VERIFY=true git clone https://github.com/user/project.git
- 方法2: 使用Git配置参数:在克隆时通过 -c 参数临时禁用SSL验证。
bash
git -c http.sslVerify=false clone https://github.com/user/project.git
- 方法3: 配置全局忽略验证
如果确实需要,可以使用以下命令:
bash
git config --global http.sslVerify false
6.验证代码拉取
没有添加证书安全忽略时克隆下载代码会提示 SSL certificate problem 如下错误:
bash
% git clone https://github.com/google/uuid
Cloning into 'uuid'...
fatal: unable to access 'https://github.com/google/uuid/': SSL certificate problem: unable to get local issuer certificate
由于是自签名的证书,添加证书忽略后,可以拉取成功。
bash
% git -c http.sslVerify=false clone https://github.com/google/uuid
Cloning into 'uuid'...
remote: Enumerating objects: 604, done.
remote: Counting objects: 100% (604/604), done.
remote: Compressing objects: 100% (236/236), done.
remote: Total 604 (delta 368), reused 577 (delta 359), pack-reused 0
Receiving objects: 100% (604/604), 181.49 KiB | 4.43 MiB/s, done.
Resolving deltas: 100% (368/368), done.
7. 验证go mod依赖拉取
准备一个简单的 Go 代码,并且有一个依赖项,这里选择 Google 的 uuid 包。
golang
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
uuidV4 := uuid.New()
fmt.Println("UUID v4:", uuidV4)
}
默认的使用 go mod tidy 时会有以下的请求路径
- 步骤1. 电脑执行
go mod tidy或者go get github.com/google/uuid - 步骤2. 访问 goproxy 获取代码 :proxy.golang.org/github.com/...
- 步骤3. 访问sum.golang.org校验哈希
这个步骤在内网离线会有以下几个问题
问题1: github.com 证书校验不通过
提示 SSL certificate problem异常
bash
/go/test # go mod tidy
go: finding module for package github.com/google/uuid
go: main imports
github.com/google/uuid: module github.com/google/uuid: git ls-remote -q origin in /go/pkg/mod/cache/vcs/c3a7687c436b44e1c0c389b1d5515300d44b6949685144fd082c00abc5f758a4: exit status 128:
fatal: unable to access 'https://github.com/google/uuid/': SSL certificate problem: unable to get local issuer certificate
需要添加git证书忽略。
bash
git config --global http.sslVerify false
问题2: 内网无法访问 goproxy
Go 1.13+ 默认启用 GOPROXY=https://proxy.golang.org,direct,内网无法访问 goproxy
bash
github.com/google/uuid: module github.com/google/uuid: Get "https://proxy.golang.org/github.com/google/uuid/@v/list": dial tcp 142.250.73.145:443: i/o timeout
可以通过设置direct直接访问 github.com
bash
go env -w GOPROXY=direct
问题3: 内网无法访问 sum.golang.org
访问sum.golang.org校验哈希,内网也是网络不通
bash
/go/test # go get github.com/google/uuid
go: downloading github.com/google/uuid v1.6.0
go: github.com/google/uuid@v1.6.0: verifying module: github.com/google/uuid@v1.6.0: Get "https://sum.golang.org/lookup/github.com/google/uuid@v1.6.0": dial tcp 142.251.34.209:443: i/o timeout
可以通过关闭 GOSUMDB 来跳过校验
bash
go env -w GOSUMDB=off
完整的执行过程
开发人员或CI/CD环境,通过添加如下3行配置即可完成依赖拉取
bash
go env -w GOSUMDB=off
go env -w GOPROXY=direct
git config --global http.sslVerify false
示例基于一个全新的 golang 容器镜像,运行结果如下:
bash
% docker run -it --rm harbor.cncfstack.com/docker.io/library/golang:1.25.2-alpine3.22 /bin/sh
/go # apk add git &> /dev/null
/go # echo '120.55.240.206 github.com' >> /etc/hosts
/go # cat /etc/hosts |grep github.com
120.55.240.206 github.com
/go # mkdir demo && cd demo
/go/demo # cat > main.go <<EOF
> package main
>
> import (
> "fmt"
> "github.com/google/uuid"
> )
>
> func main() {
> uuidV4 := uuid.New()
> fmt.Println("UUID v4:", uuidV4)
> }
> EOF
/go/demo # go env -w GOSUMDB=off
/go/demo # go env -w GOPROXY=direct
/go/demo # git config --global http.sslVerify false
/go/demo # go mod init main
go: creating new go.mod: module main
go: to add module requirements and sums:
go mod tidy
/go/demo # go mod tidy
go: finding module for package github.com/google/uuid
go: downloading github.com/google/uuid v1.6.0
go: found github.com/google/uuid in github.com/google/uuid v1.6.0
/go/demo # go run main.go
UUID v4: 8f166ed5-af61-46cd-ad03-08de12cfe4da
8. 解决非github.com域名重定向的代码仓库
有些 Go 代码的依赖源代码会通过非 "github.com" 的域名进行访问,比如 "golang.org"、"gopkg.in"、"go.uber.org" 等。
对于这些域名基本上后端的代码仓库依然是 github.com,只是做了域名的重定向调整了URL路径。解决方法是配置代理重定向到github.com即可。
即使后端不是github.com的域名,比如gitlab.com,也可以通过上述类似的方法建立对应的代码仓库,然后通过代理重定向到对应的代码仓库解决。
总结
本文提供了一种企业级的内网 Go 语言依赖拉取解决方案,可由运维人员统一维护服务,研发人员和CI/CD环境只需要3行配置即可实现内网 Go 开发自由。
虽然收集和同步代码仓库以及维护重定向的域名配置列表是个费时费力的人力工作,但是一旦完成,后续的代码依赖拉取管理就非常轻松了。
如果选择藏云阁提供服务,则这些费时费力的人力工作则不需要了,因为藏云阁已经完成了大量的代码仓库的收集和同步,以及域名重定向的配置维护,企业直接内网落地即可。