Docker 持續集成過程中的性能問題及解決方法

jopen 8年前發布 | 10K 次閱讀 Docker

Docker 的出現使開發測試生產環境的統一變得更加容易,然而在使用 docker 搭建這一整套流水線之后,卻發現它運行的卻不能像絲般潤滑,總是感覺沒有直接本地開發測試來的效率高。為了能達到一個高效流水般的持續構建,我們來看一下這個過程中 docker 的使用以及 docker 自身存在著哪些問題,我們又該如何克服這些問題,達到如絲般的潤滑。

我們首先來分解一下現在常見的一種利用 docker 做持續部署的流程:

  1. 開發者提交代碼
  2. 觸發鏡像構建
  3. 構建鏡像上傳至私有倉庫
  4. 鏡像下載至執行機器
  5. 鏡像運行

在這五步中,1 和 5 的耗時都比較短,主要耗時集中在中間 3 步,也就是 docker build, push, pull 的時間消耗,我們就來分別看一下如何加速這三個步驟。

Docker build

選擇國外構建

由于 dockerhub 的官方鏡像再國外,而這些基礎鏡像的軟件源都在國外,國內構建的時候網絡會是很大的瓶頸,有能力在國外機器進行構建,并且可以通過專線和國內進行傳輸的話,還是優先將構建節點放在國外,會省很多無謂的在網絡上的糾纏,并且很多軟件源國外的也要更穩定寫,更新也更及時。

如果只能在國內進行構建的話,建議使用國內的鏡像,或者自己在私有倉庫存一份官方鏡像,并且對鏡像進行改造,做一份軟件源都在國內的基礎鏡像,把構建過程中的網絡傳輸都控制在國內或者內網,這樣就不用和網絡進行糾纏了。

善用 .dockerignore

.dockerignore 可以減少構建時的文件傳輸,一般通過 git 進行持續構建的時候不做設置都會把 .git 文件夾進行傳輸造成很多無用的傳輸,一些與構建無關的代碼也盡量卸載 .dockerigonre 文件中。

緩存優化的 dockerfile

dockerfile 的優化也是一個比較直接的優化方式,優化的核心就是能充分利用 build cache,把每次變化的部分放在最后,一般把加入代碼放在最后一步,這樣每次構建只有最后一層是新的,其他部分都是可以用 cache 的。對于 node、python、go 之類要在構建過程中安裝依賴的服務,可以把安裝依賴和加入代碼分兩步完成,這樣在依賴不變的情況下這部分的緩存也是可以利用的。以 node 為例:

COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app

其他關于 dockerfile 優化的建議可以再單獨開一篇了,基本上每個命令都需要特殊對待才能不掉坑里,可以參考一個在線 dockerfile 語法優化器,里面會提供一些相關的 dockerfile 優化建議和一些資源,作者一定是個大好人。

smart cache

在單機模式下充分利用 build cache 是個不錯的注意,但是在多個構建機器的情況下就會有問題了。出于磁盤空間考量不可能所有機器都存著所有的鏡像,這樣緩存優化的 dockerfile 就沒有用武之地了。為了讓 cache 重新發揮作用我們可以在構建開始時將舊的鏡像 pull 下來,這樣一來就可以再次利用 cache 了。但是一來 pull 鏡像也是需要很多時間的,并且 pull 下來的鏡像并不會全部有用,會浪費一定的時間;而來如果 dockerfile 變化比較大有可能沒有一層能用 pull 下來反而會浪費更多的時間;三來倉庫內可能會有其他的鏡像更適合做當前構建的緩存所以我們需要實現一個精準的鏡像拉取,不能出錯也不能浪費。

舉個栗子,如下圖所示 image 想要構建 node:wheezy 的話那么 node:0-wheezy 是一個比較合適的鏡像來做 cache 而想要構建 node:5 的話那么 node:wheezy 和 node:0-wheezy 都不太合適,反而是 python:latest 會更合適。如果我們把倉庫中所有的鏡像都做成這樣一個森林,利用 tire 樹可以很精準的知道,哪個鏡像的哪幾層是 cache 的最好選擇,這樣精確制導不會有一點浪費。

Docker push

docker registry 在升級到 v2 后加入了很多安全相關檢查,使得原來很多在 v1 already exist 的層依然要 push 到 registry,并且由于 v2 中的存儲格式變成了 gzip,在鏡像壓縮過程中占用的時間很有可能比網絡傳輸還要多。我們簡單分解一下 docker push 的流程。

  1. buffer to diske 將該層文件系統壓縮成本地的一個臨時文件
  2. 上傳文件至 registry
  3. 本地計算壓縮包 digest,刪除臨時文件,digest 傳給 registry
  4. registry 計算上傳壓縮包 digest 并進行校驗
  5. registry 將壓縮包傳輸至后端存儲文件系統
  6. 重復 1-5 直至所有層傳輸完畢
  7. 計算鏡像的 manifest 并上傳至 registry 重復 3-5

此外判斷 already exist skip pushing 的條件變嚴格了,必須是本地計算過digest 且 該 digest 對應的文件屬在對應 repo 存在才可以。

換句話說就是如果這個鏡像層是 pull 下來的,那么是沒有digest的還是要把整個壓縮包傳輸并計算 digest,如果這個鏡像你之前并沒有比如 ubuntu 的 base image 你的 repo 第一次創建之前沒傳輸過,那么第一次也要你傳輸一次確認你真的有 ubuntu。

這里面的改進點就是在太多了,先列舉 Docker 官方已經做得和正在做的。

  1. 1.9.1 后 push 是 streaming 式的,也就是把 1 和 2 合并去掉臨時文件,直接一邊壓縮一邊傳輸。
  2. pull 鏡像后 digest 保存,大概是 1.8.3 之后添加的省去了重復計算。
  3. registry 可以直接 mount 別人 repo 中的一層到自己的 repo,只要有pull權限即可,這個工作還在進行中。

但是這只解決了一小部分問題,push 依然會很慢,docker 和 registry 的設計更多的考慮了公有云的環境設置了過多的安全防范為了防止鏡像的偽造和越權獲取,但是在一個可信的環境內如果 build 和 push 過程都是自己掌控的,那么很多措施都是多余的,我們可以設計一個自己的 smart pusher 挖掘性能的最大潛力:

  1. 壓縮傳輸 streaming 化和 docker 1.9.1 實現的類似
  2. 越過 registry 直接和存儲系統通信,直接拿掉上面 5 的傳輸時間
  3. 如果 digest 在 存儲系統中存在則不再重復傳輸,在 manifest 中寫好層次關系就好
  4. 將多層的傳輸并行化,多個層一塊傳,這樣才能充分發揮多核的優勢,docker 自帶的串行push效率實在是太低了
  5. 另外針對 build 結果進行 push 的 smart pusher 可以將流水線發揮到極致,build 每構建出一層就進行傳輸,將 build 和 push 的時間重疊利用

有了 smart pusher,push 時間的絕大多數都被隱藏到了 build 的時間中,我們把并發和流水線的技術都用上,充分發揮了多核的優勢。

Docker pull

docker pull 鏡像的速度對服務的啟動速度至關重要,好在 registry v2 后可以并行 pull 了,速度有了很大的改善。但是依然有一些小的問題影響了啟動的速度:

  1. 下載鏡像和解壓鏡像是串行的
  2. 串行解壓,由于 v2 都是 gzip 要解壓,盡管并行下載了還是串行解壓,內網的話解壓時間比網絡傳輸都要長
  3. 和 registry 通信,registry 在 pull 的過程中并不提供下載內容只是提供下載 url 和鑒權,這一部分加長了網絡傳輸而且一些 metadata 還是要去后端存儲獲取,延時還是有一些的
  4. docker pull 某些情況會卡死,不 docker restart 很難解決,而 restart 又會停止所有服務,嚴重影響服務穩定性。

為此我們還需要一個獨特設計的 smart puller 幫助我們解決最后的問題: 1. streaming downloading and extracting 和在 smarter pusher 做的類似將這兩步合為一步 2. 并行解壓,也和 smarter pusher 并行壓縮類似 3. 越過 registry 直接和后端存儲通信 4. redis 緩存 metadata

有了 smart puller 我們自然的將 docker pull 的工作和 docker daemon 解耦了,這樣再不會發生 pull 導致的 docker hang,服務穩定性也得到了增強,解綁后其實 docker 只是做一個 runtime 這一部分也可考慮改成 runc 去除掉 daemon 這個單點,不過這個工作量就比較大了。此外 smart puller 也可以幫助我們實現在 smart cache 中的精確 pull 以及 pull cache 的加速,可謂一舉多得。

總結

將 push 和 pull 的工作和 daemon 解綁,把 smart cache,smart puller 和 smart pusher 用上后,持續集成如絲般潤滑。

至于我把全系列工具都以 smart 命名,主要是為了給 Smartisan T2 開賣造勢 (逃


歡迎大家在討論區有血性的爭論、動手、拍磚、捅刀子,亮出你的看法來!本博客已經全文RSS輸出,大家可以通過訂閱oilbeater.com/atom.xml即可獲得博客更新。或者關注我的微博@oilbeater在上面我會通知我的博客更新并且還會分享一些有意思的技術文章,歡迎大家關注。


來自: http://oilbeater.com/docker/2016/01/02/use-docker-performance-issue-and-solution.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!