在介紹.gitlab-ci.yml之前,我們先看幾個概念:
GitLab Runner
一般來說,構建任務都會占用很多的系統資源 (譬如編譯代碼),而 GitLab CI
又是 GitLab
的一部分,如果由 GitLab CI
來運行構建任務的話,在執行構建任務的時候,GitLab
的性能會大幅下降。
GitLab CI
最大的作用是管理各個項目的構建狀態,因此,運行構建任務這種浪費資源的事情就交給 GitLab Runner
來做啦。因為 GitLab Runner
可以安裝到不同的機器上,所以在構建任務運行期間並不會影響到 GitLab
的性能。
GitLab Runner
的安裝特別簡單,官網有各平台的安裝方法或安裝包,此處不再贅述。
注冊
-
打開
GitLab
中的項目頁面,在項目設置中找到runners
-
在
runner
運行的機器上,用命令行注冊,比如:
gitlab-runner register --name="XX" --url="https://git.xx.com/" --token="XXX" --executor="shell"
按照提示一步一步安裝就可以了。其中,executor
可以是多種類型,簡單的話可以選shell
。有熟悉docker
的可以使用docker
。
-
配置文件在
/etc/gitlab-runner/config.toml
配置項類似下面,可能需要手動添加
builds_dir
和cache_dir
這兩個變量,再重啟服務[[runners]] name = "216XX" url = "https://git.XX.com/" token = "XX" executor = "shell" builds_dir = "/home/gitlab-runner/builds" cache_dir = "/home/gitlab-runner/cache" [runners.cache]
常見命令
sudo gitlab-runner list 查看各個 Runner 的狀態
sudo gitlab-runner stop 停止服務
sudo gitlab-runner start 啟動服務
sudo gitlab-runner restart 重啟服務
Stages
Stages
表示構建階段,說白了就是上面提到的流程。默認有3個stages
:build
, test
, deploy
。我們可以在一次 Pipeline
中定義多個 Stages
,這些 Stages
會有以下特點:
- 所有
Stages
會按照順序運行,即當一個Stage
完成后,下一個Stage
才會開始 - 只有當所有
Stages
完成后,該構建任務 (Pipeline) 才會成功 - 如果任何一個
Stage
失敗,那么后面的Stages
不會執行,該構建任務 (Pipeline) 失敗
Jobs
Jobs
表示構建工作,表示某個 Stage
里面執行的工作。我們可以在 Stages
里面定義多個 Jobs
,這些 Jobs 會有以下特點:
1、相同 Stage
中的 Jobs
會並行執行
2、相同 Stage
中的 Jobs
都執行成功時,該 Stage
才會成功
3、如果任何一個 Job
失敗,那么該 Stage
失敗,即該構建任務 (Pipeline) 失敗
.gitlab-ci.yml
.gitlab-ci.yml
用來配置 CI
用你的項目中做哪些操作,這個文件位於倉庫的根目錄。
當有新內容push
到倉庫,或者有代碼合並后,GitLab
會查找是否有.gitlab-ci.yml
文件,如果文件存在,Runners
將會根據該文件的內容開始build
本次commit
。
.gitlab-ci.yml
使用YAML
語法, 你需要格外注意縮進格式,要用空格來縮進,不能用tabs
來縮進。
約束
任務中必須得有script
部分。
示例
# 定義 stages(階段)。任務將按此順序執行。
stages:
- build
- test
- deploy
# 定義 job(任務)
job1:
stage: test
tags:
- XX #只有標簽為XX的runner才會執行這個任務
only:
- dev #只有dev分支提交代碼才會執行這個任務。也可以是分支名稱或觸發器名稱
- /^future-.*$/ #正則表達式,只有future-開頭的分支才會執行
script:
- echo "I am job1"
- echo "I am in test stage"
# 定義 job
job2:
stage: test #如果此處沒有定義stage,其默認也是test
only:
- master #只有master分支提交代碼才會執行這個任務
script:
- echo "I am job2"
- echo "I am in test stage"
allow_failure: true #允許失敗,即不影響下步構建
# 定義 job
job3:
stage: build
except:
- dev #除了dev分支,其它分支提交代碼都會執行這個任務
script:
- echo "I am job3"
- echo "I am in build stage"
when: always #不管前面幾步成功與否,永遠會執行這一步。它有幾個值:on_success (默認值)\on_failure\always\manual(手動執行)
# 定義 job
.job4: #對於臨時不想執行的job,可以選擇在前面加個".",這樣就會跳過此步任務,否則你除了要注釋掉這個jobj外,還需要注釋上面為deploy的stage
stage: deploy
script:
- echo "I am job4"
# 模板,相當於公用函數,有重復任務時很有用
.job_template: &job_definition # 創建一個錨,'job_definition'
image: ruby:2.1
services:
- postgres
- redis
test1:
<<: *job_definition # 利用錨'job_definition'來合並
script:
- test1 project
test2:
<<: *job_definition # 利用錨'job_definition'來合並
script:
- test2 project
#下面幾個都相當於全局變量,都可以添加到具體job中,這時會被子job的覆蓋
before_script:
- echo "每個job之前都會執行"
after_script:
- echo "每個job之后都會執行"
variables: #變量
DATABASE_URL: "postgres://postgres@postgres/my_database" #在job中可以用${DATABASE_URL}來使用這個變量。常用的預定義變量有CI_COMMIT_REF_NAME(項目所在的分支或標簽名稱),CI_JOB_NAME(任務名稱),CI_JOB_STAGE(任務階段)
GIT_STRATEGY: "none" #GIT策略,定義拉取代碼的方式,有3種:clone/fetch/none,默認為clone,速度最慢,每步job都會重新clone一次代碼。我們一般將它設置為none,在具體任務里設置為fetch就可以滿足需求,畢竟不是每步都需要新代碼,那也不符合我們測試的流程
cache: #緩存
#因為緩存為不同管道和任務間共享,可能會覆蓋,所以有時需要設置key
key: ${CI_COMMIT_REF_NAME} # 啟用每分支緩存。
#key: "$CI_JOB_NAME/$CI_COMMIT_REF_NAME" # 啟用每個任務和每個分支緩存。需要注意的是,如果是在windows中運行這個腳本,需要把$換成%
untracked: true #緩存所有Git未跟蹤的文件
paths: #以下2個文件夾會被緩存起來,下次構建會解壓出來
- node_modules/
- dist/
驗證gitlab-ci.yml
https://git.xx.com/ci/lint
跳過job
如果你的commit
信息包涵[ci skip]
或者[skip ci]
,不論大小寫,這個commit
將會被創建,但是job
會被跳過
shell問題
使用shell
腳本時,每步job
一開始總有不短的等待時間,對於我們而言是不必要的,除去后台jenkins_build
這步外,仍要最快20
分鍾。
之前,我曾在release
分支時,暫時將各步整合到一個job
里,時間縮短為5
分鍾。當然,這是不符合語義的。
最近,發現docker
沒有這個問題。所以,建議使用docker
。
使用docker
示例
以下是我們項目中使用的.gitlab-ci.yml
文件:
image: xx:1.0
stages:
- jenkins_build
- install
- test
- build
- e2e
- zip
- copy
- end
cache:
policy: pull
key: "$CI_COMMIT_REF_NAME"
paths:
- node_modules/
- .eslintcache
variables:
DOCKER_DRIVER: overlay2
GIT_STRATEGY: "fetch"
.template: &templateDef # 創建一個錨,'template'
only:
- master
- release
- dev
install:
stage: install
<<: *templateDef # 利用錨'templateDef'來合並
cache:
key: "$CI_COMMIT_REF_NAME"
paths:
- node_modules
script:
- cnpm i
eslint:
stage: test
<<: *templateDef
script:
- npm run eslint
unit:
stage: test
<<: *templateDef
script:
- npm run unit
build:
stage: build
<<: *templateDef
only:
- release
script:
- npm run clear_dist
- npm run build
.e2e_ci:
stage: e2e
<<: *templateDef
script:
- npm run e2e_ci
zip:
stage: zip
<<: *templateDef
only:
- release
script:
- npm run zip
## Jenkins 復制
jenkins_copyweb:
stage: copy
<<: *templateDef
only:
- release
script:
- ssh $JENKINS_SERVER_IP /jenkins/XX_copyweb.sh
## Jenkins 提交
jenkins_commit:
stage: end
<<: *templateDef
only:
- release
script:
- ssh $JENKINS_SERVER_IP /jenkins/XX_svn_commit.sh
## Jenkins 構建
jenkins_build:
stage: jenkins_build
<<: *templateDef
only:
- master
script:
- ssh $JENKINS_SERVER_IP /jenkins/build.sh
其中,XX:1.0
是我們自己創建的docker
鏡像,它主要安裝了nodejs
、cnpm
、jdk
、sshpass
,其中sshpass
不是必須的,它是使用密碼登陸宿主機時的一種方案。
現在,我們使用ssh
來與宿主機交互,需要將容器內生成的ssh
的key
(ssh-keygen -t rsa
),即/root/.ssh/id_rsa.pub
中內容,復制到宿主機的/root/.ssh/authorized_keys
文件中。
配置
配置文件/etc/gitlab-runner/config.toml
修改為
[[runners]]
name = "216xx"
url = "https://git.xx.com/"
token = "xx"
executor = "docker"
[runners.docker]
tls_verify = false
image = "xx:1.0"
privileged = false
disable_cache = false
pull_policy = "if-not-present"
volumes = ["/cache","/tmp:/tmp:rw"]
shm_size = 0
[runners.cache]
其中,pull_policy
是下載docker
鏡像image
的策略,默認會先從網上找,沒有就報錯,我們改為先從本地找;volumes
是將docker
中的數據卷掛載到宿主機上。