借著公司代碼庫遷移到私有Gitlab的契機,我接下持續集成的工作,實現了對Python服務端代碼的單元測試、靜態代碼分析和接口測試的持續集成??傮w架構如下:
執行過程:
開發提交代碼后,自動觸發gitlab-runner拉取executor鏡像執行單元測試,單元測試代碼中包含上傳測試結果到x-utest測試平臺;
單元測試通過后,gitlab-runner拉取sonar-scanner鏡像執行靜態代碼分析,分析結果評論在commit中或保存于sonarqube;
靜態代碼分析結束,執行分發操作,將代碼分發至灰度測試服務器,并運行;
執行接口測試,執行完成后上傳測試結果到x-utest測試平臺。
四個任務相互依賴,只在前一個任務狀態為成功情況下才會執行。
本文主要描述一些技術實現,會適當貼出代碼,可能能為相關從業者提供靈感與解決方案,不保證每個細節都深入講解。
Gitlab CI 基本配置
針對某個需要做CI/CD的項目,需要將代碼庫的該設置打開,并為其配置 gitlab-runner。
gitlab runner
gitlab-runner不僅可以運行在物理機,還可以運行在容器中。考慮到gitlab-runner消耗的資源少,使用容器更合適。
拉取gitlab-runner Docker 鏡像:
sudo docker pull gitlab/gitlab-runner
啟動容器:
sudo docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
在容器中執行register操作,將gitlab上的項目注冊到gitlab-runner中:
sudo docker exec -it gitlab-runner gitlab-ci-multi-runner register
輸入上述命令后會有一系列的配置需要輸入,當然也可以設置完后進行更改
按照提示輸入即可,前兩項可以在指定項目設置中CI/CD選項里的Runners settings選項中的Specific Runners里看到,tags是gitlab-ci.yml文件中所要用到的,executor選擇docker
配置成功后,我們可以在設置中CI/CD選項里的Runners settings選項中的Specific Runners里看到runner信息
本地executor鏡像
為了部署與測試,需要一個鏡像用于執行。當選用本地鏡像時,會發現如下報錯
報錯的原因在于,gitlab-runner嘗試去官方的docker hub倉庫拉取鏡像。通過修改gitlab-runner中的配置,設置只拉取本地鏡像:
修改 /etc/gitlab-runner/config.toml ,在 [runners.docker]
下,添加:
pull_policy = never # 該配置默認always,即只在線上拉取鏡像
如果有需要添加一些hosts映射,仍然在 [runners.docker]
下,添加:
extra_hosts = ["hostname:ip"]
另外為了加快單元測試執行速度,將服務端代碼的依賴提前安裝至executor鏡像中:
COPY requirement.txt .
RUN pip install -r requirement.txt
編寫 .gitlab-ci.yaml
單元測試部分
用nose執行測試
對于Python,nosetest工具可以嗅探與執行你寫的所有測試用例,并輸出結果。在執行測試前,使用nose需要使用pip安裝
pip install nose
安裝完成后,使用 nosetests
執行。
nosetests
自寫測試入口
另一個執行測試的選擇,是自寫測試入口,不依賴nose。好處是能夠將測試結果上傳至x-utest。
對測試結果做判斷,如果全部用例通過(即wasSuccessful為True),則sys.exit(0),否則sys.exit(1)
redis與mongo服務化
對于redis與mongo這種外部服務,有兩種解決方式,一是mock對數據庫的讀寫,二是使用服務化的redis與mongo,保證外部環境的一致性。(更推薦第一種方式,此處介紹第二種。)
由于設置了不從docker hub拉取鏡像,因此需要先拉取redis與mongo服務鏡像到本地
docker pull redis:2.8
docker pull mongo:3.2
在gitlab-ci.yaml中添加services:
services:
- redis:2.8
- mongo:3.2
修改代碼的local_config配置文件中的mongo與redis連接URL,指向“mongo”與“redis”
靜態代碼分析
sonarqube搭建
制做了一個docker-compose項目可以一鍵部署SonarQube平臺 <==歡迎fork/start,使用postgres作為后端數據庫,并將數據持久化在宿主機本地。
git clone https://github.com/ityoung/sonarqube-docker.git
pip install docker-compose
cd sonarqube-docker
docker-compose up
sonar scanner配置
同時我也針對Python開源了sonar-scanner鏡像的Dockerfile <==歡迎fork/start,該鏡像已經安裝pylint,方便做Python的靜態代碼分析。
git clone https://github.com/ityoung/sonar-scanner-docker.git
cd sonar-scanner-docker
docker build -t sonar-scanner .
YAML添加執行命令
啟動SonarQube后,進入IP:9000到SonarQube管理頁面,登錄admin/admin,新建一個項目,按步驟執行完成
創建完成后,獲取到執行代碼,復制這段代碼,添加到yaml中,能夠實現分析結果上傳到SonarQube。
注意:如果yaml中用到了兩個鏡像,盡量不要有before_script,否則可能兩個鏡像,觸發錯誤。
Sonar分析后評論
對于develop分支,可以不保存分析結果,而改為將分析結果評論在當次commit下。
在yaml腳本中添加如下參數:
- sonar-scanner
-Dsonar.analysis.mode=preview
-Dsonar.gitlab.commit_sha=$CI_BUILD_REF
-Dsonar.gitlab.ref_name=$CI_BUILD_REF_NAME
-Dsonar.gitlab.project_id=$CI_PROJECT_ID
注意:無新issue時默認不會評論,需要在SonarQube修改gitlab配置才會每次都評論。
持續交付
這部分交由對服務端部署更熟悉的運維操作,因此不做贅述。
接口測試
接口測試代碼在另一個倉庫,這就涉及到從另一個倉庫clone測試代碼時的權限問題。
給倉庫URL添加token能夠實現跨倉庫clone代碼:
git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.xxx.com/yx/apitest.git
附一:完整的.gitlab-ci.yml
image: pro1_executor
stages:
- unittest
- analyze
- deploy
- apitest
variables:
SONAR_HOST: "http://192.168.0.29:9000"
SONAR_PROJ: "pro_1"
SONAR_LOGIN: "b3135dd602b61ce7ff5f4202a3ec2ec0865fa7f5"
services:
- redis:3
- mongo:3.2
UnitTest:
stage: unittest
script:
- pip install nose
- python -m runtest xtest
Sonar_Preview:
image: sonar-scanner
stage: analyze
script:
- sonar-scanner
-Dsonar.analysis.mode=preview
-Dsonar.gitlab.commit_sha=$CI_BUILD_REF
-Dsonar.gitlab.ref_name=$CI_BUILD_REF_NAME
-Dsonar.gitlab.project_id=$CI_PROJECT_ID
-Dsonar.projectKey=$SONAR_PROJ
-Dsonar.sources=.
-Dsonar.host.url=$SONAR_HOST
-Dsonar.login=$SONAR_LOGIN
except:
- master
Sonar_Analyze:
image: sonar-scanner
stage: analyze
script:
- sonar-scanner
-Dsonar.projectKey=$SONAR_PROJ
-Dsonar.sources=.
-Dsonar.host.url=$SONAR_HOST
-Dsonar.login=$SONAR_LOGIN
only:
- master
Deploy_TestServer:
stage: deploy
script: echo "deploy"
API_Test:
stage: apitest
script:
- cd /
- git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.xxx.com/yx/apitest.git
- cd apitest
- pip install -r requirements.txt
- python runtest.py
參考
[1] how to access multiple repositories in CI build?, Stack Overflow