前言
本系列文章曾多次提及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,按照協議,軟件包倉庫應該返回如下類似響應:
<!DOCTYPE html><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 "<!DOCTYPE html><html><head><meta content='git.example.net/$1 git http://git.example.net/$1.git' name='go-import'></head></html>";
}
對於第二步,其實有兩個方案可用,一是使用前文所說的.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://git@192.168.197.26:10022"]
insteadOf = https://git.example.com
[url "ssh://git@192.168.197.26:10022"]
insteadOf = https://192.168.197.26:10080
[url "ssh://git@192.168.197.26:10022"]
insteadOf = git://git@git.example.com
二是https協議替換成ssh后,需要生成及指定ssh所用的私鑰。這個通過.ssh目錄做文章。
[eventer@localhost]# pwd
[eventer@localhost]# /home/eventer
[eventer@localhost]# cd .ssh
[eventer@localhost]# ssh-keygen -t rsa -C "your.email@example.com" -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 your.email@example.com
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
FilterFile文件用於指引athens哪些域名不從外網獲取,其內容如下:
D
+ git.example.com
鑒於國內的網絡環境,還需要配置DownloadMode文件,內容如下
downloadURL = "https://goproxy.io"
mode = "async_redirect"
download "git.example.com/*" {
mode = "sync"
}
download "github.com/gomods/*" {
mode = "sync"
}
download "golang.org/x/*" {
mode = "none"
}
download "github.com/pkg/*" {
mode = "redirect"
downloadURL = "https://gocenter.io"
}
配置完成之后,可在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