项目经理:我们有个 Java 的新项目要开始了,需要你帮忙设置调试一下 CI/CD Pipeline
DevOps 工程师:好的,没问题
ONE WEEK LATER...
项目经理:新项目的 CI/CD Pipeline 咋样了啊,我看研发还是手动人肉编译部署呢,严重影响了效率。CI/CD Pipeline 遇到什么问题了吗?
DevOps 工程师:已经写了50% 了,正在写其他的,然后调试,再一周能好。
项目经理:WHT,一个 CI/CD Pipeline 为啥这么久?而且前面有类似的 Java 项目,有可用的 CI/CD Pipeline,直接抽象成 template,然后用 include 语法在新项目中引入,再根据新项目的特性做微调即可。分分钟能搞定的啊。
DevOps 工程师:什么 template?什么 include?还能这么玩?
项目经理:我的乖乖……
封装和复用是软件编程设计中常用的特性,能够在很大程度上提高代码整体的可读性、可维护性。对于 CI/CD Pipeline 来讲也可以用封装和复用,来为新项目快速构建起一套合适的 CI/CD Pipeline,节约时间的同时,方便实现 Pipeline as Code,提升团队的端到端交付能力。
极狐GitLab include 语法
极狐GitLab CI/CD 是通过 YAML 语法来编写并存储到 YAML 文件中,可以将能够抽象出来的 CI/CD Pipeline(诸如单元测试、镜像构建、安全扫描等)代码存放到一个 YAML 文件,然后在其他项目的 .gitlab-ci.yml
中直接用 include
语法引入这个 YAML 文件即可。相当于用 include
一行代码实现了多行代码的功能,实现了 CI/CD Pipeline 的复用。这种做法有以下好处:
- 减少代码冗余。通过复用减少相同代码的复制使用,能有效减少代码冗余。
- 减小变更范围。如果有配置需要修改时,只要在
include
的文件中(upstream)进行修改,就可以将变更传递到使用此文件(downstream)的地方。一个变更,多处生效。 - 提高可维护性。将一个配置文件拆分成多个配置文件,避免形成“巨型”配置文件,修改时候无从下手,维护起来耗时耗力。
极狐GitLab include
的用法有以下四种,以引用不同来源的 YAML 文件:
local
引用同一项目中的流水线配置文件:
include:
- local: '/templates/.include-template.yml'
file
引入同一极狐GitLab 实例中的流水线配置文件:
include
- project: xiaomage/templates
- ref: main
file: docker-image-build-tamplates.yml
上述写法引入了 xiaomage/templates
项目下的 docker-image-build-tamplates.yml
配置文件。
remote
引用位于其他远端仓库中的流水线配置文件:
include:
- remote: 'https://jihulab.com/xiaomage/teamplates/raw/main/docker-image-build-tamplates.yml'
template
极狐GitLab 内置了很多开箱即用的 template,可以直接引入使用。所有的模版存储在 lib/gitlab/ci/templates
下面。也可以在项目中添加 .gitlab-ci.yml
文件的时候选择:
上图所示,引入了容器镜像扫描的功能。下面的 Demo 中会演示扫描效果。可以用同样的方法引入极狐GitLab DevSecOps 中的其他安全测试手段。
极狐GitLab include 中的 Override
用 include
引入的流水线配置是“共性”的内容,而实际使用的过程中是“个性”的,因此这时候需要用 Override 的方式,修改引入文件中的一些配置和参数。
比如,如果将镜像构建并推送至极狐GitLab 镜像仓库的流程定义为如下的流水线配置文件:
variables:
IMAGE_TAG: 1.0.0
build:
image: docker:latest
stage: build
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$IMAGE_TAG .
- docker push $CI_REGISTRY_IMAGE:$IMAGE_TAG
项目的 .gitlab-ci.yml
文件内容如下:
variables:
IMAGE_TAG: 2.0.0
include:
- remote: 'https://jihulab.com/xiaomage/teamplates/raw/main/docker-image-build-tamplates.yml'
则最终构建出来的镜像的 tag 是 2.0.0
。因为 .gitlab-ci.yml
中 variables::IMAGE_TAG
的值,覆盖了引入的流水线配置中的 variables::IMAGE_TAG
。这种方式成为了“合并”(merging)。
极狐GitLab include 的嵌套使用
include 语法可以实现配置文件的嵌套使用,比如在项目的 .gitlab-ci.yml
内容如下:
include:
- local: /.gitlab-ci/another-config.yml
而 /.gitlab-ci/another-config.yml
内部又使用了 include
引入了另外一个配置文件:
include:
- local: /.gitlab-ci/config-defaults.yml
最终 /.gitlab-ci/config-defaults.yml
的内容为:
default:
after_script:
- echo "Job complete."
上面的写法实现了 include
的 2 级嵌套。
关于极狐GitLab include 的其他用法,可以参考极狐GitLab 官网文档。
极狐GitLab include 实战
以 istio 中的 bookinfo 为例来演示 include 的强大功能。 bookinfo 中有 6 个服务:details、productpage、ratings、reviews、mysql、mongodb,每个都包含一个 Dockerfile:
├── details
│ ├── Dockerfile
├── mongodb
│ ├── Dockerfile
├── mysql
│ ├── Dockerfile
├── productpage
│ ├── Dockerfile
├── ratings
│ ├── Dockerfile
└── reviews
├── reviews-wlpcfg
│ ├── Dockerfile
如果完整构建这几个镜像,就需要执行 6 次镜像构建命令,在极狐GitLab CI/CD 中,构建镜像的 Job 如下:
build:
image: docker:latest
stage: build
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:1.0.0 .
- docker push $CI_REGISTRY_IMAGE:1.0.0
因此,可以重复写 6 次来完成整个镜像的构建,比如:
stages:
- build
ratings_image_build:
image: docker:latest
stage: build
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:1.0.0 .
- docker push $CI_REGISTRY_IMAGE:1.0.0
productpage_image_build:
image: docker:latest
stage: build
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:1.0.0 .
- docker push $CI_REGISTRY_IMAGE:1.0.0
details_image_build:
image: docker:latest
stage: build
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:1.0.0 .
- docker push $CI_REGISTRY_IMAGE:1.0.0
其他三个重复,不再赘述。
上述写法除了冗余、重复并没有太多价值。可以将镜像构建的代码写入一个 YAML 文件,并且存储到极狐GitLab(比如 SaaS 上),存储路径为:https://jihulab.com/xiaomage/teamplates/raw/main/docker-image-build-tamplates.yml
。然后在项目的 .gitlab-ci.yml
文件中使用 include
引入此文件即可:
variables:
IMAGE_TAG: "1.0.0"
stages:
- build
include:
- remote: 'https://jihulab.com/xiaomage/teamplates/raw/main/docker-image-build-tamplates.yml'
.image_build:
image: docker:latest
stage: build
ratings_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/ratings"
IMAGE_NAME: "ratings"
productpage_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/productpage"
IMAGE_NAME: "productpage"
details_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/details"
IMAGE_NAME: "details"
其他三个重复,不再赘述。.
而且使用了 variables
来完成,对于 include
引入的 template 文件中的 variables
进行了覆盖,这样只需要修改一次即可完成 6 个镜像的 tag 修改。触发 CI/CD Pipeline 构建,可以查看构建结果:
再进一步,加一个镜像扫描。极狐GitLab 本身支持对于容器镜像的扫描,详情可以查看过往文章云原生时代,保证容器镜像安全分几步?
只需以下两行代码即可开启容器镜像扫描:
include:
- template: Security/Container-Scanning.gitlab-ci.yml
Security/Container-Scanning.gitlab-ci.yml
是极狐GitLab 提供的众多开箱即用的 template 中的一个。
因此将镜像构建与扫描都用 include
引入:
variables:
CS_ANALYZER_IMAGE: registry.gitlab.cn/security-products/container-scanning/trivy:4
SECURE_ANALYZERS_PREFIX: "registry.gitlab.cn/security-products"
IMAGE_TAG: "1.0.0"
stages:
- build
- test
include:
- remote: 'https://jihulab.com/xiaomage/teamplates/raw/main/docker-image-build-tamplates.yml'
- template: Container-Scanning.gitlab-ci.yml
.image_build:
image: docker:latest
stage: build
ratings_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/ratings"
IMAGE_NAME: "ratings"
productpage_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/productpage"
IMAGE_NAME: "productpage"
details_image_build:
extends: .image_build
variables:
BASIC_PATH: "samples/bookinfo/src/details"
IMAGE_NAME: "details"
其他三个重复,不再赘述。
container_scanning:
image: "$CS_ANALYZER_IMAGE"
stage: test
variables:
DOCKER_IMAGE: "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
GIT_STRATEGY: fetch
DOCKER_USER: "$CI_REGISTRY_USER"
DOCKER_PASSWORD: "$CI_REGISTRY_PASSWORD"
container_scanning_ratings:
extends: container_scanning
variables:
IMAGE_NAME: "ratings"
container_scanning_productpage:
extends: container_scanning
variables:
IMAGE_NAME: "productpage"
container_scanning_details:
extends: container_scanning
variables:
IMAGE_NAME: "details"
其他三个重复,不再赘述。
可以查看构建结果:
在极狐GitLab 镜像仓库查看推送的镜像:
在极狐GitLab 安全仪表盘查看镜像扫描结果:
极狐GitLab CI/CD include
语法能够很好的解决 CI/CD Pipeline 代码的封装和复用,可以将共性的流程抽象出来封装成 template,存储在极狐GitLab 上。
当有新项目需要设置 CI/CD Pipeline,只需要用 include
语法引入,再根据新项目的实际情况做一些变动,即可完成新项目的 CI/CD Pipeline 设置,能够节省大量时间,而且便于维护。