上篇主要介紹了 GitLab WorkFlow 以及 CI/CD 做的事情,並且詳細分析 GitLab CI 跟 Runner 資訊互動是如何進行的。接下來將為大家講解 Executor 的實現,再通過兩個例子具體展示 GitLab CI 的使用。
Executor
本章主要講了在Runner 在接收到任務之後,會呼叫 Executor,Executor 是怎麼實現的,重點介紹 Docker Executor 的實現細節
其實 Runner 會去呼叫對應的 executor,由 executor 來完成接下去的工作。
圖 12 是從 GitLabMutliRunner 專案中擷取的一部分程式碼,基本可以說明 executor 的流程。
圖 12
第一步:Prepair,做準備工作;
第二步:拉取程式碼,根據 Job 裡的 git_info 欄位,Runner 去 GitLab 倉庫拉取程式碼;
第三步:還原快取,這些快取是上一次構建留下的;
第四步:下載 artifacts,這是前一個階段的產生的中間結果;
第五步:執行使用者定義的指令碼,這裡指的是 before_scripts 跟 scripts 兩部分;
第六步:執行 after_scripts;
第七步:儲存快取打包 artifacts 等;
第八步:上傳 artifacts 供下一階段使用。
基於這樣一個流程,官方提供了 ssh、shell、docker、docker-ssh、virtualbox、kubernates 等執行器,來應對實際使用中的不同場景,本次分享主要也是從 Docker 跟 Docker-ssh 進行展開的。由於Docker 的隔離性強、秒級別啟動容器、輕量、回收方便這些特點非常符合整合測試的要求。
首先,如果要使用 Docker executor,CI 配置檔案需要做如下調整:
圖 13
1.定義 image 欄位,該欄位指定了本次測試環境使用的 Docker 映象;
2. services,這個欄位定義了本次測試依賴的服務,這裡依賴的是 redis 跟 mysql。
Docker executor 的實現
第一步:Prepare,Runner 會先去下載 redis、mysql 等專案依賴的服務,啟動對映了目錄程式碼的容器,啟動對映了快取目錄的容器。
圖 14
第二步:GetSource,Runner 會在啟動一個 predefine 的容器,通過 volume from 的方式掛載專案程式碼路徑,再通過 predefine 這個容器去拉取程式碼。由於目錄共享,所以 volumes 裡的這個容器資料內容也發生了改變。
圖 15
第三步:執行指令碼,runner 會啟動一個 build 容器,通過 docker link 的方式把依賴的服務連線起來,通過 docker volume from 的方式載入專案程式碼以及快取,然後執行 job 中定義的指令碼。
圖 16
Artifacts
當圖片處理專案 Nami 剛接入到 CI 時,是分成 Test 跟 Build 兩個階段處理,Test 階段需要先編譯 Nami,然後啟動測試。等測試通過後,執行第二階段,進行 Docker Build,這時又需要重新再編譯依次 Nami,這個過程很費時。後來瞭解到了 Artifacts 這個功能,可以把這個階段中產生的檔案上傳到 GitLab 供下一個階段來使用,這樣以來就不需要二次編譯了。
圖 17
完成優化後,將整個過程拆成了 3 個階段,第一個階段進行編譯,第二個階段使用上一個階段編譯好的結果進行測試,第三個階段使用第一個階段編譯好的二進位制做 Docker Build。這樣一來,整個過程中只需要做一次編譯,大大縮短了構建耗費的時間。
圖 18
另外上傳 GitLab 還有一個好處,安裝包可以直接通過網頁下載回來。
關於 GitLab CI 的實踐經驗
C/C++ 編譯優化
CDN 專案依賴 Nginx,並且增加了公司定製的邏輯,所以每次測試都需要重新編譯 Nginx,但是眾所周知編譯是十分耗時的。為了解決這個問題,我們引入了 CCACHE。CCACHE 是一個編輯器驅動器,第一次編譯時 CCACHE 會緩衝 GCC 的 -E 輸出,編譯選項以及 O 檔案到 $HOME/.ccache 下,第二次編譯時儘量利用緩衝,必須時更新緩衝。但是這裡會有一個問題,測試跑完之後容器立刻就被回收了,怎麼把編譯快取保留到下一次測試呢?
這裡就需要用到前面提交的 cache 選項了,cache 選項可以把上一次的快取目錄保留到下一次測試中,供下一次測試使用。但是我們需要對 ccache 做一下配置,修改快取的路徑,指定到 /cache 目錄下。
$ ccache —set-config=cache_dir=/cache/.ccache
$ ccache -F 0 && ccache -M 0
Localhost 問題
圖 19
如果測試中依賴第三方服務的話,docker executor 是通過 docker link 的方式,但是採用這種方式的話,Build 這個容器就無法通過 127.0.0.1 來訪問 Redis、Mysql 了。但是研發同學常會把測試環境的配置寫成 127.0.0.1,方便本地開發測試。這樣一來,接入到 CI 之後就會有不少困擾。針對這個問題,我們給出了兩種解決方案。
第一種解決方案是使用別名:在定義 services 的時候,可以定義 alias,來指定該服務的別名,指定好別名之後,Build 容器內就會多一條該別名的解析記錄,解析到指定的容器 IP。這種方式下,研發同學只要修改下測試配置就可以把服務跟測試跑起來,不過本地開發的時候,需要增加一條解析記錄,把別名解析到 127.0.0.1。
圖 20
第二種解決方案是埠轉發,這種方式更加簡單靈活些。拿 redis 舉個例子,如果測試中以來了 redis,需要在 build 容器中安裝 3proxy 做埠轉發,並且把對 6379 埠的請求,都轉發到 redis 對應容器的 6379 上去,通過這種方式來解決訪問 127.0.0.1:6379。這種方式更加方便,研發同學不需要去修改測試的配置,但是需要在執行測試之前,去啟動 3proxy 並配置埠轉發規則。