精通 GitLab CI:打造高效的持续集成环境
GitLab CI
GitLab CI(Continuous Integration)是GitLab提供的持续集成和持续交付(CI/CD)平台。它允许开发团队自动化地构建、测试和发布软件,以减少手动操作和加快交付速度。
下面是GitLab CI的一些主要特点和功能:
-
集成版本控制:GitLab CI与GitLab代码仓库紧密集成,可以直接从仓库中触发CI流水线,并与代码变更进行关联。
-
基于流程的管道:通过在.gitlab-ci.yml文件中定义流水线的各个阶段和任务,可以创建复杂的CI/CD流程。这些流程可以包括构建、测试、静态代码分析、部署等。
-
多个执行器:GitLab CI支持多种执行器,包括Shell、SSH、Docker、Kubernetes等,以满足不同的环境需求。
-
并行构建:可以同时运行多个作业,加快构建和测试的速度。
-
集成测试:可以方便地集成各种测试框架,如单元测试、端到端测试、性能测试等。
-
自动化部署:可以将构建好的应用程序或容器直接部署到目标环境,实现自动化的部署过程。
-
实时日志和报告:在CI/CD过程中,实时监视和查看作业的日志和报告,方便排查问题和分析结果。
-
可扩展性:GitLab CI具有灵活的插件系统,可以根据需要添加各种自定义插件和扩展功能。
通过使用GitLab CI,团队可以更快地构建和测试代码,并实现自动化的软件交付流程。这有助于提高代码质量、减少错误和缺陷,并加速产品的发布周期。
咱们分为以下几步学习并实现一个GitLab
工作流
- 第一步:了解并创建
Runner
- 第二步:了解
.gitlab-ci.yml
中常用字段api
- 第四步:使用内置环境变量和自己项目设置默认变量
- 第三步:实战化项目持续集成
第一步 了解创建 Runner
GitLab CI Runner是GitLab CI/CD系统的一部分,它起着将CI/CD流水线中定义的任务和作业实际执行的作用。具体来说,GitLab CI Runner有以下几个重要的作用:
-
执行构建和测试任务:在GitLab CI/CD流程中,Runner负责运行定义的作业,例如代码编译、单元测试、集成测试等。Runner会根据配置从GitLab项目中拉取代码,并在指定的环境中执行这些任务。
-
提供多种执行环境:GitLab CI Runner支持不同类型的执行器,包括Shell、Docker、Kubernetes等。这意味着您可以根据需要选择合适的执行环境来执行任务,以确保环境一致性并满足特定的依赖需求。
-
并发执行和扩展性:GitLab CI Runner支持并发执行多个作业,可以有效地利用计算资源。您可以在需要时增加更多的Runner实例来扩展并发性和处理能力,以应对大规模的构建和测试需求。
-
日志和报告生成:Runner会生成详细的执行日志,记录每个作业的输出和结果。这些日志非常有用,可以帮助您排查问题、分析构建过程,并及时获得失败或错误的报告。这些日志和报告也可以在GitLab界面上可视化显示。
-
集成第三方工具和服务:GitLab CI Runner可以与各种其他工具和服务集成,例如代码质量检测工具、部署工具、通知服务等。这使得您可以通过Runner来自动化整个CI/CD流程,并将结果集成到其他系统中。
GitLab CI Runner的作用是将定义在GitLab CI/CD配置文件中的任务实际执行起来。
创建GitLab Runner
GitLab
是不带Runner
的需要自己去定义生成自己的Runner
。友情提示,Gitlab
的安装尽量不要和Gitlab Runner
程序在一台服务器上,另外Gitlab Runner
程序比较占用系统CPU。
- 下载对应的
Gitlab Runner
版本:
# Linux x86-64
wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# Linux x86
wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386
# Linux arm
wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm
- 赋予它执行的权限(Give it permissions to execute):
sudo chmod +x /usr/local/bin/gitlab-runner
- 创建一个GitLab CI用户(Create a GitLab CI user):
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
- 作为服务安装和运行(Install and run as service):
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
第二步:.gitlab-ci.yml
中常用关键字
在你的项目根目录创建一个
.gitlab-ci.yml
文件。它是一个YAML
文件,您可以在其中指定GitLab CI/CD
的指令。内容如下:
在CI
文件中,我们可以对一系列任务做定义,每个任务至少需要包含一条可执行的语句,在 script
定义 Runner
环境可执行的所有语句,每一个任务都会按照定义顺序执行。
# 定义 stages
stages:
- build
- test
# 定义build job
build-job:
stage: build
script:
- echo "Hello, $GITLAB_USER_LOGIN!"
# 定义test job
test-job1:
stage: test
script:
- echo "This job tests something"
stages
定义构建任务
stages
关键字用于定义构建场景,可以被构建任务引用定义自己的构建场景,stages定义的顺序也决定了任务执行的顺序,下面给出一个基本的场景示例
# 定义 stages
stages:
- test
- build
- publish
- 首先所有任务的构建场景为 test 的任务全部并行执行
- 接着会执行构建场景为 build 的所有任务
- 最后执行构建场景为 publish 的所有任务
- 其中任何一个任务失败,整个流水线会失败,并且之后的任务不会执行
stages
可以不被定义,那么程序会默认为 test build deploy
三个场景,如果一个任务没有指定自己的 stage
,那么它将默认使用 test
Jobs
Jobs
表示构建工作,表示某个Stage
里面执行的工作。 我们可以在Stages
里面定义多个Jobs
,这些Jobs
会有以下特点:
- 相同
Stage
中的Jobs
会并行执行; - 相同
Stage
中的Jobs
都执行成功时,该Stage
才会成功; - 如果任何一个
Job
失败,那么该Stage
失败,即该构建任务 (Pipeline) 失败
所以,Jobs 和 Stage 的关系图就是:
+------------------------------------------+
| |
| Stage 1 |
| |
| +---------+ +---------+ +---------+ |
| | Job 1 | | Job 2 | | Job 3 | |
| +---------+ +---------+ +---------+ |
| |
+------------------------------------------+
script
用来定义一个任务中需要执行的shell脚本
script:
- npm install
- npm run lint
- npm run build
before_script
定义任何 Jobs 运行前都会执行的命令。
before_script:
- npm install
- echo 'Install successful'
after_script
定义任何 Jobs 运行完后都会执行的命令。
after_script:
- xxxxxx # 通知所有用户完成构建
variables && Job.variables
定义环境变量。 如果定义了 Job 级别的环境变量的话,该 Job 会优先使用 Job 级别的环境变量。
variables:
BUILD_SUCCESS: '打包执行成功'
SUCCESS: 'job执行完成'
# 定义build job
build-job:
variables:
TEST: '测试'
stage: build
script:
- echo "Hello, $GITLAB_USER_LOGIN!"
- echo ${BUILD_SUCCESS}
- echo ${SUCCESS}
- echo ${TEST}
image
image
关键字允许当前CI
程序基于某个Docker
镜像中运行,当然前提是你的任务指定的tags
必须是以Docker
形式注册的Github Runner
,CI
程序会默认在你本地寻找镜像,如果不存在的话,则会从Docker Hub
拉取。
variables:
BUILD_SUCCESS: '打包执行成功'
SUCCESS: 'job执行完成'
# 定义build job
build-job:
# 以下面这个镜像运行script
image: node:2.8.1
variables:
TEST: '测试'
stage: build
script:
- echo "Hello, $GITLAB_USER_LOGIN!"
- echo ${BUILD_SUCCESS}
- echo ${SUCCESS}
- echo ${TEST}
only && except
这两个关键字定义任务什么时候将会被创建
only
定义了任务需要执行的所在分支或者标签;except
定义了任务不会执行的所在分支或者标签;
variables:
BUILD_SUCCESS: '打包执行成功'
SUCCESS: 'job执行完成'
# 定义build job
build-job:
# 以下面这个镜像运行script
image: node:2.8.1
variables:
TEST: '测试'
stage: build
script:
- echo "Hello, $GITLAB_USER_LOGIN!"
- echo ${BUILD_SUCCESS}
- echo ${SUCCESS}
- echo ${TEST}
only:
# 可以指定多个再mester、dev、或者打tag 都会运行该CI工作流
- master
- dev
- tags
# only: 或者可以这么写判断 校验尼的tag符合正则的才会触发
# variables:
# - $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+(-rc(\..+)?)?$/
except:
- feature/**
两者如果都存在在一个任务声明中,则所需引用将会使用两者交集过滤。 两者均允许使用正则。
tags
指定当前任务适用的 runners, 必须是已经注册过的runner才能指定
variables:
BUILD_SUCCESS: '打包执行成功'
SUCCESS: 'job执行完成'
# 定义build job
build-job:
# 以下面这个镜像运行script
image: node:2.8.1
variables:
TEST: '测试'
stage: build
script:
- echo "Hello, $GITLAB_USER_LOGIN!"
- echo ${BUILD_SUCCESS}
- echo ${SUCCESS}
- echo ${TEST}
only:
# 可以指定多个再mester、dev、或者打tag 都会运行该CI工作流
- master
- dev
- tags
except:
- feature/**
tags:
- ci-runnner
artifacts
定义
Job
中生成的附件。 当该Job
运行成功后,生成的文件可以作为附件 (如生成的二进制文件) 保留下来,打包发送到GitLab
,之后我们可以在GitLab
的项目页面下下载该附件。
variables:
BUILD_SUCCESS: '打包执行成功'
SUCCESS: 'job执行完成'
FILE_NAME: 'dist.zip'
# 定义build job
build-job:
# 以下面这个镜像运行script
image: node:2.8.1
variables:
TEST: '测试'
stage: build
script:
- npm run build
- cd dist/ && zip -r dist.zip ./* && mv dist.zip ../ && cd -
- echo "Hello, $GITLAB_USER_LOGIN!"
- echo ${BUILD_SUCCESS}
- echo ${SUCCESS}
- echo ${TEST}
only:
# 可以指定多个再mester、dev、或者打tag 都会运行该CI工作流
- master
- dev
- tags
except:
- feature/**
artifacts:
paths:
- ${FILE_NAME}
tags:
- ci-runnner
cache && Job.cache
定义需要缓存的文件。 每个
Job
开始的时候,Runner
都会删掉.gitignore
里面的文件。 如果有些文件(如 node_modules/)
需要多个Jobs
共用的话,我们只能让每个Job
都先执行一遍npm install
。 这样很不方便,因此我们需要对这些文件进行缓存。缓存了的文件除了可以跨Jobs
使用外,还可以跨Pipeline
使用。
stages:
- build
cache:
paths:
- node_modules/
job1:
stage: build
script:
- npm install
- npm run build
job2:
stage: build
script:
- npm install
- npm run test
allow_failure
allow_failure
关键字用于标记一个Job
(作业)是否允许失败。当设置为true
时,如果该Job
失败了,整个流水线(pipeline)的状态仍将被标记为成功(success),而不会影响后续的Jobs
。
stages:
- test
job1:
stage: test
script:
- run_tests.sh
job2:
stage: test
script:
- run_integration_tests.sh
allow_failure: true
dependencies
dependencies
关键字用于定义作业(Job
)之间的依赖关系。它指定了某个作业必须在哪些其他作业完成后才能执行。
variables:
FILE_NAME: dist.zip
stages:
- build
- deploy
build:
stage: build
only:
- tags
script:
- yarn && yarn build
- cd dist/ && zip -r dist.zip ./* && mv dist.zip ../ && cd -
- ls
artifacts:
# expire_in 产物保留多少天
expire_in: 30 days
paths:
- ${FILE_NAME}
tags:
- runner
deploy:
stage: deploy
script:
# - 部署相关命令
- echo "project done"
only:
- tags
# dependencies build 留下来的dist产物
dependencies:
- build
tags:
- runner
以上就是经常使用的一些关键字了,如果不能满足使用可以去官网去查看: 地址
第三步:内置环境变量和项目自定义环境变量
GitLab CI
中有一些内置的环境变量简单说几个如下:
- CI_PROJECT_NAME(当前正在构建的项目名称)
- CI_COMMIT_TAG (commit 的 tag 名称,任务对应的 tag)
- CI_COMMIT_REF_NAME(用于构建项目的分支或Git tag名称)
- CI_REPOSITORY_URL (Git 仓库地址)
- GITLAB_USER_EMAIL (触发CI的用户邮箱)
你也可以使用 export 关键字打印出所有的变量,需要注意的是,该关键字也会打印出任务中定义的所有私有变量
job:
script:
- export
内置变量打印的例子如下:
export CI_JOB_ID = '50'
export CI_COMMIT_SHA = 'XXXX'
export CI_COMMIT_SHORT_SHA = 'XXX'
export CI_COMMIT_REF_NAME = 'master'
export CI_REPOSITORY_URL = 'XXXX'
export CI_COMMIT_TAG = '1.0.0'
export CI_JOB_NAME = 'XXX'
export CI_JOB_STAGE = 'XX'
export CI_JOB_MANUAL = 'XX'
export CI_JOB_TRIGGERED = 'XX'
export CI_JOB_TOKEN = 'XXX'
export CI_PIPELINE_ID = 'XX'
export CI_PIPELINE_IID = 'XX'
export CI_PAGES_DOMAIN = 'XX'
export CI_PAGES_URL = 'XX'
export CI_PROJECT_ID = 'XX'
export CI_PROJECT_DIR = 'XX'
export CI_PROJECT_NAME = 'XX'
export CI_PROJECT_TITLE = 'XX'
export CI_PROJECT_NAMESPACE = 'XX'
export CI_PROJECT_PATH = 'XX'
export CI_PROJECT_URL = 'XX'
export CI_REGISTRY = 'XX'
export CI_REGISTRY_IMAGE = 'XX'
export CI_REGISTRY_USER = 'XX'
export CI_REGISTRY_PASSWORD = 'XX'
export CI_RUNNER_ID = 'XX'
export CI_RUNNER_DESCRIPTION = 'XX'
export CI_RUNNER_TAGS = 'docker, linux'
export CI_SERVER = 'yes'
export CI_SERVER_URL = 'https://example.com'
export CI_SERVER_HOST = 'example.com'
export CI_SERVER_PORT = '443'
export CI_SERVER_PROTOCOL = 'https'
export CI_SERVER_NAME = 'GitLab'
export CI_SERVER_REVISION = '70606bf'
export CI_SERVER_VERSION = '8.9.0'
export CI_SERVER_VERSION_MAJOR = '8'
export CI_SERVER_VERSION_MINOR = '9'
export CI_SERVER_VERSION_PATCH = '0'
export GITLAB_USER_EMAIL = 'CC'
export GITLAB_USER_ID = '42'
除了使用内置环境变量、使用variables
关键字定义的变量外,还可以再当前项目创建变量如下:
创建后也可以直接使用如下:
variables:
FILE_NAME: dist.zip
stages:
- build
build:
stage: build
script:
- echo ${FILE_NAME} # variables 定义的变量
- echo ${CI_PROJECT_NAME} # 内置变量
- echo ${TEST_KEY} # 在项目下面自定义的变量
tags:
- runner
还可以再GitLab
一个目录分组下创建该目录下的公共环境变量,公共环境变量可以再当前分组下面的项目都可以直接使用,用法跟再项目下面自定义变量一样。
第四步:实战化
GitLab CI
的目的是定义一些工作流,自动化地构建、测试和发布软件,以减少手动操作和加快交付速度。再不用工作流的时候大家都怎么交付的呢?
-
本地
yarn build
,然后把打包出来的dist
交给后端同学部署,又或者自己部署到环境上。 -
本地
yarn build
,然后把打包出来的dist
提交到后端仓库指定的静态资源目录下面,然后由后端整体打包构建服务。 -
使用
jenkins
帮你打包部署,执行定义好的工作流。
咱们先说说3的问题为何使用GitLab CI
代替jenkins
的优势:
- 每次构建任务需要去
jenkins
点一下。 - 使用
GitLab
的webhooks
监听push
等动作来触发jenkins
执行的(这个先不在这里细说了),多了一步。 - 无法使用
Gitlab
的内置的环境变量。
后面我们就把重心都切到了GitLab CI
上面了,那么咱们回到正题如何使用GitLab CI
工作流分别帮咱们使用1,2
呢?
第一种: 直接把静态资源部署到开发、生产服务器上
同步本地和目标服务器的插件咱们使用
rsync
、ansible-playbook
、sshpass
等来实现用脚本登录服务器操作静态资源文件
如果本机或者远程计算机没有安装 rsync,可以用下面的命令安装。注意,传输的双方都必须安装 rsync。
# Debian
$ sudo apt-get install rsync
# Red Hat
$ sudo yum install rsync
# Arch Linux
$ sudo pacman -S rsync
使用方法看阮一峰rsync 用法教程
variables:
FILE_NAME: dist.zip
stages:
- build
build:
stage: build
script:
- yarn && yarn build
- cd dist/ && zip -r dist.zip ./* && mv dist.zip ../ && cd -
- ls
artifacts:
# expire_in 产物保留多少天
expire_in: 30 days
paths:
- ${FILE_NAME}
tags:
- runner
deploy:
stage: deploy
script:
- sudo rsync
- echo "project done"
only:
- tags
dependencies:
- build
tags:
- runner
第二种: 交到后端仓库指定的静态资源目录
定义工作流直接把代码打包,然后把
dist
文件夹上传到后端工程里面(后端git仓库)。如下:
variables:
USER_NAME: '你的用户名(需要跟你git的保持一致)'
USER_EMAIL: '你得邮箱(需要跟你git的保持一致)'
BRANCH: ${CI_COMMIT_REF_NAME}
BUILD_SUCCESS: '打包执行成功'
SUCCESS: 'job执行完成'
stages:
- prod_deploy
prod_deploy:
stage: prod_deploy
script:
# 打包-配置
- npm -v
- yarn && yarn build
- ls
- echo ${BUILD_SUCCESS}
# - ssh-Key-配置
- mkdir ~/.ssh -p
- chmod 700 ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- eval $(ssh-agent -s)
# # PRIVATE_SSH_KEYE 在该仓库中定义的变量
- echo ${PRIVATE_SSH_KEYE};
- ssh-add <(echo "$PRIVATE_SSH_KEYE")
- git config --global user.name ${USER_NAME};
- git config --global user.email ${USER_EMAIL};
- echo ${BRANCH};
# 推送仓库配置
- ls
- cd .ci && yarn
# node 提交git 脚本再下面文件中
- node index
- echo ${SUCCESS}
only:
- master
#dependencies: 表示依赖关系的
artifacts:
paths:
- ${FILE_NAME}
tags:
- runner
${PRIVATE_SSH_KEYE}
是你本地的ssh私钥,放在了自定义变量里面。以下命令获取私钥:
cd ~/.ssh
cat id_rsa
大概node脚本就是如下流程:
- 拉取后端仓库切换到要提交的分支;
- 删除原本的前端静态资源;
- 复制新打包出来的资源到后端仓库里面;
- git提交;
- 删除拉下来的后端仓库;
// node inde
const chalk = require('chalk');
const shell = require('shelljs');
const { log } = console;
const FULL_NAME = '你的打包后的文件名称';
const GIT_PATH = 'clone的后端的ssh地址';
const GIT_BRANCH = '后端仓库要提交的分子';
const PROJECT_AS_NAME = 'project-clone';
function deploy() {
// 上传git
shell.rm('-rf', `${PROJECT_AS_NAME}/`);
shell.exec('git config user.name');
shell.exec(`git clone ${GIT_PATH} -b ${GIT_BRANCH} ${PROJECT_AS_NAME}`);
shell.exec('ls');
shell.cd(PROJECT_AS_NAME);
shell.rm('-rf', `${FULL_NAME}/`);
shell.rm('-rf', `${FULL_NAME}/`);
shell.cp('-R', `../../${FULL_NAME}`, `${FULL_NAME}`);
shell.exec('pwd');
shell.exec('git add .');
shell.exec('git status');
log(chalk.blue('git add 成功!'));
//这里是运行的sh 文件 放下面了
shell.exec('sh ../command.sh');
log(chalk.blue('git commit 成功!'));
shell.exec('git push');
shell.cd('..');
shell.exec('pwd');
shell.rm('-rf', `${PROJECT_AS_NAME}/`);
shell.rm('-rf', `${FULL_NAME}`);
log(chalk.blue('git 推送成功!'));
}
deploy();
由于没办法使用node
脚本提交git commit
把该指令放在了sh
文件中如下:
#!/bin/bash
git commit -m $'feat:前端更新'
echo 'commit success'
以上只是适合我的工作中的应用场景,可以参考下定制属于自己的工作流。其实还有些其他常见的流程比如:再项目推送完成后可以向项目组上的人发送邮件,通知他们部署成功,又或者可以调用
钉钉
的webhooks
等达到一个通知的功能。
总结
以上就是
GitLab CI
的知识点了,如果对你有帮助,欢迎点赞收藏。
参考链接:
转载自:https://juejin.cn/post/7250386282400628797