美文网首页
CI/CD持续集成/持续部署实践

CI/CD持续集成/持续部署实践

作者: Transy | 来源:发表于2021-04-24 22:48 被阅读0次

    1. 概述

    本文是对CI/CD具体流程的一种实践,仅代表个人观点。在阅读本文前掌握以下内容,更方便理解。

    1. 对K8S有基本了解,部署过具体应用;
    2. 使用过Helm部署K8S应用,了解Helm基本原理;
    3. 熟练使用Jenkins,理解流水线任务和Jenkins公共库的概念;
    4. 熟悉Harbor、GitLab和sonarQube相关工具;
    

    本文目标如下:

    1. 实现微服务应用从提交代码到部署应用到K8S的全自动化流程,其中包括:代码下载、静态分析、单元测试、编译发布、环境切换和安装部署等工作,整个过程不需要人工干预;
    2. 借助Jenkins Library公共库,对部署过程的步骤进行抽象,不需要每个项目都写Jenkinsfile文件(如果特殊情况也可以定义自己的);
    3. 借助Kubectl Config工具,对K8S集群环境访问进行动态切换,实现不同集群环境部署。
    4. 借助Helm Chart自定义Chart功能,制定标准的K8S发布所需资源模板Chart,开发人员无需关注部署的K8S YAML全部语法,直接指定values.yaml文件中需要的资源即可;

    另外,除了在部署流程的标准化、自动化之外,还需要考虑面向微服务应用开发人员和运维人员的配置人性化问题。例如:让一个开发人员去了解复杂的K8S配置,理论上不太实际,同样的运维人员对部署应用所需的条件(例如:CPU和内存)等也不能同等对待,需要开发人员配合给出。实现将运维和研发的关注点分离。

    CI/CD具体流程如下:


    CI/CD流程

    如上图,为CI/CD整个发布流程,该方案特点:

    1. CI/CD发起支持手工构建和GitLab的WebHook构建两种启动方式;
    2. 支持Maven结构的单模块和多模块微服务应用的发布;
    

    具体流程说明如下:

    1. 由开发人员PUSH代码或手工在Jenkins平台触发构建;
    2. 开发人员提交代码后,GitLab会向Jenkins发起WebHook调用;
    3. Jenkins平台通过指定的"流水线"配置的GitLab上的Jenkins Library地址,下载Jenkinsfile文件;
    4. JenkinsFile文件中通过"@Library"命令加载在Jenkins配置的公共库,并调用公共库里面的"build"方法;
    5. 从GitLab上下载项目源文件;
    6. 通过maven插件sonarqube对源代码进行静态分析,并将结果上传到SonarQube服务器上;
    7. 开始单元测试;
    8. 通过Maven插件jib打包镜像并PUSH到Harbor仓库;
    9. 通过Kubectl Config工具切换要部署到的K8S平台;
    10. 通过Helm install向指定的K8S发起创建POD请求;
    11. 最后,K8S接收到Helm的安装请求后,从Harbor中下载Docker镜像和Helm Chart部署系统。
    

    2. 微服务应用

    微服务应用以容器的方式运行在K8S Pod上,在Kubernetes集群中,Pod是所有业务类型的基础,也是K8S管理的最小单位级,它是一个或多个容器的组合。这些容器共享存储、网络和命名空间,以及如何运行的规范。在Pod中,所有容器都被同一安排和调度,并运行在共享的上下文中。对于具体应用而言,Pod是它们的逻辑主机,Pod包含业务相关的多个应用容器。
    微服务应用部署结构如下:



    如上图所示,微服务应用部署包括以下三个容器:

    1. Skywalking-Agent-Sidecar容器:initContainers容器,该容器在POD启动前执行,执行完成后立即停止,主要目的是将Skywalking Agent所需要的
       文件拷贝进容器组中,其中连接Skywalking服务器的配置文件是通过ConfigMap方式注入进容器;
    2. 微服务应用容器:该容器为主容器,运行了程序员定义的微服务程序(采用java -cp方式运行,运行目录为/app,包括classes、libs和resources
       三部分)。启动时采用了-javaagent代理Skywalking运行,用于收集链路日志记录,同时内置Consul服务发现和服务配置,通过访问
       Consul-Agent-SideCar与Consul Server进行通信;
    3. Consul-Agent-SideCar容器:该容器伴随微服务主容器一起运行,生命周期与主容器一样,主要目的是在Consul Server服务器上建立一个Consul 
       Client,用于在微服务应用与Consul Server之间转发请求,主容器微服务只需要通过127.0.0.1:8500进行通信,而不用关心Consul Server的部署IP
       和端口;Consul Agent连接服务器的配置信息是通过ConfiMap注入进来的;
    

    2.1 Skywalking-Agent-Sidecar镜像

    skywalking-agent-sidecar镜像包括skywalking agent所需的文件,如下:

    在微服务应用容器运行时,作为initContainers容器,项目Pod启动时负责拷贝文件到指定位置,供项目启动时调用,运行完成后会停止该容器。

    2.2 Consul-Agent-Sidecar镜像

    consul-agent-sidecar镜像是一个边车模式的镜像,与微服务应用容器生命周期保持一致,主要负责与远端的Consul Server通信,这样项目容器通过127.0.0.1:8500访问即可,不用关心Consul Server的IP地址和端口。


    2.3 微服务应用镜像

    微服务应用镜像是将开发代码打包成镜像,能够运行在K8S上。需要部署的项目都是采用Maven的文件结构,具体如下:


    2.3.1 集成jib插件

    微服务应用项目需要集成jib插件,jib无需Docker守护程序即可为Java应用程序构建优化的Docker和OCI映像-无需深入掌握Docker最佳实践。它可以作为Maven和Gradle的插件以及Java库使用。

    1. 快速-快速部署您的更改。Jib将您的应用程序分成多个层,将依赖项与类分开。现在,您不必等待Docker重建整个Java应用程序-只需部署已更改的层。
    2. 可重现-用相同的内容重建容器映像始终会生成相同的映像。永远不要再次触发不必要的更新。
    3. 无守护程序-减少CLI依赖性。从Maven或Gradle内部构建Docker映像,然后推送到您选择的任何仓库(例如:Harbor)。无需再编写Dockerfile
       并调用docker build / push。
    

    平台已经提供,只需要在pom.xml中加入如下配置即可:

    <properties>
      <!-- 去掉发布后镜像的环境前缀 -->
      <docker.image.project.version.prefix></docker.image.project.version.prefix>
      <!-- 自定义镜像启动主类,默认为com.egrand.EgrandCloudApplication -->
      <docker.image.project.main.class>com.egrand.EgrandCloudXXXApplication</docker.image.project.main.class>
    </properties>
    <build>
      <plugins>
        <!-- 引入jib插件 -->
          <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
          </plugin>
       </plugins>
    </build>
    
    2.3.2 集成sonarQube

    Static Analysis采用sonarQube来实现,需要安装sonarQube服务。Sonar(SonarQube)是一个开源平台,用于管理源代码的质量。Sonar 不只是一个质量数据报告工具,更是代码质量管理平台。支持的语言包括:Java、PHP、C#、C、Cobol、PL/SQL、Flex 等。

    1. 代码覆盖:通过单元测试,将会显示哪行代码被选中;
    2. 改善编码规则;
    3. 搜寻编码规则:按照名字,插件,激活级别和类别进行查询;
    4. 项目搜寻:按照项目的名字进行查询;
    5. 对比数据:比较同一张表中的任何测量的趋势;
    

    进行代码静态分析时,需要准备以下条件:

    1. 安装sonar服务器端,这里不详细介绍;
    2. 生成Login Token;
    3. 修改maven的setting.xml文件,增加如下配置:
      <pluginGroups>
          <pluginGroup>org.sonarsource.scanner.maven</pluginGroup>
      </pluginGroups>
      <profiles>
          <profile>
            <id>sonar</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <!-- Optional URL to server. Default value is http://localhost:9000 -->
                <sonar.host.url>
                    http://127.0.0.1:9000
                </sonar.host.url>
                <sonar.login>
                    [上一步生成的Login Token]
                </sonar.login>
            </properties>
        </profile>
      </profiles>
      
      其中sonar.login是在sonar服务端生成的登录token。
    4. 访问sonar服务器,新建项目
      具体过程不介绍,但最终会生成一个”项目标识",需要配置到下一步骤的。
    5. 配置项目pomx.xml文件
      <properties>
          <sonar.projectKey>[在sonar服务器上新建项目的项目标识]</sonar.projectKey>
      </properties>
      
    2.3.3 bootstrap.yml规范

    微服务应用在通过Jenkins平台部署到K8S形成pod后,在pod中会采用边车模式运行Consul-Agent-Sidecar容器,微服务应用注册到Consul是采用localhost:8500的方式进行通信即可;
    同时在Jenkins Library中运行jib,是默认采用"mvn clean compile jib:build -DsendCredentialsOverHttp=true -Dprofile.active=prod"来运行的,其中prod代表的是K8S线上环境(包括K8S测试环境和K8S正式环境等,不区分);
    综上述,在配置bootstrap.yml中配置prod时一定保持如下:

    server:
      port: 8888
    spring:
      application:
        name: egrand-cloud-platform-demo
      profiles:
        active: @profile.active@
      cloud:
        consul:
          config:
            # 设置配置的基本文件夹,默认值 config 可以理解为配置文件所在的最外层文件夹
            prefix: platform
            # 配置环境分隔符,默认值 "," 和 default-context 配置项搭配
            # 例如应用 orderService 分别有环境 default、dev、test、prod
            # 只需在 config 文件夹下创建 orderService、orderService-dev、orderService-test、orderService-prod 文件夹即可
            profile-separator: '-'
            # 指定配置格式为 yaml
            format: YAML
    # 本地开发环境
    ---
    spring:
      profiles: dev
      cloud:
        consul:
          # consul注册中心的ip地址
          host: ${CONSUL_HOST:10.1.10.179}
          # consul注册中心端口
          port: ${CONSUL_PORT:8600}
          # 配置中心相关配置
          config:
            # 认证token
            acl-token: ${CONSUL_TOKEN:6983d4bf-87d6-591a-c3d4-dde5c05c4f28}
    # 线上环境
    ---
    spring:
      profiles: prod
      cloud:
        consul:
          # consul注册中心的ip地址
          host: localhost
          # consul注册中心端口
          port: 8500
          # 配置中心相关配置
          config:
            # 认证token
            acl-token: ${CONSUL_TOKEN}
    
    

    4. Jenkins Library公共库

    4.1 目标

    Jenkins Library公共库目标是规范Jenkins发布步骤,将每个项目在构建中的步骤进行抽象,规范参数传递来实现不同项目的公共构建。用户可以在新建Jenkins任务时加载公共依赖,调用公共方法,达到重复利用的目的。

    1. 规范化:对每个项目中的步骤进行抽象实现,同时规范化输入参数;
    2. 统一化:如果有改动,改动这一处即可;
    

    4.2 目录结构

    Jenkins Library存储在GitLab上,具体目录结构如下:

    egrand-cloud-jenkins-library
       |--- deploy
       |      |--- Jenkinsfile  // 公共的Jenkinsfile,项目在创建Jenkins任务时,引用该文件开始构建,该文件调用vars/build.groovy的build方法
       |--- vars
       |--- build.groovy  // 构建主文件,为Jenkinsfile提供调用方法build
       |--- gitlab.groovy  // 子流程步骤,供build.groovy调用,完成从GitLab上下载源代码
       |--- jib.groovy  // 子流程步骤,供build.groovy调用,完成maven插件jib构建,构建镜像并推送到Harbor中
       |--- kubecm.groovy  // 子流程步骤,供build.groovy调用,完成kubectl config环境切换和Helm uninstall/install操作,部署到K8S集群
       |--- sonar.groovy  // 子流程步骤,供build.groovy调用,完成静态代码分析,并推送到SonarQube服务器;
    

    4.3 运行步骤

    如上图,Jenkins Library存放在GitLab上,其中包括了公共的Jenkinsfile和其他脚本文件。

    • Jenkinsfile文件:
    // 加载jenkins公共库
    @Library('jenkins-library') _
    /**
     * 构建参数,具体参数如下:
     * cloud             运行K8S的云节点,默认值为kubernetes,在Jenkins的"Configure Clouds(配置集群)"中配置
     * serviceAccount    K8S运行账号,默认是jenkins2,需要在K8S集群中分配账号信息
     * chart             Helm的自定义chart,默认为egrand/backend-deployment-chart
     * profileActive     Maven编译环境,默认为prod
     * gitUrl            Git源码地址
     * branches          Git源码分支,接收jenkins在任务中配置的branch参数值
     * credentialsId     Git认证ID,在jenkins中配置
     * imageTag          镜像版本,默认值是根据gitlab提交信息生成
     * preImageTag       上一镜像版本,从GitLab提交历史获取的
     * moduleDir         多模块运行目录(对于多模块项目才需要设置,单模块不需要)
     * deployToEnv       部署到的环境,包括dev:开发环境;prod:生成环境
     *                   注意:如果不填写该值,则自动根据branches字段值来判断
     *                   1. branches的值等"master"或者"refs/heads/master",则deployToEnv值为prod
     *                   2. branches的值等"dev"或者"refs/heads/dev",则deployToEnv值为dev
    **/
    build([
        gitUrl:"${gitUrl}",
        imageTag: "${gitCommitId}",
        preImageTag: "${gitPreCommitId}",
        branches: "${branch}",
        moduleDir: "${env.moduleDir}"
    ])
    

    如上,Jenkinsfile定义了公共的脚本,在新建项目的Jenkins任务时可以引用,通过调用build方法,进入执行流程,以下是相关参数说明:

    参数名称 说明
    cloud 运行K8S的云节点,默认值为kubernetes,在Jenkins的"Configure Clouds(配置集群)"中配置
    serviceAccount K8S运行账号,默认是jenkins2,需要在K8S集群中分配账号信息
    gitUrl Git源码地址
    branches Git源码分支,值包括:dev(部署到K8S的测试环境)、master(部署到K8S的正式环境)
    credentialsId Git认证ID,在jenkins中配置
    chart Helm的自定义chart,包括:egrand/backend-deployment-chart、egrand/foreend-deployment-chart
    imageTag 镜像版本,默认值是根据gitlab提交信息生成,截取前7位,与项目中jib插件生成的镜像版本保持一致
    preImageTag 上一次镜像版本,用于在部署时删除前一个部署,避免出现重复的pod
    profileActive Maven编译环境,默认为prod
    moduleDir 多模块运行目录(对于多模块项目才需要设置,单模块不需要)
    deployToEnv 部署到的环境,包括dev:开发环境;prod:生成环境 如果不指定deployToEnv,则如下: 根据branches值自动生成发布环境 * master ---> 生产环境(k8s) * dev ---> 开发环境(k8s)
    • 执行脚本

    执行脚本包括build.groovy、gitlab.groovy、sonar.groovy、jib.groovy和kubecm.groovy五个脚本,其中build.groovy作为脚本的进入口,同时定义了pipeline流水线。</pre>
    脚本说明如下:

    脚本 备注
    build.groovy 构建主文件,为Jenkinsfile提供调用方法build
    gitlab.groovy 子流程步骤,供build.groovy调用,完成从GitLab上下载源代码
    sonar.groovy 子流程步骤,供build.groovy调用,完成静态代码分析,并推送到SonarQube服务器;
    jib.groovy 子流程步骤,供build.groovy调用,完成maven插件jib构建,构建镜像并推送到Harbor中
    kubecm.groovy 子流程步骤,供build.groovy调用,完成kubectl config环境切换和Helm uninstall/install操作,部署到K8S集群

    5. Helm自定义Chart

    5.1 概述

    Helm是Kubernetes的包管理器,类似于Python的pip centos的yum,主要用来管理 Charts。Helm Chart是用来封装Kubernetes原生应用程序的一系列YAML文件。可以在你部署应用的时候自定义应用程序的一些Metadata,以便于应用程序的分发。

    * 对于应用发布者而言,可以通过Helm打包应用、管理应用依赖关系、管理应用版本并发布应用到软件仓库。
    * 对于使用者而言,使用Helm后不用需要编写复杂的应用部署文件,可以以简单的方式在Kubernetes上查找、安装、升级、回滚、卸载应用程序
    

    5.2 介绍

    backend-deployment-chart是基于Helm自定义的后端微服务发布chart,里面定义了K8S生成所需的所有资源和YAML定义,目的是简化和规范发布流程;

    * 屏蔽K8S复杂的YAML配置文件,利用Helm的-f指定参数覆盖文件或通过--set命令覆盖参数,
      就能够基于backend-deployment-chart发布项目的前端文件;
    * 提供Skywalking Ageng功能,记录调用链路日志给服务器端;
    * 提供Consul Client边车模式,让后端微服务访问本地Consul Client,而不需要关系Consul Server在哪里;
    

    backend-deployment-chart目录如下:

    foreend-deployment-chart
       |---templates                                // 模板文件
       |      |---_helpers.tpl                      // 帮助模板,可以理解为定义公共的组合变量
       |      |---consulAgent-configMap.yaml        // Consul Agent配置字典文件
       |      |---deployment.yaml                   // k8s的deployment方式yaml定义文件,用于生成Deployment pod
       |      |---harbor-secret.yaml                // Harbor密文配置文件
       |      |---hpa.yaml                          // 水平动态扩展(暂时不用)
       |      |---ingress.yaml                      // ingress定义文件(内网采用NodePort方式,暂时不用)
       |      |---service.yaml                      // 服务YAML定义,用于暴露接口(目前采用NodePort方式)
       |      |---serviceaccount.yaml               // K8S帐户YAML定义,默认为default
       |      |---skywalking-configMap.yaml         // Skywalking Agent代理的配置字典文件
       |---Chart.yaml                               // 包含了该chart的描述。你可以从模板中访问它。
       |---values.yaml                              // 包含了chart的默认值 。这些值会在用户执行helm install 或 helm upgrade时被覆盖。
               
    

    foreend-deployment-chart已经发布到harbor中,通过以下命令生成pod:

    # 运行线上chart生成一个POD,默认的namespace为default
    > helm install [pod名称] egrand/backend-deployment-chart
    # 采用values.yaml复写参数运行线上的Chart并生成一个新的pod
    > helm install -f values.yaml [pod名称] egrand/backend-deployment-chart
    

    可以通过以下代码下载:

    # 搜索镜像
    > helm search repo backend
    # 下载egrand源下的backend-deployment-chart并解压
    > helm pull egrand/backend-deployment-chart --untar
    

    5.3 部署约定

    为规范后端微服务部署流程,制定的规定如下:

    • Skywalking初始化容器
      通过上面的架构图,可以知道,Skywalking的相关配置文件时通过初始化容器运行时拷贝进入主容器中的,其中Skywalking Agent连接Skywalking Server的配置文件是通过ConfigMap注入进来的,文件名为:agent.config,存储路径为:/usr/skywalking/agent/config/;
    • 微服务镜像规范
      建议采用jib打包Docker镜像,平台Maven中已内置插件,运行命令即可;
    • Consul Client运行约定
      一个微服务应用对应一个Consul Client,同生共死,相互之间通过127.0.0.1进行通信,其中Consul Client连接Consul Server的配置信息是通过ConfigMap注入进来的文件,文件名为:agent.json,存储路径为“/etc/consul/config/”;

    5.4 部署

    下面介绍如何通过覆盖参数配置来实现一个项目的微服务应用部署。

    其实很简单,backend-deployment-chart已经帮我们定义好了部署需要的资源,
    我们只需要覆盖对应默认参数即可,就像程序开发中调用一个已经写好的公共方法,只需要传入参数即可。
    
    • -f 覆盖默认参数部署
      在新的目录下(随便哪个目录)新建文件一个文件名为values.yaml文件,加入如下内容:

      # Consul Agent ConfigMap配置
      consulAgentConfigmap: |
        {
          "bind_addr": "0.0.0.0",
          "client_addr": "0.0.0.0",
          "datacenter": "egdPlatform",
          "primary_datacenter": "egdPlatform",
              "encrypt": "EXz7LFN8hpQ4id8EDYiFoQ==",
          "data_dir": "/consul/data",
          "acl": {
              "enabled": true,
              "default_policy": "deny",
              "enable_token_persistence": true,
              "tokens": {
                  "agent": "b07df4e4-1183-5496-35fe-0ac6eb726d05"
              }
          },
          "log_level": "INFO",
          "log_file": "/consul/consul.log",
          "log_rotate_duration": "24h",
          "enable_syslog": false,
          "enable_debug": true,
          "connect":{
              "enabled": true
            },
              "ui": true,
          "start_join":[
              "consul-0.consul.kube-platform.svc.cluster.local",
              "consul-1.consul.kube-platform.svc.cluster.local",
              "consul-2.consul.kube-platform.svc.cluster.local"
            ]
        }
      # skywalking配置文件configMap
      skywalkingConfig: |
        agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
        collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:10.1.10.178:11800}
        logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
        logging.level=${SW_LOGGING_LEVEL:INFO}
        plugin.mount=${SW_MOUNT_FOLDERS:plugins,activations}
      #Consul SideCar镜像配置
      consulSideCarImage:
        #是否开启
        enabled: true
      # 初始化容器配置
      initContainer:
        #是否开启
        enabled: true
      #应用镜像配置
      image:
        #仓库地址
        repository: [微服务应用镜像地址]
        #镜像版本,默认为appVersion.
        tag: "[微服务应用镜像版本]"
      

      将第一步生成镜像的地址和版本填入,运行以下命令部署

      > helm install -f values.yaml demo egrand/backend-deployment-chart
      

      即可完成部署。

    5.5 values.yaml参数详解

    # Default values for backend-deployment-chart.
    # This is a YAML-formatted file.
    # Declare variables to be passed into your templates.
    
    # Pod副本数量
    replicaCount: 1
    # 中文名称
    displayName: "微服务应用"
    # 部署到的命名空间
    namespace: ""
    # 所属层级(方便在Kuboard中查看,默认为svc)
    #       svc:微服务层
    #       gateway;网关层
    #       cloud:中间件
    #       web:展现层
    layer: ""
    # 名称(覆盖变量backend-deployment-chart.name的值,默认值为Chart.Name)
    # backend-deployment-chart.name变量用于定义标签app.kubernetes.io/name的值,
    # 形成app.kubernetes.io/name: backend-deployment-chart.name
    # 利用该标签和app.kubernetes.io/instance: .Release.Name进行标签关联selector选择用
    nameOverride: ""
    # 发布全名称,定义Pod的名称(覆盖变量backend-deployment-chart.fullname的值,默认[Release.Name]-[Chart.Name])
    fullnameOverride: ""
    # pod注解
    podAnnotations: {}
    # 安全设置
    podSecurityContext: {}
    # fsGroup: 2000
    # 节点选择
    nodeSelector: {}
    # 容忍设置
    tolerations: []
    # 亲和性设置
    affinity: {}
    # pod上定义共享存储卷列表
    volumes: {}
    
    #镜像拉取密文配置
    imageCredentials:
      # 覆盖名称(与config是冲突的,如果填写该值,则在K8S namespace查找配置字典)
    #  nameOverride: "harborsecret"
      config:
        # 镜像仓库地址
        registry: http://10.1.10.212:8082/
        # 镜像登录名
        username: admin
        # 镜像登录密码
        password: wiki2012
    
    #Consul Agent代理配置字典
    #consulAgentConfigMap:
    #  # 覆盖名称(与config是冲突的,如果填写该值,则在K8S namespace查找配置字典)
    #  nameOverride: "consul-agent"
    #  # 配置字典配置信息
    #  config:
    #    agent.json: |
    #      {
    #        "bind_addr": "0.0.0.0",
    #        "client_addr": "0.0.0.0",
    #        "datacenter": "egdPlatform",
    #        "primary_datacenter": "egdPlatform",
    #            "encrypt": "EXz7LFN8hpQ4id8EDYiFoQ==",
    #        "data_dir": "/consul/data",
    #        "acl": {
    #            "enabled": true,
    #            "default_policy": "deny",
    #            "enable_token_persistence": true,
    #            "tokens": {
    #                "agent": "b07df4e4-1183-5496-35fe-0ac6eb726d05"
    #            }
    #        },
    #        "log_level": "INFO",
    #        "log_file": "/consul/consul.log",
    #        "log_rotate_duration": "24h",
    #        "enable_syslog": false,
    #        "enable_debug": true,
    #        "connect":{
    #            "enabled": true
    #          },
    #            "ui": true,
    #        "start_join":[
    #            "consul-0.consul.kube-platform.svc.cluster.local",
    #            "consul-1.consul.kube-platform.svc.cluster.local",
    #            "consul-2.consul.kube-platform.svc.cluster.local"
    #          ]
    #      }
    
    
    # skywalking配置字典
    #skywalkingAgentConfigMap:
    #  # 覆盖名称(与key和value是冲突的,如果填写该值,则在K8S namespace查找配置字典)
    #  nameOverride: "skywalking-config"
    #  # 配置字典配置信息
    #  config:
    #    agent.config: |
    #      agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
    #      collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:10.1.10.178:11800}
    #      logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
    #      logging.level=${SW_LOGGING_LEVEL:INFO}
    #      plugin.mount=${SW_MOUNT_FOLDERS:plugins,activations}
    
    #应用镜像配置
    image:
      #仓库地址
      repository: 10.1.10.212:8082/library/nginx
      #镜像版本,默认为appVersion.
      tag: "latest"
      #镜像拉去策略:Always(总是拉取 pull)、
      #            IfNotPresent(默认值,本地有则使用本地镜像,不拉取)、
      #            Never(只使用本地镜像,从不拉取)
      pullPolicy: Always
      #环境变量设置,采用name和value格式。
    #  env:
    #    - name: TZ
    #      value: Asia/Shanghai
    #    - name: spring.datasource.dynamic.datasource.master.url
    #      value: >-
    #        jdbc:mysql://10.1.10.188:3306/egrand-cloud-ky3h-cis?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8&connectTimeout=60000&socketTimeout=30000
    #    - name: springfox.documentation.swagger.v2.host
    #      value: >-
    #        10.1.10.212/ky3h
      # 安全设置
      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
      # runAsNonRoot: true
      # runAsUser: 1000
      # 资源配置
      resources: {}
        # We usually recommend not to specify default resources and to leave this as a conscious
        # choice for the user. This also increases chances charts run on environments with little
        # resources, such as Minikube. If you do want to specify resources, uncomment the following
        # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
        # limits:
        #   cpu: 100m
        #   memory: 128Mi
        # requests:
      #   cpu: 100m
      #   memory: 128Mi
      #镜像挂载
      volumeMounts: {}
        #主机模式
    #    - mountPath: /usr/local/skywalking/agent
    #      mountPropagation: None
    #      name: skywalking-agent
        #PVC模式
    #    - name: atm-data
    #        persistentVolumeClaim:
    #          claimName: kube-yuncang
    
    #Consul SideCar镜像配置
    consulSideCarImage:
      #是否开启
      enabled: false
      #镜像名称
      name: "consul-agent-sidecar"
      #仓库地址
      repository: 10.1.10.212:8082/egrand-cloud-deploy/consul-agent-sidecar
      #镜像版本
      tag: "latest"
      #镜像拉去策略:Always(总是拉取 pull)、
      #            IfNotPresent(默认值,本地有则使用本地镜像,不拉取)、
      #            Never(只使用本地镜像,从不拉取)
      pullPolicy: IfNotPresent
      # 资源配置
      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
        # runAsNonRoot: true
        # runAsUser: 1000
      # 资源配置
      resources: {}
        # We usually recommend not to specify default resources and to leave this as a conscious
        # choice for the user. This also increases chances charts run on environments with little
        # resources, such as Minikube. If you do want to specify resources, uncomment the following
        # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
        # limits:
        #   cpu: 100m
        #   memory: 128Mi
        # requests:
        #   cpu: 100m
        #   memory: 128Mi
    
    # 初始化容器配置
    initContainer:
      #是否开启
      enabled: false
      #镜像名称
      name: "skywalking-agent-sidecar"
      #仓库地址
      repository: 10.1.10.212:8082/egrand-cloud-deploy/skywalking-agent-sidecar
      #镜像版本
      tag: "latest"
      #镜像拉去策略:Always(总是拉取 pull)、
      #            IfNotPresent(默认值,本地有则使用本地镜像,不拉取)、
      #            Never(只使用本地镜像,从不拉取)
      pullPolicy: IfNotPresent
      # 资源配置
      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
      # runAsNonRoot: true
      # runAsUser: 1000
      # 资源配置
      resources: {}
        # We usually recommend not to specify default resources and to leave this as a conscious
        # choice for the user. This also increases chances charts run on environments with little
        # resources, such as Minikube. If you do want to specify resources, uncomment the following
        # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
        # limits:
        #   cpu: 100m
        #   memory: 128Mi
        # requests:
      #   cpu: 100m
      #   memory: 128Mi
      # 运行参数配置
      args:
        - '-c'
        - >-
          mkdir -p /skywalking/agent && cp -r /usr/skywalking/agent/* /skywalking/agent
      # 运行命令配置
      command:
        - sh
    
    # 服务配置
    service:
      # service的类型访问方式。一般为NodePort、ClusterIP、LoadBalancer
      type: NodePort
      # 内部端口
      ports: {}
    #    - name: names
    #      port: 80
    #      protocol: TCP
    #      targetPort: 80
    
    # 帐户设置,create为false时,会自动设置帐户名为default
    serviceAccount:
      # 是否创建帐户
      create: false
      # 帐户注解
      annotations: {}
      # 帐户名,不设置并且create为true,则帐户名为backend-deployment-chart.fullname
      # If not set and create is true, a name is generated using the fullname template
      name: ""
    
    ingress:
      enabled: false
      annotations: {}
        # kubernetes.io/ingress.class: nginx
        # kubernetes.io/tls-acme: "true"
      hosts:
        - host: chart-example.local
          paths:
          - path: /
            backend:
              serviceName: chart-example.local
              servicePort: 80
      tls: []
      #  - secretName: chart-example-tls
      #    hosts:
      #      - chart-example.local
    
    autoscaling:
      enabled: false
      minReplicas: 1
      maxReplicas: 100
      targetCPUUtilizationPercentage: 80
      # targetMemoryUtilizationPercentage: 80
    
    以上参数都可以通过定义values.yaml进行复写,在安装时使用helm install -f values.yaml demo egrand/backend-deployment-chart覆盖默认参数。
    
    5.5.1 微服务应用配置说明

    微服务应用镜像是由开发人员将开发好的微服务上传到Harbor,在生产容器时会根据指定Harbor镜像地址自动下载并启动。在配置中通过以下配置声明微服务镜像:

    #应用镜像配置
    image:
      #仓库地址
      repository: 10.1.10.212:8082/library/nginx
      #镜像版本,默认为appVersion.
      tag: "latest"
      #镜像拉去策略:Always(总是拉取 pull)、
      #            IfNotPresent(默认值,本地有则使用本地镜像,不拉取)、
      #            Never(只使用本地镜像,从不拉取)
      pullPolicy: Always
      #环境变量设置,采用name和value格式。
    #  env:
    #    - name: TZ
    #      value: Asia/Shanghai
    #    - name: spring.datasource.dynamic.datasource.master.url
    #      value: >-
    #        jdbc:mysql://10.1.10.188:3306/egrand-cloud-ky3h-cis?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8&connectTimeout=60000&socketTimeout=30000
    #    - name: springfox.documentation.swagger.v2.host
    #      value: >-
    #        10.1.10.212/ky3h
      # 安全设置
      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
      # runAsNonRoot: true
      # runAsUser: 1000
      # 资源配置
      resources: {}
        # We usually recommend not to specify default resources and to leave this as a conscious
        # choice for the user. This also increases chances charts run on environments with little
        # resources, such as Minikube. If you do want to specify resources, uncomment the following
        # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
        # limits:
        #   cpu: 100m
        #   memory: 128Mi
        # requests:
      #   cpu: 100m
      #   memory: 128Mi
      #镜像挂载
      volumeMounts: {}
        #主机模式
    #    - mountPath: /usr/local/skywalking/agent
    #      mountPropagation: None
    #      name: skywalking-agent
        #PVC模式
    #    - name: atm-data
    #        persistentVolumeClaim:
    #          claimName: kube-yuncang
    

    如上,可以通过repository和tag指定了镜像下载地址和版本,通过env指定容器运行的环境变量,通过resources可以设定资源限制等。

    注意:环境变量如果是整形,要加单引号,如下:

    #应用镜像配置
    image:
      #仓库地址
      repository: 10.1.10.212:8082/egrand-cloud/egrand-cloud-demo-server
      #镜像版本
      tag: "prod-latest"
      env:
        - name: CONSUL_HOST
          value: 10.1.10.179
        - name: CONSUL_PORT
          value: '8500'
    
    5.5.2 Consul Client SideCar配置说明

    该容器默认是关闭的,如果有需要可以开启,该容器采用SideCar模式运行在微服务应用容器的旁边,使微服务应用容器中的Consul能够使用127.0.0.1:8500方式实现与Consul Server的通信,而启用该容器需要设置连接Consul Server的配置信息,配置中采用ConfigMap实现,配置如下:

    consulAgentConfigMap:
      # 覆盖名称(与config是冲突的,如果填写该值,则在K8S namespace查找配置字典)
      nameOverride: "consul-agent"
      # 配置字典配置信息
      config:
        agent.json: |
          {
            "bind_addr": "0.0.0.0",
            "client_addr": "0.0.0.0",
            "datacenter": "egdPlatform",
            "primary_datacenter": "egdPlatform",
                "encrypt": "EXz7LFN8hpQ4id8EDYiFoQ==",
            "data_dir": "/consul/data",
            "acl": {
                "enabled": true,
                "default_policy": "deny",
                "enable_token_persistence": true,
                "tokens": {
                    "agent": "b07df4e4-1183-5496-35fe-0ac6eb726d05"
                }
            },
            "log_level": "INFO",
            "log_file": "/consul/consul.log",
            "log_rotate_duration": "24h",
            "enable_syslog": false,
            "enable_debug": true,
            "connect":{
                "enabled": true
              },
                "ui": true,
            "start_join":[
                "consul-0.consul.kube-platform.svc.cluster.local",
                "consul-1.consul.kube-platform.svc.cluster.local",
                "consul-2.consul.kube-platform.svc.cluster.local"
              ]
          }
    

    如上,为Consul Client连接Consul Server的配置信息,可以采用两种配置方式:

    1. 如果在我们部署的K8S NameSpace下已经新建了一个ConfigMap,可以通过指定参数nameOverride值即可。

      consulAgentConfigMap:
        # 覆盖名称(与config是冲突的,如果填写该值,则在K8S namespace查找配置字典)
        nameOverride: "consul-agent"
      
    2. 如果想新建一个ConfigMap,通过指定config参数值即可

      consulAgentConfigMap:
        # 配置字典配置信息
        config:
          agent.json: |
            {
              "bind_addr": "0.0.0.0",
              "client_addr": "0.0.0.0",
              "datacenter": "egdPlatform",
              "primary_datacenter": "egdPlatform",
                  "encrypt": "EXz7LFN8hpQ4id8EDYiFoQ==",
              "data_dir": "/consul/data",
              "acl": {
                  "enabled": true,
                  "default_policy": "deny",
                  "enable_token_persistence": true,
                  "tokens": {
                      "agent": "b07df4e4-1183-5496-35fe-0ac6eb726d05"
                  }
              },
              "log_level": "INFO",
              "log_file": "/consul/consul.log",
              "log_rotate_duration": "24h",
              "enable_syslog": false,
              "enable_debug": true,
              "connect":{
                  "enabled": true
                },
                  "ui": true,
              "start_join":[
                  "consul-0.consul.kube-platform.svc.cluster.local",
                  "consul-1.consul.kube-platform.svc.cluster.local",
                  "consul-2.consul.kube-platform.svc.cluster.local"
                ]
            }
      
    5.5.3 Skywalking initContainer配置说明

    微服务应用部署支持Skywalking配置,这要求微服务应用在启动命令java -cp 中要制定-javaagent=[skywalking包路径]方式才能使用,默认是关闭的,如下:

    # 初始化容器配置
    initContainer:
      #是否开启
      enabled: false
      #镜像名称
      name: "skywalking-agent-sidecar"
      #仓库地址
      repository: 10.1.10.212:8082/egrand-cloud-deploy/skywalking-agent-sidecar
      #镜像版本
      tag: "latest"
      #镜像拉去策略:Always(总是拉取 pull)、
      #            IfNotPresent(默认值,本地有则使用本地镜像,不拉取)、
      #            Never(只使用本地镜像,从不拉取)
      pullPolicy: IfNotPresent
      # 资源配置
      securityContext: {}
        # capabilities:
        #   drop:
        #   - ALL
        # readOnlyRootFilesystem: true
      # runAsNonRoot: true
      # runAsUser: 1000
      # 资源配置
      resources: {}
        # We usually recommend not to specify default resources and to leave this as a conscious
        # choice for the user. This also increases chances charts run on environments with little
        # resources, such as Minikube. If you do want to specify resources, uncomment the following
        # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
        # limits:
        #   cpu: 100m
        #   memory: 128Mi
        # requests:
      #   cpu: 100m
      #   memory: 128Mi
      # 运行参数配置
      args:
        - '-c'
        - >-
          mkdir -p /skywalking/agent && cp -r /usr/skywalking/agent/* /skywalking/agent
      # 运行命令配置
      command:
        - sh
    

    该容器的目的就是将Skywalking需要应用到的第三方包和配置拷比到容器组中,使得微服务应用容器能够访问到,在启用了skywalking之后,还要进行Skywalking服务器连接配置,指定服务器地址和参数,这样采用将微服务应中的链路日志推送到服务器上。这里采用ConfigMap挂接方式进行:

    # skywalking配置字典
    skywalkingAgentConfigMap:
      # 覆盖名称(与key和value是冲突的,如果填写该值,则在K8S namespace查找配置字典)
      nameOverride: "skywalking-config"
      # 配置字典配置信息
      config:
        agent.config: |
          agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
          collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:10.1.10.178:11800}
          logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
          logging.level=${SW_LOGGING_LEVEL:INFO}
          plugin.mount=${SW_MOUNT_FOLDERS:plugins,activations}
    

    如上,具有两种方式:

    1. 如果微服务应用运行的K8S Namespace中已经存在一个ConfigMap,则只需要指定其名称即可:

      # skywalking配置字典
      skywalkingAgentConfigMap:
        # 覆盖名称(与key和value是冲突的,如果填写该值,则在K8S namespace查找配置字典)
        nameOverride: "skywalking-config"
      
    2. 如果想新建一个ConfigMap,则配置config参数即可;

      # skywalking配置字典
      skywalkingAgentConfigMap:
        # 配置字典配置信息
        config:
          agent.config: |
            agent.service_name=${SW_AGENT_NAME:Your_ApplicationName}
            collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:10.1.10.178:11800}
            logging.file_name=${SW_LOGGING_FILE_NAME:skywalking-api.log}
            logging.level=${SW_LOGGING_LEVEL:INFO}
            plugin.mount=${SW_MOUNT_FOLDERS:plugins,activations}
      
    5.5.3 imagePullSecrets配置说明

    Chart中通过定义如下参数来默认给出了镜像拉取认证:

    #镜像拉取密文配置
    imageCredentials:
      # 覆盖名称(与config是冲突的,如果填写该值,则在K8S namespace查找配置字典)
    #  nameOverride: "harborsecret"
      config:
        # 镜像仓库地址
        registry: http://10.1.10.212:8082/
        # 镜像登录名
        username: admin
        # 镜像登录密码
        password: wiki2012
    

    如上,如果想复用命名空间已经存在的Secret,则修改本地的values.yaml如下:

    #镜像拉取密文配置
    imageCredentials:
      # 覆盖名称(与config是冲突的,如果填写该值,则在K8S namespace查找配置字典)
      nameOverride: "harborsecret"
    
    5.5.4 Service服务配置说明

    服务配置参数默认是采用ClusterIP方式暴露端口的,如果没有端口暴露,则无需配置,如果需要配置,可以通过以下方式复写默认配置:

    # 服务配置
    service:
      # service的类型访问方式。一般为NodePort、ClusterIP、LoadBalancer
      type: NodePort
      # 端口配置
      ports:
        - name: names
          port: 80
          protocol: TCP
          targetPort: 80
          NodePort: 80
    

    6. Jenkins&GitLab配置

    本章节介绍Jenkins的公共库配置,如何新建任务并实现与GitLab的结合。

    1. 利用Jenkins公共库配置,规范发布流程;
    2. 利用Jenkins的WebHook功能实现与GitLab通信;
    

    6.1 Jenkins Library公共库配置

    配置Jenkins Library目的是能够在Jenkinsfile文件中通过"@Library"方式远程加载出来,所以需要指定其名称以及位置等信息。登录到Jenkins管理界面,依次点击”系统设置->系统配置“ 找到"Global Pipeline Libraries",然后如图配置即可:


    6.2 Jenkins单模块任务配置

    需要发布一个应用时,按如下步骤操作(以下以egrand-cloud-demo-server发布为例)



    如上,就是一个单模块的微服务应用。

    6.2.1 新建Jenkins任务
    1. 新建Jenkins任务
      点击【新建任务】按钮,进入如下页面:



      输入一个任务名称,选择”流水线“构建。
      注:任务名称必须以英文开头,且长度不能超过30个字符;(该限制是在发布K8S POD时要求的限制)

    2. 点击【确认】按钮,如下图:


    3. 配置”构建触发器“,点击页签”构建触发器“,如下图:



      选择”Generic Webhook Trigger",并在“Post content parameters”中添加如下参数:

    variable Expression Description
    branch $.ref JSONPath 获取WebHook中GitLab的分支信息参数
    gitUrl $.project.git_http_url JSONPath 获取WebHook中GitLab的http地址信息参数
    gitCommitId $.after JSONPath 获取WebHook中GitLab最新的提交id号
    gitPreCommitId $.before JSONPath 获取WebHook中GitLab上一次提交id号

    然后,查找到以下界面:



    设置GitLab需要传递的Token值,这个值会在GitLab发起WebHook的时候传递过来,用于关联GitLab项目和具体任务。

    1. 配置流水线,点击页签【流水线】,如下:



      如上图,指定了Jenkins Library在GitLab上的地址,同时设置了Jenkinsfile的脚本路径。

    2. 点击【保存】即可。
    6.2.2 GitLab WebHook配置

    上一步我们已经完成了一个任务的创建,并且在第3步中得到了一个Token值,现在需要在GitLab上将项目和Jenkins任务关联起来,需要登录GitLab进行操作:

    1. 登录GitLab管理界面,查找到需要关联的GitLab项目


    2. 在该项目下选择菜单“Settings-->Integrations",进入Hook界面配置:



      如上图,输入URL地址,规范为:http://[Jenkins访问地址]:[Jenkins访问端口]/generic-webhook-trigger/invoke?token=[在Jenkins任务中的设置的token值]

    3. 点击按钮[Save]即可。


    4. 可以通过[Test]下拉菜单来进行测试,测试是否能够正常触发Jenkins任务。

    6.3 Jenkins多模块任务配置

    需要发布一个应用时,按如下步骤操作(以下以egrand-cloud-project发布为例)


    如上图,为多模块的应用,现在需要发布egrand-cloud-ram-server模块。

    6.3.1 新建Jenkins任务
    1. 新建Jenkins任务
      点击【新建任务】按钮,进入如下页面:



      输入一个任务名称,选择”流水线“构建。
      注:任务名称必须以英文开头,且长度不能超过30个字符;(该限制是在发布K8S POD时要求的限制)

    2. 点击【确认】按钮,如下图:


    3. 配置”构建触发器“,点击页签”构建触发器“,如下图:


    选择”Generic Webhook Trigger",并在“Post content parameters”中添加如下参数:

    variable Expression Description
    branch $.ref JSONPath 获取WebHook中GitLab的分支信息参数
    gitUrl $.project.git_http_url JSONPath 获取WebHook中GitLab的http地址信息参数
    gitCommitId $.after JSONPath 获取WebHook中GitLab最新的提交id号
    gitPreCommitId $.before JSONPath 获取WebHook中GitLab上一次提交id号
    commits $.commits[0].['modified','added','removed'][*] JSONPath 获取WebHook中GitLab提交的最新日志记录,用于判断是否为本模块。

    配置GitLab在发起WebHook时的Token,配置以下界面:



    设置GitLab需要传递的Token值,这个值会在GitLab发起WebHook的时候传递过来,用于关联GitLab项目和具体任务。



    设置 Optional filter,用于对提交的最新日志做正则表达式运算,判断是否提交的代码包含该任务。
    1. 配置流水线,点击页签【流水线】,如下:



      设置Jenkinsfile文件的GitLab地址和路径,这里引用Jenkins Library公共的Jenkinsfile文件。

    2. 点击【保存】即可。
    6.3.2 GitLab WebHook配置

    Jenkins Library存储在GitLab上,具体目录结构如下:

    egrand-cloud-jenkins-library
    |--- deploy
    |      |--- Jenkinsfile     // 公共的Jenkinsfile,项目在创建Jenkins任务时,引用该文件开始构建,该文件调用vars/build.groovy的build方法
    |--- vars
           |--- build.groovy    // 构建主文件,为Jenkinsfile提供调用方法build
           |--- gitlab.groovy   // 子流程步骤,供build.groovy调用,完成从GitLab上下载源代码
           |--- jib.groovy      // 子流程步骤,供build.groovy调用,完成maven插件jib构建,构建镜像并推送到Harbor中
           |--- kubecm.groovy   // 子流程步骤,供build.groovy调用,完成kubectl config环境切换和Helm uninstall/install操作,部署到K8S集群
           |--- sonar.groovy    // 子流程步骤,供build.groovy调用,完成静态代码分析,并推送到SonarQube服务器;
    

    7. K8S集群环境

    K8S集群环境分为测试环境和生成环境,为了保持发布时的统一处理,规定如下:

    1. GitLab master分支 ----> K8S生产环境
    2. GitLab dev分支    ----> K8S测试环境
    

    具体切换时使用kubectl config 工具切换。
    注:在部署时,会删除掉上次部署的pod和本次部署的pod先,然后再部署;

    相关文章

      网友评论

          本文标题:CI/CD持续集成/持续部署实践

          本文链接:https://www.haomeiwen.com/subject/yandrltx.html