全棧之初識 Gitlab-CI -- 進入持續集成的世界

本文將會對Gitlab CI進行簡要介紹,包括Gitlab Runner,Gitlab CI中的相關概念以及.gitlab-ci.yml的常用配置。

一、GitLab CI 是什麼?

GitLab CI 是GitLab內置的進行持續集成的工具,只需要在倉庫根目錄下創建.gitlab-ci.yml 文件,並配置GitLab Runner;每次提交的時候,gitlab將自動識別到.gitlab-ci.yml文件,並且使用Gitlab Runner執行該腳本。

二、Gitlab Runner

1、簡介

GitLab-Runner就是一個用來執行.gitlab-ci.yml 腳本的工具。可以理解成,Runner就像認真工作的工人,GitLab-CI就是管理工人的中心,所有工人都要在GitLab-CI裏面註冊,並且表明自己是爲哪個項目服務。當相應的項目發生變化時,GitLab-CI就會通知相應的工人執行對應的腳本。

2、Runner類型

GitLab-Runner可以分類兩種類型:Shared Runner(共享型)和Specific Runner(指定型)。

  • Shared Runner:所有工程都能夠用的,且只有系統管理員能夠創建。
  • Specific Runner:只有特定的項目可以使用。

3、Runner搭建

▍Runner 安裝

以Linux爲例:

# For Debian/Ubuntu 
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash 

# For RHEL/CentOS 
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash

其他系統請參考官網文檔:https://docs.gitlab.com/runner/install/

▍獲取Runner註冊Token
安裝好Runner之後,需要向Gitlab進行註冊,註冊Runner需要GitLab-CI的url和token。可根據需求註冊選擇所需類型Runner。

獲取Shared Runner註冊Token:
使用管理員用戶登錄,進入Admin Area -> OverView -> Runners界面。
在這裏插入圖片描述
獲取Specific Runner註冊Token:
進行項目倉庫 -> settings -> CI/CD界面
在這裏插入圖片描述
▍註冊Runner
執行gitlab-ci-multi-runner register命令進行Runner註冊,期間會用到前期獲取的url及token;註冊完成之後,GitLab-CI就會多出一條Runner記錄:
在這裏插入圖片描述
更多系統註冊,請參考閱讀官方文檔:https://docs.gitlab.com/runner/register/

三、相關概念

▍管道(pipeline)

每個推送到 Gitlab 的提交都會產生一個與該提交關聯的管道(pipeline),若一次推送包含了多個提交,則管道與最後那個提交相關聯,管道(pipeline)就是一個分成不同階段(stage)的作業(job)的集合。

▍階段(Stage)

階段是對批量的作業的一個邏輯上的劃分,每個 GitLab CI/CD 都必須包含至少一個 Stage。多個 Stage 是按照順序執行的,如果其中任何一個 Stage 失敗,則後續的 Stage 不會被執行,整個 CI 過程被認爲失敗。
在這裏插入圖片描述
以圖中所示爲例,整個 CI 環節包含三個 Stage:build、test 和deploy。

  • build 被首先執行。如果發生錯誤,本次 CI 立刻失敗;
  • test 在 build 成功執行完畢後執行。如果發生錯誤,本次 CI 立刻失敗;
  • deploy 在 test 成功執行完畢後執行。如果發生錯誤,本次 CI 失敗。

下圖是Gitlab對階段和階段狀態的展示:
在這裏插入圖片描述

▍作業(Job)

作業就是運行器(Runner)要執行的指令集合,Job 可以被關聯到一個 Stage。當一個 Stage 執行的時候,與其關聯的所有 Job 都會被執行。在有足夠運行器的前提下,同一階段的所有作業會併發執行。作業狀態與階段狀態是一樣的,實際上,階段的狀態就是繼承自作業的。

作業必須包含script(由Runner執行的shell腳本),隨着項目越來越大,Job 越來越多,Job 中包含的重複邏輯可能會讓配置文件臃腫不堪。.gitlab-ci.yml 中提供了 before_script 和 after_script 兩個全局配置項。這兩個配置項在所有 Job 的 script 執行前和執行後調用。

Job 的執行過程中往往會產生一些數據,默認情況下 GitLab Runner 會保存 Job 生成的這些數據,然後在下一個 Job 執行之前(甚至不侷限於當次 CI/CD)將這些數據恢復。這樣即便是不同的 Job 運行在不同的 Runner 上,它也能看到彼此生成的數據。

在瞭解了 Job 配置的 script、before_script、after_script 和 cache 以後,可以將整個 Job 的執行流程用一張圖概括:
在這裏插入圖片描述

四、創建.gitlab-ci.yml 文件

1、什麼是.gitlab-ci.yml文件

從7.12版本開始,GitLab CI使用YAML文件(.gitlab-ci.yml)來管理項目配置。該文件存放於項目倉庫的根目錄,並且包含了你的項目如何被編譯的描述語句。YAML文件使用一系列約束敘述定義了Job啓動時所要做的事情。

2、Job

Job是.gitlab-ci.yml文件中最基本的元素,由一系列參數定義了任務啓動時所要做的事情,用戶可以創建任意個任務;每個任務必須有一個獨一無二的名字,但有一些保留keywords不能用於Job名稱,image,services,stages,types,before_script,after_script,variables,cache。

Job被定義爲頂級元素,並且至少包括一條script語句,如果一個 Job 沒有顯式地關聯某個 Stage,則會被默認關聯到 test Stage。

示例:

job1:
# 關聯到bulid階段
stage: build
# 所需執行的腳本
script:
- execute-script-for-job1

3、參數詳情

下面是關於配置CI/CD管道的常用參數詳細說明。

▍stages
用於定義所有作業(job)可以使用的全局階段,gitlab-ci.yml允許靈活定義多個階段,stages元素的順序定義了作業執行的順序。Job關聯的stage名相同時,該多個Job將並行執行(在擁有足夠Runner情況下)。下一個階段的job將會在前一個階段的job都完成的情況下執行。

如果文件中沒有定義 stages,那麼則默認包含 build、test 和 deploy 三個 stage。Stage 中並不能直接配置任何具體的執行邏輯,具體的執行邏輯應該在 Job 中配置。

示例:

stages:
  - build
  - test
  - deploy

▍stage
階段是根據每個Job定義的,並且依賴於全局定義的階段。它允許將作業(Job)分組到不同的階段。

示例:

stages:
  - build
  - test
  - deploy

job 1:
  stage: build
  script: make build dependencies

job 2:
  stage: build
  script: make build artifacts

job 3:
  stage: test
  script: make test

job 4:
  stage: deploy
  script: make deploy

▍script
script是一段由Runner執行的shell腳本。

示例:

job:
  script: "bundle exec rspec"

這個參數也可以使用數組包涵好幾條命令:

job:
  script:
    - uname -a
    - bundle exec rspec

有些時候,script命令需要被單引號或者雙引號所包裹。舉個例子,命令中包涵冒號的時候,該命令需要被引號所包裹,這樣YAML解析器才知道該命令語句不是“key: value”語法的一部分。當命令中包涵以下字符時需要注意打引號:: { } [ ] , & * #? | - < > = ! % @

▍image and services
這兩個選項允許開發者指定任務運行時所需的自定義的docker鏡像和服務。

示例:

#爲每個作業定義不同的映像和服務
test:2.1:
  image: ruby:2.1
  services:
  - postgres:9.3
  script:
  - bundle exec rake spec

test:2.2:
  image: ruby:2.2
  services:
  - postgres:9.4
  script:
  - bundle exec rake spec

▍before_script和after_script
before_script是用於定義一些在所有任務執行前所需執行的命令, 包括部署工作,可以接受一個數組或者多行字符串。after_script用於定義所有job執行過後需要執行的命令,可以接受一個數組或者多行字符串。

示例:

#定義全局 before_script:
default:
  before_script:
    - global before script
#覆蓋全局before_script
job:
  before_script:
    - execute this instead of global before script
  script:
    - my command
  after_script:
    - execute this after my script

▍only and except

  • only和except兩個參數說明了job什麼時候將會被創建:
  • only定義了job需要執行的所在分支或者標籤
  • except定義了job不會執行的所在分支或者標籤

以下是這兩個參數的幾條用法規則:

  • only和except如果都存在在一個job聲明中,則所需引用將會被only和except所定義的分支過濾
  • only和except允許使用正則
  • only和except允許使用指定倉庫地址,但是不forks倉庫
    此外,only和except允許使用以下一些特殊關鍵字:
描述
branches 當一個分支被push上來
tags 當一個打了tag的分支被push上來
api 當一個pipline被piplines api所觸發調起,詳見piplines api(https://docs.gitlab.com/ce/api/pipelines.html)
external 當使用了GitLab以外的CI服務
pipelines 針對多項目觸發器而言,當使用CI_JOB_TOKEN並使用gitlab所提供的api創建多個pipelines的時候
pushes 當pipeline被用戶的git push操作所觸發的時候
schedules 針對預定好的pipline而言(每日構建一類~,具體請看https://docs.gitlab.com/ce/user/project/pipelines/schedules.html
triggers 用token創建piplines的時候
web 在GitLab頁面上Pipelines標籤頁下,你按了run pipline的時候

下面的例子,job將會只在issue-開頭的refs下執行,反之則其他所有分支被跳過:

job:
  # use regexp
  only:
    - /^issue-.*$/
  # use special keyword
  except:
    - branches

更多配置詳情,請參考官網文檔:
https://docs.gitlab.com/ee/ci/yaml/README.html#parameter-details

驗證.gitlab-ci.yml
GitLab CI的每個實例都有一個名爲Lint的嵌入式調試工具,它可以驗證.gitlab-ci.yml文件的內容,進入項目倉庫->CI/CD->CI Lint,示例如下:
在這裏插入圖片描述

五、.gitlab-ci.yml 文件實例

# image: docker:latest

# services:

# variables:
#   DOCKER_DRIVER: overlay

# 定義執行 Job 的步驟
stages:
  - lint # 執行格式校驗
  - build # build 成 Image
  - push # push 到 Image 倉庫
  - deploy # 執行步驟(拷貝運行文件到測試機,並運行)
  - clearup # 清除 runner 本地的Image

# 每次執行 Job 之前(登錄到我們自己的 Image 倉庫)
before_script:
  - docker info
  - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" "$CI_REGISTRY" --password-stdin

# 每次執行 Job 之後(退出)
after_script:
  - docker logout

# 第一步:執行 code lint 檢測
linting:
  # 基於提前準備好的 Image 進行
  image: *******:8081/sit-develop-tool/siteslinter:1.0.7
  # 執行的是上面定義的哪一步
  stage: lint
  # 執行這一步之前做什麼
  before_script:
    - ls -a
    - cat .eslintrc.js
    - cat .gitignore
  # 執行這一步之後做什麼
  after_script:
    - ls -a
    - cat .eslintrc.js
    - cat .gitignore
  # 執行 Eslint 檢測
  script:
    - /home/app/.npm-global/bin/eslint -c .eslintrc.js --ext .js,.vue --color --ignore-path .gitignore .
  # 這個 Job 運行在哪個 Gitlab Runner 上
  tags: 
    - NODE2
  # 除了...都運行此 Job
  except:
    - develop
    - master
    - tags

# 第二步:將代碼 build 成 Image (在 Runner 上)
building:
  stage: build
  script:
    # build (後面 tag 取 commit id 的 0-7 位)
    - docker build --no-cache -t $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:7} .
  tags:
    - NODE2
  # 只在...時運行此 Job
  only:
    refs:
      - develop

# build 生產環境
building-prod:
  stage: build
  script:
    - cat nuxt.config.js
    - docker build --no-cache -t $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:7} .
  tags:
    - NODE2
  # 只有下 Tag 時執行
  only:
    - tags
  # 所有 branch 有變動時都不執行
  except:
    - branches

# 第三步:將 Image push 到 Image 倉庫(在 Gitlab 上)
pushing:
  stage: push
  script:
    - docker push $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:7}
  tags:
    - NODE2
  only:
    refs:
      - develop
      - tags

# 第四步:將 sh 文件 copy 到測試機並執行(文件中會在測試機上將 Image run 成 container)
deploying:
  image: ******/sit-develop-tool/tool-ansible:1.0.1
  before_script:
    - 'echo "Start to deploying..."'
  stage: deploy
  # 使用 ansible 工具, 將 lib 文件夾和 start.sh 文件 copy 到測試機並運行
  # inventory 文件中會記錄登錄到測試機的信息(ip, account, psw)
  script:
    - |
        ansible '*' -i `pwd`/inventory -m shell -a "mkdir -p /srv/dmz-proxy/" -b
        ansible '*' -i `pwd`/inventory -m copy -a "src=lib dest=/srv/dmz-proxy/ owner=root group=root" -b
        ansible '*' -i `pwd`/inventory -m copy -a "src=start.sh dest=/srv/dmz-proxy/ owner=root group=root" -b
        ansible "*" -i `pwd`/inventory -m shell -a "chmod 777 /srv/dmz-proxy/start.sh"
        # 執行 start.sh 並傳入 Image name
        ansible "*" -i `pwd`/inventory -m shell \
                    -a "cd /srv/dmz-proxy/ && bash start.sh $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:7}" \
                    -b
        ansible "*" -i `pwd`/inventory -m shell -a "cd /srv/dmz-proxy/ && rm -rf start.sh*" -b
  # 定義此作業完成部署的環境名稱
  environment:
    name: production
  tags:
    - NODE2
  only:
    refs:
      - develop

deploying-prod:
  image: ******/sit-develop-tool/tool-kubectl-deployment:1.0.2
  stage: deploy
  script:
    - sed -i "s/GITLAB_REGISTRY_IMAGE/******:${CI_COMMIT_SHA:0:7}/g" deployments/WevService-deploy.yml
    - cat deployments/WevService-deploy.yml
    - kubectl delete secret regcred || true
    - kubectl create secret generic regcred --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson
    - kubectl apply -f deployments/ --record
    - kubectl get services -o wide
    - kubectl get pods -o wide
  environment:
    name: production
  tags:
    - NODE2
  only:
    - tags
  except:
    - branches

# 第五步:清除 Runner 上的 Image
clearing:
  image: docker:latest
  stage: clearup
  script:
    - docker rmi $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:7}
    - docker rm $(docker ps -a -q) || true
  tags:
    - NODE2
  only:
    refs:
      - develop

clearing-prod:
  image: docker:latest
  stage: clearup
  script:
    - docker images --filter=reference='*******' | grep " [months]* ago" | awk '{print $3}' | xargs docker image rm
    - docker images
  tags:
    - NODE2
  only:
    - tags
  except:
    - branches

覺得有幫助的小夥伴右上角點個贊~

在這裏插入圖片描述

掃描上方二維碼關注我的訂閱號~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章