本系列文章曾多次提及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来解决了。
Defining Go Modules,协议的开头,Russ Cox就指明了go modules的设计目标,只要包括以下六点:
协议的主体内容主要包括:
项 | 内容 | 描述 |
---|---|---|
软件包版本 | 遵循语义版本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请求接口:
其中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
}
以下两个请求为可选实现:
依前文所述,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 |
前方高能预警,为避免不必要的踩坑行为,特提醒如下。
以上两点,已经可以避免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体验过程应更为轻松。
安装步骤:
[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"
[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
前文已经讲述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,按照协议,软件包仓库应该返回如下类似响应:
<html><head><meta content='git.example.net/module git https://git.example.net/module.git' name='go-import'>head>html>
之后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
欢迎关注个人公众号:不一样的go语言