CICD是什麼
我們的開發模式經歷瞭如下的轉變:瀑布模型->敏捷開發→DevOps(Development、Operations的組合詞,是一組過程、方法與系統的統稱)
後來隨着DevOps的興起,出現了持續集成(Continuous Integration)、持續交付(Continuous Delivery) 、持續部署(Continuous Deployment) 的新方法,關於持續集成、持續交付、持續部署,總結如下:
- 持續集成的重點是將各個開發人員的工作集合到一個代碼倉庫中。通常,每天都要進行幾次,主要目的是儘早發現集成錯誤,使團隊更加緊密結合,更好地協作。
- 持續交付的目的是最小化部署或釋放過程中固有的摩擦。它的實現通常能夠將構建部署的每個步驟自動化,以便任何時刻能夠安全地完成代碼發佈(理想情況下)。
- 持續部署是一種更高程度的自動化,無論何時對代碼進行重大更改,都會自動進行構建/部署。
持續集成的好處是什麼?
持續集成可以使問題儘早暴露,從而也降低了解決問題的難度,正如老馬所說,持續集成無法消除bug,但卻能大大降低修復的難度和時間。
持續交付的好處是什麼?
持續交付的好處在於快速獲取用戶反饋;適應市場變化和商業策略的變化。開發團隊保證每次提交的修改都是可上線的修改,那麼決定何時上線,上線哪部分功能則完全由產品業務團隊決定。
雖然持續交付有顯著的優點,但也有不成立的時候,比如對於嵌入式系統的開發,往往需要軟硬件的配合。
持續部署的好處是什麼?
持續部署的目標是通過減少批量工作的大小,並加快團隊工作的節奏,幫助開發團隊在其開發流程中消除浪費。這使團隊能夠一直處於一種可持續的平穩流狀態, 讓團隊更容易去創新、試驗,並達到可持續的生產率
市面上的CI有很多,如果在github上搜一下ci工具,也會搜到很多,比如:
相關概念
runner
部署 Gitlab runner 官方文檔:Run GitLab Runner in a container | GitLab
關聯 runner 到 gitlab 官方文檔:Registering runners | GitLab
我們可以簡單的把 Gitlab runner 給理解成.gitlab-ci.yml
文件內容的執行者,.gitlab-ci.yml
告訴了 Gitlab runner 去做什麼。
Gitlab runner 不是一個配置項,它是需要專門部署的,比如用 docker 部署一個 runner 鏡像到可以連接內網的容器。也可以使用公司內配好的 shared runners.
runner是一個單獨的程序需要安裝,並配置到gitlab
pipeline(管道流水線)
- 一次 Pipeline 其實相當於一次構建任務,裏面可以包含多個流程(
Stage
),比如自動構建、自動進行單元測試、自動進行代碼檢查等流程 ; - 任何提交或者 Merge Request 的合併都可以觸發 Pipeline ;
Stage(構建階段)
- Stage表示構建階段,就是上面提到的流程 ;
- 可以在一次
Pipeline
中定義多個Stage
; - Stage有如下特點 :
- 所有 stages 會按照順序運行,即當一個 stage 完成後,下一個 Stage纔會開始
- 只有當所有 Stage 成功完成後,該構建任務
Pipeline
纔算成功 - 如果任何一個 Stage失敗,那麼後面的 Stage 不會執行,該構建任務 (Pipeline) 失敗
階段是對批量的作業的一個邏輯上的劃分,每個 pipeline
都必須包含至少一個 Stage
。多個 Stage是按照順序執行的,如果其中任何一個 Stage失敗,則後續的 Stage不會被執行,整個 CI 過程被認爲失敗。
Jobs(任務)
- job表示構建工作,表示某個stage裏面執行的工作 ;
- 一個stage裏面可以定義多個job ;
- jobs有如下特點 :
- 相同 stage 中的jobs 會並行執行
- 相同 stage 中的 jobs 都執行成功時,該 stage 纔會成功
- 如果任何一個job 失敗,那麼該 stage 失敗,即該構建任務 (Pipeline) 失敗
.gitlab-ci.yml 文件
.gitlab-ci.yml 文件被用來管理項目的 runner 任務,Gitlab CI通過.gitlab-ci.yml文件管理配置job,該文件定義了statge順序、job應該如何觸發和工作、執行什麼腳本、如何構建pipeline等流程
Yaml Syntax 寫法詳情具體請見 => YAML Syntax ‒ Ansible Documentation
yml和json寫法的對比
例子1
1.數組寫法
{ "array": ["red", "blue", "yellow"] }
2.yml寫法
array: - red - blue - yellow
例子2
1.對象寫法
{ "student1": { "name": "小明" }, "array": ["red", "blue", "yellow"], "student2": { "name": "小明" } }
2.json寫法
# 我是註釋 student1: name: 小明 array: - red - blue - yellow student2: name: 小明
簡單例子
如
這裏的四個Statge(階段): Verify、Build、Dockerpush、Deploy
四個,這四個階段組成一條Pipeline
每個階段都有一個job,所以總共四個job,也就是unit-test
、java-package
、docker-push
、service-1
這四個,當然,每個stage可以由多個job組成
對應.gitlab-ci.yml
## 定義pipeline流程:verify->build->dockerpush->deploy stages: - verify - build - dockerpush - deploy #單元測試 #java編譯 java-package: stage: build tags: - test-cicd script: - echo build #push鏡像 docker-push: stage: dockerpush tags: - test-cicd script: - echo docker-push #deploy service-1: stage: deploy tags: - test-cicd script: - echo deploy
runner執行流程
Gitlab-CI 關鍵字
Gitlab CI 的關鍵字有很多,但實際常用的只有較小一部分。在此部分會對常用關鍵字進行詳解。
如果需要特殊配置可參考關鍵字文檔=> Keyword reference for the .gitlab-ci.yml file | GitLab
script
需要被運行的腳本。以數組形式配置。
pipeline_job: ...... script: - cd <文件夾目錄路徑> - npm install - npm build ......
before_script和after_script
隨着項目越來越大,Job 越來越多,Job 中包含的重複邏輯可能會讓配置文件臃腫不堪。.gitlab-ci.yml 中提供了 before_script
和 after_script
兩個全局配置項。這兩個配置項在所有 Job 的 script
執行前和執行後調用。
before_script
和 script
在一個上下文中是串行執行的,after_script
是獨立執行的。所以根據執行器(在runner註冊的時候,可以選擇執行器,docker,shell 等)的不同,工作樹之外的變化可能不可見,例如,在before_script中執行軟件的安裝。
你可以在任務中定義 before_script
,after_script
,也可以將其定義爲頂級元素,定義爲頂級元素將爲每一個任務都執行相應階段的腳本或命令。
variables 變量
在 Gitlab-CI 中,變量大致可分爲三類:
1.Gitlab 給我們預先定義的變量,比如CI_COMMIT_BRANCH
.
Predefined variables reference | GitLab
2.Setting => Gitlab CI/CD => variables 中定義的變量
variables: TEST_VAR: "All jobs can use this variable's value" job1: variables: TEST_VAR_JOB: "Only job1 can use this variable's value" script: - echo "$TEST_VAR" ......
image
CI 流水線運行環境的 docker 鏡像(任何相關的代碼運行環境鏡像皆可),比如docker鏡像安裝了代碼質量掃碼工具
.sonarqube: stage: check tags: - "public" image: sonarsource/sonar-scanner-cli:4.6 allow_failure: false variables: SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task cache: key: "SONAR_${CI_JOB_NAME}" paths: - .sonar/cache script: - | if [ -f "sonar-project.properties" ];then sonar-scanner -Dsonar.sources=. -Dsonar.branch.name=$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME fi only: refs: - merge_requests
stages
Gitlab CI 允許我們進行自定義的流水線階段配置,我們可以將自己的流水線自我劃分爲若干stages
,然後在不同的 stage 來做不同的事
## 定義pipeline流程:verify->build->dockerpush->deploy stages: - verify - build - dockerpush - deploy
stage
stage 爲 stages 的一個子項,在我們自定義單個流水線任務時可以配置
unit-test: stage: verify # 屬於哪個流程 tags: - test-cicd # 在哪個runner上面執行,在註冊runner可以自定義 script: - echo unit-test # 執行腳本
cache
緩存多個流水線任務之間共用的文件,目錄, etc.
cache: key: cache-node-modules # 在這裏寫出需要緩存的文件的路徑,在此爲node_modules paths: - node_modules
retry
一個 job 可以被重新執行的最大數量。必須是個正整數, 0-2, 2 爲最大值
- when 可設置在特定失敗原因的情況下執行
job: script: rspec retry: max: 2 when: runner_system_failure
when
when可以設置以下值:
- on_success - 只有前面stages的所有工作成功時才執行。 這是默認值。
- on_failure - 當前面stages中任意一個jobs失敗後執行。
- always - 無論前面stages中jobs狀態如何都執行。
manual
- 手動執行(GitLab8.10增加)。
stages: - build - cleanup_build - test - deploy - cleanup build_job: stage: build script: - make build cleanup_build_job: stage: cleanup_build script: - cleanup build when failed when: on_failure test_job: stage: test script: - make test deploy_job: stage: deploy script: - make deploy when: manual cleanup_job: stage: cleanup script: - cleanup after jobs when: always
本說明:
- 只有當build_job失敗的時候纔會執行`cleanup_build_job 。
- 不管前一個job執行失敗還是成功都會執行`cleanup_job 。
- 可以從GitLab界面中手動執行deploy_jobs。
manual:
- 在GitLab的用戶界面中顯示該作業的“播放”按鈕
- 意味着deploy_job僅在單擊“播放”按鈕時纔會觸發job。
修改後例子
stages: - verify - build - dockerpush - deploy - cleanup before_script: - pwd after_script: - echo after_script #單元測試 unit-test: stage: verify tags: - test-cicd script: - echo unit-test #java編譯 java-package: stage: build tags: - test-cicd script: - echo build #push鏡像 docker-push: stage: dockerpush tags: - test-cicd script: - echo docker-push #deploy service-1: stage: deploy tags: - test-cicd script: - echo deploy when: manual # 手動觸發job,只有點擊按鈕纔會觸發 cleanup_job: stage: cleanup script: - echo clean up when: always # 前面的job成功與否,都會執行該job
only & except
only和except兩個參數說明了job什麼時候將會被創建:
- only定義了job需要執行的所在分支或者標籤
- except定義了job不會執行的所在分支或者標籤
以下是這兩個參數的幾條用法規則:
- only和except如果都存在在一個job聲明中,則所需引用將會被only和except所定義的分支過濾.
- only和except允許使用正則
only
和except
可同時使用。如果only
和except
在一個job配置中同時存在,則以only
爲準,跳過except
(從下面示例中得出)。only
和except
允許使用特殊的關鍵字:branches
,tags
和triggers
。only
和except
允許使用指定倉庫地址但不是forks的倉庫(查看示例3)
例子1
.job將會只在issue-開頭的refs下執行,反之則其他所有分支被跳過:
job: # use regexp only: - /^issue-.*$/ # use special keyword except: - branches
例子2
job只會在打了tag的分支,或者被api所觸發,或者每日構建任務上運行
job: # use special keywords only: - tags # tag 分支 commit 之後觸發 - triggers # API 觸發 - schedules # 每日構建觸發
例子3
.job將會在父倉庫gitlab-org/gitlab-ce的非master分支有提交時運行。
job: only: - branches@gitlab-org/gitlab-ce except: - master@gitlab-org/gitlab-ce
模塊化寫法
隨着流水線任務的變多,把所有的任務都寫在.gitlab-ci.yml 文件中會顯得很臃腫
解決方法是拆分多個任務到不同的模塊
在.gitlab.yml
文件中按照如下寫法即可引入不同的 yml 文件
include: - local: /.gitlab-ci/cache.yml - local: /.gitlab-ci/check.yml - local: /.gitlab-ci/pre-build.yml - local: /.gitlab-ci/build.yml - local: /.gitlab-ci/deploy.yml - local: /.gitlab-ci/publish-report.yaml
完整例子
例子1
# .gitlab-ci.yml # 鏡像爲node的環境鏡像,如果用的是別的環境可以更改爲別的項目環境的鏡像 image: node:latest # 自定義stages stages: - first_stage - second_stage - third_stage - fourth_stage - fifth_stage # 自定義任務1 job_1_push: only: - pushes # 設置使用fe tags標籤的shared runner tags: - yehanli # 當前任務需要執行的腳本 script: - echo 'job1 ========= 完成' # 設置當前任務的stage stage: first_stage # 自定義任務2 job_2_push: only: - pushes tags: - yehanli script: - echo 'job2 ========= 完成' stage: third_stage # 自定義任務3 job_3_push: only: - pushes tags: - yehanli script: - echo 'job3 ========= 完成' stage: fourth_stage # 設置當前任務爲手動觸發 when: manual # 自定義任務4 job_4_push: only: - pushes - tags tags: - yehanli script: - echo 'job4 ========= 完成' stage: fourth_stage when: always # 自定義任務5 job_5_web: only: # 設置爲點擊run pipeline時觸發,流水線不自動觸發 - web tags: - yehanli script: - echo 'job5 ========= 完成' stage: fifth_stage