GitLab CI:如何通过Include实现服务模板扩展而非覆盖?
问题场景
我们公司通过中央仓库提供流水线模板,项目只需引入对应模板即可运行CI。但当同时引入多个服务模板(如MariaDB和Kafka)时,后引入的模板会覆盖前一个的services配置,导致最终测试任务仅保留最后一个服务,无法同时启用多个依赖服务。
可行解决方案
方案1:使用!reference指令合并服务列表(GitLab 14.0+)
利用GitLab CI的!reference特性,让每个服务模板仅提供自身的服务和变量片段,主测试任务负责合并所有引入的片段。
修改服务模板结构:
每个服务模板不再直接重写test:python,而是定义独立的可引用片段:templates/service_mariadb.yaml:.service-mariadb: variables: MYSQL_HOST: 'mariadb' MYSQL_PORT: '3306' MYSQL_DATABASE: 'test' MYSQL_USER: 'root' MYSQL_ROOT_PASSWORD: 'some--dummy-password-very-insecure' DB_URL: 'mysql://$MYSQL_USER:$MYSQL_ROOT_PASSWORD@$MYSQL_HOST:$MYSQL_PORT/$MYSQL_DATABASE' services: - name: 'custom.domain.something/mariadb:latest' alias: 'mariadb' .test:python:with-mariadb: services: !reference [.service-mariadb, services] variables: !reference [.service-mariadb, variables]templates/service_kafka.yaml:.service-kafka: services: - name: 'custom.domain.something/kafka:latest' alias: 'kafka' variables: EVENTD_BROKER_URL: 'kafka:9094' KAFKA_ADVERTISED_LISTENERS: 'INTERNAL://:9092,EXTERNAL://kafka:9094' .test:python:with-kafka: services: !reference [.service-kafka, services] variables: !reference [.service-kafka, variables]更新主Python模板:
主模板的test:python任务通过extends合并所有引入的服务片段,同时保留基础配置:templates/python.yaml:.test:python:base: image: custom.domain.tld/python script: - pytest -vv test:python: extends: - .test:python:base - .test:python:with-mariadb # 引入MariaDB时自动生效 - .test:python:with-kafka # 引入Kafka时自动生效当项目仅引入MariaDB模板时,
.test:python:with-kafka不存在,GitLab会自动忽略不存在的扩展;同时引入两个模板时,两个片段的services和variables会被合并(变量不冲突时叠加,冲突时后引入的覆盖)。
方案2:通过共享基础模板叠加服务
定义一个共享的基础服务模板,每个服务模板依次扩展该模板并添加自身服务,实现服务列表的累加。
初始化共享基础模板:
在中央仓库创建空的基础服务模板(或包含通用配置):templates/base_services.yaml:.base:services: services: [] variables: {}修改服务模板继承逻辑:
每个服务模板扩展.base:services,并通过!reference引用原有服务列表,添加自身服务:templates/service_mariadb.yaml:.base:services: extends: .base:services services: - !reference [.base:services, services] - name: 'custom.domain.something/mariadb:latest' alias: 'mariadb' variables: MYSQL_HOST: 'mariadb' MYSQL_PORT: '3306' MYSQL_DATABASE: 'test' MYSQL_USER: 'root' MYSQL_ROOT_PASSWORD: 'some--dummy-password-very-insecure' DB_URL: 'mysql://$MYSQL_USER:$MYSQL_ROOT_PASSWORD@$MYSQL_HOST:$MYSQL_PORT/$MYSQL_DATABASE'templates/service_kafka.yaml:.base:services: extends: .base:services services: - !reference [.base:services, services] - name: 'custom.domain.something/kafka:latest' alias: 'kafka' variables: EVENTD_BROKER_URL: 'kafka:9094' KAFKA_ADVERTISED_LISTENERS: 'INTERNAL://:9092,EXTERNAL://kafka:9094'更新主Python模板:
让test:python任务扩展.base:services,自动继承所有叠加的服务和变量:templates/python.yaml:.test:python: image: custom.domain.tld/python script: - pytest -vv test:python: extends: - .test:python - .base:services每次引入新的服务模板,
.base:services都会被扩展一次,服务列表和变量会持续累加,实现多服务共存。
方案3:通过文件存在性自动激活服务(满足零配置需求)
结合GitLab CI的include:local条件判断,让项目只需添加特定空文件(如.use-mariadb、.use-kafka)即可自动激活对应服务模板,无需手动修改.gitlab-ci.yaml。
更新项目基础模板:
在中央仓库创建一个入口模板,包含所有服务的条件引入逻辑:templates/python_pipeline.yaml:include: # 基础Python流水线 - project: devops/gitlab file: /templates/python.yaml # 条件引入MariaDB服务 - project: devops/gitlab file: /templates/service_mariadb.yaml rules: - exists: - .use-mariadb # 条件引入Kafka服务 - project: devops/gitlab file: /templates/service_kafka.yaml rules: - exists: - .use-kafka # 配合方案1或方案2的服务合并逻辑,确保多服务能共存项目使用方式:
项目只需引入入口模板,并添加对应空文件即可激活服务:# 项目的.gitlab-ci.yaml include: - project: devops/gitlab file: /templates/python_pipeline.yaml- 需要MariaDB时,在项目根目录创建空文件
.use-mariadb - 需要Kafka时,创建空文件
.use-kafka - 同时需要两个服务时,创建两个空文件即可
- 需要MariaDB时,在项目根目录创建空文件
总结
推荐优先使用方案1+方案3的组合:既通过!reference实现服务的安全合并,又通过文件存在性判断实现零配置激活,完全符合"集中维护、员工无需编写CI配置"的需求。
内容的提问来源于stack exchange,提问作者Kound

