跳转至

Gitlab CI进阶之共享CI库

一 背景

目前对于gitlab CI是在单独的项目下创建.gitlab-ci.yaml文件来定义部署过程,对于共同的一些步骤比如构建部署等,在每一个gitlab CI文件中编写,为了能够使代码在不同项目复用,将其存放在一个专门用于构建的gitlab CI仓库,其他项目想要使用该stage可以引用公共的CI文件,后续仅需要维护公共的gitlab CI库即可,但是需要公共CI库将一些特征数据提取出来,由 CI 确保代码风格一致,并执行单元测试和静态检查等。由于仓库数量众多,如何有效地组织和管理 CI 配置成了问题。经过长时间的探索和优化,我整理了一些经验,希望对你有所帮助。公共库需要较好的扩展性与兼容性。

二 基础语法解析

在共享仓库中将单个操作抽象为一个原子jobs,单独写在一个文件中,这样可以在模版中引用这些原子jobs,根据不同的变量,tags的runner,及branch可以任意组合成需要的模版。对此主要用到两个Gitlab CI中的关键字,include和extends。

include和extends是配合使用的,include为引用项目中的yaml文件,extends,为继承文件中的具体jobs

2.1 Include

2.1.1 功能

利用include关键字能够引用其他外部的yaml文件,这有助于将CI/CD配置分解为多个文件,并提高长配置文件的可读性。

  • 可以将通用的一些操作,抽象为单个原子jobs,编写共享gitlab ci库,在模版中incloud这些原子操作jobs,来组合成模版。
  • 在单个的项目中,可以include共享库中预定于好的模版,仅重写项目中的一些变量即可。

2.1.2 方式

include加载其他外部yaml文件,文件名称扩展必须为.yaml.yml

include支持加载方法包含以下四种:

Method Description
local Include a file from the local project repository.
file Include a file from a different project repository.
remote Include a file from a remote URL. Must be publicly accessible.
template Include templates which are provided by GitLab.

2.1.3 示例

  • include:local:

include:本地包含与.gitlab来自同一存储库的文件-词yml. 它是使用相对于根目录(/)的完整路径引用的。

您只能在配置文件所在的同一分支上使用Git当前跟踪的文件。换句话说,当使用包括:本地,确保两者都是。gitlab-词yml本地文件在同一个分支上。

include:
  - local: '/templates/.gitlab-ci-template.yml'

或者简短的方法:

include: '.gitlab-ci-production.yml'
  • include:file

要在同一个GitLab实例下包含来自另一个私有项目的文件,请使用包括:文件。使用相对于根目录(/)的完整路径引用此文件。例如:

include:
  - project: 'my-group/my-project'
    file: '/templates/.gitlab-ci-template.yml'

也可以指定ref.

include:
  - project: 'my-group/my-project'
    ref: master
    file: '/templates/.gitlab-ci-template.yml'

  - project: 'my-group/my-project'
    ref: v1.0.0
    file: '/templates/.gitlab-ci-template.yml'

  - project: 'my-group/my-project'
    ref: 787123b47f14b552955ca2786bc9542ae66fee5b # Git SHA
    file: '/templates/.gitlab-ci-template.yml'
  • include:remote

Remote用于包含来自不同位置的文件,使用HTTP/HTTPS,通过使用完整的URL引用。远程文件必须通过简单的GET请求公开访问,因为远程URL中的身份验证架构不受支持。例如:

include:
  - remote: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
  • include:template

利用template可以加载gitlab上已经预置的一些模版,

https://gitlab.com/gitlab-org/gitlab/tree/master/lib/gitlab/ci/templates

# File sourced from GitLab's template collection
include:
  - template: Auto-DevOps.gitlab-ci.yml

也可以引用多个模版

include:
  - template: Android-Fastlane.gitlab-ci.yml
  - template: Auto-DevOps.gitlab-ci.yml

2.2 Extends

2.2.1 功能

可以把一些公共属性或者方法(主要是Script)也进行统一管理。将其抽离在单独的jobs中,在具体的stages中进行继承。

extends定义使用extends的作业将从中继承的条目名。

它的使用相较于yaml anchors,更灵活和更易读。

2.2.2 示例

.tests:
  script: rake test
  stage: test
  only:
    refs:
      - branches

rspec:
  extends: .tests
  script: rake rspec
  only:
    variables:
      - $RSPEC

在上述的示例中,rspec 的job内嵌了.tests模版的job,gitlab将要执行深度合并,结果为:

rspec:
  script: rake rspec
  stage: test
  only:
    refs:
      - branches
    variables:
      - $RSPEC

三 实战

3.1 项目结构

├── README.md
├── jobs                                # 基础jobs
   ├── build.yaml                      # 构建jobs
   ├── check.yaml                      # k8s 部署检测jobs
   ├── deploy.yaml                     # k8s 部署jobs
   ├── scan.yaml                       # k8s 代码扫描jobs
   └── test.yaml                       # k8s dan单元测试jobs
└── templates                           # pipelines 模版
    ├── backend-k8s-private.yaml        # 私有化后端部署模版
    ├── backend-k8s-saas.yaml           # saas后端部署模版
    ├── frontend-k8s-private.yaml       # 私有化前端部署模版
    └── frontend-k8s-saas.yaml          # saas前端部署模版
  • 将原子操作抽象成单个jobs
  • 在templates中定义后端私有化几saas 的CI模版,在其中引用

3.2 CI jobs及模版

3.2.1 jobs

在jobs中定义原子操作,在此实例build

.build:
  script:
    - echo -e "\033[5;35;40m Building Dockerfile-based application ${REGISTRY}/${NAMESPACE}/${APP_NAME}:${APP_TAG}... \033[0m"
    - docker build -t "${REGISTRY}/${NAMESPACE}/${APP_NAME}:${APP_TAG}" .
    - echo "${REGISTRY_PWD}" | docker login ${REGISTRY} -u "${REGISTRY_USER}" --password-stdin
    - docker push "${REGISTRY}/${NAMESPACE}/${APP_NAME}:${APP_TAG}"
    - docker rmi ${REGISTRY}/${NAMESPACE}/${APP_NAME}:${APP_TAG}
  retry:
    max: 2
    when:
      - always

.upload:
  script:
    - cd /builds/${PROJECT_GROUP}
    - tar -zcf /tmp/${PROJECT_NAME}.tgz ${PROJECT_NAME}
    - coscmd config -a${ACCESS_ID} -s${ACCESS_KEY} -b${BUCKET} -r${REGION}
    - coscmd upload /tmp/${PROJECT_NAME}.tgz ${OBJECT_PATH}/
  retry:
    max: 2
    when:
      - always

我们可以其中定义build和upload多个操作,在其中不定义stage/branch/tags等。

3.2.2 templates

在templates中定义不同场景的CI模版,在其中可以自己根据项目是私有化部署或是saas 来include不同的jobs文件,并继承其中具体jobs。

在自定义模版中,主要包含四个部分:

  • 导入jobs的yaml文件
  • 定义全局就stage中变量
  • 定义模版运行CI的stage
  • 实现具体stage,管理具体stages,继承jobs。

在此义deploy来示例:

# 1.导入作业模版
include:
  - project: 'devops/gitlabci-templates'
    ref: master
    file: 'jobs/test.yaml'
  - project: 'devops/gitlabci-templates'
    ref: master
    file: 'jobs/scan.yaml'
  - project: 'devops/gitlabci-templates'
    ref: master
    file: 'jobs/build.yaml'
  - project: 'devops/gitlabci-templates'
    ref: master
    file: 'jobs/deploy.yaml'
  - project: 'devops/gitlabci-templates'
    ref: master
    file: 'jobs/check.yaml'

# 2.定义全局及stage变量
variables:
  # 2.1 全局变量
  # 2.1.1 全局基本不用修改变量
  # 全局默认构建容器
  IMAGE: docker:latest
  # 镜像仓库
  REGISTRY: ccr.ccs.tencentyun.com
  # gitlab项目组
  PROJECT_GROUP: devops
  # 部署配置
  DEPLOY_DIR: deploy
  DEPLOY_NAME: deployment.yaml
  # 配置文件
  CONFIG_DIR: cfg
  CONFIG_KEY: config.yaml

  # 部署不同环境的目录
  DEV_ENV_DIR: dev
  TEST_ENV_DIR: test
  PROD_ENV_DIR: prod

  # 配置namespace,该名称空间在镜像仓库和k8s中名称空间保持一致
  DEV_NAMESPACE: anchnet-devops-dev
  TEST_NAMESPACE: anchnet-devops-test
  PROD_NAMESPACE: anchnet-devops-prod
  # 定义各阶段使用镜像
  SCAN_IMAGE: sonarsource/sonar-scanner-cli:latest
  TEST_IMAGE: python:3.6
  UPLOAD_IMAGE: ccr.ccs.tencentyun.com/anchnet-devops-common-test/python-coscmd:latest
  DEPLOY_IMAGE: kubesphere/kubectl:v1.0.0
  # 定义上传到cos配置
  OBJECT_PATH: /smartant/saas
  BUCKET: go2tencent-1253329830
  REGION: ap-shanghai

  # 2.1.2 全局需要根据项目自定义变量
  # 主要用于对k8s资源操作
  APP_NAME: smartant-backend
  # 项目名称与代码仓库名称保持一致,用于对目录操作
  PROJECT_NAME: smartant_backend


  # 2.2 各个stage变量
  # 2.2.1 代码扫描测试,test-scan变量
  REQUIREMENTS: requirements/requirements.txt
  TEST_DIR: test
  TEST_PARAM: --include=../application.py,../logs.py,../libs/*.py,../views/*.py  --omit="test_*.py" runtests.py
  # 2.2.2 构建和上传源码到cos,build-upload变量


  # 2.2.3 部署到k8s,deploy变量
  DEV_CONFIG_FILE: config_dev.yaml
  TEST_CONFIG_FILE: config_test.yaml
  PROD_CONFIG_FILE: config_prod.yaml
  # 部署在k8s中k8s的configmap名称,一般为APP_NAME-cm
  CM_NAME: smartant-backend-cm

  # 2.2.4 检查k8s deploy服务,check变量


before_script:
  - export APP_TAG="${CI_COMMIT_TAG:-${CI_COMMIT_SHA::8}}"

# 3.配置运行stage
stages:
  - test-scan
  - build-upload
  - deploy
  - check


# 4.作业配置

image: ${IMAGE}


# --------------test and scan stage----------------
# dev环境代码扫描
scan-dev:
  image: ${SCAN_IMAGE}
  tags:
    - devops-dev-runner
  stage: test-scan
  extends: .scan
  only:
    - dev

# dev环境单元测试
test-dev:
  image: ${TEST_IMAGE}
  tags:
    - devops-dev-runner
  stage: test-scan
  extends: .test
  only:
    - dev


# --------------build upload stage---------------
# dev环境构建镜像
build-dev:
  variables:
    NAMESPACE: ${DEV_NAMESPACE}
  tags:
    - devops-dev-runner
  stage: build-upload
  extends: .build
  only:
    - dev


# test环境构建镜像
build-test:
  variables:
    NAMESPACE: ${DEV_NAMESPACE}
  tags:
    - devops-test-runner
  stage: build-upload
  extends: .build
  only:
    - test

# 正式环境构建镜像
build-prod:
  variables:
    NAMESPACE: ${PROD_NAMESPACE}
  tags:
    - devops-prod-runner
  stage: build-upload
  extends: .build
  only:
    - master

# 正式环境打包代码上传至cos
upload-prod:
  image: ${UPLOAD_IMAGE}
  variables:
    NAMESPACE: ${PROD_NAMESPACE}
  tags:
    - devops-prod-runner
  stage: build-upload
  extends: .upload
  only:
    - master

# --------------deploy stage---------------
# dev环境发布pages
pages:
  tags:
    - devops-dev-runner
  stage: deploy
  dependencies:
    - test-dev
  extends: .pages
  only:
    - dev

# dev 环境部署
deploy-dev:
  image: ${DEPLOY_IMAGE}
  variables:
    NAMESPACE: ${DEV_NAMESPACE}
    DEPLOY_ENV: ${DEV_ENV_DIR}
    CONFIG_FILE: ${DEV_CONFIG_FILE}
  tags:
    - devops-dev-runner
  stage: deploy
  extends: .deploy
  only:
    - dev

# test环境部署
deploy-test:
  image: ${DEPLOY_IMAGE}
  variables:
    NAMESPACE: ${TEST_NAMESPACE}
    DEPLOY_ENV: ${DEV_ENV_DIR}
    CONFIG_FILE: ${DEV_CONFIG_FILE}
  tags:
    - devops-test-runner
  stage: deploy
  extends: .deploy
  only:
    - test

# --------------check stage---------------
# dev环境k8s测试
check-dev:
  image: ${DEPLOY_IMAGE}
  variables:
    NAMESPACE: ${DEV_NAMESPACE}
  tags:
    - devops-dev-runner
  stage: check
  extends: .check
  only:
    - dev

# test环境k8s测试
check-test:
  image: ${DEPLOY_IMAGE}
  variables:
    NAMESPACE: ${TEST_NAMESPACE}
  tags:
    - devops-test-runner
  stage: check
  extends: .check
  only:
    - test

在该模版中利用stage中的image/tags/runner/branch进行灵活组合,从而实现适应不同场景的CI。

至此共享CI模版库就已经创建完成,需要在具体的项目中进行应用。

3.3 项目中引用

在具体的项目中引用非常简单,只需要在代码仓库中编写.gitlab-ci.yaml即可实现CI继承,在此我们示例一个python的后端saas项目,继承backend的saas模版。

include:
  - project: 'devops/gitlabci-templates'
    ref: master
    file: 'templates/backend-k8s-saas.yaml'

variables:
  APP_NAME: smartant-api-linux
  # 项目名称与代码仓库名称保持一致,用于对目录操作
  PROJECT_NAME: smartant_api_linux

  # 2.2 各个stage变量
  # 2.2.1 代码扫描测试,test-scan变量
  REQUIREMENTS: requirements/requirements.txt
  TEST_DIR: test
  TEST_PARAM: --include=../application.py,../logs.py,../libs/*.py,../views/*.py  --omit="test_*.py" runtests.py
  # 2.2.2 构建和上传源码到cos,build-upload变量

  # 2.2.3 部署到k8s,deploy变量
  DEV_CONFIG_FILE: config_dev.yaml
  TEST_CONFIG_FILE: config_test.yaml
  PROD_CONFIG_FILE: config_prod.yaml
  # 部署在k8s中k8s的configmap名称,一般为APP_NAME-cm
  CM_NAME: smartant-api-linux-cm

可以看到在具体的项目中集成gitlab CI引用模版库非常的简单,只用引用文件,并根据项目来定义变量即可完成集成。

四 注意事项

4.1 引用注意

  • 引用的时候如果为同一个仓库可以使用include:local进行同一个仓库文件本地引用
  • 对于在同一个gitlab服务器上的项目,可以使用project来引用,注意引用末尾没有.git

4.2 共享库注意事项

  • jobs中的原子操作不用定义stages,和branch,及运行的gitlab runner的tags
  • 将公共变量提出来,将根据项目定义的变量也提出来,提高共享库的可扩展性及灵活性
  • 在具体项目中引用模版,重新定义变量,将覆盖全局变量

五 反思

为了提升CI复用性和扩展性及规范CI流程,gitlab CI共享库非常好的解决了此问题,但是要求编写的具体jobs需要能无状态化,需要具备很高的扩展性和维护行,对于前期的规划和编写jobs都提出了很高的要求。

六 参考链接