<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          完美的內(nèi)部私有Go module 拉取方案

          共 10755字,需瀏覽 22分鐘

           ·

          2021-09-19 12:47

          # 1. 問(wèn)題來(lái)由

          Go 1.11版本[1]后,Go命令拉取依賴的公共go module不再是“痛點(diǎn)”。如下圖所示:

          圖:從公司內(nèi)部經(jīng)由公共GOPROXY服務(wù)拉取公共go module

          我們?cè)诠?組織內(nèi)部?jī)H需要為環(huán)境變量GOPROXY配置一個(gè)公共GOPROXY服務(wù)即可輕松拉取所有公共go module(公共module即開源module)。

          但隨著公司內(nèi)Go使用者增多以及Go項(xiàng)目的增多,“代碼重復(fù)”問(wèn)題就出現(xiàn)了。抽取公共代碼放入一個(gè)獨(dú)立的、可被復(fù)用的內(nèi)部私有倉(cāng)庫(kù)成為必然。這樣我們便有了拉取私有g(shù)o module的需求!

          一些公司或組織的所有代碼都放在公共vcs托管服務(wù)商那里(比如github.com),私有g(shù)o module則直接放在對(duì)應(yīng)的公共vcs服務(wù)的private repository(私有倉(cāng)庫(kù))中。如果你的公司也是如此,那么拉取托管在公共vcs私有倉(cāng)庫(kù)中的私有g(shù)o module也很容易,見下圖:

          圖:從公司內(nèi)部直接拉取托管在公共vcs服務(wù)上的私有g(shù)o module

          當(dāng)然這個(gè)方案的一個(gè)前提是:每個(gè)開發(fā)人員都需要具有訪問(wèn)公共vcs服務(wù)上的私有g(shù)o module倉(cāng)庫(kù)的權(quán)限,憑證的形式不限,可以是basic auth的user和password,也可以是personal access token(類似github那種),只要按照公共vcs的身份認(rèn)證要求提供即可。

          但是如果私有g(shù)o module放在公司內(nèi)部的vcs服務(wù)器上,就像下面圖中所示:

          圖:私有g(shù)o module放在組織/公司內(nèi)部的vcs服務(wù)器上

          那么我們?cè)撊绾巫孏o命令自動(dòng)拉取內(nèi)部服務(wù)器上的私有g(shù)o module呢?

          一些gopher會(huì)說(shuō):“這很簡(jiǎn)單啊! 這和拉取托管在公共vcs服務(wù)上的私有g(shù)o module沒(méi)有什么分別啊”。持這種觀點(diǎn)的gopher多半來(lái)自大廠。大廠內(nèi)部有完備的IT基礎(chǔ)設(shè)施供開發(fā)使用,大廠內(nèi)部的vcs服務(wù)器都可以通過(guò)域名訪問(wèn)(比如git.bat.com/user/repo),因此大廠內(nèi)部員工可以像訪問(wèn)公共vcs服務(wù)那樣訪問(wèn)內(nèi)部vcs服務(wù)器上的私有g(shù)o module,就像下面圖中所示:

          圖:大廠方案:直接拉取內(nèi)部vcs倉(cāng)庫(kù)上的私有g(shù)o module

          我們看到:在上面這個(gè)方案中,公司搭建了一個(gè)內(nèi)部goproxy服務(wù)(即上圖中的in-house goproxy),這樣的目的一來(lái)是為那些無(wú)法直接訪問(wèn)外網(wǎng)的開發(fā)機(jī)器以及ci機(jī)器提供拉取外部go module的途徑,二來(lái)由于in-house goproxy的cache的存在,還可以加速公共go module的拉取效率。對(duì)于私有g(shù)o module,開發(fā)機(jī)將其配置到GOPRIVATE環(huán)境變量中,這樣Go命令在拉取私有g(shù)o module時(shí)不會(huì)再走GOPROXY,而會(huì)采用直接訪問(wèn)vcs(如上圖中的git.bat.com)的方式拉取私有g(shù)o module。

          當(dāng)然大廠還可能采用下圖所示方案將外部go module與私有g(shù)o module都交給內(nèi)部統(tǒng)一的Goproxy服務(wù)去處理:

          圖:大廠方案: 統(tǒng)一代理方案

          在這種方案中,開發(fā)者僅需要將GOPROXY配置為in-house goproxy便可以統(tǒng)一拉取外部go module與私有g(shù)o module。但由于go命令默認(rèn)會(huì)對(duì)所有通過(guò)goproxy拉取的go module進(jìn)行sum校驗(yàn)(到sum.golang.org),而我們的私有g(shù)o module在公共sum驗(yàn)證server中沒(méi)有數(shù)據(jù)記錄,因此,開發(fā)者需要將私有g(shù)o module填到GONOSUMDB環(huán)境變量中,這樣go命令就不會(huì)對(duì)其進(jìn)行sum校驗(yàn)了。不過(guò)這種方案有一處要注意:那就是in-house goproxy需要擁有對(duì)所有private module所在repo的訪問(wèn)權(quán)限,這樣才能保證每個(gè)私有g(shù)o module的拉取成功!

          好了,問(wèn)題來(lái)了!對(duì)于那些沒(méi)有完備內(nèi)部IT基礎(chǔ)設(shè)施,還想將私有g(shù)o module放在公司內(nèi)部的vcs服務(wù)器上的小廠應(yīng)該如何實(shí)現(xiàn)私有g(shù)o module的拉取方案呢?

          # 2. 可供小廠參考的一個(gè)解決方案

          小廠雖小,但目標(biāo)不能低。小廠雖然IT基礎(chǔ)設(shè)施薄弱或不夠靈活,但也不能因此給開發(fā)人員帶去太多額外的“負(fù)擔(dān)”。因此,對(duì)比了上面的兩個(gè)大廠可能采用的方案,我們更傾向于后者。這樣,我們就可以將所有復(fù)雜性都交給in-house goproxy這個(gè)節(jié)點(diǎn),開發(fā)人員就可以做的足夠簡(jiǎn)單。但小廠沒(méi)有DNS,無(wú)法用域名…,我們?cè)撛趺磳?shí)現(xiàn)這個(gè)方案呢?在這一節(jié)中,我們就實(shí)現(xiàn)這個(gè)方案。

           0. 方案示例環(huán)境拓?fù)?/span>

          我們先為后續(xù)的方案實(shí)現(xiàn)準(zhǔn)備一個(gè)示例環(huán)境,其拓?fù)淙缦聢D:

           1. 選擇一個(gè)goproxy實(shí)現(xiàn)

          Go module proxy協(xié)議規(guī)范[2]發(fā)布后,Go社區(qū)出現(xiàn)了很多成熟的Goproxy開源實(shí)現(xiàn)。從最初的athens[3],再到國(guó)內(nèi)的兩個(gè)優(yōu)秀的開源實(shí)現(xiàn):goproxy.cn[4]和goproxy.io[5]。其中,goproxy.io在官方站點(diǎn)給出了企業(yè)內(nèi)部部署的方法[6],基于這一點(diǎn),我們就基于goproxy.io來(lái)實(shí)現(xiàn)我們的方案(其余的goproxy實(shí)現(xiàn)應(yīng)該也都可以實(shí)現(xiàn))。

          我們?cè)谏蠄D中的in-house goproxy節(jié)點(diǎn)上執(zhí)行下面步驟安裝goproxy:

          $mkdir ~/.bin/goproxy
          $cd ~/.bin/goproxy
          $git clone https://github.com/goproxyio/goproxy.git
          $cd goproxy
          $make

          編譯后,會(huì)在當(dāng)前的bin目錄(~/.bin/goproxy/goproxy/bin)下看到名為goproxy的可執(zhí)行文件。

          建立goproxy cache目錄:

          $mkdir /root/.bin/goproxy/goproxy/bin/cache

          啟動(dòng)goproxy:

          $./goproxy -listen=0.0.0.0:8081 -cacheDir=/root/.bin/goproxy/goproxy/bin/cache -proxy https://goproxy.io
          goproxy.io: ProxyHost https://goproxy.io

          啟動(dòng)后goproxy在8081端口監(jiān)聽(即便不指定,goproxy的默認(rèn)端口也是8081),指定的上游goproxy服務(wù)為goproxy.io。

          注意:goproxy的這個(gè)啟動(dòng)參數(shù)并不是最終版本的,這里僅僅想驗(yàn)證一下goproxy是否能按預(yù)期工作。

          接下來(lái),我們來(lái)驗(yàn)證一下goproxy的工作是否如我們預(yù)期。

          我們?cè)陂_發(fā)機(jī)上配置GOPROXY環(huán)境變量指向10.10.20.20:8081:

          // .bashrc
          export GOPROXY=http://10.10.20.20:8081

          生效環(huán)境變量后,執(zhí)行下面命令:

          $go get github.com/pkg/errors

          結(jié)果如預(yù)期,開發(fā)機(jī)順利下載了github.com/pkg/errors包。

          在goproxy側(cè),我們看到了下面日志:

          goproxy.io: ------ --- /github.com/pkg/@v/list [proxy]
          goproxy.io: ------ --- /github.com/pkg/errors/@v/list [proxy]
          goproxy.io: ------ --- /github.com/@v/list [proxy]
          goproxy.io: 0.146404 /github.com/@v/list
          goproxy.io: 0.156404 /github.com/pkg/@v/list
          goproxy.io: 0.157200 /github.com/pkg/errors/@v/list

          并且在goproxy的cache目錄下,我們也看到了下載并緩存的github.com/pkg/errors包:

          $cd /root/.bin/goproxy/goproxy/bin/cache
          $tree
          .
          └── pkg
              └── mod
                  └── cache
                      └── download
                          └── github.com
                              └── pkg
                                  └── errors
                                      └── @v
                                          └── list

          8 directories, 1 file

           2. 自定義包導(dǎo)入路徑并將其映射到內(nèi)部的vcs倉(cāng)庫(kù)

          小廠可能沒(méi)有為vcs服務(wù)器分配域名,我們也不能在Go私有包的導(dǎo)入路徑中放入ip地址,因此我們需要給我們的私有g(shù)o module自定義一個(gè)路徑,比如:mycompany.com/go/module1。我們統(tǒng)一將私有g(shù)o module放在mycompany.com/go下面的代碼倉(cāng)庫(kù)中。

          接下來(lái)的問(wèn)題是,當(dāng)goproxy去拉取mycompany.com/go/module1時(shí),應(yīng)該得到mycompany.com/go/module1對(duì)應(yīng)的內(nèi)部vcs上module1 倉(cāng)庫(kù)的地址,這樣goproxy才能從內(nèi)部vcs代碼服務(wù)器上下載到module1對(duì)應(yīng)的代碼。

          圖:goproxy如何得到mycompany.com/go/module1所對(duì)應(yīng)的vcs倉(cāng)庫(kù)地址呢?

          其實(shí)方案不止一種[7]。這里我們使用一個(gè)名為govanityurls[8]的工具,這個(gè)工具在我以前的文章[9]中曾提到過(guò)。

          結(jié)合govanityurls和nginx,我們就可以將私有g(shù)o module的導(dǎo)入路徑映射為其在vcs上的代碼倉(cāng)庫(kù)的真實(shí)地址。下面的圖解釋了具體原理:

          首先,goproxy要想將收到的拉取私有g(shù)o module(mycompany.com/go/module1)的請(qǐng)求不轉(zhuǎn)發(fā)給公共代理,需要在其啟動(dòng)參數(shù)上做一些手腳,如下面修改后的goproxy啟動(dòng)命令:

          $./goproxy -listen=0.0.0.0:8081 -cacheDir=/root/.bin/goproxy/goproxy/bin/cache -proxy https://goproxy.io -exclude "mycompany.com/go"

          這樣凡是與-exclude后面的值匹配的go module拉取請(qǐng)求,goproxy都不會(huì)轉(zhuǎn)給goproxy.io,而是直接請(qǐng)求go module的“源站”。而上面圖中要做的就是將這個(gè)“源站”的地址轉(zhuǎn)換為企業(yè)內(nèi)部vcs服務(wù)中的一個(gè)倉(cāng)庫(kù)地址。由于mycompany.com這個(gè)域名并不存在,從圖中我們看到:我們?cè)趃oproxy所在節(jié)點(diǎn)的/etc/hosts中加了這樣一條記錄:

          127.0.0.1 mycompany.com

          這樣goproxy發(fā)出的到mycompany.com的請(qǐng)求實(shí)則是發(fā)向了本機(jī)。而上圖中所示,監(jiān)聽本機(jī)80端口的正是nginx,nginx關(guān)于mycompany.com這一主機(jī)的配置如下:

          // /etc/nginx/conf.d/gomodule.conf

          server {
                  listen 80;
                  server_name mycompany.com;

                  location /go {
                          proxy_pass http://127.0.0.1:8080;
                          proxy_redirect off;
                          proxy_set_header Host $host;
                          proxy_set_header X-Real-IP $remote_addr;
                          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                          proxy_http_version 1.1;
                          proxy_set_header Upgrade $http_upgrade;
                          proxy_set_header Connection "upgrade";
                  }
          }

          我們看到對(duì)于路徑為mycompany.com/go/xxx的請(qǐng)求,nginx將請(qǐng)求轉(zhuǎn)發(fā)給了127.0.0.1:8080,而這個(gè)服務(wù)地址恰是govanityurls[10]工具監(jiān)聽的地址。

          govanityurls這個(gè)工具是前Go核心開發(fā)團(tuán)隊(duì)成員Jaana B.Dogan[11]開源的一個(gè)工具,這個(gè)工具可以幫助gopher快速實(shí)現(xiàn)自定義Go包的go get導(dǎo)入路徑[12]。

          govanityurls本身就好比一個(gè)“導(dǎo)航”服務(wù)器。當(dāng)go命令向自定義包地址發(fā)起請(qǐng)求時(shí),實(shí)則是將請(qǐng)求發(fā)送給了govanityurls服務(wù),之后govanityurls將請(qǐng)求中的包所在倉(cāng)庫(kù)的真實(shí)地址(從vanity.yaml配置文件中讀取)返回給go命令,后續(xù)go命令再?gòu)恼鎸?shí)的倉(cāng)庫(kù)地址獲取包數(shù)據(jù)。

          注:govanityurls的安裝方法很簡(jiǎn)單,直接go install/go get github.com/GoogleCloudPlatform/govanityurls即可。

          在我們的示例中,vanity.yaml的配置如下:

          host: mycompany.com

          paths:
            /go/module1:
                repo: ssh://[email protected]/module1
                vcs: git

          也就是說(shuō)當(dāng)govanityurls收到nginx轉(zhuǎn)發(fā)的請(qǐng)求后,會(huì)將請(qǐng)求與vanity.yaml中配置的module路徑相匹配,如果匹配ok,則會(huì)將該module的真實(shí)repo地址通過(guò)go命令期望的應(yīng)答格式予以返回。在這里我們看到,module1對(duì)應(yīng)的真實(shí)vcs上的倉(cāng)庫(kù)地址為:ssh://[email protected]/module1。

          于是goproxy會(huì)收到這個(gè)地址,并再次向這個(gè)真實(shí)地址發(fā)起請(qǐng)求,并最終將module1緩存到本地cache并返回給客戶端。

          注意:由于這個(gè)方案與大廠的第二個(gè)方案是一樣的,因此goproxy需要有訪問(wèn)mycompany.com/go下面所有g(shù)o module對(duì)應(yīng)的真實(shí)vcs倉(cāng)庫(kù)的權(quán)限。

           3. 開發(fā)機(jī)(客戶端)的設(shè)置

          前面示例中,我們已經(jīng)將開發(fā)機(jī)的GOPROXY環(huán)境變量設(shè)置為goproxy的服務(wù)地址。但我們說(shuō)過(guò)凡是通過(guò)GOPROXY拉取的go module,go命令默認(rèn)都會(huì)將其sum值到公共GOSUM服務(wù)器上去校驗(yàn)。但我們實(shí)質(zhì)上拉取的是私有g(shù)o module,GOSUM服務(wù)器上并沒(méi)有我們的go module的sum數(shù)據(jù)。這樣會(huì)導(dǎo)致go build命令報(bào)錯(cuò),無(wú)法繼續(xù)構(gòu)建過(guò)程。

          因此,開發(fā)機(jī)客戶端還需將mycompany.com/go作為一個(gè)值設(shè)置到GONOSUMDB環(huán)境變量中,這就告訴go命令,凡是與mycompany.com/go匹配的go module,都無(wú)需做sum校驗(yàn)了。

           4. 方案的“不足”

          當(dāng)然上述方案也不是完美的,它也有自己的不足的地方:

          • 開發(fā)者還是需要額外配置GONOSUMDB變量

          由于Go命令默認(rèn)會(huì)對(duì)從GOPROXY拉取的go module進(jìn)行sum校驗(yàn),因此我們需要將私有g(shù)o module配置到GONOSUMDB環(huán)境變量中,這給開發(fā)者帶來(lái)了一個(gè)小小的“負(fù)擔(dān)”。

          緩解措施:小廠可以將私有g(shù)o項(xiàng)目都放在一個(gè)特定域名下,這樣就無(wú)需為每個(gè)go私有項(xiàng)目單獨(dú)增加GONOSUMDB配置了,只需要配置一次即可。

          • 新增私有g(shù)o module,vanity.yaml需要手工同步更新

          這個(gè)是這個(gè)方案最不靈活的地方了,由于目前govanityurls功能有限,我們針對(duì)每個(gè)私有g(shù)o module可能都需要單獨(dú)配置其對(duì)應(yīng)的vcs倉(cāng)庫(kù)地址以及獲取方式(git, svn or hg)。

          緩解方案:在一個(gè)vcs倉(cāng)庫(kù)中管理多個(gè)私有g(shù)o module,就像etcd[13]那樣。相比于最初go官方建議的一個(gè)repo只管理一個(gè)module,新版本的go在一個(gè)repo管理多個(gè)go module[14]方面已經(jīng)有了長(zhǎng)足的進(jìn)步。

          不過(guò)對(duì)于小廠來(lái)說(shuō),這點(diǎn)額外工作與得到的收益相比,應(yīng)該也不算什么!^_^

          • 無(wú)法劃分權(quán)限

          在上面的方案說(shuō)明時(shí)也提到過(guò),goproxy所在節(jié)點(diǎn)需要具備訪問(wèn)所有私有g(shù)o module所在vcs repo的權(quán)限,但又無(wú)法對(duì)go開發(fā)者端做出有差別授權(quán),這樣只要是goproxy能拉取到的私有g(shù)o module,go開發(fā)者都能拉取到。

          不過(guò)對(duì)于多數(shù)小廠而言,內(nèi)部所有源碼原則上都是企業(yè)內(nèi)部公開的,這個(gè)問(wèn)題似乎也不大。如果覺得這是個(gè)問(wèn)題,那么只能使用上面的大廠的第一個(gè)方案了。

          # 3. 小結(jié)

          無(wú)論大廠小廠,當(dāng)對(duì)Go的使用逐漸深入后,接納的人增多,開發(fā)的項(xiàng)目增多且越來(lái)越復(fù)雜后,拉取私有g(shù)o module這樣的問(wèn)題肯定會(huì)擺到桌面上來(lái)。

          對(duì)于大廠的gopher來(lái)說(shuō),這可能不是問(wèn)題,甚至對(duì)他們都是透明的。但對(duì)于小廠等內(nèi)部IT基礎(chǔ)設(shè)施不完備的組織而言,的確需要自己動(dòng)手解決。

          這篇文章為小廠搭建Go私有庫(kù)以及從私有庫(kù)拉取私有g(shù)o module提供了一個(gè)思路以及一個(gè)參考實(shí)現(xiàn)。

          如果覺得上面的安裝配置步驟有些繁瑣,有興趣深入的朋友可以將上述幾個(gè)程序(goproxy, nginx, govanityurls)打到一個(gè)容器鏡像中,實(shí)現(xiàn)一鍵安裝設(shè)置。

           參考資料

          [1] Go 1.11版本: https://mp.weixin.qq.com/s?__biz=MzIyNzM0MDk0Mg==&mid=100000482&idx=1&sn=b5a588b8b4cd63ac57b29ee6e64438aa&chksm=6863e5035f146c152ae2a7460dea924df4b14a56bbcbee1966934abed3fcfd492bc6f56928b2#rd

          [2] Go module proxy協(xié)議規(guī)范: https://pkg.go.dev/cmd/go@master#hdr-Module_proxy_protocol

          [3] athens: https://tonybai.com/2018/11/26/hello-go-module-proxy/

          [4] goproxy.cn: https://github.com/goproxy/goproxy

          [5] goproxy.io: https://github.com/goproxyio/goproxy

          [6] 企業(yè)內(nèi)部部署的方法: https://goproxy.io/zh/docs/enterprise.html

          [7] 方案不止一種: https://tonybai.com/2020/11/15/another-approach-to-customize-package-import-path

          [8] govanityurls: https://tonybai.com/2017/06/30/go-get-go-packages-in-private-code-repo-by-govanityurls

          [9] 我以前的文章: https://tonybai.com/2017/06/28/set-custom-go-get-import-path-for-go-package

          [10] govanityurls: https://github.com/GoogleCloudPlatform/govanityurls

          [11] Jaana B.Dogan: https://rakyll.org

          [12] 實(shí)現(xiàn)自定義Go包的go get導(dǎo)入路徑: https://tonybai.com/2017/06/30/go-get-go-packages-in-private-code-repo-by-govanityurls/

          [13] etcd: https://github.com/etcd-io/etcd

          [14] 一個(gè)repo管理多個(gè)go module: https://golang.google.cn/doc/modules/managing-source#multiple-module-source

          [15] 改善Go語(yǔ)?編程質(zhì)量的50個(gè)有效實(shí)踐: https://www.imooc.com/read/87

          [16] Kubernetes實(shí)戰(zhàn):高可用集群搭建、配置、運(yùn)維與應(yīng)用: https://coding.imooc.com/class/284.html

          [17] 我愛發(fā)短信: https://51smspush.com/

          [18] 鏈接地址: https://m.do.co/c/bff6eed92687

             


          喜歡明哥文章的同學(xué)
          歡迎長(zhǎng)按下圖訂閱!

          ???

          瀏覽 91
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色成人视频网站在线观看 | 女人十八看毛片 | 伊人大香蕉在线免费 | 久久久7777 | 操逼网1 操逼五码 |