theme: juejin
前言
作为一名前端开发人员,除了掌握基本三大主流前端框架,如果业余时间能够再学习一点运维知识,能够自己搭建一套完成的前端自动化部署流程,对个人或者对公司都是一个不小的进步和收获. 本人玩过3年的jenkins
自动化部署, 目前公司在使用gitlab
提供的CI/CD
, 下面慢慢带你了解CICD
GitLab
是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的web服务。也是大家最最常用的代码私有管理平台, 促进了整个软件开发的发展的工具. GitLab
除了代码管理的功能以外, 还为可爱程序猿朋友们提供了 CI/CD
持续交付能力, 方便日常开发中频繁部署的痛苦.
CI/CD
持续交付能够让我们的开发、测试、部署流程更加规范化,规避很多风险、减少测试、部署成本.
接下来我通过搭建一个前端持续部署的例子,带大家一起了解一下GitLab
的 CI/CD
流程.
一、DevOps
DevOps
一词的来自于 Development
和 Operations
的组合,突出重视软件开发人员和运维人员的沟通合作,通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠。DevOps
其实包含了三个部分:开发、测试和运维。换句话 DevOps
希望做到的是软件产品交付过程中IT工具链的打通,使得各个团队减少时间损耗,更加高效地协同工作。
DevOps
强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件
如果从字面上来理解,DevOps
只是Dev
(开发人员)+Ops
(运维人员),实际上,它是一组过程、方法与系统的统称,其概念从2009 年首次提出发展到现在,内容非常丰富,有理论也有实践,包括组织文化、自动化、精益、反馈和分享等不同方面。
二、什么是CI/CD
CI/CD
是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD
的核心概念是持续集成
、持续交付
和持续部署
。作为一个面向开发和运营团队的解决方案,CI/CD
主要针对在集成新代码时所引发的问题。
具体而言,CI/CD
可让持续自动化和持续监控贯穿于应用的整个生命周期(从集成和测试阶段,到交付和部署)。这些关联的事务通常被统称为“CI/CD
管道”,由开发和运维团队以敏捷方式协同支持。
持续集成CI
(Continuous integration),它属于开发人员的自动化流程。成功的 CI
意味着应用代码的新更改会定期构建、测试并合并到共享存储库中。该解决方案可以解决在一次开发中有太多应用分支,从而导致相互冲突的问题
持续部署CD
(CD-continuous deployment): 是基于某种工具或平台实现代码自动化的构建
、测试
和部署
到线上环境以实现交付高质量的产品,持续部署在某种程度上代表了一个开发团队的更新迭代速率。
持续交付CD
(Continuous Delivery): 持续交付
是在持续部署的基础之上,将产品交付到线上环境,因此持续交付是产品价值的一种交付,是产品价值的一种盈利的实现。
三、为什么要做持续集成
-
[x] 持续集成服务(Continuous Integration,简称 CI)
-
[x] 它绑定
Gitlab
上面的项目,只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,完成构建,还能部署到服务器。 -
[x] 持续集成指的是只要代码有变更,就自动运行构建和测试,反馈运行结果。确保符合预期以后,再将新代码”集成”到主干。
-
[x] 持续集成的好处在于,每次代码的小幅变更,就能看到运行结果,从而不断累积小的变更,而不是在开发周期结束时,一下子合并一大块代码。
-
[x]
CI
流程在每次团队成员push/merge
后之后自动触发部署 -
[x] 提高前端的开发效率和开发测试之间的协调效率
-
[x] 从更细的粒度把握代码质量
四、gitlab 的CI/CD
而 GitLab CI / CD
是GitLab
内置的工具,用于通过连续方法进行软件开发,主要包含了: 持续集成(CI)
、连续交付(CD)
、持续部署(CD)
功能, 如果你还没有使用过gitlab
,请看我之前的文章: docker 中 gitlab 安装配置与使用, 很详细的介绍了gitlab
的使用。
为了使用GitLab
CI/CD
,你需要一个托管在GitLab
上的应用程序代码库,并且在根目录中的.gitlab-ci.yml
文件中指定构建、测试和部署的等脚本。
4.1 gitlab ci 流程
1.gitlab-ci: 通过在项目根目录下配置.gitlab-ci.yml
文件,可以控制ci流程的不同阶段,例如install/检查/编译/部署服务器。gitlab平台会扫描.gitlab-ci.yml文件,并据此处理ci流程
-
ci流程在每次团队成员
push/merge
后之后触发。每当你push/merge
一次,gitlab-ci
都会检查项目下有没有.gitlab-ci.yml
文件,如果有,它会执行你在里面编写的脚本,并完整地走一遍从intall
、eslint
、build
、服务器的流程 -
gitlab-ci提供了指定ci运行平台的机制,它提供了一个叫
gitlab-runner
的软件,只要在对应的平台(机器或docker)上下载并运行这个命令行软件,并输入从gitlab
交互界面获取的token
,就可以把当前机器和对应的gitlab-ci
流程绑定,也即:每次跑ci都在这个平台上进行。 -
gitlab-ci的所有流程都是可视化的,每个流程节点的状态可以在
gitlab
的交互界面上看到,包括执行成功或失败。如下图所示,因为它的执行看上去就和多节管道一样,所以我们通常用pipeLine
来称呼它
总结: 你需要一个托管在
GitLab
上的应用程序代码库,并且在根目录中的.gitlab-ci.yml
文件中指定构建、测试和部署的脚本; 在这个文件中,你可以定义要运行的脚本,定义包含的依赖项,选择要按顺序运行的命令和要并行运行的命令,定义要在何处部署应用程序,以及指定是否 要自动运行脚本或手动触发脚本; 一旦你已经添加了.gitlab-ci.yml
到仓库中,GitLab
将检测到该文件,并使用名为GitLab Runner
的工具运行你的脚本.
4.2 概念介绍
# 指定脚本执行的镜像环境,如下为node环境为14.17.1
image: node:14.17.1
# 单个job执行之前执行
before_script:
- echo '====== 准备构建中 ========='
# 配置单个stage的执行顺序,串行
stages:
- install
- build
# 单个stage配置
# 安装依赖
npm_install:
only:
- master
stage: install
script:
- yarn
- ls -al
# 单个stage配置
# 构建
webpack_build:
only:
- master
stage: build
script:
- yarn build
# 单个job全部执行完之后执行
after_script:
- echo "====== 构建结束 ========="
-
.gitlab-ci.yml: 首先这是一个 YAML 文件,
yaml
文件的语法,是定义CI/CD
所有流程,每一个项目都有自己的.gitlab-ci.yml
文件,一般存放在项目的根目录, 其中最常用的对象有stages
、job
、cache
、script
、image
、tags
。后面会详细的介绍使用 -
pipeline: 是
Gitlab
根据项目的.gitlab-ci.yml
所配置流程,比如安装依赖包,进行代码 review、构建、编译、发布等一整套的流程 ,它由许多个任务节点组成, 而这些Pipeline
上的每一个任务节点,都是一个独立的Job
-
Job: 每个
Job
都会配置一个stage
属性,来表示这个Job
所处的阶段。一个Pipleline
有若干个stage
,每个stage
上有至少一个Job
-
GitLab Runner: 对项目执行
pipeline
的程序,需要单独安装; 所有CI/CD
的任务都是在他内部去进行执行的,他和GitLab
是两个不同的软件,虽然都是GitLab
发布的,但功能是不一样,一个代码管理,一个是流水线CI/CD
的执行环境, 是整个自动化的重要环境, 之后会单独介绍安装使用
五、GitLab Runner 安装以及配置
根据以上的贮备条件,在整个gitlab
的 CI/CD
环境中, 是离不开GitLab Runner
的执行器, 所以在使用时需要有Runner 的环境, 由它来执行我们的构建,部署流程; Runner
可以运行在虚拟机、物理机(GNU/Linux
、macOS
和 Windows
)、docker
容器,或者一个容器集群, 为了演示方便,这里介绍如何使用 Docker
去安装 GitLab Runner
以及配置
Docker
环境下安装 GitLab Runner
1. 从Docker镜像仓库 拉取gitlab镜像
$ docker pull gitlab/gitlab-runner
# ps: 网络不好可能会要很长时间哦
# 拉取完成,可以通过如下命令查询
$ docker images
2. 运行 gitlab-runner 容器
$ docker run -d --name gitlab-runner --restart always \
-v $HOME/docker/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner
# -v $HOME/docker/gitlab-runner/config 表示将 gitlab-runner 配置挂在中本地,
# 防止重启丢失, 可以根据自己情况, 设置路径, 需要确保目录已经存在
# 运行完显示如下图,表示容器已经启动
# 查看已经运行的容器
$ docker ps
3. 在 GitLab 注册 runner
GitLab 中注册runner 会分以下几种情况:
-
全局注册: 全局注册整个
gitlab
项目都可以使用,使用范围广, 设置入口,请管理员进入GitLab管理区域,设置”>“CI/CD
”,然后展开“runner”部分 -
项目组: 只能当前组内的项目使用,设置入口,进入组的管理页面,
设置
”>“CI/CD
”,然后展开“runner
”部分 -
项目: 只能针对当前的项目设置, 设置入口,项目管理页面,
设置
”>“CI/CD
”,然后展开runner
部分,不过不建议使用吧, 最小单位还是以组为单位
可以根据自己的需求来注册, 我这里将会使用组(front-end
)的方式来介绍注册, 住哟这个组存放是都是前端项目,所以在 runner 中初始化的环境都会一致, 接下来开始配置, 入口如下图:
这时候我们展开,就能看到这个设置Runner的区域, 之后会看“Set up a group Runner manually” 手动配置Runner的 区域
4.进入Runner容器内
$ docker exec -it gitlab-runner bash
# 进入容器之后,执行下面命令,开始注册
$ gitlab-runner register # 之后根据下图提示按照步骤填写即可
通过以上命令后,就创建成功runner
啦,这时候我们去GitLab
中我们创建Runner
的区域刷新就能看到了
如果你也使用的docker
来运行 runner
, 那可以直接通过下面的命令直接设置上面参数:
docker run --rm -v $HOME/docker/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register \
--non-interactive \
--executor "docker" \
--docker-image alpine:latest \
--url "" \
--registration-token "" \
--description "docker-runner" \
--tag-list "docker" \
--run-untagged="true" \
--locked="false" \
--access-level="not_protected"
注意: 这条命令中有两个关键属性,url
和 registration-token
,分别对应我们的 GitLab
地址和 Runner
的注册 token
。 需要把配置路径改成自己.
参数说明:
-
executor:执行器,可选
docker
、k8s
、shell
-
description:
runner
的描述 -
tag-list:
runner
的tag
,使用逗号分隔,如果一个项目有多个Runner
,需要根据tag
来指定使用那个 Runner 来运行任务 -
locked:是否锁定,锁定后,只能适用于当前的组/项目,不能被其他使用
4.安装node 环境
我们需要部署前端项目,所以离不开node
的执行环境, 所有需要在 runner
中安装node
,这个runner
是共享的, 可能出现不同的用户需要不同的node
版本, 我建议使用nvm
来管理不同的node
版本, 使用nrm
来管理不同npm
镜像源
# 查看nvm 查看node 版本
$ nvm list
v11.0.0
-> v12.10.0 # 箭头指向表示当前版本
default -> v12.10.0
node -> stable (-> v12.10.0) (default)
stable -> 12.10 (-> v12.10.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/fermium (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.23.0 (-> N/A)
lts/erbium -> v12.20.0 (-> N/A)
lts/fermium -> v14.15.3 (-> N/A)
# 通过 install 安装新的版本
$ nvm install 13.13.0
# 切换node 版本
$ nvm use 13.13.0
# 卸载 node 版本
$ nvm uninstall 13.13.0
安装 nrm
镜像源源管理工具
# 安装
$ npm install -g nrm
# 查看
$ nrm ls
* npm -------- http://registry.npmjs.org/ # 带* 已使用的
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
# 添加镜像源
$ nrm add <registry> <url> # registry: 名称 url: 源的路径
# 切换源
$ nrm use taobao
# 删除
$ nrm del <registry>
# 测速
nrm test <registry>
注意以上只是提供非docker
环境中使用, 在docker 中 可以指定 image
到此位置我们环境已经搭建完毕了, 接下来开始制定前端项目 CI/CD
流程的设计, 我将使用的vite
创建vue3.x
项目来演示,如果还没有开始学习vue3
,那你可以看看我之前写的让你30分钟快速掌握vue 3的入门帮助文档, 在这里就不演示创建的具体过程了; 既然说到了制定前端项目 CI/CD
流程, 大家不妨想想自己平时开发上线的过程是怎么样的呢? 此时值得大家去思考. 说到始制定前端项目 CI/CD
流程的设计, 在gitlab
中的CI 怎么制定呢? 其实之前已经说过来,接下来的重头戏要出场了.gitlab-ci.yml
,
六、制定前端项目 CI/CD
流程的设计
CI
流程的运行控制,决定于项目根目录下编写的配置文件—— .gitlab-ci.yml
,正因如此,我们需要掌握YML的基本语法规则。YML
是一种编写配置文件的语言,比JSON更为简洁和方便,因此,我们首先要掌握的就是YML文件的编写语法。 有关 yaml
文件的语法,大家可以参考阮一峰 YAML 教程
其次这个文件是定义 CI/CD
所有流程,包括 stage
,job
。每一个项目都有自己的.gitlab-ci.yml
文件,一般存放在项目的根目录。如果不存放在根目录,则需要特殊配置一下 CI/CD
才能被 runner 调用。
接下来需要了解的就是gitlab-ci独特的配置关键字,这些关键字将在.gitlab-ci.yml中使用,并用来控制一个pipeline具体的运作过程, gitlab提供了很多配置关键字,其中最基础和常用的有这么几个
-
Pipeline: 流水线,一次流水线相当于一次构建任务,里面可以包含多个阶段,比如
install -> eslint -> build -> deploy
等流程 ; -
image: 指定当前
CI
,所需要的环境,镜像版本,CI/CD
脚本运行环境的docker
镜像,镜像就是一种文件存储形式,可以理解为是一个环境的集合,内含多种文件; -
stages: 定义在
YML
文件的最外层,它的值是一个数组,用于定义一个pipeline
不同的流程节点,表示构建阶段,每个stage串行同步执行。一旦有一个stage中的一个job失败了,那么下一个stage的任务便不会执行。如果当前stage定义了多个任务,那么其中一个任务失败,另外一个任务还是会被继续执行。但是只有当所有 stages 成功完成后,该构建任务 (Pipeline) 才算成功。 -
jobs
:job
表示某个stage
里面执行的工作,一个stage
里面可以定义多个job
。
jobs
有如下特点 :
1.相同 stage
中的jobs
会并行执行
2.相同 stage
中的 jobs
都执行成功时,该 stage
才会成功
3.如果任何一个job
失败,那么该 stage
失败,即该构建任务 (Pipeline
) 失败
-
stage: 是一个字符串,且是
stages
数组的一个子项,表示的是当前的pipeline
节点 -
script: 它是当前
pipeline
节点运行的shell
脚本, 这个script
是我们控制CI
流程的核心,从安装,编译到部署都是通过script
中定义的shell
脚本来完成的 -
tags: tags是当前Job的标记,这个
tags
关键字是很重要,因为gitlab
的runner
会通过tags
去判断能否执行当前这个Job
(可以在在项目的CI/CD
下runner
配置查看) -
cache: 方便优化
job
速度,缓存在随后的job
的一些文件、目录, 在前端最多就是node_modules
-
only: 使用
only
关键字可以限定job
的运行 ,如限定某些分支、某些push
、pr
合并、触发job
其实还有很多的job
配置参数, 如下图, 之后用到在说明吧
job
之后,我们来看一下整个 pipelines
:配置
6.1 根据上面的配置信息实现前端的部署
设想我们前端上线时的流程如下:
梳理和规划Pipeline
的不同阶段和过程, 在编写.gitlab-ci.yml
前,首先需要考虑的是我们的pipeline分几个阶段处理
install
阶段: 安装npm
相关的依赖eslint
阶段:执行eslint
检查,判断代码格式是否符合规范,如果不符合则pipeline
终止。test
阶段: 自动化测试,提高代码的质量(不过很多开发团队都没有这个习惯, 希望能加上)build
阶段: 编译生成生产代码, 主要通过构建打包工具执行编译处理deploy
阶段: 部署阶段,也就是把刚才bulid阶段生成的生产代码,部署到生产访问的服务器上
根据上图我们可以定义出5个阶段, 编写.gitlab-ci.yml
配置文件
image: node:latest # 指定node版本 (非docker 环境下不需要)
variables: # 定义变量
project: $CI_PROJECT_NAME # 系统变量获取当前项目名称
SCRIPTS_PATH: /home/gitlab-runner/runner-scripts
stages: # 定义阶段的执行顺序 分为 5 个阶段
- install
- eslint
- test
- build
- deploy
cache: # 设置缓存, 当然是为了重复运行pipeline的时候不会重复安装全部node_modules的包,从而减少pipeline的时间,提高pipeline的性能。
paths:
- node_modules
install-job: # 执行 npm install
tags:
- fe-runner
stage: install
script:
- echo npm install
- npm install --registry=http://registry.npm.taobao.org # 安装依赖设置镜像源
eslint-job: # 执行eslint
tags:
- fe-runner
stage: eslint
script:
- echo npm eslint
- npm run eslint # package script
test-job: # 执行自动化测试
tags:
- fe-runner
stage: test
script:
- echo npm test
- npm run test # package script
build-job: # 执行build
tags:
- fe-runner
stage: build
script:
- echo npm build
- npm run build # package script
artifacts: # 在 job 成功后将一个文件列表或目录列表制成制品上传, 并在gitlab交互界面上提供下载
expire_in: 1 week # 一星期之后自动删除
paths:
- dist
only # 使用 only 关键字可以限定 job 的运行 ,如限定某些分支、某些 push、pr 合并、触发 job
- master
- develop
deploy-job:
tags:
- fe-runner
stage: deploy
script:
- echo npm deploy
- bash $SCRIPTS_PATH/deploy.sh "x.x.x.x" # 通过ssh 将构建后的文件传到目标服务上
将此文件提交到代码仓库中, 自动触发流水线, 之后在项目中就能看到相应的信息
当前stage
的执行情况能在交互面板上能看的清清楚楚:
- 正在执行是蓝色
- 尚未执行是灰色
- 执行成功是绿色
- 执行失败是红色
gitlab CI/CD 支持 variables, 一些隐秘变量的使用,先来介绍一下啊,比如你要在 .gitlab-ci.yml 中使用一些密码、私钥,直接使用时非常不安全的。GitLab 提供了一种在 CI/CD 中安全使用隐秘变量的方式,如下图添加变量:
添加变量后在 .gitlab-ci.yml 中使用 $+变量名 的方式来使用,我们要在 Variables 中配置我们需要的变量,例如: 用户名、服务器密码、docker hub 账号密码等, 个人觉得这个不是一个
七、部署成功/失败通知
我们知道Gitlab的Pipeline中的Job执行成功或者失败之后,对应Job状态会改变为passedorfailed,当Job的状态改变之后, 我们需要再到Gitlab CI/CD的页面下查看Job的状态,去看看有没有打包完成。
实践表明,某些时候我们的gitlab执行Job往往需要等待很长且不稳定。导致我们反复到CI/CD的页面看看Job是否执行完毕; 所以我们需要一个更友好,高效的Job执行完成状态的反馈方案, 目前可以实现的方法:
其实就是在一个群引入webhook自定义机器人, 剩下的自行处理吧, 这个需要注册平台, 比较麻烦, 可以根据自己使用办公软件来集成即可
这里我需要说是, 可以通过 when: on_failure、 when: on_success 来处理通知的状态
八、gitlab-ci 进阶
前端项目做 CI/CD 其实还是蛮好做的,如果部署的是静态页面,直接放到对应的服务器里就行了, 但是在工作中发布和部署,又分很多场景。test 环境、uat 环境、预发布环境、正式环境, 静态资源上传CDN
、OSS
、S3
等资源库,不可能想上面的pipeline 的配置能完成了, 需要提供跟高级的配置才能完成, 为了能体验更高级的配置,我们需要先学科普一些高级的用法
1. YAML的片段复用
上面我们编写了.gitlab-ci.yml
配置。但在实际项目的运行中,.gitlab-ci.yml
的编写可能会渐趋复杂, 其中难免会出现相同的配置, 那么这个时候YML的一些其他语法功能就派上用场了.
例如我们部署的环境有分别有test、uat、prod 分别使用不同的分支,这时我们可以定义YAML
的片段复用
YML的语法为我们提供了这个功能:
-
使用 &符号可以定义一个片段的别名
-
使用 <<符号和 * 符号可以将别名对应的YML片段导入
# 定义复用片段
.access_branch_template: &access_branch
only:
- master
- develop
# 使用片段
install-job: # 执行 npm install
<<: *access_branch
tags:
- fe-runner
stage: install
script:
- echo npm install
- npm install --registry=http://registry.npm.taobao.org # 安装依赖设置镜像源
2. YAML的模块化
在实际的运用中, 我们很多前端项目, 都需做自动化部署, 虽然有yml的YAML的片段复用, 也无法满足我们使用, 不可能为每个项目都复制一份 .gitlab-ci.yml
的流水线配置, 思考一下此时怎么处理呢?
使用YAML的模块化, 将它分成多个yml文件, 然后把其他YML文件导入到入口YML文件(.gitlab-ci.yml)中. 统一维护, 方便管理.
我们可以按照不同类型的项目定义好不同的流水线,日录:
- h5项目: .gitlab-ci-h5.yml
- node 项目: .gitlab-ci-node.yml
- react 项目: .gitlab-ci-react.yml
- react ssr 项目: .gitlab-ci-react-srr.yml
那么我们可以在项目中.gitlab-ci.yml中这么写,根据不同的项目就可以对它们做合并
include:
- '/.gitlab-ci-node.yml' # 本地
- '/.gitlab-ci-*.yml' # 使用通配符
- remote:
'https://gitlab.com/example-project/-/raw/master/.gitlab-ci.yml' # 远程地址
# 导入gitlab 仓库仓库中项目
include:
- project: 'cidevops/cidevops-newci-service'
ref: master
file: 'templates/default-pipeline.yml
3. extend关键字
gitlab-ci 提供了extend关键字,它的功能和前面提到的YML的片段功能是一样的,不过写法更加优雅, 使用方法如下:
# 定义复用片段
.access_branch_template
only:
- master
- develop
# 你用片段
install-job: # 执行 npm install
extend: .access_branch_template
tags:
- fe-runner
stage: install
script:
- echo npm install
- npm install --registry=http://registry.npm.taobao.org # 安装依赖设置镜像源
4. When
表示当前Job在何种状态下运行,它可设置为如下值:
- on_success: 当 job 执行成功时自定义通知
- on_failure 当 job 执行错误时自定义通知
- manual: 定义该 job 是手动触发的,默认不写是自动执行触发
- always: 执行当前Job,而不管先前pipeline的Job状态如何
- delayed: 将job 的执行延迟指定的持续时间
根据以上一共的特性, 我们可以将之前的 pipeline
改成基于多分多环境的自动化部署优化, 做出新的优化调整, 如下图:
结合所学的基础知识, 已经对整个gitlab
的 CI
的整个过程已经有初步的认识了,后期我会根据gitflow
整体流程, 来优化pipeline
,你也可以根据自己所了解的支持去实现自己的项目的pipeline
.
友情连接
-
docker 中 gitlab 安装配置与使用(如果您对gitlab使用不太熟悉建议阅读)
-
让你30分钟快速掌握vue 3入门帮助文档(强烈建议大家一定要查阅)
暂无评论内容