GitLab-CI/CD入門實操
以Spring boot項目為例。傳統(tǒng)方式是本地生成jar包,F(xiàn)TP上傳服務(wù)器,重啟服務(wù);如果是內(nèi)網(wǎng)測試服,也可以在服務(wù)器上安裝git拉取代碼,在服務(wù)器上編譯打包。但這都需要人為干預(yù),于是CI/CD就出現(xiàn)了。
- CI:Continuous Integration(持續(xù)集成)。自動構(gòu)建和測試每次提交的代碼,以確保所引入的更改符合所有測試、準(zhǔn)則和代碼合規(guī)性標(biāo)準(zhǔn)。
- CD:Continuous Delivery(持續(xù)交付)和Continuous Deployment(持續(xù)部署)。基于CI,前者側(cè)重于交付給客戶或質(zhì)量團隊(比如決定是否對新版本進行壓測),而后手動部署/自動部署,如果是自動部署的話就是持續(xù)部署了。
CI/CD的工具有很多,最流行的當(dāng)屬jenkins。不過以筆者為數(shù)不多的經(jīng)驗來看,作為后起之秀的gitlab更簡單一點,也更靈活,不會像jenkins那樣笨重。當(dāng)然,兩者的概念都是挺多的,沒有師父,光靠自己入門都不容易。
GitLab-CI/CD流程示例

從左往右看,首先研發(fā)人員完成需求提交代碼到 GitLab。GitLab 觸發(fā)一次 Build,構(gòu)建好服務(wù),然后開始跑單元測試、集成測試。等待測試結(jié)果通過后,再由負(fù)責(zé)該項目的同事進行 CodeReview,灰度發(fā)布,正式部署到線上。
概念
本文基于GitLab 13.7版本
Pipline
Pipelines comprise:
- Jobs, which define what to do. For example, jobs that compile or test code.
- Stages, which define when to run the jobs. For example, stages that run tests after stages that compile the code.
Jobs are executed by runners. Multiple jobs in the same stage are executed in parallel, if there are enough concurrent runners.
Stages
- Manage:項目周期或團隊周期的各項數(shù)據(jù)統(tǒng)計分析。主要是各環(huán)節(jié)耗時統(tǒng)計,比如對于典型的Issue(提出問題)->Plan(列入計劃)->Code(編碼)->Test(測試)->Package(打包)流程,每個環(huán)節(jié)的耗時決定了整體問題處理的響應(yīng)速度。
- Plan:借助諸多工具進行有效的項目管理。
- Create:代碼管理。
- Verify:代碼質(zhì)量分析、代碼合并(持續(xù)集成)、單元測試等。
- Package:將代碼打包,并作為依賴庫對外提供。
- Secure(ULTIMATE版提供):檢查應(yīng)用程序是否存在可能導(dǎo)致未經(jīng)授權(quán)訪問、數(shù)據(jù)泄漏或拒絕服務(wù)的安全漏洞。GitLab可以對應(yīng)用程序的代碼執(zhí)行靜態(tài)和動態(tài)測試,查找已知的缺陷并在合并請求中報告它們。然后可以在合并之前修復(fù)缺陷。安全團隊可以使用儀表板獲取項目和組的高級視圖,并在需要時啟動修正過程。
- Release:持續(xù)交付。
- Configure:配置[文件]參與DevOps各環(huán)節(jié)。
- Monitor:GitLab收集并顯示已部署應(yīng)用程序的性能指標(biāo),以便您可以立即知道代碼更改如何影響生產(chǎn)環(huán)境。
- Defend:若干用于服務(wù)安全防御的中間件。
上述包含了GitLab-DevOps整個流程的所有環(huán)節(jié),CI/CD只是其中的一部分。
GitLab Runner
可以安裝在任意機子上,通過它可以[在一臺機子上]注冊多個runner實例到gitlab服務(wù)器。每個runner用于執(zhí)行一個或多個具體任務(wù)(如build、test)。
runner有以下三類,可用范圍從大到小
Shared runnersare available to all groups and projects in a GitLab instance.Group runnersare available to all projects and subgroups in a group.Specific runnersare associated with specific projects. Typically, specific runners are used for one project at a time.
我們可以直接安裝GitLab Runner到宿主機,也可以使用docker方式安裝。注意這兩種方式會影響到后續(xù).gitlab-ci.yml中對pipline的定義。比如要編譯maven項目,如果executor設(shè)為shell,那么若宿主機中安裝有mvn命令,前者可以在scripts中直接使用mvn,而后者并不能,只能通過定義Dockerfile,在其中定義搭建mvn環(huán)境到編譯代碼的整個流程。
采用docker方式安裝的話,可以參考Docker搭建自己的Gitlab CI Runner
docker pull gitlab/gitlab-runner:latest
docker run -d --name louwen-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
注冊runner實例(可注冊多個實例)
docker exec -it louwen-runner gitlab-runner register
會讓我們填一系列配置項,如下:
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://192.168.1.26:9980/
Enter the registration token:
cJMXGJWx7qx9AmpSc6ee
Enter a description for the runner:
[a0debaaf80a9]: runner for InkScreen-API project
Enter tags for the runner (comma-separated):
InkScreenAPI
Registering runner... succeeded runner=cJMXGJWx
Enter an executor: docker-ssh, shell, docker-ssh+machine, kubernetes, custom, parallels, ssh, virtualbox, docker+machine, docker:
docker
Enter the default Docker image (for example, ruby:2.6):
maven:3-jdk-8
完事后,我們在gitlab->xxxProjct中就能找到該runner:

Gitlab 15.6 版本前,一個項目可以關(guān)聯(lián)多個
runner,然后使用tags設(shè)定用于執(zhí)行job的 runners,然而一個 runner 只能關(guān)聯(lián)一個項目。它們是多對一的關(guān)系。這就造成了,每一個 CI/CD 項目,至少要新注冊一個 runner,而實際情況,大部分項目的 runner 設(shè)定是一樣的,而且博主個人理解,項目關(guān)聯(lián)的只是注冊 runner 時添加的配置節(jié),運行時實例都是需要的時候臨時啟動的,所以也不存在進程安全、資源競爭的問題;相反,博主目前并沒有遇到一個項目需要設(shè)置多個 runner 的場景。所以,這種反向多對一的設(shè)計就很怪。
所幸,從 Gitlab 15.6 開始,原本用于注冊 runner 的
registration token被廢棄,推薦使用Runner authentication token,該 token 不再是具體項目的屬性,而是 runner 實例本身的屬性,如此,多個項目可以借此使用同一個 runner。
接下來,就可以定義項目構(gòu)建流程了。項目的構(gòu)建流程是由項目根目錄的.gitlab-ci.yml文件控制的。當(dāng)然了,一個pipeline可以涉及到多個runner。
.gitlab-ci.yml
定義一個pipline,以下為示例
variables:
DOCKER_TLS_CERTDIR: "/certs"
# stage也可以自定義
stages:
- build jar
# - test
- build and run image
#job's name 可以隨意取
buildJar:
stage: build jar
variables:
# 若要使cache生效,須指定-Dmaven.repo.local
MAVEN_OPTS: "-Dmaven.repo.local=.m2"
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .m2
only:
- dev
script:
# package 已包含 test 步驟,所以流程中不需要另外配置test job
- mvn clean package
tags:
- inkscreen_api
artifacts:
paths:
- target/admin.jar
expire_in: 3600 seconds
deploy:
stage: build and run image
image: docker:stable
services:
- docker:dind
only:
- dev
variables:
IMAGE_NAME: newton/inkscreen-api:$CI_COMMIT_REF_NAME
PORT: 38082
script:
- docker build --build-arg JAR_PATH=target/admin.jar -t $IMAGE_NAME .
- docker run -p $PORT:$PORT -d --name inkscreen-$CI_COMMIT_REF_NAME --env spring.redis.host=myredis $IMAGE_NAME
tags:
- inkscreen_api
cache
cache常用在dacker-based job之間傳遞文件。比如項目依賴的公共jar包,jobA辛辛苦苦從網(wǎng)上down了下來,結(jié)果運行完了,jobA所在容器也跟著被移除,自然里面的所有文件都不存在了。后續(xù)其它job用到相同的jar包還要重新下載。同樣的,pipline多次執(zhí)行,jobA自己每次也要重新下載。
為了解決這個問題,gitlab-ci采用了cache的方式。指定文件/目錄,每次job結(jié)束前將其打包,放到/etc/gitlab-runner/config.toml中對應(yīng)的[runners.docker][volumes]指定的卷內(nèi),其它job(包括自己)運行前,對應(yīng)的cache都會被加載并解壓到容器內(nèi)。
artifacts
artifacts是job生成的中間產(chǎn)物,會以壓縮包(.zip)的形式生成。它會自動上傳到gitlab服務(wù)器,the artifacts will be downloaded and extracted in the context of later stages。所以它和cache很像,但是設(shè)計它們的初衷是不同的。
Don't use caching for passing artifacts between stages, as it is designed to store runtime dependencies needed to compile the project:
-
cache: For storing project dependenciesCaches are used to speed up runs of a given job in subsequent pipelines, by storing downloaded dependencies so that they don't have to be fetched from the internet again (like npm packages, Go vendor packages, etc.) While the cache could be configured to pass intermediate build results between stages, this should be done with artifacts instead.
-
artifacts: Use for stage results that will be passed between stages.Artifacts are files generated by a job which are stored and uploaded, and can then be fetched and used by jobs in later stages of the same pipeline. In other words, you can't create an artifact in job-A in stage-1, and then use this artifact in job-B in stage-1. This data will not be available in different pipelines, but is available to be downloaded from the UI.
The name artifacts sounds like it's only useful outside of the job, like for downloading a final image, but artifacts are also available in later stages within a pipeline.
另外,同樣key的cache會被覆蓋,而artifacts一旦生成就固定了,當(dāng)然我們可以設(shè)置expire_in,過期刪除之。
實戰(zhàn)
我們定義一個最簡單的pipline:第一步編譯生成jar包,第二步將jar包導(dǎo)入docker鏡像并運行,在某些環(huán)節(jié)還需加入代碼review。因為最后我們會以docker容器運行jar包,所以這里不建議docker-based runner/executor的形式,因為該形式導(dǎo)致Docker-in-Docker的場景,帶來可能的一些麻煩且難以解決的問題(比如內(nèi)嵌容器如何關(guān)聯(lián)外部服務(wù)以及對外提供服務(wù))。所以我們直接宿主機安裝GitLab Runner。
如果一定要以docker-based形式,那么可參看使用GitLab CI和Docker自動部署SpringBoot應(yīng)用。在該文中,并沒有在runner所在宿主機中運行容器,而是將生成的鏡像發(fā)布到鏡像倉庫,再登錄目標(biāo)服務(wù)器拉取鏡像運行,所以不存在Docker-in-Docker的麻煩事。
安裝&注冊[GitLab ]Runner
# 1.Add the official GitLab repository
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
# 2.Install the latest version of GitLab Runner
export GITLAB_RUNNER_DISABLE_SKEL=true; sudo -E yum install gitlab-runner
注冊runner
sudo gitlab-runner register -n \
--url http://192.168.1.26:9980/ \
--registration-token cJMXGJWx7qx9AmpSc6ee \
--executor shell \
--tag-list "inkscreen_hostrunner" \
--description "Host Runner for InkScreen"
Add the gitlab-runner user to the docker group:
sudo usermod -aG docker gitlab-runner
.gitlab-ci.yml
stages:
- build jar
- build and run image
#job's name 可以隨意取
buildJar:
stage: build jar
variables:
# 默認(rèn)是clone,改為fetch加快拉取速度(若本地?zé)o則會自動clone)
GIT_STRATEGY: fetch
only:
- dev
script:
- >
docker run -d --rm --name justforpackage-$CI_COMMIT_REF_NAME
-v "$(pwd)":/build/inkscreen
-v /inkscreen/maven/m2:/root/.m2
-w /build/inkscreen
maven:3-jdk-8 mvn clean package
- sleep 60
tags:
- inkscreen_hostrunner
artifacts:
paths:
- louwen-admin/target/louwen-admin.jar
expire_in: 3600 seconds
testDeploy:
stage: build and run image
only:
- dev
variables:
# 不拉取代碼
GIT_STRATEGY: none
IMAGE_NAME: louwen/inkscreen-api:$CI_COMMIT_REF_NAME
PORT: 38082
before_script:
# 移除舊容器和鏡像。這里為什么要寫成一行,下面有講
- if [ docker ps | grep inkscreen-$CI_COMMIT_REF_NAME ]; then docker stop inkscreen-$CI_COMMIT_REF_NAME; docker rm inkscreen-$CI_COMMIT_REF_NAME; docker rmi $IMAGE_NAME; fi
script:
- docker build --build-arg JAR_PATH=louwen-admin/target/louwen-admin.jar -t $IMAGE_NAME .
- >
docker run -d --name inkscreen-$CI_COMMIT_REF_NAME
-p $PORT:$PORT
--network my_bridge --env spring.redis.host=myredis
-v /inkscreen/inkscreen-api/logs/:/logs/
-v /inkscreen/inkscreen-api/louwen-admin/src/main/resources/:/configs/
$IMAGE_NAME
tags:
- inkscreen_hostrunner
注意build jar環(huán)節(jié)我們sleep了60秒,是因為docker run并不會等待內(nèi)部腳本執(zhí)行完,而是啟動后就直接返回了,此時jar包尚未生成,所以此處阻塞一段時間等待打包結(jié)束。正常應(yīng)該寫一段腳本循環(huán)判斷jar包是否已生成,若生成或超時則跳出循環(huán),此處作為演示簡單sleep。
在testDeploy任務(wù)中,before_script被我寫成了一行,最初版本是:
before_script:
# 若未找到記錄,則該條命令會返回1,gitlab就直接報錯返回了ERROR: Job failed: exit status 1
- docker ps | grep inkscreen-$CI_COMMIT_REF_NAME
- >
if [ $? -eq 0 ]
then
docker stop inkscreen-$CI_COMMIT_REF_NAME
docker rm inkscreen-$CI_COMMIT_REF_NAME
docker rmi $IMAGE_NAME
fi
后改為
before_script:
# 將檢測語句直接作為條件內(nèi)置,解決了上面的問題
- >
if
docker ps | grep inkscreen-$CI_COMMIT_REF_NAME
then
docker stop inkscreen-$CI_COMMIT_REF_NAME
docker rm inkscreen-$CI_COMMIT_REF_NAME
docker rmi $IMAGE_NAME
fi
報錯syntax error near unexpected token 'fi',估計是換行/回車格式的原因。上述兩個問題都可以通過單獨建.sh文件的方式解決,我這里簡單地將所有語句排成一行。
Dockerfile
生成鏡像自然少不了Dockerfile
FROM openjdk:8-jdk-oracle
MAINTAINER louwen
# 外部傳入,主程序路徑
ARG JAR_PATH
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
COPY $JAR_PATH /app.jar
EXPOSE 38082
ENTRYPOINT ["java","-jar","/app.jar"]
題外話,其實我們完全可以將build jar環(huán)節(jié)也放在Dockerfile中,如下
#
# build jar stage
#
FROM maven:3-jdk-8 AS MAVEN_BUILD
COPY pom.xml /build/
COPY src /build/src/
WORKDIR /build/
RUN mvn clean package
#
FROM openjdk:8-jdk-oracle
MAINTAINER louwen
COPY --from=MAVEN_BUILD /build/target/*.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
代碼規(guī)范
目前較流行的代碼檢測工具是SonarQube,不過其社區(qū)版本對同一個代碼倉庫無法區(qū)分不同分支,從而實現(xiàn)按代碼的不同分支顯示對應(yīng)分支的掃描結(jié)果。這里我們使用Gitlab-CI的Code Quality stage,其使用的是Codeclimate,它是為代碼質(zhì)量分析平臺提供的一個命令行接口工具,通過它可以在本機 Docker 容器中對要分析的代碼執(zhí)行質(zhì)量分析,并生成分析報告。我們熟知常用的代碼質(zhì)量檢測工具例如 SonarQube、CheckStyle 等等,而 Codeclimate 接入了這些工具,而且支持我們自定義檢測工具。
按照官方說法,使用Code Quality需要基于docker-based runner/executor,所以我們另外使用docker方式安裝GitLab-Runner并注冊一個runner(參考上述概念小節(jié)),executor選擇docker。
include:
- template: Code-Quality.gitlab-ci.yml
# 以下配置參考網(wǎng)上一些資料,據(jù)說是官方示例,然而我沒有在官方文檔找到
code_quality:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
# gitlab 13.6及之后版本支持
REPORT_FORMAT: html
allow_failure: true
services:
- docker:dind
script:
# 鏡像版本號格式參看 https://gitlab.com/gitlab-org/ci-cd/codequality/-/tree/master#versioning-and-release-cycle
# - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--net=host
--env SOURCE_CODE="$PWD"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:${VERSION:-latest}" /code
artifacts:
paths: [ gl-code-quality-report.html ]
tags:
- InkScreenAPI
執(zhí)行的時候可能會卡在拉取鏡像環(huán)節(jié)。手動docker pull registry.gitlab.com/gitlab-org/security-products/codequality:latest,發(fā)現(xiàn)各種超時。我開個阿里云香港ECS的搶占式實例(便宜)然后docker pull | save | load將鏡像文件遷移到公司測試服,還是報Unable to find image 'registry.gitlab.com/gitlab-org/security-products/codequality:latest' locally,不知如何將host中的鏡像映射到docker:stable中。看來還是得kexue上網(wǎng)。
理論上,需要專人在合適的時候?qū)μ峤坏拇a進行質(zhì)量把關(guān),一般這工作可以放在Merge Request下進行。Merge Request的工作流程可以參看在團隊中使用GitLab中的Merge Request工作模式
FAQ
dial tcp: lookup docker on 192.168.1.1:53: no such host錯誤。
This error occurs with docker-based gitlab runners such as the one we’re that are configured using a docker executor. The error message means that the inner docker container doesn’t have a connection to the host docker daemon.
解決:將/etc/gitlab-runner/config.toml中對應(yīng)的[runners.docker]節(jié)點設(shè)置privileged = true,增加卷映射volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"][或|并]在.gitlab-ci.yml的job定義中增加services: - docker:dind。(可能需要重啟runnerdocker restart gitlab-runner)
表述不準(zhǔn)確,參看gitlab-runner 中的 Docker-in-Docker
-
Cannot connect to the Docker daemon at tcp://docker:2375. Is the docker daemon running?錯誤
解決:參看gitlab-runner 中的 Docker-in-Docker -
拉取代碼時提示
warning: failed to remove xxxx: Permission denied
簡單粗暴地編輯/etc/passwd,將gitlab-runner賬號對應(yīng)的uid:gid改為0:0(和root一樣)。 -
Code Quality提示
docker: Error response from daemon: Head https://registry.gitlab.com/v2/gitlab-org/security-products/codequality/manifests/13-7-stable: Get https://gitlab.com/jwt/auth?scope=repository%3Agitlab-org%2Fsecurity-products%2Fcodequality%3Apull&service=container_registry: dial tcp [2606:4700:90:0:f22e:fbec:5bed:a9b9]:443: connect: cannot assign requested address.
在scripts->docker run增加參數(shù)--net=host
其它
發(fā)件郵箱配置
在pipline流程執(zhí)行過程中,我們希望有任何風(fēng)吹草動都能及時收到消息,郵件就是一個比較好的提醒方式。
vi /etc/gitlab/gitlab.rb
### GitLab email server settings
###! Docs: https://docs.gitlab.com/omnibus/settings/smtp.html
###! **Use smtp instead of sendmail/postfix.**
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.exmail.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "xxxx@yyyy.com"
gitlab_rails['smtp_password'] = "xxxxxxxx"
gitlab_rails['smtp_domain'] = "exmail.qq.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
### Email Settings
gitlab_rails['gitlab_email_enabled'] = true
##! If your SMTP server does not like the default 'From: gitlab@gitlab.example.com'
##! can change the 'From' with this setting.
##! 要與上面的 smtp_user_name 保持一致
gitlab_rails['gitlab_email_from'] = 'xxxx@yyyy.com'
# gitlab_rails['gitlab_email_display_name'] = 'Example'
# gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'
# gitlab_rails['gitlab_email_subject_suffix'] = ''
# gitlab_rails['gitlab_email_smime_enabled'] = false
# gitlab_rails['gitlab_email_smime_key_file'] = '/etc/gitlab/ssl/gitlab_smime.key'
# gitlab_rails['gitlab_email_smime_cert_file'] = '/etc/gitlab/ssl/gitlab_smime.crt'
# gitlab_rails['gitlab_email_smime_ca_certs_file'] = '/etc/gitlab/ssl/gitlab_smime_cas.crt'
gitlab-ctl reconfigure使配置生效
測試
gitlab-rails console
irb(main):003:0> Notify.test_email('whatever@qq.com', 'Message Subject', 'Message Body').deliver_now
登錄whatever@qq.com查看受否收到信件。
jenkins + gitlab
如果使用jenkins作為CI/CD工具,代碼由gitlab托管,那么它們之間的交互需要兩個token:
- api token,用于jenkins調(diào)用gitlab api使用
- ssh密鑰對,jenkins拉取代碼使用(當(dāng)然我們也可以使用用戶名/密碼方式拉取)
mvn package、install、deploy都干了什么
- mvn clean package依次執(zhí)行了clean、resources、compile、testResources、testCompile、test、jar(打包)等7個階段。
- mvn clean install依次執(zhí)行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install等8個階段。
- mvn clean deploy依次執(zhí)行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install、deploy等9個階段。
由上可知:
- package命令完成了項目編譯、單元測試、打包功能,但沒有把打好的可執(zhí)行jar包(war包或其它形式的包)布署到本地maven倉庫和遠(yuǎn)程maven私服倉庫
- install命令完成了項目編譯、單元測試、打包功能,同時把打好的可執(zhí)行jar包(war包或其它形式的包)布署到本地maven倉庫,但沒有布署到遠(yuǎn)程maven私服倉庫
- deploy命令完成了項目編譯、單元測試、打包功能,同時把打好的可執(zhí)行jar包(war包或其它形式的包)布署到本地maven倉庫和遠(yuǎn)程maven私服倉庫
alpine
Alpine Linux 是一個社區(qū)開發(fā)的面向安全應(yīng)用的輕量級Linux發(fā)行版。很多鏡像都會專門基于Alpine構(gòu)建,大小會小很多。比如:
- gitlab/gitlab-runner:latest based on Ubuntu.
- gitlab/gitlab-runner:alpine based on Alpine with much a smaller footprint (~160/350 MB Ubuntu vs ~45/130 MB Alpine compressed/decompressed).
修改GitLab-ce域名
剛部署好的GitLab新建的項目ssh地址一般是個短鏈接如git@AKDJF3ld:xxx,有時候會不太好使,可以通過配置文件的修改,指向域名。參看Docker部署GitLab并實現(xiàn)基本配置。還有一種方式是修改容器內(nèi)的/var/opt/gitlab/gitlab-rails/etc/gitlab.yml,特別是要修改端口的時候。
vim /opt/gitlab/embedded/service/gitlab-rails/config/gitlab.yml
# host: 192.168.xx.xx
# port: xxxx
# 進入容器內(nèi),不要在外部docker restart 或者 容器內(nèi) gitlab-ctl reconfigure,否則配置又還原回去了
gitlab-ctl restart
參考資料
當(dāng)談到 GitLab CI 的時候,我們該聊些什么(上篇)
什么是devops,基于Gitlab從零開始搭建自己的持續(xù)集成流水線(Pipeline)
持續(xù)集成之.gitlab-ci.yml篇
Building Docker images with GitLab CI/CD
自動化 DevOps 使用 Codeclimate 執(zhí)行代碼質(zhì)量分析
GitLab CI/CD
基于 Gitlab 的 Code Review 最佳實踐

浙公網(wǎng)安備 33010602011771號