揭祕騰訊內部 Go Modules Proxy 服務

01 背景

近日騰訊正式對外發布 2020 年度《騰訊研發大數據報告》,這份由騰訊技術委員會出品的報告,披露了過去一年騰訊在研發投入、研發效能及開源協同等方面的重要數據。C++ 蟬聯騰訊最受歡迎的編程語言。隨着雲計算和微服務相關技術的進一步發展,Go 語言使用次數增速第一,並超越 JavaScript 成爲騰訊第二受歡迎的編程語言。基於如此巨大的 Go 開發者基數,內部開發體驗就顯得尤爲重要。

但是長久以來 Go 的版本管理確實是大家開發中遇到最頭疼的問題,在 GOPATH 模式中,我們將所需依賴代碼拉取到 GOPATH/src 中,這樣在編譯過程中,Go 會去 GOPATH 中根據 import path 尋找響應的代碼庫。但是如果我們重新拉取了最新的代碼或者自己修改了這個依賴已有的代碼,整個編譯過程不會察覺到這些變更,可能會造成編譯失敗或者線上故障,於是從 Go 1.11 起,Go 官方引入了 Go Modules 來解決版本管理的問題,用一句話解釋什麼是 Go Modules 那就是:

Modules are how Go manages dependencies.

Go Modules 是一種分散式設計,儘管有很多諸如 goproxy.io 這樣的公共服務,但是 Go Modules 設計中沒有一個公開的註冊中心,模塊作者可以在代碼倉庫中創建版本標籤來發布新版本。

$ git tag v1.2.3
$ git push --tags

其他開發者就可以立刻下載這個版本了

$ go get -d example.com/mod@v1.2.3

02 使用 GOPROXY

我們建議默認配置使用 GOPROXY 來獲取依賴模塊,原因有下面幾個:

2.1

企業內部證書問題收斂

很多公司內的證書都是自己簽發的,在各個操作系統、瀏覽器、工具中都不被信任,這給內部的開發者帶來了額外的配置負擔,他們在使用各種工具時,需要強行去信任這個內部簽發的證書,我們可以再 goproxy server 這一層做同一信任配置,這樣衆多開發者就無需再進行額外操作。

2.2

海外資源可用性高

諸如 golang.org/x 類似的一些常用的包託管於谷歌服務器上,我們需要用到 proxy 幫我們進行獲取,這裏就不展開了。

2.3

模塊拉取速度快

有些人說只有海外資源纔有加速的需求,其實不是這樣的,即使是內網的代碼依賴模塊,如果你使用 proxy 也比直接拉取快很多,接下來我們分析下原因。

Go 可以通過 HTTP 協議通過 proxy 下載依賴模塊,這要比直接從源代碼庫中拉取快 5 到 20 倍。GOPROXY 的協議是無狀態的,它的設計非常簡單以致於我們可以使用一個靜態的文件服務來實現它。Go 內部在實現的時候也支持file:// 協議,這樣避免了比如在測試場景中獲取依賴時產生對網絡的依賴,可以直接使用一個本地文件系統作爲 proxy 服務。下圖是我們使用騰訊名字服務項目(北極星)進行的一項拉取測試。

使用 goproxy 下載之所以如此之快追究其原因有下面幾個:

  1. Go 無需下載整個代碼倉庫或者某個完整的 commit,它只需要下載相應版本的 zip 包,這個包中只包含編譯所需的源碼文件。

  2. 在 Go 做版本選擇時,Go 只需使用到 mod 文件,可以避免下載整個代碼倉庫或者 zip 包。

  3. 如果這些 mod 和 zip 文件已經在 proxy 的緩存中了,獲取非常快,甚至像 goproxy.io 這樣的公共服務還使用了騰訊雲的全球 CDN 進行了加速,並且在亞洲、美洲和歐洲部署了獨立的服務進行回源。

2.4

降低了對源代碼託管服務(Git)的依賴性

如果有一天公司內部的 gitlab 或者外部的 github 服務臨時不可用了,雖然我們無法提交發布新版本了,但是如果 proxy 緩存了你之前的依賴模塊,我們已有的編譯將不會受到影響, CI/CD 也不會失敗。甚至有些源碼倉庫被刪除了,我們還可以繼續進行編譯。

2.5

fallback 機制

如果用戶配置的 goproxy 服務不可用了怎麼辦?好在 Go 支持配置逗號或者管道分隔的 goproxy 列表,如果第一個服務器返回了 404 或者 410,甚至是不可用(超時),Go 可以根據用戶的配置嘗試使用其他 goproxy 服務或者直接使用本地直接獲取的方式。這種 fallback 機制使得 Go 不依賴於某一個 goproxy 服務的可用性。

2.6

更加安全可控

使用公司內部的 goproxy 可以防止公司內部開發人員配置不當造成項目中 import path 泄露到公網中,避免給一些敏感業務和項目帶來不必要的損失和麻煩。

03 Goproxy For Tencent

既然使用 goproxy 服務有這麼多的好處,那麼爲騰訊內部提供一個穩定的 goproxy 服務勢在必行了,而且很多團隊的呼聲也非常高。可惜谷歌實現的 proxy 不是開源的,於是我們使用了 goproxy.io 開源項目。首先確保要運行的服務器是已經安裝了 go 命令,goproxy 項目是開源的,用 go 語言開發,使用 Go modules 可以很方便的進行編譯:

git clone https://github.com/goproxyio/goproxy.git
cd goproxy
make

編譯好的文件位置是 ./bin/goproxy , 使用 ./bin/goproxy -h 查看參數使用說明:

Usage of ./bin/goproxy:
  -cacheDir string
        go modules cache dir  [指定 Go 模塊的緩存目錄]
  -exclude string
        exclude host pattern  [proxy 模式下指定哪些 path 不經過上游服務器]
  -listen string
        service listen address [服務監聽端口,默認 8081]
  -proxy string
        next hop proxy for go modules [指定上游 proxy server,推薦 goproxy.io]

由於我們還需要加速內網代碼模塊,所以我們使用 router 模式:

                                         direct
                      +----------------------------------> internal repos
                      |
                 match|pattern
                      |
                  +---+---+           +----------+
go get  +-------> |goproxy| +-------> |goproxy.io| +---> golang.org/x/net
                  +-------+           +----------+
                 router mode           proxy mode

使用下面命令行啓動服務:

./bin/goproxy -listen=0.0.0.0:80 -cacheDir=/data/modules -proxy https://goproxy.io -exclude "git.tencent.com"

配置好環境變量,接下來開發者就可以從這個服務上拉取代碼了:

export GOPROXY=http://[你的服務器IP]:80
export GOSUMDB=off
go get github.com/pkg/errors

這樣用戶可以通過上面的配置拉取外部公共依賴模塊和內部公共模塊代碼庫,如果要編譯的項目對內部私有代碼庫有依賴怎麼辦呢,還需要配置一個變量:

GOPRIVATE=*.corp.example.com,rsc.io/private

關於這個變量的具體介紹可以參考:https://goproxy.io/zh/docs/GOPRIVATE-env.html

由於騰訊內部開發者和 Go 項目衆多,我們還實現了一個內部的 sumdb 服務,所以即使是騰訊內部代碼倉庫也可以被記錄哈希值,防止項目依賴模塊重新打標造成錯誤的編譯,從而帶來不可挽回的損失, 同時也保障了編譯項目的源碼安全。最後,由於這個項目支持 Promethues 監控,接下來把服務指標接入到 Promethues 監控中,並且把架構調整好,騰訊內部 goproxy 服務的架構其實也並不複雜:

總語

Go Modules 的引入確實大大提升了開發者體驗,它解決了 GOPATH 模式下很多解決不了的問題。目前騰訊內部 goproxy 服務每天的請求量在百萬級別,它幫助了騰訊雲、騰訊文檔、騰訊新聞、騰訊遊戲等多個 BG 和部門,爲大家的編譯節省了大量的等待時間,提高了內部服務的編譯成功率和開發體驗。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/GuUHq3uyPiMCQg_G1odAWA