前言
本系列文章曾多次提及go的依赖管理,提到了私仓,构件系统等概念,也曾提及当前流行的go构件系统,如athens,jfrog artifactory。鉴于jfrog的收费特性,本文只选择athens着重介绍。包括安装、使用、踩坑、源码分析等。
简要介绍
在依赖管理方面,go相较于其他语言,其提供了从开放的源码仓库(github、bitbucket等)动态获取源码的工具,最早是go get,go1.11后又新增了go mod。而像java,其本身并没有提供类似的命令,因而才诞生了maven、gradle这样的构建系统。它们的构件则来源于公共仓库或者自行搭建的私有仓库。go的创造者估计是想回避掉这些问题,而直接由官方来提供,这样更便利于开发人员。但其短板则在于私仓,因为私仓在网络、权限、安全等方面会有诸多的限制,并不能像github那样公开,在这样的情况下,go get或者go mod也只能是心有由而力不足。鉴于此,go提出了GOPROXY的概念,通过vgo download protocol协商软件包的获取。剩下的网络、权限等问题就交由proxy server来解决了。
go软件包协议
Defining Go Modules,协议的开头,Russ Cox就指明了go modules的设计目标,只要包括以下六点:
- 希望约定以tag的方式发布软件包,同时又能支持获取指定commit的软件包;
- 希望不借助其他版本管理工具为实现软件包的获取;
- 希望支持软件包的多版本管理;
- 希望在获取软件包时拥有前置个人或公司的代理仓库或私有仓库的能力;
- 希望支持在未来扩展实现公有仓库,同时又能在没有公有仓库的情况下正常运作;
- 希望废弃vendor文件夹。
协议的主体内容主要包括:
项 | 内容 | 描述 |
---|---|---|
软件包版本 | 遵循语义版本2.0(Semantic Versioning) | 同时支持tag与commit方式 |
go mod | go.mod | 定义go.mod文件的内容 |
软件包 | 定义软件包在源码库的组织方式 | 支持分支方式、子目录方 |
发布方式 | 定义软件包的发布方式 | 软件包仓库需支持go-get=1参数获取软件包meta |
打包方式 | 定义软件包的打包方式 | 在构件系统中以zip格式保存 |
下载协议 | 定义软件包如何从构件系统获取 | 实现四个或六个http请求接口 |
代理服务 | 定义GOPROXY服务系统的概念 | 如goproxy.io, gocenter.io |
download protocol约定以下四个http请求接口:
- GET baseURL/module/@v/list: 获取指定软件包的所有已知版本信息,每个版本一行。
- GET baseURL/module/@v/version.info: 获取指定软件包特定版本的json格式元数据。
- GET baseURL/module/@v/version.mod: 获取指定软件包特定版本的mod文件。
- GET baseURL/module/@v/version.zip: 获取指定软件包特定版本的zip源码包。
其中version.info响应定义如下:
type RevInfo struct {
Version string // version string
Name string // complete ID in underlying repository
Short string // shortened ID, for use in pseudo-version
Time time.Time // commit time
}
以下两个请求为可选实现:
- GET baseURL/module/@t/yyyymmddhhmmss:获取与指定时间戳最近的一个版本,响应数据与version.json接口一致
- GET baseURL/module/@t/yyyymmddhhmmss/branch: 获取指定分支下,与指定时间戳最近的一个版本,响应数据与version.json接口一致。
athens私仓搭建
依前文所述,go的构件系统其实是由两部分组成的,一是实现了vgo download protocol的服务;二是软件包仓库。前者是纯代理模式,比如goproyx.io,gocenter.io,其本身并不存储软件包,而只是转发请求从指定仓库(github等)获取所需软件包。后者真的就只是一个软件包仓库(如github,github目前并不是GOPROXY,虽然它可以是)。而athens则既是代理又是软件包仓库(类似于nexus,属于本地缓存仓,原始软件包仍然在公共仓库或私有仓库)。接下来的篇幅,着重于介绍如何安装并使用athens。
athens本身使用go语言编写,除了通过常规运行方式之外,还支持docker、k8s的方式运行,本文介绍的方式为第一种。常规的运行方式遇到问题或者踩到深坑,可较快速找到问题所在,不需要再去考虑装载容器的因素。
系统与网络环境说明:
项 | 说明 |
---|---|
操作系统 | linux, centos 7 |
gitlab | 内网ip, 192.168.197.26,http端口10080,ssh端口10022,域名git.example.com |
athens宿主机 | 内网ip, 192.168.197.205 |
go | go 1.12 |
git client | git-bash |
前方高能预警,为避免不必要的踩坑行为,特提醒如下。
- 务必为gitlab配置域名
- 务必使用https
以上两点,已经可以避免ip、域名、端口诸多烦人的问题。比如go get指令是不支持这样的指令的:
[eventer@localhost]# go get github.com:8080/gomods/athens
go get github.com:8080/gomods/athens: malformed module path "github.com:8080/gomods/athens": invalid char ':'
当然如果gitlab满足以上两点, 同时http与ssh端口又是默认80与443端口,那么athens体验过程应更为轻松。
安装步骤:
- 下载&编译athens
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer
[eventer@localhost]# git clone https://github.com/gomods/athens.git
[eventer@localhost]# cd athens
[eventer@localhost]# make build-ver VERSION="0.2.0"
- 运行athens
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer/athens
[eventer@localhost]# ./athens -config_file=./config.dev.toml
到这一步,默认配置的情况下,go的构件系统已经搭建好了,只需要将自己开发环境的go环境变量GOPROXY指向这里即可。IP地址:192.168.197.205:3000。其中192.168.197.205是athens安装机器ip。
- 修改默认配置
打开config.dev.toml,可能要修改的配置项说明如下。
配置项 | 说明 |
---|---|
Port | 端口号,默认3000 |
StorageType | memory, disk, mongo, gcp, minio, s3。本安装过程选择disk |
FilterFile | 过滤策略,与GlobalEndpoint配合使用。 |
GlobalEndpoint | 全局代理,即athens的GOPROXY,可设置为https://goproxy.io |
NETRCPath | 自动登录脚本,放在当前用户home目录下,文件名.netrc此处可用于访问git |
GithubToken | github访问令牌,用于访问github |
HGRCPath | Mercurial自动登录脚本,放在当前用户home目录下,文件名.hgrc |
Stogage->Storage.Disk | 当StorageType为disk时,需要修改此处 |
FileterFile
athens约定的软件包过滤策略,当前可用策略包括D、-、+三种。D必须存在且放在第一行;-表示禁止下载此软件包,若请求,报403;+表示不从指定的GlobalEndpoint下载,而直接从域名指定的仓库获取软件包。-与+对软件包的策略可指定至版本,多个版本用,号分隔,甚至可使用版本修饰符(~, ^, >)。此外#开头的行表示注释,会被忽略。
D
# 内网的gitlab不需要通过GlobalEndpoint下载
+ git.example.com
+ github.com/gomods/athens v0.1,v0.2,v0.4.1
修饰符 | 说明 |
---|---|
~ | ~1.2.3表示激活所有大于等于3的patch版本,在语义化版本方案中,最后一位的3表示补丁版本。如1.2.3, 1.2.4, 1.2.5 |
^ | ^1.2.3表示激活所有大于等于2的minor与大于等于3的patch版本。如1.2.3, 1.3.0 |
< | <1.2.3表示激活所有小于1.2.3的版本。如1.2.2, 1.0.0, 0.1.1 |
这个过滤策略主要用于API兼容或者软件包license改变时使用。
NETRCPath
自动登录脚本,即遇到指定machine需要输入用户名密码时,则从登录脚本中寻找是否有匹配的配置项。示例如下:
# filename: .netrc
machine 192.168.197.26
login eventer
password 123456
- gitlab访问配置
前文已经讲述vgo download protocol,因此当athens接收到go get或go mod的指令时,需要做出正确的回应。这四个接口是依次执行下去的,后一个接口依赖于前一个接口的响应。而这其中的关键在于两个地方,其中一个是软件包仓库支持的获取元数据的接口(GET baseURL/module?go-get=1),第二个是获取软件包(GET baseURL/module/@v/version.zip)。当athens收到GET baseURL/module/@v/version.info指令时,如果本地没有,会直接调用go mod命令去指定的软件包仓库下载。而这个命令的第一步就是获取软件包元数据,即向软件包仓库发起请求GET baseURL/module?go-get=1,按照协议,软件包仓库应该返回如下类似响应:
之后go会使用git clone命令下载zip软件包至本地,至此go mod的使命完成。然后athens将软件包加上版本号,改名后放至本地仓库中,等待GET baseURL/module/@v/version.zip。
可以看到关键的两步,第一步要返回准确的git地址,第二步要能访问git并clone下来。
对于第一步,低版本的gitlab可能会返回不正确的meta,这里提供的方案是使用nginx来处理。方案如下(不同gitlab环境可能nginx的配置会有所不同,比如路径匹配):
if ($args ~* "^go-get=1") {
set $condition goget;
}
if ($uri ~ ^/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)) {
set $condition "${condition}path";
}
if ($condition = gogetpath) {
return 200 "";
}
对于第二步,其实有两个方案可用,一是使用前文所说的.netrc文件,二是使用ssh。本文选择的是ssh。
ssh方案要处理两件事情,一是go调用git clone命令,使用的是https协议,需要替换为ssh协议,这可以通过gitconfig文件完成,示例如下:
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer
[eventer@localhost]# touch .gitconfig
.gitconfing内容如下:
# filename: .gitconfig
[url "ssh://[email protected]:10022"]
insteadOf = https://git.example.com
[url "ssh://[email protected]:10022"]
insteadOf = https://192.168.197.26:10080
二是https协议替换成ssh后,需要生成及指定ssh所用的私钥。这个通过.ssh目录做文章。
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer
[eventer@localhost]# cd .ssh
[eventer@localhost]# ssh-keygen -t rsa -C "[email protected]" -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/data/rfchina/.ssh/id_rsa): git_id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in aaaa.
Your public key has been saved in aaaa.pub.
The key fingerprint is:
SHA256:0/jCNSuQ5vuOl/URiFH466dm+B+dgVSaVi3T5eSKs1Q [email protected]
The key's randomart image is:
+---[RSA 4096]----+
| o. oo+|
| o =o+o|
| + .= Eoo|
| ..ooo.+ . |
| + S +.=.o |
| o o +o+.+ o |
| . +=o.o.o |
| o+o+ o. |
| o+o+o+. |
+----[SHA256]-----+
RSA秘钥对生成之后,将公钥放置到gitlab的ssh keys中,私钥放在home目录下的.ssh目录中。
然后配置config文件,指定哪个host使用哪个私钥。
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer
[eventer@localhost]# cd .ssh
[eventer@localhost]# touch config
config内容如下:
# filename: config
Host 192.168.197.26
HostName 192.168.197.26
Port 10022
StrictHostKeyChecking no
IdentityFile /home/eventer/.ssh/git_id_rsa
配置完成之后,可在athens宿主机上尝试运行git clone命令,如果 能顺序获取到源码,则配置正确。否则需要根据athens的log并在宿主机上尝试运行go mod或go get指令来检查问题。
至此,athens可以访问私有的gitlab源码库了。go私仓安装教程毕。
如果采用docker方式安装,athens的环境变量已在config.dev.toml中的注释中给出。简略安装步骤如下:
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer
[eventer@localhost]# mkdir -p athens/storage
[eventer@localhost]# cd athens/storage
[eventer@localhost]# mkdir gitconfig
[eventer@localhost]# mkdir ssh-keys
[eventer@localhost]# export $ATHENS_STORAGE=/home/eventer/athens/storage
[eventer@localhost]# docker run -d -v $ATHENS_STORAGE:/var/lib/athens \
> -v $ATHENS_STORAGE/gitconfig/.gitconfig:/root/.gitconfig \
> -v $ATHENS_STORAGE/ssh-keys:/root/.ssh \
> -e ATHENS_DISK_STORAGE_ROOT=/var/lib/athens \
> -e ATHENS_FILTER_FILE=/var/lib/athens/FilterFile \
> -e ATHENS_GLOBAL_ENDPOINT=https://goproxy.io \
> -e ATHENS_STORAGE_TYPE=disk \
> --name athens-proxy \
> --restart always \
> -p 3000:3000 \
> gomods/athens:latest
在执行docker run命令之前,将前文所述的.gitconfig拷贝至/home/eventer/athens/storage/gitconfig目录下,同时将config与git_id_rsa文件拷贝至/home/eventer/athens/storage/ssh-keys目录下。
#查看docker日志
[eventer@localhost]# docker logs -f athens-proxy
#进入docker
[eventer@localhost]# docker exec -it athens-proxy /bin/sh
#重启docker
[eventer@localhost]# docker restart athens-proxy
#停止docker
[eventer@localhost]# docker stop athens-proxy
#删除docker
[eventer@localhost]# docker rm athens-proxy