Gitlab的多项目流水线支持由一个项目的流水线触发另一个项目的流水线,并可以在一个可视化整个流水线及流水线间的相互依赖关系,解决了项目间协同的问题。
Gitlab从12.7版本开始引入了父子流水线特性,在12.9版本引入动态子流水线特性。子流水线可以根据阶段顺序自由地执行,不用等待父流水线不相干的工作,配置被拆分得更小,减少了理解整体配置的认知负担,同时由于导入在子流水线完成,也减少了命名空间冲突的可能,界面体验也有改善。
Gitlab子流水线
项目组有一个专门用于生成自定义容器镜像的Gitlab项目,该项目下很多目录,每个目录对应于一种自定义镜像,目录下有Dockerfile及相关文件,通过运行目录下的名称类似buildXXX.sh这样的脚本来构建和上传镜像。
为了有一个具备权威性、稳定性、方便性的地方来构建这些镜像,想到了利用Gitlab动态子流水线的特性,通过搜索项目里所有镜像构建shell脚本文件,并提取脚本里的镜像名称和标签,再生成以各镜像名称和标签命名的动态步骤,最后由人员手动触发构建镜像。
这可能不是一个很好的方案,比如会生成很多并不需要的Job、没有全部自动化、缺少对镜像间依赖关系的处理等,但也不失为一种思路,而且这些问题也可以通过进一步分析Dockerfile之间的关系、本次变更涉及的文件等优化解决,下面两张图为最终呈现的效果。
流水线
Job列表
父流水线和Gitlab官网提供的案例一样,由generate-config、child-pipeline两个Job(名称可自定)构成,前一个名为generate-config的Job会生成名为generated-config.yml的Gitlab流水线定义文件,该文件包含了需要动态运行的Job,通过artifacts机制传递给后续名为child-pipeline的Job,并触发它。
为了演示清楚,生成generated-config.yml文件Shell脚本在下面的代码中暂时省略掉了,下文将补上。
复制
stages: - prepare - imagegenerate-config: stage: prepare script:- 这里会生成generated-config.yml,暂时省略 artifacts:paths: - generated-config.ymlchild-pipeline: stage: image trigger:include: - artifact: generated-config.ymljob: generate-config
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
由于动态生成的每个Job的定义都是一样的,所以在生成的generated-config.yml中利用Gitlab流水线模板机制定义了一个Job模板,以复用和精简代码。
下面的代码片段来自generated-config.yml文件的最前面部分,我们可以看到定义了一个名为build_image的模板,该模板定义的job将手动触发,并启动一个带有docker、oc命令的镜像来运行构建脚本,构建脚本中首先为脚本授予执行权限,然后进入脚本所在目录,最后运行该脚本。具体脚本文件将由SCRIPT_PATH变量在每个具体Job中提供,比如从第11行开始的名为redis-cluster:5.0.7-ocp的job,引入了build_image模板,提供了SCRIPT_PATH变量的值。
复制
.job_template: &build_image stage: build when: manual image: nexus.yourcompany.com/tools/docker-18.09.7/oc:4.6.21 script:- chmod +x ${SCRIPT_PATH}- cd ${SCRIPT_PATH%/*} - /bin/sh ./${SCRIPT_PATH##*/} redis-cluster:5.0.7-ocp: variables:SCRIPT_PATH: /tmp/TBzsZA42/0/docker-images/redis-cluster/build-openshift.sh <<: *build_image
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
在生成动态子流水线的脚本中,首先生成模板部分,创建并输出到文件。
复制
generate-config: stage: prepare script:- |- echo -e " .job_template: &build_imagestage: buildwhen: manualimage: nexus.yourcompany.com/tools/docker-18.09.7/oc:4.6.21script: - chmod +x \${SCRIPT_PATH} - cd \${SCRIPT_PATH%/*} - /bin/sh ./\${SCRIPT_PATH##*/} " > generated-config.yml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
加下来生成每个Job,依次为查找build*.sh文件、搜索docker push并提取镜像名称和Tag、改为用制表符分割为两列、遍历每行并读取到file和image遍历、在循环中用echo命令append到文件。
复制
generate-config: stage: prepare script:- 生成模板的脚本- |- find -iname 'build*.sh' \ | xargs grep -ios ".*docker\s*push\s*.*/[^/\\\$]*" \ | sed 's|\.sh:|.sh\t|g' \ | sed -e 's|\([^\t]*\)\t.*\/\([^\/]*\)|\1\t\2|g' \ | while read file image; do SCRIPT_PATH="$CI_PROJECT_DIR${file:1}"; \ echo -e " $image:variables: SCRIPT_PATH: "${SCRIPT_PATH}"<<: *build_image" \ >> generated-config.yml ; \ done
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
下面是生成的generated-config.yml片段。
复制
.job_template: &build_image stage: build when: manual image: nexus.yourcompany.com/tools/docker-18.09.7/oc:4.6.21 script:- chmod +x ${SCRIPT_PATH}- cd ${SCRIPT_PATH%/*} - /bin/sh ./${SCRIPT_PATH##*/}redis-cluster:5.0.7-ocp: variables:SCRIPT_PATH: /tmp/TBzsZA42/0/docker-images/redis-cluster/build-openshift.sh <<: *build_image redis-cluster:5.0.7: variables:SCRIPT_PATH: /tmp/TBzsZA42/0/docker-images/redis-cluster/build.sh <<: *build_image
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
下面是完整的项目流水线定义:
复制
stages: - prepare - imagegenerate-config: stage: prepare script:- |- echo -e " .job_template: &build_imagestage: buildwhen: manualimage: nexus.yourcompany.com/tools/docker-18.09.7/oc:4.6.21script: - chmod +x \${SCRIPT_PATH} - cd \${SCRIPT_PATH%/*} - /bin/sh ./\${SCRIPT_PATH##*/} " > generated-config.yml- |- find -iname 'build*.sh' \ | xargs grep -ios ".*docker\s*push\s*.*/[^/\\\$]*" \ | sed 's|\.sh:|.sh\t|g' \ | sed -e 's|\([^\t]*\)\t.*\/\([^\/]*\)|\1\t\2|g' \ | while read file image; do SCRIPT_PATH="$CI_PROJECT_DIR${file:1}"; \ echo -e " $image:variables: SCRIPT_PATH: "${SCRIPT_PATH}"<<: *build_image" \ >> generated-config.yml ; \ done artifacts:paths: - generated-config.ymlchild-pipeline: stage: image trigger:include: - artifact: generated-config.ymljob: generate-config
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.