使用 GitLab Runner 配置 CI/CD 流水线
使用 GitLab Runner 配置 CI/CD 流水线
前言
本文以实际例子介绍如何使用 GitLab Runner
配置 CI/CD
流水线,实现持续集成和持续交付。
安装配置 Runner
.gitlab-ci.yml
文件中定义的作业运行在 Runner 中。 Runner 需要通过网络访问 GitLab.
虽然 Runner 和 GitLab 可以安装在一台机器上,但不推荐这么做。
Runner 可以由多个项目中共享,但出于安全等方面的原因,一个项目或一组项目最好有专门的服务器、虚拟机或容器运行 Runner.
安装配置的官方文档见公司 GitLab 服务器中相关帮助。
安装
各种平台下的安装方法详见官方安装帮助文档。
RHEL/CentOS/Fedora
# For RHEL/CentOS/Fedora
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
# For RHEL/CentOS/Fedora
sudo yum install gitlab-runner
详见官方安装帮助文档。
配置
Step1: 获取注册令牌 (token)
以配置 Specific Runners 为例,在 GitLab 实例中,进入 项目设置 » CI/CD 页面,展开 Runner 可以获取项目的注册令牌。在这个页面也可以修改 Runner 和 CI/CD 的一些配置。
例如,对于 vue-element-admin 这个项目,可以转到这个链接。
泄露注册令牌可能会产生一些安全问题,故应妥善保管。万一泄露,可在本页面重置注册令牌。
Step2: 规划 Executors
Shell
Shell 的优势是灵活、调试简单,但一些手工操作并不能从代码中体现出来。在项目初期可以考虑这种方式。
Docker
Docker 是应用最广的方式,可以适应各种类型程序的编译打包。
Step3: 注册
除了 Executors, 在注册 Runner 的过程中还需要考虑命名、标签等,不同平台的注册方法也略有不同,详见官方文档。
Linux
- 运行命令
sudo gitlab-runner register
- 按提示输入 GitLab instance URL, URL 可从前面复制注册令牌的页面复制
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab.eaglesoftware.cn/
- 按提示输入注册令牌
Please enter the gitlab-ci token for this runner:
xxxxxxxxxxxxxxxxxxxx
- 按提示输入 Runner 描述信息,以后可从 CI/CD 设置页面修改
Please enter the gitlab-ci description for this runner:
[localhost.localdomain]: vm-wuzhuangzhuang
- 按提示输入与 Runner 关联的标签,标签可以用于选择 Runner, 以后可从 CI/CD 设置页面修改
Please enter the gitlab-ci tags for this runner (comma separated):
vm-wzz,centos
- 按提示输入 Runner Executors
Please enter the executor: docker+machine, docker-ssh+machine, kubernetes, virtualbox, docker, docker-ssh, parallels, shell, ssh, custom:
shell
- 如果选择 Docker 作为 executor, 还需要输入默认镜像,在 .gitlab-ci.yml 中未定义镜像时使用。
示例
官方文档有各种各样的示例。这里以 vue-element-admin 这个项目的配置演进为例,说明配置 CI/CD 过程中遇到的问题、想要达成的目标,以及解决办法。
项目说明
CI/CD 流水线一般由提交代码触发,包括自动编译(打包)、自动化测试、自动部署等步骤 (stages)。
要配置 CI/CD 流水线,首先需要知道手工如何实现这些工作,然后再编写代码实现自动化。
vue-element-admin 这个项目是基于 npm 的前端演示项目,没有后端和数据库等组件,项目组提供了一台 CentOS 虚拟机用于打包和部署,但没有提供自动化测试相关过程,所以只需在同一台机器上做打包和部署两个过程。
Version 1: Shell 版
Step1: Runner 安装配置
见前面的描述。
Step2: 打包工具安装
虽然这个项目有比较详尽的 README.md, 但仍没说明不支持最新的 node 12.16.1, 也没有说明需要安装 Development Tools. (实际上 .travis.yml 中有一行 node_js: 10)
如果对一个历史比较悠久的项目做一点小改动,这可能是一个需要时间修补的小坑。
# 去除以前安装的 nodejs
sudo yum remove nodejs
# 清除以前的安装源
sudo yum clean all
# 按官方文档 https://github.com/nodesource/distributions/blob/master/README.md#debinstall 设置安装源
curl -sL https://rpm.nodesource.com/setup_10.x | sudo bash -
# 安装 nodejs
sudo yum install -y nodejs
# 安装开发工具
yum install gcc-c++ make
# 或
# yum groupinstall 'Development Tools'
# 查看版本信息
npm version
查看版本信息的结果:
{ npm: '6.13.4',
ares: '1.15.0',
brotli: '1.0.7',
cldr: '35.1',
http_parser: '2.9.3',
icu: '64.2',
modules: '64',
napi: '5',
nghttp2: '1.39.2',
node: '10.19.0',
openssl: '1.1.1d',
tz: '2019c',
unicode: '12.1',
uv: '1.28.0',
v8: '6.8.275.32-node.55',
zlib: '1.2.11' }
Step3: 配置权限
因为后面想直接用 gitlab-runner 用户操作 docker 服务,故将 gitlab-runner 加入 docker 组中。
sudo usermod -aG docker gitlab-runner
sudo -u gitlab-runner -H docker info
Step4: 编写 .gitlab-ci.yml 文件
.gitlab-ci.yml
配置流水线如何运行,其编写详见 GitLab CI/CD Pipeline Configuration Reference.
v1.1 - 初始版本
第 1 版设置 2 个步骤,打包和部署, .gitlab-ci.yml
文件的内容如下。
stages:
- build
- deploy
# 所有 stage 之前的操作
before_script:
- pwd
- npm set registry https://mirrors.huaweicloud.com/repository/npm/
cache:
paths:
- node_modules/
- dist
build_job:
stage: build
tags:
- centos
- vm-wzz
script:
- echo '打包'
- npm install
- rm -rf ./dist
- npm run build
deploy_job:
stage: deploy
tags:
- centos
- vm-wzz
script:
- echo '部署'
- cd docker-compose
- docker-compose stop
- docker-compose up -d
在项目组提供的虚拟机上, 总耗时 03:06, 其中 build_job 耗时 02:03, deploy_job 耗时 01:02, deploy_job 耗时超出预期。
经检查,deploy_job 这一步要从 GitLab 上重新拉取代码,前一步 build_job 生成的 node_modules/ 和 dist/ 会被删除,然后从缓存区复制过来,虽然是本地缓存,但 node_modules/ 占用空间多达几百兆,而且不是两个步骤公用的,没有必要进行缓存。
116 ./public
1184 ./.git
44 ./plop-templates
9324 ./dist
8 ./build
12 ./docker-compose
84 ./mock
450948 ./node_modules
2868 ./src
40 ./tests
465488 .
v1.2 - 合并打包和部署
合并两个阶段后,总耗时 02:32.
修改后的 .gitlab-ci.yml
文件内容如下。
stages:
- build
# 所有 stage 之前的操作
before_script:
- pwd
- npm set registry https://mirrors.huaweicloud.com/repository/npm/
cache:
paths:
- node_modules/
- dist
build_job:
stage: build
tags:
- shell
- vm-wzz
script:
- echo '打包+部署'
- npm install
- npm run build
- cd docker-compose
- docker-compose stop
- docker-compose up -d
在 package.json
没有变化时,npm install
没有必要每次运行。
v1.3 - npm install 仅在需要时运行
在前面的基础上,继续修改 .gitlab-ci.yml
文件,只在 package.json
文件发生变化时执行 npm install
.
新提交版本触发的流水线最快总耗时 01:23。
stages:
- npm_install
- build
# 所有 stage 之前的操作
before_script:
- pwd
- npm set registry https://mirrors.huaweicloud.com/repository/npm/
cache:
paths:
- node_modules/
npm_install_job:
stage: npm_install
tags:
- shell
- vm-wzz
script:
- npm install
only:
changes:
- package.json
build_job:
stage: build
tags:
- shell
- vm-wzz
script:
- npm run build
- cd docker-compose
- docker-compose stop
- docker-compose up -d
按照同样的方法,也可以在 docker-compose/ 文件夹下面的文件发生变化时,重新构建 Docker 镜像。
曾经踩过的坑(修复的问题)
网络问题还是 Git 版本问题?
使用项目组提供的虚拟机,曾经有多次出现拉取代码出错的问题,而在华为云的云主机上注册的 Runner 从未出现过类似问题,当时怀疑是公司网络不稳定。
出现过的错误有:
- The remote end hung up unexpectedly
重新初始化现存的 Git 版本库于 /home/gitlab-runner/builds/z5U7Uaf7/0/opd/vue-element-admin/.git/
error: RPC failed; result=6, HTTP code = 0
fatal: The remote end hung up unexpectedly
ERROR: Job failed: exit status 1
- Could not resolve host: gitlab.eaglesoftware.cn
Fetching changes with git depth set to 50...
00:01
重新初始化现存的 Git 版本库于 /home/gitlab-runner/builds/z5U7Uaf7/0/opd/vue-element-admin/.git/
fatal: unable to access 'https://gitlab-ci-token:[MASKED]@gitlab.eaglesoftware.cn/opd/vue-element-admin.git/': Could not resolve host: gitlab.eaglesoftware.cn; Unknown error
ERROR: Job failed: exit status 1
- Could not resolve host: github.com (npm install 过程中出现的问题)
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t https://github.com/nhn/raphael.git
npm ERR!
npm ERR! fatal: unable to access 'https://github.com/nhn/raphael.git/': Could not resolve host: github.com; Unknown error
npm ERR!
npm ERR! exited with error code: 128
npm ERR! A complete log of this run can be found in:
npm ERR! /home/gitlab-runner/.npm/_logs/2020-03-09T07_25_38_871Z-debug.log
ERROR: Job failed: exit status 1
- git fetch-pack: expected shallow list
Running on localhost.localdomain...
00:00
Fetching changes with git depth set to 50...
00:03
重新初始化现存的 Git 版本库于 /home/gitlab-runner/builds/z5U7Uaf7/0/opd/vue-element-admin/.git/
fatal: git fetch-pack: expected shallow list
fatal: The remote end hung up unexpectedly
ERROR: Job failed: exit status 1
前 3 个可能真是网络不稳定,但第 4 个多次重试仍然不行,经百度搜索,确认是 CentOS 7 在安装 Runner 时默认安装的 Git 版本过低 (git version 1.8.3.1), 升级 Git 版本 (git version 2.22.2) 后恢复正常。由此可见,即使是个虚拟机,最好也安装个靠谱的版本。
CentOS 7 下的升级过程在 Git 官网有链接,这里使用 ius.io 提供的安装源:
yum install \
https://repo.ius.io/ius-release-el7.rpm \
https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum repolist
yum remove git
yum provides git
yum install git222
git --version
# 删除 git 时同时删除了依赖 git 的 gitlab-runner, 所以重新安装
yum install gitlab-runner
node 版本
node 之类的工具版本更新比较快,而且新版本不一定支持原先的代码。辛辛苦苦安装好 node 12.16.1, 还要想办法清除。详见前面描述。
如果使用 Docker, 这个坑修复的成本会低很多。
Docker Engine 未启动
$ docker-compose stop
Couldn't connect to Docker daemon at http+docker://localhost - is it running?
If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable.
ERROR: Job failed: exit status 1
这个问题比较明显,启动 Docker Engine 并设置开机启动后恢复正常。
systemctl start docker
systemctl enable docker
Version 2: Docker 版
Why
前面的版本虽然完成了打包和部署,但坑比较多,而且打包和部署是在同一台机器上,而测试环境、正式运行环境一般都是与开发环境分离的。
在这个版本中,我们使用 docker 完成打包,并使用 ssh 把打包好的软件部署到运行环境中。
这种部署方式需要运行 runner 的机器要能通过网络访问部署目标机器,比如从公司内网可以部署到云服务器,但反过来就不太方便;如果目标机器的 IP 地址是动态获取的,也容易出现问题。
Step1: Runner 安装配置
见前面的描述,以下略有不同:
Step2: 生成密钥对并分别配置到目标机器和 GitLab 环境变量
生成密钥对,不设置 passphrase, 然后将公钥配置到目标机器。
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/96129/.ssh/id_rsa): id_rsa_ci_test
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_ci_test.
Your public key has been saved in id_rsa_ci_test.pub.
$ ssh-copy-id -i id_rsa_ci_test.pub root@10.44.111.27
$ cat id_rsa_ci_test
在 GitLab 项目设置 » CI/CD 页面,新增 3 个变量:
- SERVER_HOST: 目标机器的 IP 地址, 如前面的 10.44.111.27
- SERVER_USER: 目标机器的用户 login, 如前面的 root
- STAGING_PRIVATE_KEY: 私钥的全部内容,如前面 cat id_rsa_ci_test 的输出结果
这 3 个变量会在接下来的 .gitlab-ci.yml 文件中用到。
Step3: 编写 .gitlab-ci.yml 文件
测试好的 .gitlab-ci.yml 文件如下。
image: node:10.19.0-buster
stages:
- npm_install
- build_docker
- deploy
variables:
TARGET_DIR: ~/vue-element-admin/
# 所有 stage 之前的操作
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- mkdir -p ~/.ssh
- eval $(ssh-agent -s)
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ssh-add <(echo "$STAGING_PRIVATE_KEY")
- npm set registry https://mirrors.huaweicloud.com/repository/npm/
stage_npm_install:
stage: npm_install
tags:
- docker
- vm-wzz
cache:
paths:
- node_modules/
script:
- npm install
only:
changes:
- package.json
stage_build_docker:
stage: build_docker
tags:
- docker
- vm-wzz
script:
- ssh $SERVER_USER@$SERVER_HOST "mkdir -p $TARGET_DIR/docker-compose/"
- scp -r docker-compose/* $SERVER_USER@$SERVER_HOST:$TARGET_DIR/docker-compose/
- ssh $SERVER_USER@$SERVER_HOST "docker-compose -f $TARGET_DIR/docker-compose/docker-compose.yml build"
only:
changes:
- docker-compose/**/*
stage_deploy:
stage: deploy
artifacts:
paths:
- dist/
tags:
- docker
- vm-wzz
cache:
paths:
- node_modules/
policy: pull
script:
- npm run build
- ssh $SERVER_USER@$SERVER_HOST "mkdir -p $TARGET_DIR/dist/"
- scp -r dist/* $SERVER_USER@$SERVER_HOST:$TARGET_DIR/dist/
- ssh $SERVER_USER@$SERVER_HOST "mkdir -p $TARGET_DIR/docker-compose/"
- scp -r docker-compose/* $SERVER_USER@$SERVER_HOST:$TARGET_DIR/docker-compose/
- ssh $SERVER_USER@$SERVER_HOST "cd $TARGET_DIR/docker-compose/ && docker-compose stop && docker-compose up -d"
这里把整个流水线分成 3 步,第 1 步为 npm_install, 只在 package.json 发生变化时运行,生成的文件作为后续步骤的缓存。
第 2 步为 build_docker, 在 docker-compose/ 文件夹下的内容发生变化时运行,由于其中并没有 Dockerfile, 直接使用的 nginx 服务,这一步并没有实质作用。
第 3 步为 deploy, 拉取第 1 步生成的缓存目录 node_modules/, 进行打包,并将打包后的软件复制到目标机器,指示目标机器重新启动容器。
注意事项
由于第 3 步依赖第 1 步生成的缓存,而第 1 步只在 package.json 发生变化时才触发运行,在首次提交 .gitlab-ci.yml 而 package.json 未同时变化时,流水线只运行第 3 步会出现失败。
此时,应从 GitLab 项目的 CI / CD 页面单击“运行流水线”,手动触发完整流水线的运行,以后再提交代码时触发运行的流水线将不会因此问题失败。
结果分析
由于拉取 docker 镜像、复制文件等相比 Version 1 的方式都将带来额外的资源消耗,增加了向 GitLab 上传工件的环节,流水线最快总耗时为 02:55 (在 package.json 等不发生变化时), 比 Version 1 的最佳结果约慢 90 秒钟。
但这种部署方式的几乎把所有步骤都写入代码中,非常清楚,而性能的下降可以通过更好的硬件设施来弥补。
更好的部署方式是把打包好的工件(如生成容器镜像)推送到统一的注册中心,在运行环境中拉取工件运行。这样,只有注册中心需要有固定的域名或 IP 地址,但这对资源要求较高,暂不考虑这种方式。
No Comments