一、前言
本文介紹的內容包含:
- 理解CI/CD及其必要性
- gitlab-runner安裝與註冊
- gitlab-ci配置說明
- ssh免密登入
- rsync部署檔案
- 多環境釋出與回滾
- 高頻出現的問題並解決
閱讀本文需要你:
- 有一定的ssh知識基礎,一份SSH操作指南
- gitlab ci/cd概念基礎
- 花10min左右的時間
二、CI/CD科普
2.1 CI與CD
持續整合(Continuous Integration)指開發人員在特性分支(頻繁)提交程式碼,立即執行構建和單元測試,程式碼通過測試標準後整合到主幹的過程。強調的是分支程式碼的提交、構建與單元測試,這個過程的產出是單元測試報告。
說明:這裡的 test 是指unit test(圖片來源見文末參考連結)持續互動(Continuous Delivery)是在持續整合的基礎上,將構建的程式碼部署到「類生產環境」,完成QA測試之後手動部署到生成環境的過程。強調程式碼部署,這個過程產出測試報告。
說明:這裡的 test 是真的test持續部署(Continuous Deployment)是持續互動的下一步,強調部署生產環境程式碼的過程自動化,同時可以處理上線通知等操作。
說明:與持續互動主要就是手動跟自動的區別。2.2 CI/CD的必要性
以一言概之的話我想應該是:機械的事情讓機器做。一個開發團隊,沒有CI/CD,我想可能是這樣子的:無法管理程式碼多人多地協作(git repository也是CI的一部分),系列的shell需要人工處理,程式碼的釋出需要登入伺服器等等;相反,擁有CI/CD,這些事情都交給機器去完成,騰出的碎片時間去做更有意義的事情(比如摸魚放鬆下)。
2.3 理想的CI/CD開發流應該是怎樣的?
我認為理想的CI/CD開發流應該包含三個階段:build
、deploy
和notify
。build階段專注做程式碼構建與單元測試,deploy階段專注做test/gray/prod環境的程式碼部署,notify階段專注做上線通知,如下圖;
以下內容圍繞build和deploy兩個階段完成從0到1的部署。筆者的系統環境:Ubuntu 18.04.1 LTS
三、Gitlab-Runner安裝並註冊
3.1 安裝runner
sudo apt-get install gitlab-runner
複製程式碼
3.2 註冊runner
sudo gitlab-runner register
複製程式碼
之後是QA式操作,按照提示語輸入資訊即可,可參考官網操作,需要注意:
①. gitlab host和token在你的gitlab專案上找,頁面路徑是:Settings >> CI/CD >> Runners
②. runner執行器選擇 docker,image(映象)輸入 node:8.11.2-stretch
註冊成功後,我們就能在:Settings >> CI/CD >> Runners下看到我們註冊的runner
四、Gitlab-Runner配置
在專案根目錄下新建 .gitlab.yml
檔案,加入如下內容:
unit_test
、compile
和deploy_test
是自定義的job名字,另外幾點配置說明:
cache
: cache設定快取檔案,這裡快取node_module依賴包,提高job構建效率,定義在全域性,對所有的job生效;stage
: 設定build和deploy兩個階段artifacts
: 下載檔案。定義compile產出的dist資料夾快取到gitlab伺服器,提供下載(gitlab web頁面下載)或者在同一個stage的各job之間共享;only
: 定義job的觸發條件,可以指定分支名、tags
(打tag時觸發)等(這些條件是或的關係,滿足其中一個即觸發);when
: 定義job的觸發時機,值可以為:on_success
、always
、manual
等;dependencies
: 定義當前job所要依賴的job;environment
: 定義當前job所屬的環境,對回滾操作非常有用,後面詳述;- deploy_test這個job的
before_script
有很長一段內容,這裡的作用是配置ssh免密登入,後面詳述。
五、ssh免密登入
5.1 為什麼需要免密登入?
- 構建產出的dist檔案要傳輸到目標伺服器(測試機/生成機),要麼基於http網路協議、要麼基於ssh協議(或其他檔案傳輸協議?)
- 基於http需要寫檔案接收介面,這裡直接使用基於ssh傳輸檔案的rsync,簡單、安全!
- runner內定義的一系列script是在一個docker容器內執行的,無法人工干預,那麼登入伺服器就要做成免密。
先在本機(註冊runner所在的機器)配一遍免密登入伺服器的流程:
5.2. 生成一對公私鑰
使用rsa作為非對稱加密方式:
ssh-keygen -t rsa -C "$(whoami)@$(hostname)-$(date -I)"
複製程式碼
說明:一路enter就好了,切記 Enter passphrase 時直接enter,這樣就是 no passphrase。如果你非要加個password,對不起,沒救了!
5.3. 定義ssh config內容
在~/.ssh/config檔案寫入以下內容(檔案不存在直接建立):
Host any_name
Port your_port
HostName server_ip
User user
IdentityFile ~/.ssh/id_rsa
複製程式碼
說明:定義ssh的config檔案是為了快捷訪問,就像你配置host一樣,沒有hostname,你只能訪問ip。配置後你就可以通過 ssh any_name
登入伺服器了。當然,不出意外,會要求你輸入伺服器的登入密碼。
5.4. 免密登入
免密登入的精髓就是:把本機的公鑰儲存到目標伺服器的authorized_keys檔案內(該檔案伺服器上不存在可以直接建立。)
ssh-copy-id -i ~/.ssh/id_rsa.pub username@ip
複製程式碼
特別地:如果你的埠不是預設的22埠,則加上埠號 -p PORT
5.5. 驗證登入
ssh any_name
複製程式碼
不出意外,你應該可以直接登入伺服器了。那麼,我們回到gitlab的配置上~
六、gitlab上定義ssh配置資訊
我們進入gitlab頁面位置:Settings >> CI/CD >> Environment variables下定義 .gitlab.yml
上出現的幾個變數:
SSH_PRIVATE_KEY
:把本機(runner所在機器)的私鑰複製過來:~/.ssh/id_rsaTEST_CONFIG
:把剛才ssh config定義的資訊複製過來:~/.ssh/configTEST_KNOWN_HOST
:定義這個變數是為了讓ssh對伺服器進行身份確認(不然會被ssh認為是一個不被信任的環境),變數值使用以下命令生成:
ssh-keyscan -p PORT IP
複製程式碼
七、使用rsync傳輸檔案
rsync -rve ssh dist/ user@hostname:project_path/dist
複製程式碼
說明:hostname
就是你在ssh config
定義的Host
值。rsync操作指南
八、定義environment
設定environment的好處是可以對各發布環境進行管理,特別是線上釋出,出現bug可以及時操作回滾。 在gitlab web頁面位置:Operations >> environments 可以檢視當前專案下的environments,點選其右側的預覽按鈕即可檢視對應環境的釋出效果
點選其中一個environment:test_env
,可以檢視當前環境下的所以釋出記錄,右側的按鈕可以執行回滾操作。
九、踩坑小記
在整個搭建過程,很多都是關於ssh登入伺服器的問題,擇幾個高頻出現的問題說明下:
9.1. Host key verification failed
當我們初次使用ssh登入伺服器的時候,ssh會要求驗證遠端伺服器的身份,通過身份驗證之後才允許連線。解決該問題有兩種方式:
- 設定免身份認證:
在ssh config配置中加一段
StrictHostKeyChecking no
,如此.gitlab.yml
配置中就可以去掉關於known_hosts
的設定了; - 通過遠端伺服器的公鑰指紋進行身份認證:
ssh-keyscan -p PORT IP
複製程式碼
將指令碼輸出的結果儲存在~/.ssh/known_hosts
檔案中(@gitlab上定義ssh配置資訊部分有提及),這樣ssh在登入之前會從該檔案中拿到目標伺服器的公鑰指紋進行身份確認。
9.2. Permission denied, please try again
出現這個問題是沒有配置「ssh免密登入」,配置操作見@免密登入部分
9.3. rsync: Failed to exec a: No such file or directory
這個問題一般情況下並不會出現,但卻是個實實在在的坑。我在deploy的job裡面通過rsync將構建生成的dist目錄上傳至伺服器,丟擲不存在該目錄的錯誤。
我在compile這個job執行後,list出根目錄下的檔案/夾(gitlab-runner的輸出):
在本機檢視根目錄下的檔案/夾: 可以看見,gitlab-runner執行構建後實實在在是生成了dist目錄,但進入下一個job的時候卻提示不存在!問題出在: dist目錄並不是由構建直接生成的資料夾,而是release-[timestamp]
目錄的軟連結(筆者用的是Ubuntu,在webpack配置裡面設定了個騷操作:每次構建產出一個release-[timestamp]
目錄,同時建立一個軟鏈。軟鏈不是一個目錄,它的內容就是目標資料夾的地址)。
在我的.gitlab.yml
配置裡面,artifacts
快取的是dist
,沒有把實際的資料夾release-[timestamp]
快取,那麼進入下一個job的時候,自然就提示不存在該目錄了。解決辦法是:
cp dist public
複製程式碼
在compile
這個job裡面,構建之後複製一份dist目錄,再將public
目錄交由artifacts
快取。