什麼是devops,基於Gitlab從零開始搭建自己的持續整合流水線(Pipeline)
一、gitlab 實現的 auto devops
1. DevOps中的一些概念與原則
(1) 什麼是持續整合
持續整合(Continuous integration,簡稱CI)指的是,頻繁地(一天多次)將程式碼整合到主幹。
它的好處主要有兩個。
- 快速發現錯誤。每完成一點更新,就整合到主幹,可以快速發現錯誤,定位錯誤也比較容易。
- 防止分支大幅偏離主幹。如果不是經常整合,主幹又在不斷更新,會導致以後整合的難度變大,甚至難以整合。
持續整合的目的,就是讓產品可以快速迭代,同時還能保持高質量。它的核心措施是,程式碼整合到主幹之前,必須通過自動化測試。只要有一個測試用例失敗,就不能整合。
(2) 持續交付、持續部署的概念
持續交付(Continuous delivery)指的是,頻繁地將軟體的新版本,交付給質量團隊或者使用者,以供評審。如果評審通過,程式碼就進入生產階段。
持續部署(continuous deployment)是持續交付的下一步,指的是程式碼通過評審以後,自動部署到生產環境。
(3) 持續整合系統的組成
- 一個自動構建過程,包括自動編譯、分發、部署和測試等。
- 一個程式碼儲存庫,即需要版本控制軟體來保障程式碼的可維護性,同時作為構建過程的素材庫。
- 一個持續整合伺服器。
(4) 為什麼要引入auto devops
部署的環境問題 ;
Dev, QA, Ops的進度問題 ;
持續整合的好處 :
編譯問題與Bug可以在push或合併之後第一時間發現並解決;
Devops使持續交付成為可能,使產品隨時可交。過去公司做測試可能需要十幾、二十幾個元件,整合一次往往要一兩個小時,費力費時,而且複雜容易出錯,而一旦配置出錯的話耗時會更久。因此,一次整合測試一週才會做一次,測出Bug要到下一週才能更新,再做測試,這個週期會非常漫長。而持續整合的意義就在於減少風險,和重複的過程,最終提高工作效率。
2. GitLab CI中的一些概念
(1) Pipeline
- 一次 Pipeline 其實相當於一次構建任務,裡面可以包含多個流程,比如自動構建、自動進行單元測試、自動進行程式碼檢查等流程 ;
- 任何提交或者 Merge Request 的合併都可以觸發 Pipeline ;
(2) stages
- stages 表示構建階段,就是上面提到的流程 ;
- 可以在一次 Pipeline 中定義多個 stage ;
- stages有如下特點 :
- 所有 stages 會按照順序執行,即當一個 stage 完成後,下一個 stage 才會開始
- 只有當所有 stages 成功完成後,該構建任務 (Pipeline) 才算成功
- 如果任何一個 stage 失敗,那麼後面的 stages 不會執行,該構建任務 (Pipeline) 失敗
(3) jobs
- job表示構建工作,表示某個stage裡面執行的工作 ;
- 一個stage裡面可以定義多個job ;
- jobs有如下特點 :
- 相同 stage 中的jobs 會並行執行
- 相同 stage 中的 jobs 都執行成功時,該 stage 才會成功
- 如果任何一個job 失敗,那麼該 stage 失敗,即該構建任務 (Pipeline) 失敗
(4) gitlab runner
- 執行構建任務的一個服務 ;
- 把構建任務放到runner裡面而不是在CI裡面做是不想把”構建”這個重任(通常較大的工程構建都比較小號資源) 放到gitlab上而影響gitlab效能。通過把gitlab runner安裝到不同機器上,讓這臺單獨的機器來執行構建任務
二、使用 Gitlab 實現一個簡單的流水線 (Pipeline)
1. 準備工作
(1) 從docker hub下載gitlab/gitlab-runner映象
root# docker pull gitlab/gitlab-runner
(2) 準備一個用來完成stage的映象
多個步驟使用同一個映象來建立容器,也可以根據不同stage準備多個特定的映象。
這裡只是介紹一個使用docker
來完成多個簡單stage
的demo
,就拿一個最簡單的能完成任務的映象,一個簡單的Dockerfile如下:
FROM docker.io/ubuntu
MAINTAINER user.name user.email
# install tools
USER root
RUN apt-get update
RUN apt-get install -y git cmake g++ gcovr
ENTERPOINT /bin/bash
這篇文章介紹的流水線有兩個步驟,第一個步驟完成構建,第二個步驟完成單元測試以及單元測試覆蓋率的計算。其實兩個stage完全可以放到一個容器中來進行。
為模擬真實的流水線,每個環節做特定的工作。這裡假設兩個stage完全不同,需要使用不同的容器來完成。
下面是我們根據需要建立的兩個不同映象 :
具體docker的基本操作請自行Google,也可以參考《每天五分鐘玩轉Docker容器技術》這本書。
(3) 安裝docker-compose
root# sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
如果上面的源下不了,使用下面的方式 :
yum install -y epel-release
yum install -y python-pip
pip install --upgrade pip
pip install docker-compose
(4) 編寫docker-compose.yml
一個簡單的docker-compose.yml檔案:
runner:
image: gitlab/gitlab-runner
restart: always
container_name: gitlab-runner
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /srv/gitlab-runner/config:/etc/gitlab-runner
解釋一下:
- /var/run/docker.sock:Docker 守護程序 (Docker daemon) 預設監聽的 Unix 域套接字(Unix domain socket),容器中的程序可以通過它與 Docker 守護程序進行通訊。
- /srv/gitlab-runner/config:
runner
的配置檔案,可以通過修改這個目錄下的config.toml檔案來修改runner
配置。host主機中的/srv/gitlab-runner/config/config.toml這個檔案被對映到runner中的/etc/gitlab-runner/config.toml檔案中,主機上的/srv/gitlab-runner/config/config.toml的修改,會導致runner內部的/etc/gitlab-runner/config.toml做同步修改。 - 下面通過docker-compose啟動的容器就是流水線的
runner
,流水線在這個runner
裡面觸發並開始執行,之後runner
會接著建立另外的docker容器,來完成流水線中的構建和單元測試任務。-v
表示掛載,runner
通過與主機通訊,看似在runner
中建立容器,其實是在host主機中建立的. 這個也比較好驗證,因為runner中並沒有並沒有安裝docker,如何啟動容器;另外流水線完成後在host主機中通過docker ps -a
可以看到中間生成的臨時容器。
1. 使用docker-compose啟動容器
root# docker-compose up -d
2. 上面使用docker-compose的方式啟動容器,完全可以換成使用docker run來啟動容器
docker run -d --name gitlab-runner --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
gitlab/gitlab-runner
(5) 在工程中開啟 auto devops 選項
專案設定 –> CI/CD –> General pipelines settings –> Enable Auto DevOps
使用一個具體的 runner
如果這個專案打開了shared runner
,裡面可能有多個 runner,我們不想用別人的 runner,只想用自己剛註冊的 runner,可以把 shared runner 選項關閉,或者也可以在.gitlab-ci.yml裡面的 stage
裡面,使用 tags
關鍵字指定特定的 runner 。關閉共享 runner 如下圖:
(6) 編寫 .gitlab-ci.yml 檔案
.gitlab-ci.yml 這個檔案以
yaml
的格式描述了整個流水線有哪些流程,應該做哪些事。具體語法就不說了,可以Google下。編寫完成以後,這個檔案需要放到倉庫的根目錄,受Git版本控制。yaml
格式在編寫時容易出錯,可以在 “Gitlab 側邊欄 CI/CD –> Pipelines”頁面,右上角有個 “CI Lint” 按鈕,進去後輸入編寫的 .gitlab-ci.yml 檔案內容,點選左下角 “Validate” 按鈕。
下面是工程中需要用到的 .gitlab-ci.yml:
2. 註冊runner
root# docker exec -it gitlab-runner gitlab-runner register
3. runner的配置檔案
配置檔案路徑: /srv/gitlab-runner/config/config.toml
concurrent = 1
check_interval = 0
[[runners]]
name = "docker_runner"
url = "https://gitlab.com/"
token = "xxxxxxxxxxxxxxxxxxxxxx"
executor = "docker"
[runners.docker]
tls_verify = false
image = "docker:latest"
privileged = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0
pull_policy = "never"
[runners.cache]
Insecure = false
正常情況下,註冊完畢後是沒有 pull_policy = "never"
這一行的,可以先手動加上。這放到下面的”docker映象的拉取策略“來說。
4. 一次Pipeline的體驗
提交程式碼
流水線在執行的時候
流水線執行完畢
流水線總體概況
5. 其他一些需要注意的地方
(1) 如何節省因為特定容器配置的時間
在.gitlab-ci.yml裡面,一個stage可能需要一個特定的容器來做任務,這樣的話,預設會首先從 docker hub 裡面 pull,並且如果使用剛 pull 下來的映象生成容器,還需要更新源以安裝配置所需環境,這時候可以考慮使用Dockerfile來配置特定的映象來做特定任務,在一個 stage 中使用本地映象來建立容器(容器可以在秒級啟動,這個時間跟整個構建流程來說是可以接受的)。使用本地映象,需要在
/srv/gitlab-runner/config/config.toml
裡面新增pull-policy策略,策略有多個可選,可以設定為優先使用本地映象,如果本地不存在映象,再從docker hub裡面pull,pull-policy的使用語法是pull _policy = "if_not_present"
,if_not_present
這個關鍵字好像不能用了,可以直接換成never
,不使用遠端映象。
(2) docker 映象的拉取策略有三種
- never 任何情況下都不從 docker hub 拉取映象
- always 任何情況下都不使用本地映象
- if-not-present 優先使用本地映象,如果本地不存在該映象,會從 docker hub 拉取