神兵利器: GitLab CI/CD UP 工作效率 🚀🚀🚀

前言

  在互联网行业中,当新产品的一个个需求确立下来,开发同学不仅仅需要进行代码开发,在每个功能完善的同时,都需要重新发布测试环境,换而言之,开发同学还肩负着持续的部署工作。

  为什么需要频繁部署?

  因为产品时由多个功能点组合而成,所以一般会拆分为各个功能点划分给不同的开发同学协同合作推进产品成型,每一个功能点完成后,都需要发布到测试环境交由测试同学进行验证,若出现 Bug,则告知开发同学进行修复,因此,在一个功能点完成后,一个 Bug 修复后,都需要发布到测试环境。

  开发同学是如何进行部署工作的呢?
  开发同学一般是在功能点完成或 Bug 修复后,进行一系列以下操作:

  • ① 将代码打包
  • ② 上传到测试环境服务器
  • ③ 删除(备份)旧包,kill 旧包进程,start 新包进程

  这一系列人工操作,是重复且会花费开发同学一定时间的。
  那么,以上操作能否自动化?降低开发同学的部署时间,提升团队的工作效率呢?

  当然可以,这个时候就需要使用 CI/CD 工具了,并且它的作用远不止于此。

GitLab

  GitLab 是由 GitLab Inc.开发的一款基于 Git 的完全集成的软件开发平台(fully 集成软件 development platform)。另外,GitLab 且具有 wiki 以及在线编辑、issue 跟踪功能、CI/CD 等功能。

CI/CD

  GitLab CI/CD 是 GitLab 内置的一款工具,用于通过持续方法论 (页面存档备份,存于互联网档案馆)(continuous methodologies)的软件开发。
  该持续方法论包含三个部分:

  • 持续集成(Continuous Integration,即 CI):每次在上传代码块到基于 Git 仓库时,持续集成会运行脚本去构建、测试、校验代码,这些操作是在合并到默认分支之前进行的
  • 持续交付(Continuous Delivery,即 CD):在持续集成之后(即合并到默认分支之后),持续交付将进行手动部署应用
  • 持续部署(Continuous Deployment,即 CD):在持续集成之后(即合并到默认分支之后),持续部署将进行自动部署应用

原理

  当开发者配置了 GitLab CI/CD,那么当开发者使用 git 提交(commit),就会触发 CI/CD 相关的一系列操作,这一系列操作由 GitLab Runner 执行,相关配置记载于.gitlab-ci.yml文件中,执行的结果将在 Gitlab 页面中展示。

部署

RPM 方式

  下载 RPM 包之后进行安装配置即可。

下载

1
wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-12.9.0-ce.0.el7.x86_64.rpm

安装

1
rpm -ivh gitlab-ce-12.9.0-ce.0.el7.x86_64.rpm

配置

1
2
3
# 编辑站点地址,默认 80 端口
vim /etc/gitlab/gitlab.rb
external_url 'http://127.0.0.1:8888'
时区配置

  时区设置为”Asia/Shanghai”:

1
49 # gitlab_rails[‘time_zone’] = ‘UTC’ —> gitlab_rails[‘time_zone’] = ‘Asia/Shanghai’

git 仓库存储目录配置

  git 仓库存储目录默认为/var/opt/gitlab/git-data,由于存储数据一般比较多,所以最好专门指定存储目录。

  修改git_data_dirs的配置:

1
2
3
4
5
380 # git_data_dirs({
381 # "default" => {
382 # "path" => "/mnt/nfs-01/git-data"
383 # }
384 # })

  修改为:

1
2
3
4
5
380 git_data_dirs({
381 "default" => {
382 "path" => "/data/softwares/gitlab/data"
383 }
384 })

配置后初次启动

1
2
# 配置,若无报错则配置后启动,会在 /opt/gitlab/etc 下生成各应用服务配置文件,在 /var/log/gitlab/ 存放日志
gitlab-ctl reconfigure

  初始的 Gitlab 默认管理员帐户的用户名为root

  后续若有配置修改要求,修改配置文件后使用命令gitlab-ctl restart重启所有组件服务即可启用新的配置。

相关命令

命令 说明
gitlab-ctl start 启动
gitlab-ctl restart 重启
gitlab-ctl stop 停止
gitlab-ctl status 状态

Docker 方式

1
vim docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
version: '3'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
privileged: true
logging:
driver: "json-file"
options:
max-size: "5g"
environment:
TZ: 'Asia/Shanghai'
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://10.57.22.19:8180'
gitlab_rails['time_zone'] = 'Asia/Shanghai'
gitlab_rails['smtp_enable'] = false
gitlab_rails['gitlab_shell_ssh_port'] = 2224
ports:
- '8180:8180'
- '2224:22'
volumes:
- './config:/etc/gitlab'
- './data:/var/opt/gitlab'
- './logs:/var/log/gitlab'
1
2
3
4
# 启动
docker-compose up -d
# 查看日志
docker-compose logs -f

  成功后浏览器访问10.57.22.19:8180即可访问 Gitlab。

外部域名

  若要通过外部域名访问可以通过如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
version: '3'
services:
gitlab:
image: 'gitlab/gitlab-ce:latest'
container_name: gitlab
restart: always
privileged: true
logging:
driver: "json-file"
options:
max-size: "5g"
environment:
TZ: 'Asia/Shanghai'
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://test.gitlab.com'
gitlab_rails['time_zone'] = 'Asia/Shanghai'
gitlab_rails['smtp_enable'] = false
gitlab_rails['gitlab_shell_ssh_port'] = 2224
ports:
- '8180:80'
- '2224:22'
volumes:
- './config:/etc/gitlab'
- './data:/var/opt/gitlab'
- './logs:/var/log/gitlab'

  注意http://test.gitlab.com得反向代理到安装 Gitalb 机器的 ip + 80 端口,即 http://test.gitlab.com –> 10.55.22.9:80

使用

基础设置

  GitLab 安装完成之后,第一次打开 UI 界面时可设置管理员帐号密码(密码要求八位数)。
  若忘记执行上面的操作,初始密码也可通过下面的命令查看:

1
2
3
4
5
6
7
8
9
10
sudo cat /etc/gitlab/initial_root_password
# WARNING: This value is valid only in the following conditions
# 1. If provided manually (either via `GITLAB_ROOT_PASSWORD` environment variable or via `gitlab_rails['initial_root_password']` setting in `gitlab.rb`, it was provided before database was seeded for the first time (usually, the first reconfigure run).
# 2. Password hasn't been changed manually, either via UI or via command line.
#
# If the password shown here doesn't work, you must reset the admin password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.

Password: Qz4D/O0Gd2AhwEZsI3qOVDJtEuyCvMx1vHQQ33yO4=

# NOTE: This file will be automatically deleted in the first reconfigure run after 24 hours.

  在设置好管理员帐号之后,选择 🔧 (Admin Area),点击Settings,选择General进行属性设置

  • Account and limit:将Gravatar enabled前的 ✅ 关闭,禁止获取用户头像(影响性能造成卡顿)
  • Sign-up restrictions:将其下的Sign-up enabled前的 ✅ 关闭,禁止用户注册帐号

用户管理

  在 🔧 (Admin Area)下Overview下的Dashboard下存在以下三个面包栏:

  • Projects:项目
  • Users:用户
  • Groups:群组

  点击Users中的New user进行用户创建:

  • Account:
    • Name:帐号昵称
    • Username:登陆用户名
    • Email:登陆邮箱
  • Password: 添加用户后可以修改密码,用户第一次登陆后会被强制要求修改密码
  • Access:
    • Project limit:允许此用户创建的项目数,默认 100000,可以设置的小一点,如 100
    • Can create group:是否允许当前用户创建用户组,默认开启,可以关闭
    • Access level: 用户权限等级,可选:
      • Regular: 普通用户,可以访问他们的项目和群组
      • Regular: 管理员,可以访问所有组,项目和用户,并且可以管理此安装的所有功能

群组

  作为程序员,借助代码管理工具(如 GitLab)来管理代码是再正常不过的事情了。
  程序员基本都服务于软件公司,公司产品线可能有多条,那么一般会存在不同的开发部门,比如团队 A 负责开发 X 产品,团队 B 负责开发 Z 产品。
  通常而言,对于不同团队的代码,权限各自隔离,比如说:

  • X 产品的代码应当只有 A 团队人员才能看到
  • Z 产品的代码应当只有 B 团队人员才能看到

  为了做到团队间的代码权限隔离,GitLab 引入群组的概念,不同团队开发时可以分别新建群组,然后在群组下组建各自团队的人员,进行协同开发工作。

  在 GitLab 新建组时,可见行等级设置值存在三个:

  • Private:此群组内部项目只有项目组内的成员可以看到
  • Internal:此群组内部项目可被登陆用户看到
  • Public:此群组内部项目可被任何授权用户看到

  大部分情况下,创建项目组时,可见行等级使用Private

数据迁移

  若要将某台 GitLab 的数据迁移到另一台服务器上,需要进行如下操作:

  • ① 在原服务器备份数据
  • ② 在新服务器还原数据

数据备份

  通过下面的命令可以进行数据备份操作。

1
sudo gitlab-rake gitlab:backup:create

  此命令执行后,会在/var/opt/gitlab/backups文件夹下生成一个名为[TIMESTAMP]_gitlab_backup.tartar文件,此文件会包含所有的数据库数据、所有的repo数据,以及所有的附件。

  TIMESTAMP是以秒为单位的时间戳,用来区分不同的备份文件,如1650948390_2022_04_26_14.0.2_gitlab_backup.tar

数据还原

  若要进行数据还原工作,我们需要确保还原的的tar文件在配置文件中指定的备份数据存放目录(默认为/var/opt/gitlab/backups)下面。

  因此,我们需要首先将相关文件上传后放置到此目录下:

1
2
3
4
sudo cp 1650948390_2022_04_26_14.0.2_gitlab_backup.tar /var/opt/gitlab/backups/
# 更改文件权限
sudo chown git /var/opt/gitlab/backups/1650948390_2022_04_26_14.0.2_gitlab_backup.tar
sudo chgrp git /var/opt/gitlab/backups/1650948390_2022_04_26_14.0.2_gitlab_backup.tar

  之后,就可以通过命令来停止相关服务器,然后执行恢复操作了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 停止相关数据连接服务,从 GitLab 13.0 开始,Puma 代替 Unicorn 作为默认的 Web 服务器,请根据版本选择执行相关命令
# gitlab-ctl stop unicorn
sudo gitlab-ctl stop puma
sudo gitlab-ctl stop sidekiq

# 确认状态
sudo gitlab-ctl status

# 从相关备份文件中恢复数据,恢复过程中需要按照提示输入两次 yes 并回车
# 注意:这里不需要带 _gitlab_backup.tar 后缀
sudo gitlab-rake gitlab:backup:restore BACKUP=1650948390_2022_04_26_14.0.2

# 重启 gitlab
sudo gitlab-ctl restart

注意事项

Docker 日志文件过大

  使用 Docker 部署时限制日志文件时,日志文件会越来越大,由于即使修改限制重启后已有 Docker 无法生效,此时可以使用另一种方式去定时清理指定镜像的日志文件(root 用户):

1
0 2 * * * cat /dev/null > /var/lib/docker/containers/88f47da0443c28baedcdbc38cc5db26fd3e3b8abd6ce6678321784aee0d78f39/88f47da0443c28baedcdbc38cc5db26fd3e3b8abd6ce6678321784aee0d78f39-json.log

版本一致

  备份操作对gitlab的版本是严格要求一致的,这代表两台 GitLab 的服务器版本必须一致才可进行数据迁移工作,我们可以通过以下命令查看gitlab版本:

1
2
cat /opt/gitlab/embedded/service/gitlab-rails/VERSION
14.0.2

  还原会将原 Gitlab 的帐号密码同步导入新 Gitlab 中。

迁移到 Docker

  直接将备份文件上传到 Docker Gitlab 映射目录,之后进入容器进行还原操作,最后重启容器即可。

1
2
3
docker-compose exec gitlab /bin/bash

gitlab-rake gitlab:backup:restore BACKUP=1650948390_2022_04_26_14.0.2

部分文件无法还原(可以忽略通过其他手段重置)

  注意迁移时以下文件不会还原:

  • /etc/gitlab/gitlab.rb
  • /etc/gitlab/gitlab-secrets.json

  手动上述文件传到新机器上对应目录覆盖即可。

1
2
3
4
5
sudo chown root:root gitlab*
sudo cp gitlab.rb config/
sudo cp gitlab-secrets.json config/

docker-compose restart gitlab

迁移后命令报错

  Gitlab 执行部分命令时,爆以下错误:

1
OpenSSL::Cipher::CipherError ():

  通过执行以下命令修复即可:

1
2
3
4
5
sudo gitlab-rails c
settings = ApplicationSetting.last
settings.update_column(:runners_registration_token_encrypted, nil)
settings.update_column(:encrypted_ci_jwt_signing_key, nil)
settings.save!

迁移后 CI/CD 界面报错 500

  通过执行以下命令后刷新页面即可:

1
2
3
4
5
6
7
gitlab-rails dbconsole
# 查询出指定项目
select name, runners_token_encrypted from projects where name = 'xxx';
# 清除 runners
update projects set runners_token_encrypted = null where name = 'xxx';
# 也可以直接清除所有项目的 runners
update projects set runners_token_encrypted = null;

定时备份(Docker 方式)

编写脚本

1
2
3
cd /data/software/gitlab/
mkdir backup_logs
vim gitlab_backup.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# 当前日期
curTime=`date +'%Y%m%d'`
# 记录的日志文件
BACKUP_LOG_FILE=/data/software/gitlab/backup_logs/backup.log
# 备份根目录
BACKUP_ROOT_DIR=/data/software/gitlab/data/backups

# 执行备份脚本
echo "[INFO] $(date +%Y-%m-%d_%H:%M:%S) gitlab backup file generate done" >> ${BACKUP_LOG_FILE}

docker exec gitlab /bin/bash -c 'gitlab-rake gitlab:backup:create'

echo "[INFO] $(date +%Y-%m-%d_%H:%M:%S) gitlab backup compress done" >> ${BACKUP_LOG_FILE}

echo "[INFO] $(date +%Y-%m-%d_%H:%M:%S) gitlab backup done" >> ${BACKUP_LOG_FILE}
echo " " >> ${BACKUP_LOG_FILE}

# 删除过期数据,这里设置了 30 天
docker exec gitlab /bin/bash -c 'find /var/opt/gitlab/backups -type f -mtime +30 -name "*.tar" -exec rm -rf {} \;'
echo "[INFO] $(date +%Y-%m-%d_%H:%M:%S) gitlab backup check delete done" >> ${BACKUP_LOG_FILE}

定时执行脚本

  Linux 安装 crontab 工具(有可跳过):

1
yum install -y crontab

  创建定时备份任务:

1
2
3
4
5
6
7
8
# 添加定时任务
crontab -e

# 每天凌晨一点执行
00 1 * * * sh /data/software/gitlab/gitlab_backup.sh

# 查看定时任务
crontab -l

版本升级

  Gitlab 进行版本升级时,过程需要按三步走:

  • ① 确定当前运行版本
  • ② 确定升级目标版本
  • ③ 确定逐个升级的版本
  • ④ 按升级过程中的版本依次升级即可

实践

  当前运行版本为【14.0.2】,升级目标版本为【15.0.2】,根据其确定当前版本和升级路径(Gitlab 升级只能逐个版本升级,不能跨版本),升级路径在官网——升级说明查看参照:

目标版本 当前版本 升级路径 说明
15.1.0 14.6.2 14.6.2 -> 14.9.5 -> 14.10.5 -> 15.0.2 -> 15.1.0 需要依次升级三个版本: 14.9, 14.10, and 15.0.
15.0.0 14.6.2 14.6.2 -> 14.9.5 -> 14.10.5 -> 15.0.2 需要依次升级两个版本: 14.9 and 14.10.
14.6.2 13.10.2 13.10.2 -> 13.12.15 -> 14.0.12 -> 14.3.6 => 14.6.2 需要依次升级三个版本: 13.12, 14.0, and 14.3.

  注意,首先我们需要将14.0.2升级到次要版本14.0.12

  因此,我们从运行版本为【14.0.2】到升级目标版本【15.0.2】依次需要经过以下版本:

  • 14.3.6
  • 14.6.2
  • 14.9.5
  • 14.10.5
  • 15.0.2

  若为docker方式部署,可以依次下载相关镜像:

1
2
3
4
5
docker pull gitlab/gitlab-ce:14.3.6-ce.0
docker pull gitlab/gitlab-ce:14.6.2-ce.0
docker pull gitlab/gitlab-ce:14.9.5-ce.0
docker pull gitlab/gitlab-ce:14.10.5-ce.0
docker pull gitlab/gitlab-ce:15.0.2-ce.0

扩展——强制修改帐号密码

  若忘记了 root 密码,可以通过下面的操作强制修改密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 进入相关控制台
sudo gitlab-rails console -e production
#
# 找到自己需要重置的用户 id 号,管理员账户通常 id 为 1
user=User.where(id:1).first
# 修改密码
user.password='Qz4D/O0Gd1hwEZsI7qOVD22CvMxncxvHQQNfyO4='
# 确认密码
user.password_confirmation='Qz4D/O0Gd1hwEZsI7qOVD22CvMxncxvHQQNfyO4='
# 保存密码
user.save!
# 退出
quit

Gitlab-Runner

简介

  GitLab Runner 是一个开源项目,用于运行作业并将结果发送回 GitLab。它与 GitLab CI 结合使用,GitLab CIGitLab 随附的用于协调作业的开源持续集成服务。

部署

环境要求

  • GitLab Runner 版本应与 GitLab 版本同步,避免兼容性问题
  • GitLab Runner 依赖于 Git,若未安装建议 root 用户执行安装 Git

    1
    2
    3
    4
    5
    6
    7
    8
    sudo yum -y remove git
    sudo yum -y remove git-*

    sudo yum -y install https://packages.endpointdev.com/rhel/7/os/x86_64/endpoint-repo.x86_64.rpm

    sudo yum install git
    # 查看版本
    git --version
  • 若要使用 Docker 请安装最新版本,GitLab Runner 需要最少的 Docker v1.13.0

下载与安装

1
2
3
4
# 下载 gitlab-runner 的 rpm 包
wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-runner/yum/el7/gitlab-runner-12.9.0-1.x86_64.rpm
# 安装 gitlab-runner
rpm -ivh gitlab-runner-12.9.0-1.x86_64.rpm

  安装成功后,将自动创建一个gitlab-runner用户,gitlab-ci的执行 Runner 时的用户目录~都将指向/home/gitlab-runner目录。

Runner 的类型与状态

  GitLabRunner 存在三种类型的 Runner 对象:

  • shared : 可以运行整个平台项目的作业(gitlab)
  • group: 可以运行特定 group 下的所有项目的作业(group)
  • specific: 可以运行指定的项目作业(project)

  Runner 对象存在以下两种状态:

  • locked: 锁定状态,无法分配到其他项目的作业当中
  • paused: 暂停状态,不会运行任何新的作业

Runner 注册到 GitLab

  安装成功后,就可以将 GitLab Runner 注册到 GitLab 的 Runners 中,让它们两者配合工作。
  那么,如何将 GitLab Runner 注册到 GitLab 的 Runners 中呢?
  这需要经历以下步骤:

  • ① 输入gitlab-runner register命令,开始注册工作
  • ② 填入需要注册到的 GitLab 的 url 地址
  • ③ 填入对应 Runner 的 token
  • ④ 填入当前 Runner 的描述信息
  • ⑤ 填入 tag 说明
  • ⑥ 选择执行器的种类(包括 shell、docker 等)

  下面是以上步骤执行过程的日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 开始注册 Runner
$ gitlab-runner register
Runtime platform arch=amd64 os=linux pid=20303 revision=4c96e5ad version=12.9.0
WARNING: Running in user-mode.
WARNING: The user-mode requires you to manually start builds processing:
WARNING: $ gitlab-runner run
WARNING: Use sudo for system-mode:
WARNING: $ sudo gitlab-runner...

# 输入 GitLab CI 的 URL
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://10.211.55.23:8888/
# 输入 GitLab CI 的 Token
Please enter the gitlab-ci token for this runner:
bSBM4D8GaNEr4-DzZvT-
# 输入此 Runner 在 GitLab CI 中的描述说明
Please enter the gitlab-ci description for this runner:
[Gitlab-Runner]: test
# 输入此 Runner 的 Tag
Please enter the gitlab-ci tags for this runner (comma separated):
test
Registering runner... succeeded runner=bSBM4D8G
# 选择执行器的类型(这里我们选择 shell )
Please enter the executor: shell, ssh, virtualbox, docker+machine, custom, docker, docker-ssh+machine, kubernetes, docker-ssh, parallels:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

三种类型的 Runner 对象 token 的获取方式

shared 类型(全局)

  进入系统设置 -> Runners

  注册全局 Runner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[admin@centos7aids data]$ sudo gitlab-runner register
Runtime platform arch=amd64 os=linux pid=25438 revision=8925d9a0 version=14.1.0
Running in system-mode.

Enter the GitLab instance URL (for example, https://gitlab.com/):
http://xx.xx.xx.xx:8180/
Enter the registration token:
s52PGzxxxtX8S7Qjqmhj
Enter a description for the runner:
[centos7aids]: global
Enter tags for the runner (comma-separated):
global
Registering runner... succeeded runner=s52PGzwz
Enter an executor: custom, docker, docker-ssh, parallels, kubernetes, shell, ssh, virtualbox, docker+machine, docker-ssh+machine:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

group 类型(特定组)

  进入group -> Settings -> CI/CD -> Runners -> Group Runners

specific 类型(特定项目)

  进入具体的项目 -> Settings -> CI/CD -> Runners -> Specific Runners

命令

  见下表说明:

命令 说明
gitlab-runner start 启动
gitlab-runner stop 停止
gitlab-runner restart 重启
gitlab-runner status 状态
gitlab-runner run 运行
gitlab-runner register 注册
gitlab-runner verify 检查注册的 Runner 是否可以连接,但不验证 GitLab 服务是否正在使用
gitlab-runner verify –delete 检查注册的 Runner 是否可以连接,删除无法连接的 Runner
gitlab-runner list 列出所有运行的 Runner
gitlab-runner unregister 在 GitLab 注销已注册的某个 Runner
gitlab-runner unregister –url http://gitlab.example.com/ –token b5kan 在 GitLab 通过令牌注销已注册的 Runner
gitlab-runner unregister –name test-runner 在 GitLab 通过名称注销已注册的 Runner
gitlab-runner unregister –all-runners 在 GitLab 注销所有已注册的 Runner
gitlab-runner uninstall 停止运行 GitLab Runner 并将其卸载

  建议执行以上命令时使用 root 用户操作,避免文件权限问题。

1
2
3
sudo gitlab-runner install --working-directory /home/gitlab-runner --user root
sudo gitlab-runner start
sudo gitlab-runner status

流水线作业

  通过 Gitlab CI 进行 CI/CD 操作,只用在代码仓库里编辑和维护一个.gitlab-ci.yml文件,将需要完成的 job(作业)拆分一一编写进.gitlab-ci.yml,每当代码有更新,Gitlab CI 会读取.gitlab-ci.yml里的内容,生成一条 Pipeline (管道)去执行文件中创建的各种 job(作业),进行 CI/CD 的操作。

语法

  要想编写合适的.gitlab-ci.yml使作业运行在流水线当中,需要熟悉相关命令的语法。

Keyword Description
script 运行的 Shell 命令或脚本
image 使用 docker 映像
services 使用 docker 服务映像
before_script 在作业运行前运行脚本
after_script 在作业运行后运行脚本
stages 定义管道中的阶段,运行顺序
only 限制创建作业的条件
except 限制未创建作业的条件
rules 条件列表,用于评估和确定作业的选定属性,以及是否创建该作业。不能only与/ except一起使用
tags 用于选择 Runner 的标签列表
allow_failure 允许作业失败,失败的 job 不会影响提交状态
when 什么时候开始运行工作
environment 作业部署到的环境的名称
cache 在后续运行之间应缓存的文件列表
artifacts 成功时附加到作业的文件和目录列表
dependencies 通过提供要从中获取工件的作业列表,限制将哪些工件传递给特定作业
retry 发生故障时可以自动重试作业的时间和次数
timeout 定义自定义作业级别的超时,该超时优先于项目范围的设置
parallel 多个作业并行运行
trigger 定义下游管道触发
include 允许此作业引用外部 YAML 文件
extends 该作业将要继承的配置条目
pages 上载作业结果以用于GitLab页面
variables 在作业级别上定义作业变量

job——作业定义

  在每个项目中,均可使用名为.gitlab-ci.yml的 YAML 文件配置GitLab CI / CD Pipeline (管道)。

  在 Pipeline 中,可以定义一个或多个 job(作业),对每个 job 而言:

  • 必须具有唯一的名称(不能使用关键字)
  • 是独立执行的
  • 至少要包含一个 script

  如下,我们在 Pipeline 中定义了两个作业,每个作业运行不同的命令。命令可以是shell或脚本。

1
2
3
4
5
job1:
script: "execute-script-for-job1"

job2:
script: "execute-script-for-job2"

script——作业执行的脚本

  script命令代表 job 中要执行的脚本。

  script命令使用时需要用单引号或双引号引起来。
  举个例子,包含冒号的命令( : )需要加引号,以便被包裹的 YAML 解析器将其作为一个字符串,而不是一个键值对,因此使用特殊字符时要特别小心,特殊字符包括: :{}[],&*#?|-<>=!%@.

before_script

  若想在每个作业之前运行脚本,可以使用before_script命令。
  对此命令而言:

  • 取值必须是一个数组
  • 既可在全局定义,也可在 job 中定义,在 job 中定义则会覆盖全局
1
2
3
4
5
6
7
8
9
before_script:
- echo "start execute CI/CD process!!!"

job1:
before_script:
- echo "start execute job1 !!!"
script:
- echo "mvn clean "
- echo "mvn install"
after_script

  若想在每个作业(包括失败的作业)之后运行脚本,可以使用after_script命  对此命令而言:

  • 取值必须是一个数组
  • 既可在全局定义,也可在 job 中定义,在 job 中定义则会覆盖全局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
before_script:
- echo "start execute CI/CD process !!!"


job1:
before_script:
- echo "start execute job1 !!!"
script:
- echo "mvn clean "
- echo "mvn install"
after_script:
- echo "finish execute job1!!!"

after_script:
- echo "finish execute CI/CD process !!!"
before_script VS after_script

  before_scriptafter_script有什么区别呢?
  对after_script命令而言,其执行失败不会导致作业失败,但若before_script 命令执行失败,则会导致整个作业失败,那么其他作业将不再执行。

  另外,若作业执行失败,是不会影响after_script命令运行的(这类似 Java 的 finally)。

only & except——作业的创建条件

  作业什么时候会被创建?
  在.gitlab-ci.yml中,通过onlyexcept两个参数定义相关策略对 job 的创建进行限制:

  • only:定义了作业需要执行的分支或者标签
  • except:定义了作业不会执行的分支或者标签

  下面是策略规则:

  • onlyexcept 可以使用正则表达式。
  • onlyexcept 允许指定用于过滤 forks 作业的存储库路径。
  • onlyexcept 可同时使用,若在一个作业中同时定义了 onlyexcept ,则同时 only except 进行过滤(注意,不是忽略 except 条件) 。
  • onlyexcept 中可以使用特殊的关键字,如 branchestagsapiexternalpipelinespushesschedulestriggerswebmerge_requestschats 等,说明见下表:
描述
branches 当一个分支被 push 上来
tags 当一个打了 tag 的分支被 push 上来
api 当一个 pipline 被 piplines api 所触发调起
external 当使用了 GitLab 以外的 CI 服务
pipelines 针对多项目触发器而言,当使用 CI_JOB_TOKEN 并使用 gitlab 所提供的 api 创建多个 pipelines 的时候
pushes 当 pipeline 被用户的 git push 操作所触发时
schedules 针对预定好的 pipline
triggers 用 token 创建 piplines 时
web 在 GitLab 的 Pipelines 标签页下,用户点击run pipline按钮时
特定分支

  下面的例子,job将会只在*issue-开头的分支下执行。

1
2
3
job:
only:
- /^issue-.*$/

  在这个例子中,job 只会在打了 tag 的分支,或者被 api 所触发,或者每日构建任务上运行

1
2
3
4
5
6
job:
# use special keywords
only:
- tags
- triggers
- schedules
默认策略
  • 当一个作业没有定义 only 规则时,其默认为 only: ['branches', 'tags']
  • 如果一个作业没有定义 except 规则时,则默认 except 规则为空。

  因此,下面这个两个例子是等价的:

1
2
3
4
5
6
job:
script: echo 'test'

job:
script: echo 'test'
only: ['branches', 'tags']

tags——绑定作业执行时的 Runner

  tags 关键字用于指定 GitLab Runner 运行时中使用哪一个 Runner 去执行作业。

  下面这个例子中,只有注册 Runner 时定义了 rubypostgres 两个标签的 Runner 才能执行作业:

1
2
3
4
job:
tags:
- ruby
- postgres

stages——作业执行顺序

  既然.gitlab-ci.yml文件中可以定义各种各样的作业,那么如何确定这些作业的执行顺序呢?

  这可以借助stages,在其中可以定义一系列的stage(阶段)后,后续将不同的stage和作业进行绑定,那么作业就会按照stages中定义的stage先后顺序依次执行。

  举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
stages:
- test
- package

job1:
stage: test
script:
- mvn test

job2:
stage: package
script:
- mvn package

  我们定义了两个阶段,创建了两个作业绑定不同的阶段,这样就会先执行测试的 job1,然后在执行打包的 job2

variables

  在 .gitlab-ci.yml 配置文件中可通过 variables 关键字配置全局变量或作业级局部变量,变量特点如下:

  • 可以在 variables 关键字中定义非敏感性配置
  • 可以在 GitLab WEB 界面定义一些敏感性配置变量,或者可能变动的变量
  • variables 关键字使用在作业层级时,它会覆盖全局变量或预定义变量
  • 全局变量可以在各个作业中作业,而作业级别的局部变量只能在该作业中使用
  • script 中使用 export 可以导出当前可用的变量信息
  • 作业内部修改全局变量只对当前作用生效,不会影响其他作业
  • 可以使用赋值语句对全局变量或局部变量进行重新赋值

when——作业能否执行

  when参数用于对当前作业是否执行添加时机限制策略。

  when取值策略包括:

  • on_success:只在前置执行过的所有作业都成功时才执行当前作业,默认值,因此有任何失败时管道流会停止执行后续任务
  • on_failure:只在前置执行过的作业中有一个失败的时候才执行
  • always:无论之前作业执行状态如何,总是执行
  • manual:在 GitLab UI 页面手动执行作业
  • delayed: 延迟执行作业(前面的作业结束时计时器马上开始计时),配合start_in关键字一起作用,start_in设置的值必须小于或等于 1 小时,start_in 值示例: 10 seconds 、 30 minutes 、 1 hour

artifacts

  artifacts用于指定在作业成功、失败、或者一直等待状态下时,将一系列的文件或文件夹附加到作业中。

  • artifacts 可以称为 工件或者归档文件
  • 作业完成后,工件被发送到 GitLab,可以在 GitLab Web 界面下载
  • 默认情况下,只有成功的作业才会生成工件
  • 并不是所有的 executor 执行器都支持工件
  • 工件的详细介绍可参考 Introduction to job artifacts
paths

  artifactspaths参数可指定哪些文件或文件夹会被打包成工件(仅可使用当前项目工作空间内的文件)

  • 要在不同作业间传递工件,需要额外使用dependencies参数

  
下面示例,将目录 binaries/ 和文件 .config 打包成工件:

1
2
3
4
artifacts:
paths:
- binaries/
- .config
禁用工件传递

  若要禁用工件传递,可使用空依赖关系定义作业:

1
2
3
4
job:
stage: build
script: mvn package
dependencies: []
工件优化存储

  可仅为打标记的release发布版本创建工作,以避免临时构建产生大量的存储需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
default-job:
script:
- mvn test -U
except:
- tags

release-job:
script:
- mvn package -U
artifacts:
paths:
- target/*.jar
only:
- tags

  上面的示例中,default-job 作业不会在打标记的release发布版本中执行,而 release-job 只会在打标记的release发布版本执行,并且将 target/*.war 打包成工件以供下载。

name

  工件的默认名称是 artifacts ,当下载时名称是 artifacts.zip
  通过 artifacts:name 关键字可以自定义工件的归档名称,这样你可以为每个工件设置独一无二的名称,归档名称可以使用预定义的变量。
  如果分支名称中包含斜杠(比如 feature/my-feature ),推荐使用 $CI_COMMIT_REF_SLUG 代替 $CI_COMMIT_REF_NAME 作为工件名称。

  使用作业名称使用工件名称:

1
2
3
4
5
job:
artifacts:
name: "$CI_JOB_NAME"
paths:
- binaries/

  使用当前分支或tag版本标签名作为工件名称:

1
2
3
4
5
job:
artifacts:
name: "$CI_COMMIT_REF_NAME"
paths:
- binaries/

  同时使用当前作业名称以及当前分支或tag版本标签名作为工件名称:

1
2
3
4
5
job:
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
paths:
- binaries/

untracked(使用较少,了解即可)

  untracked 可将未加入 git 版本库的文件作为工件文件。

  • artifacts:untracked 将会忽略配置文件 .gitignore

  将所有的未跟踪文件打包成工件:

1
2
artifacts:
untracked: true

  将所有的未跟踪文件以及目录 binaries 中文件打包成工件:

1
2
3
4
artifacts:
untracked: true
paths:
- binaries/

when(使用较少,了解即可)

  when用于在作业失败时或者忽略失败时上传工件。

  when 可以设置以下值:

  • on_success:默认值,当作业成功上传工件
  • on_failure:当作业失败上传工件
  • always:无论作业是否成功一直上传工件

expire_in

  expire_in 用于设置工件的过期时间。

  • 你可以点击界面上的 Keep 保持按钮,永久保存工件
  • 工件到期后,默认情况下每小时删除一次工件(通过 cron 作业),删除后不能再访问该工件
  • 工件默认有效期为 30 天,可通过 Admin area –> Settings –> Continuous Integration and Deployment 设置默认有效期

  下面示例中工件有效期为一小时:

1
2
3
job:
artifacts:
expire_in: 1h

reports

  reports 用于收集测试报告,并在GitLab UI界面中显示出来。

  • 无论作业是否成功,都会收集测试报告
  • 可以通过设置工件的打包路径 artifacts:paths 添加测试的报告输出文件。
  • artifacts:reports:junit 可以用来收集单元测试的报告,查看 JUnit test reports 获取更详细的信息和示例

dependencies

  dependencies 依赖关键字需要和 artifacts 工件关键字联合使用,允许你在不同作业间传递工件。

  • 默认情况下,会传递所有本作业之前阶段的所有工件
  • 需要在作业上下文中定义 dependencies 依赖关键字,并指出所有需要使用的前序工件的作业名称列表。 作业列表中不能使用该作业后的作业名称
  • 定义空的依赖项,将下不会下载任何工件
  • 使用依赖项不会考虑前面作业的运行状态

  快速入门示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 在 build:osx 和 `build:linux` 两个作业中,都定义了工件
build:osx:
stage: build
script: make build:osx
artifacts:
paths:
- binaries/

build:linux:
stage: build
script: make build:linux
artifacts:
paths:
- binaries/
# test:osx 作业执行时,将会下载并解压 build:osx 的工件内容
test:osx:
stage: test
script: make test:osx
dependencies:
- build:osx
# test:linux 也会获取 build:linux 的工件。
test:linux:
stage: test
script: make test:linux
dependencies:
- build:linux
# deploy 作业会下载全部工件
deploy:
stage: deploy
script: make deploy

  上面示例中,

parallel——相同作业并发执行

  一种作业允许并行执行,通过parallel参数指定并行执行的实例数,取值必须大于或等于 2 并且小于或等于 50。

  携带parallel参数的作业将创建 N 个并行运行的实例. 它们从job_name 1/Njob_name N/N依次命名。

1
2
3
4
5
test:
stage: test
script:
- mvn test
parallel: 5

实战:Java 项目自动部署

  一般 Java 项目会使用 Maven 工具打包,因此若想通过 GitLab-Runner 自动部署 Java 项目,需要在 GitLab-Runner 所在服务器安装 Maven 环境。

Maven 环境搭建

1
yum install -y java-1.8.0-openjdk-devel.x86_64
1
2
3
4
5
6
7
# 下载 maven,地址:https://maven.apache.org/download.cgi
tar -zxvf apache-maven-3.8.5-bin.tar.gz
mv apache-maven-3.8.5/ maven
mkdir -p /data/softwares
mv maven/ /data/softwares/
cd /data/softwares/maven
mkdir repository
1
2
3
4
5
6
7
8
9
10
11
# 修改 maven 配置文件
vim /data/softwares/maven/conf/settings.xml
# 设置本地仓库路径
<localRepository>/data/softwares/maven/repository</localRepository>
# 设置镜像仓库
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</mirror>
1
2
3
4
5
6
7
8
# 赋予 gitlab-runner /data 目录访问权限
chown -R gitlab-runner /data/
chgrp -R gitlab-runner /data/
vim /etc/profile
export MAVEN_HOME=/data/softwares/maven
export PATH=${PATH}:${MAVEN_HOME}/bin

source /etc/profile

应用服务器通道建立

  一般情况下,GitLab-Runner 服务和 Java 应用分别部署到两台不同的服务器上,因此 GitLab-Runner 自动部署应用时需要和 Java 应用服务器建立连接,连接可以使用以下两种方式:

  • sshpass指定密码连接
  • ssh配置密钥,免密登陆

  这里我们使用常用的第二种方式,配置步骤如下:

1
2
3
4
# 生产密钥
ssh-keygen
# 复制公钥到指定服务器
ssh-copy-id -i ~/.ssh/id_rsa.pub user@xxx.xxx.xxx.xxx

脚本编写

  在后端应用增加.gitlab-ci.yml并配置脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
variables:
package_path: target
package_name: gateway.jar
remote_server: admin@10.22.39.22
remote_post: 22
upload_path: /data/backend/gateway/tmp
start_path: /data/backend/gateway

stages:
- build-upload
- restart

maven-build-upload:
stage: build-upload
only:
- dev_1.0
tags:
- test
before_script:
- mvn clean install -Dmaven.test.skip=true
- echo "package success!!!"
script:
- scp -vr -P $remote_post ./$package_path/$package_name $remote_server:$upload_path
- echo "upload success!!!"
dependencies: []
retry: 2

restart:
stage: restart
only:
- dev_1.0
tags:
- test
script:
- ssh -p$remote_post $remote_server "cd ${start_path}; ./deploy.sh"
dependencies: []

注意

  通过ssh执行上面部分脚本时可能成功,如通过ssh执行java -jar启动应用时,发现找不到JAVA_HOME
  研究后发现远程登录和直接登录执行的文件是不一样的:

  • /etc/profile:当用户登录时,该文件被执行
  • /etc/bashrc:当bash shell被打开时,该文件被执行

  ssh作为远程登录的方式进入,触发执行的是/etc/bashrc文件,而不是/etc/profile,所以会发生找不到JAVA_HOME的问题,因此,需要将java的配置信息配置到bashrc的文件中去,配置步骤如下所示:

  • ① 命令 vim ~/.bashrc 进入到文件;
  • ② 文件末尾增加 Java 相关变量保存退出
  • ③ 执行source ~/.bashrc命令更新

实战:前端项目自动部署

Node 环境搭建

1
2
3
4
5
6
7
mkdir -p /data/softwares
cd /data/softwares
wget https://nodejs.org/dist/v14.17.0/node-v14.17.0-linux-x64.tar.gz
tar -zxvf node-v14.17.0-linux-x64.tar.gz
mv node-v14.17.0-linux-x64 node-v14.17.0

vim /etc/profile

  文件末尾增加:

1
export PATH=$PATH:/data/softwares/node-v14.17.0/bin

  刷新配置生效:

1
source /etc/profile

  测试是否成功:

1
node -v

1
2
3
4
5
6
# gitlab-runner 用户下
npm i yarn -g
npm install -g @vue/cli-service
yarn global add @vue/cli
#or
npm i pnpm -g
1
2
3
# 赋予 gitlab-runner /data/softwares 目录访问权限
chown -R gitlab-runner /data/softwares
chgrp -R gitlab-runner /data/softwares

应用服务器通道建立

  同上。

脚本编写

  在前端应用增加.gitlab-ci.yml并配置脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
variables:
remote_server: admin@192.168.0.20
remote_post: 22
upload_path: /data/golden/frontend/tmp/system
front_path: /data/golden/frontend/system/
compress_file: dist.tar.gz

stages:
- install-build
- dist-upload
- deploy

#cache:
# key: ${CI_BUILD_REF_NAME}
# paths:
# - node_modules/

install-build:
stage: install-build
tags:
- global
only:
- test
# 执行脚本
script:
- npm install
- npm run build:test
artifacts:
expire_in: 48h
paths:
- dist/
after_script:
- "cd ${CI_PROJECT_DIR}/dist; tar -cvf ${compress_file} *"

# 上传压缩包
dist-upload:
stage: dist-upload
tags:
- global
only:
- test
script:
- scp -vr -P $remote_post ${CI_PROJECT_DIR}/dist/${compress_file} $remote_server:$upload_path

# 解压更新
deploy:
stage: deploy
tags:
- global
script:
- ssh $remote_server "mv ${upload_path}/${compress_file} ${front_path}"
- ssh $remote_server "cd ${front_path}; tar -xvf ${compress_file}"

参考

文章信息

时间 说明
2022-03-23 初稿
2022-03-26 完稿
2022-06-19 新增数据迁移小节
2022-12-16 新增定时任务备份小节
2023-02-12 新增定 CI/CD 实战小节
0%