.NET DevOps 接入指南 | 使用GitLab流水线模板

2022-03-22  本文已影响0人  圣杰

引言

通过前面几节的介绍,想必你已能够基本玩转GitLab流水线。接下来就来介绍下实际项目中一些流水线的高级设置。比如使用流水线模板来统一相同项目类型的流水线流程。

定义模板

通过上面流水线的案例,不难发现一个完整的从构建、测试、打包、发布的流水线配置,相对较长。对于一个解决方案中有多个发布项目的场景,如果都在一个.gitlab-ci.yml文件中配置,那整个流水线的可读性将很差。如果能定义模板,那么不仅可以增加的流水线的可读性,而且可以在相同类型的项目中进行复用。GitLab的流水线就提供的有模板功能,允许用户按照规范定义自己的模板。下面就来定义一个可复用的模板,来简化流水线的配置。

创建模板仓库

demos组下创建一个新的项目,命名为:ci-cd templates,并创建jobstemplates两个文件夹。其中jobs文件夹用于创建各个job模板,方便复用;templates文件夹用于定义流水线模板。

定义构建Job模板

jobs文件夹中添加build.yml文件,文件中定义.build-job如下,其中引用BUILD_IMAGEBUILD_SHELL变量用来指定构建镜像和构建脚本。

.build-job:
    image: $BUILD_IMAGE
    stage: build
    script:
        - $BUILD_SHELL
        - echo "build passed"

定义测试Job模板

jobs文件夹中添加test.yml文件,文件中定义.test-job如下,其中引用TEST_IMAGETEST_SHELL变量用来指定构建镜像和测试脚本。

.test-job:   
  image: $TEST_IMAGE
  stage: test 
  script:    
    - $TEST_SHELL
    - echo "test passed"

定义发布Job模板

jobs文件夹中添加publish.yml文件,文件中定义.publish-job如下,其中引用PUBLISH_IMAGEPUBLISH_SHELLPUBLISH_PATH变量用来指定构建镜像、发布脚本和发布目录。

.publish-job:
  image: $PUBLISH_IMAGE
  stage: deploy
  script:
    - $PUBLISH_SHELL
    - echo "publish passed"
  artifacts:
    paths:
      - $PUBLISH_PATH

定义构建镜像Job模板

jobs文件夹中添加build-image.yml文件,文件中定义.build-image-job如下,其中引用BUILD_IMAGE_SHELLIMAGE_TAG变量用来指定构建镜像脚本和镜像标签。

.build-image-job:
    image: docker:19.03.13
    variables:
        DOCKER_HOST: tcp://docker:2376
        DOCKER_TLS_CERTDIR: "/certs"
        DOCKER_TLS_VERIFY: 1
        DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
    services:
        - docker:19.03.13-dind
    tags:
        - docker
    stage: deploy
    before_script:
        - until docker info; do sleep 1; done
        - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY
        - echo $IMAGE_TAG
    script:
        - eval $BUILD_IMAGE_SHELL
        - echo 'build docker image succeed'
        - docker push $IMAGE_TAG
        - docker image rm $IMAGE_TAG

定义Deployment Job模板

jobs文件夹中添加apply-deploy.yml文件,文件中定义.apply-deployment-job如下,主要用于创建通用Deployment模板,并进行占位符替换。

.apply-deployment-job:
    stage: deploy
    image: bitnami/kubectl:1.19
    environment:
        name: $ENV
    before_script:
        - kubectl version
        - kubectl create secret docker-registry gitlab-registry-secret 
          --docker-server="$CI_REGISTRY" 
          --docker-username="$CI_DEPLOY_USER" 
          --docker-password="$CI_DEPLOY_PASSWORD" 
          --docker-email="$GITLAB_USER_EMAIL"  
          -o yaml --dry-run=client | kubectl apply -f -
    script: 
        - |
            cat << EOF > app.deployment.yaml 
            apiVersion: apps/v1
            kind: Deployment
            metadata:
              name: __APP_NAME__-deployment
              labels:
                app: __APP_NAME__
              annotations:
                app.gitlab.com/app: __APP_NAME__
                app.gitlab.com/env: __ENV__  
            spec:
              replicas: 3
              selector:
                matchLabels:
                  app: __APP_NAME__
              template:
                metadata:
                  labels:
                    app: __APP_NAME__
                spec:
                  imagePullSecrets:
                  - name: gitlab-registry-secret
                  containers:
                  - name: __APP_NAME__
                    image: __IMAGE_TAG__
                    ports:
                    - containerPort: 80
            EOF
        - sed -i "s#__APP_NAME__#$APP_NAME#g" app.deployment.yaml
        - sed -i "s#__IMAGE_TAG__#$IMAGE_TAG#g" app.deployment.yaml
        - sed -i "s#__ENV__#$ENV#g" app.deployment.yaml
        - cat app.deployment.yaml        
        - kubectl apply -f app.deployment.yaml
        - echo 'create deployment succeed' 
    cache: []

定义Service Job模板

jobs文件夹中添加apply-service.yml文件,文件中定义.apply-service-job如下,主要用于创建通用Service模板,并进行占位符替换。

.apply-service-job:
    stage: deploy
    environment:
        name: $ENV
    image: bitnami/kubectl:1.19
    before_script: []
    script:
        - |
            cat << EOF > app.service.yaml 
            apiVersion: v1
            kind: Service
            metadata:
              name: __APP_NAME__-service  
              labels:
                app: __APP_NAME__
            spec:
              selector:
                app: __APP_NAME__
              ports:
                - protocol: TCP
                  port: 80
                  targetPort: 80            
            EOF
        - sed -i "s#__APP_NAME__#$APP_NAME#g" app.service.yaml
        - cat app.service.yaml        
        - kubectl apply -f app.service.yaml
        - echo 'create service succeed' 
    cache: []

定义Ingress Job 模板

在jobs文件夹中添加apply-ingress.yml文件,文件中定义.apply-ingress-job如下,主要用于创建通用Ingress模板,并进行占位符替换。

.apply-ingress-job:
    stage: deploy
    environment:
        name: $ENV
    image: bitnami/kubectl:1.19    
    before_script: []
    script:
        - |
            cat << EOF > app.ingress.yaml 
            apiVersion: networking.k8s.io/v1
            kind: Ingress
            metadata:
              name: __APP_NAME__-ingress
              labels:
                  app: __APP_NAME__
              annotations:
                cert-manager.io/cluster-issuer: letsencrypt-prod
                acme.cert-manager.io/http01-edit-in-place: "true" 
                kubernetes.io/ingress.class: gitlab-nginx
                kubernetes.io/ingress.provider: nginx
                nginx.ingress.kubernetes.io/rewrite-target: /
            spec:
              tls:
                - hosts:
                    - __INGRESS_HOST__
                  secretName: __APP_NAME__-tls
              rules:
                - host: __INGRESS_HOST__
                  http:
                    paths:
                      - path: __INGRESS_PATH__                      
                        pathType: Prefix
                        backend:
                          service:
                            name:  __APP_NAME__-service
                            port:
                              number: 80         
            EOF
        - sed -i "s#__APP_NAME__#$APP_NAME#g" app.ingress.yaml
        - sed -i "s#__INGRESS_HOST__#$INGRESS_HOST#g" app.ingress.yaml
        - sed -i "s#__INGRESS_PATH__#$INGRESS_PATH#g" app.ingress.yaml
        - cat app.ingress.yaml        
        - kubectl apply -f app.ingress.yaml
        - echo 'create ingress succeed' 
    cache: []

定义ASP.NET Core 流水线模板

templates文件夹下创建aspnetcore-pipeline.yml,通过GitLab定义的includesextends完成流水线的预置,如下所示:

include:
    - project: 'demos/cicd-templates'
      ref: main
      file: 
        - 'jobs/build.yml'
        - 'jobs/test.yml'
        - 'jobs/publish.yml'
        - 'jobs/build-image.yml'
        - 'jobs/apply-deploy.yml'
        - 'jobs/apply-service.yml'
        - 'jobs/apply-ingress.yml'
        
variables:
    ENV: development
    INGRESS_PATH: '/'
    DOTNET_SDK_IMAGE: mcr.microsoft.com/dotnet/sdk:5.0    
    NUGET_CACHE_DIR: '.nuget'
    SLN_PATH: './'
    APP_NAME: $CI_PROJECT_PATH_SLUG
    BUILD_PROJET_PATH: $SLN_PATH
    TEST_PROJECT_PATH: $SLN_PATH
    PUBLISH_PROJECT_PATH: $SLN_PATH
    PUBLISH_PATH: publish/
    BUILD_IMAGE: $DOTNET_SDK_IMAGE
    TEST_IMAGE: $DOTNET_SDK_IMAGE
    PUBLISH_IMAGE: $DOTNET_SDK_IMAGE
    BUILD_SHELL: dotnet build $BUILD_PROJECT_PATH --no-restore
    TEST_SHELL: dotnet test $TEST_PROJECT_PATH --no-restore
    PUBLISH_SHELL: dotnet publish --no-restore $PUBLISH_PROJECT_PATH -o $PUBLISH_PATH
    IMAGE_TAG: $CI_REGISTRY_IMAGE/$APP_NAME:$CI_COMMIT_SHORT_SHA
    BUILD_IMAGE_SHELL: "docker build -t ${IMAGE_TAG} $PUBLISH_PATH -f $PUBLISH_PROJECT_PATH/Dockerfile"
    
cache: &global_cache
    key: $CI_PROJECT_PATH_SLUG
    paths:
        - './*/obj/project.assets.json' 
        - $NUGET_CACHE_DIR

before_script: 
    - dotnet restore ${SLN_PATH} --packages $NUGET_CACHE_DIR

stages:
    - build
    - test
    - deploy

build-job:
    stage: build
    extends: .build-job 

test-job:
    stage: test
    extends: .test-job
    cache:
        <<: *global_cache
        policy: pull

publish-job:
    stage: deploy
    when: manual
    extends: .publish-job
    cache:
        <<: *global_cache
        policy: pull

build-image-job:
    stage: deploy
    extends: .build-image-job
    tags: ["docker"]
    cache: []    
    needs:
        - publish-job

apply-deployment-job:
    stage: deploy
    extends: .apply-deployment-job
    cache: []
    needs:
        - publish-job
        - build-image-job

apply-service-job:
    stage: deploy
    when: manual
    extends: .apply-service-job
    cache: []

apply-ingress-job:
    stage: deploy
    when: manual
    extends: .apply-ingress-job
    cache: []    

完成后,该ASP.NET Core 流水线模板创建完毕,整个模块库的代码结构如下图所示:


image

使用流水线模板

回到我们的示例项目AspNetCore.CiCd.Demo,修改.gitlab-ci.yml如下所示:

include:
    - project: 'demos/cicd-templates'
      ref: main
      file: 'templates/aspnetcore-pipeline.yml' # 直接引用上面定义的ASP.NET Core 流水线模板
variables:  # 配置流水线预置遍历
    APP_NAME: aspnetcore-cicd-web  # 指定应用名称
    TEST_PROJECT_PATH: './AspNetCore.CiCd.Web.Tests/' # 指定测试项目路径
    PUBLISH_PROJECT_PATH: './AspNetCore.CiCd.Web/' # 指定发布项目路径
    INGRESS_HOST: demo.shengjie.dev # 指定Ingress域名
    INGRESS_PATH: /dotnet # 指定Ingress 路径
提交后,流水线会按预置的流水线模板进行运行,运行效果如下图所示: image

通过这种方式,可以发现,仅需管理员创建好通用的流水线模板,比如前端模板,后端模板,其他项目仅需简单按需引用即可,大大简化了项目流水线的配置难度。

限定分支

以上的流水线有个问题就是,任何的提交都会触发流水线,这样会相对浪费流水线资源。而按照标准Git Flow 流程,对合并到developreleasemain分支的提交进行持续构建、测试即可。而对于发布应该限定在releasemain分支。那如何进行设置呢?简单,可以通过使用rulesonly关键字进行限定。比如修改构建Job和测试Job模板如下,即可限定持续构建和测试自动运行在maindevelopmain分支。

build-job:
    stage: build
    extends: .build-job 
    only: ["main","develop","/^release.*$/","merge_requests"]    
test-job:
    stage: test
    extends: .test-job
    cache:
        <<: *global_cache
        policy: pull
    only: ["main","develop","/^release.*$/","merge_requests"]    
通过修改发布Job模板如下,即可限定持续部署仅能运行在`main`和`release`分支。
publish-job:
    stage: deploy
    when: manual
    extends: .publish-job
    cache:
        <<: *global_cache
        policy: pull
    only: 
        - main
        - /^release.*$/
上一篇下一篇

猜你喜欢

热点阅读