基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

参考:http://www.mydlq.club/article/47/

一、CICD 流程图参考

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

二、必要的插件说明

  • Git: Jenkins 安装中默认安装 Git 插件,所以不需要单独安装。利用 git 工具可以将 github、gitlab 等等的地址下载源码。
  • Docker: Jenkins 安装中默认安装 Docker 插件,所以不需要单独安装。利用 Docker 插件可以设置 Docker 环境,运行 Docker 命令,配置远程 Docker 仓库凭据等。
  • Kubernetes: Kubernetes 插件的目的是能够使用 Kubernetes 集群动态配置 Jenkins 代理(使用Kubernetes调度机制来优化负载),运行单个构建,等构建完成后删除该代理。这里我们需要用到这个插件来启动 Jenkins Slave 代理镜像,让代理执行 Jenkins 要执行的 Job。
  • Kubernetes Cli: Kubernetes Cli 插件作用是在执行 Jenkins Job 时候提供 kubectl 与 Kubernetes 集群交互环境。可以在 Pipeline 或*式项目中允许执行 kubectl 相关命令。它的主要作用是提供 kubectl 运行环境,当然也可以提供 helm 运行环境。
  • Config File Provider: Config File Provider 插件作用就是提供在 Jenkins 中存储 properties、xml、json、settings.xml 等信息,可以在执行 Pipeline 过程中可以写入存储的配置。例如,存入一个 Maven 全局 Settings.xml 文件,在执行 Pipeline Job 时候引入该 Settings.xml ,这样 Maven 编译用的就是该全局的 Settings.xml。
  • Pipeline Utility Steps: 这是一个操作文件的插件,例如读写 json、yaml、pom.xml、Properties 等等。在这里主要用这个插件读取 pom.xml 文件的参数设置,获取变量,方便构建 Docker 镜像。
  • Git Parameter: 能够与 Git 插件结合使用,动态获取 Git 项目中分支信息,在 Jenkins Job 构建前提供分支选项,来让项目执行人员选择拉取对应分支的代码。

三、必要的凭据说明

选择 <凭据->系统->全局凭据->添加凭据> 来新增 Git、Docker Hub、Kubernetes 等认证凭据。

  • Git 认证凭据

配置的参数值:

    类型:Username with password
    范围:全局
    用户名(Git 用户名): 略
    密码(Git 密码):略
    ID:global-git-credential
    描述:全局 Git 凭据
  • Kubernetes Token 凭据

配置的参数值:

    类型:Secret text
    范围:全局
    Secret(K8S Token 串):略
    ID:global-kubernetes-credential
    描述:全局的 K8S Token
  • Docker 仓库认证凭据

配置的参数值:

    类型:Username with password
    范围:全局
    用户名(Docker 仓库用户名):略
    密码(Docker 仓库密码):略
    ID:docker-hub-credential
    描述:Docker 仓库认证凭据

四、Jenkins 配置 Kubernetes 插件

Kubernetes Plugin 基本配置

  • 配置连接 Kubernetes 参数

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

注意: 如果你的 Jenkins 也是安装在 Kubernetes 环境中,那么可以直接使用 Kubernetes 集群内的 Kubernetes API 地址,如果 Jnekins 是在安装在正常物理机或者虚拟机环境中,那么使用集群外的 Kubernetes API 地址。

  • 配置 Jenkins 地址

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

注意: 这里的 Jenkins 地址是供 Slave 节点连接 Jenkins Master 节点用的,所以这里需要配置 Jenkins Master 的 URL 地址。这里和上面一样,也是考虑 Jenkins 是部署在 Kubernetes 集群内还是集群外。如果 Jnekins 中配置了 /jenkins 前缀,则 URL 后面加上 /jenkins,否则不加,这个地址根据自己的 Jnekins 实际情况来判断。

Kubernetes 插件 Pod 模板配置

  • 配置 Pod 名称和标签列表

配置 Pod 模板的名称和标签列表名,Pod 模板名可用于子模板继承,标签列表可用于 Jenkins Job 中指定,使用此 Pod 模板来执行任务。

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

  • 配置 Pod 的原始 yaml

在 Pod 的原始 yaml 配置中,加入一段配置,用于改变 Kubernetes Plugin 自带的 JNLP 镜像,并指定 RunAsUser=0 来使容器以 Root 身份执行任务,并设置 privileged=true 来让 Slave Pod 在 Kubernetes 中拥有特权。

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

yaml 内容如下:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-slave
spec:
  securityContext:
    runAsUser: 0
    privileged: true
  containers:
  - name: jnlp
    tty: true
    workingDir: /home/jenkins/agent
    image: jenkins/agent:4.10-1-alpine

Jenkins Slave JNLP 镜像官方地址: https://hub.docker.com/r/jenkins/agent/

Kubernetes 插件 Container 配置

  • 配置 Maven 镜像

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Maven 镜像可以从官方 Docker Hub 下载,地址:https://hub.docker.com/_/maven

  • 配置 Docker In Docker 镜像

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

这里推荐使用 dind 版本的镜像,Docker-IN-Docker 镜像可以从官方 Docker Hub 下载,地址:https://hub.docker.com/_/docker

  • 配置 Kubectl 镜像

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Kubectl 镜像可以从官方 Docker Hub 下载,地址:https://hub.docker.com/r/bitnami/kubectl

Container 存储挂载配置

由于上面配置的 Maven、Docker 等都需要挂载存储,Maven 中是将*仓库下载的 Jar 存储到共享目录,而 Docker 则是需要将宿主机的 Docker 配置挂载到 Docker In Docker 容器内部,所以我们要对挂载进行配置。

  • 创建 Maven 存储使用的 PVC

例如:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: maven
  namespace: cicd
spec:
  storageClassName: cephfs
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 100Gi
  • 配置 Maven 挂载

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

  • 配置 Docker 挂载

Kubernetes 中 Pod 的容器是启动在各个节点上,每个节点就是一台宿主机,里面进行了很多 Docker 配置,所以我们这里将宿主机的 Docker 配置挂载进入 Docker 镜像。选择添加卷,选择 Host Path Volume 按如下添加配置:

路径 /usr/bin/docker:

主机路径(宿主机目录):/usr/bin/docker
挂载路径(容器内的目录):/usr/bin/docker

路径 /var/run/docker.sock:

主机路径(宿主机目录):/var/run/docker.sock
挂载路径(容器内的目录):/var/run/docker.sock

路径 /etc/docker:

主机路径(宿主机目录):/etc/docker
挂载路径(容器内的目录):/etc/docker

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

五、Jenkins 创建相关文件

之前安装了 Config File Provider 插件,该插件功能就是可以在 Jenkins 上存储一些配置文件,例如,我们经常使用到的 yaml、properties、Dockerfile、Maven 的 Settings.xml 等文件,都可以存储到 Jenkins 该插件中。

打开 系统管理->Managed files ,在其中新增几个文件:

  • Maven 配置文件: Maven 的 Settings.xml 配置文件。
  • Dockerfile 文件: Dockerfile 脚本。
  • Kubernetes 部署文件: 将应用部署到 kubernetes 的 Deployment 文件。

新增 Maven 配置文件

选择 Add a new Config—>Global Maven settings.xml 来新增一个 Maven 全局 Settings.xml 文件:

ID: global-maven-settings
Name: MavenGlobalSettings
Comment: 全局 Maven Settings.xml 文件
Content: 内容如下

示例文件内容:

<?xml version="1.0" encoding="UTF-8"?>

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <pluginGroups>
  </pluginGroups>

  <proxies>
  </proxies>

  <servers>
  </servers>
  
  <mirrors>
    <!--Aliyun Maven-->
    <mirror>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  
  <profiles>
  </profiles>

</settings>

为了加快 jar 包的下载速度,这里将仓库地址指向 aliyun Maven 仓库地址。

新增 Dockerfile 文件

选择 Add a new Config—>Custom file 来新增一个 Dockerfile 文件:

ID: global-dockerfile-file
Name: Dockerfile
Comment: 全局 Dockerfile 文件
Content: 内容如下

示例文件内容:

FROM openjdk:8u222-jre-slim
VOLUME /tmp
ADD target/*.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JVM_OPTS="-Xss256k -Duser.timezone=Asia/Shanghai -Djava.security.egd=file:/dev/./urandom"
ENV JAVA_OPTS=""
ENV APP_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JVM_OPTS $JAVA_OPTS -jar /app.jar $APP_OPTS" ]

新增 Kubernetes 部署文件

选择 Add a new Config—>Custom file 来新增一个 Kubernetes 部署文件:

ID: global-kubernetes-deployment
Name: deployment.yaml
Comment: 全局 Kubernetes 部署文件
Content: 内容如下

示例文件内容:

apiVersion: v1
kind: Service
metadata:
  name: #APP_NAME
  labels:
    app: #APP_NAME
spec:
  type: NodePort
  ports:
  - name: server          #服务端口
    port: 8080  
    targetPort: 8080
  - name: management      #监控及监控检查的端口 
    port: 8081
    targetPort: 8081
  selector:
    app: #APP_NAME
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: #APP_NAME
  labels:
    app: #APP_NAME
spec:
  replicas: #APP_REPLICAS
  selector:
    matchLabels:
      app: #APP_NAME
  strategy:
    type: Recreate          #设置更新策略为删除策略
  template:
    metadata:
      labels:
        app: #APP_NAME
    spec:
      containers:
      - name: #APP_NAME
        image: #APP_IMAGE_NAME
        imagePullPolicy: Always
        ports:
        - containerPort: 8080   #服务端口
          name: server
        - containerPort: 8081   #监控及监控检查的端口 
          name: management
        env:
        - name: "update_uuid"
          value: "#APP_UUID"    #生成的随机值,放置执行kubectl apply时能够执行
        resources: 
          limits:
            cpu: 2000m
            memory: 1024Mi
          requests:
            cpu: 1000m
            memory: 512Mi

为了模板能够动态替换某些值,上面模板中设置了几个可替换的参数,用 #变量名称 来标记,后面我们在执行 Pipeline 时候将里面的 #xxx变量 标记替换掉。

六、流水线脚本编写和插件使用说明

脚本中设置全局超时时间

设置任务超时时间,如果在规定时间内任务没有完成,则进行失败操作,格式如下:

timeout(time: 60, unit: 'SECONDS') {
    // 脚本
}

脚本中使用 Git 插件

Git 插件方法使用格式,及其部分参数:

  • changelog: 是否检测变化日志
  • url: Git 项目地址
  • branch: Git 分支
  • credentialsId: Jenkins 保存的 Git 凭据 ID 值
git changelog: true,
    url: "http://gitlab.xxxx/xxx.git"
    branch: "master",
    credentialsId: "xxxx-xxxx-xxxx-xxxx",

脚本中使用 Kubernetes 插件

Kubernetes 插件中存在 PodTemplate 方法,在执行脚本时候,会自动在 Kubernetes 中创建 Pod Template 配置的 Slave Pod,在其中执行 podTemplate 代码块中的脚本。

def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        print "在 Slave Pod 中执行任务"  
    }
}

podTemplate 方法参数简介:

  • cloud: 之前 Kuberntes 插件配置的 Cloud 名称
  • label: 之前 Kuberntes 插件配置的 Cloud 中 Pod Template 里面的 Label 标签名称。

脚本中使用 Docker 镜像

在之前配置了 Kubernetes 插件的 Pod Template 配置中,配置了几个容器,每个容器中都有特定的功能的环境,例如:

  • Maven 容器中能够执行 mvn 命令。
  • Kubectl 容器能够执行 kubectl 命令。
  • Docker In Docker 容器中能够执行 Docker 命令。

既然每个容器都能提供特定的环境,那么再执行执行 Pipleline 脚本时候,就可以在不同的镜像中使用不同的环境的命令:

Maven 镜像

container('maven') {  
    sh "mvn install
}

Docker In Docker 镜像

container('docker') {  
    sh "docker build -t xxxxx:1.0.0 .
}

Kubectl 镜像

container('kubectl') {  
    sh "kubectl apply -f xxxx.yaml"
}

脚本中引入 Jenkins 中预先存储的文件

在之前的 系统设置->File Manager 中,存储了很多文件,例如:

  • Docker 的镜像构建脚本文件 Dockerfile。
  • Maven 的全局设置文件 Settings.xml
  • Kubernetes 的部署文件 deployment.yaml

在使用 Pipleline 脚本时候,我们需要将这些文件文本提取出来,创建在执行任务的流程中,创建这些文件可以使用 Config File Provider 插件提供的 configFileProvider 方法,如下所示:

创建 settings.xml 文件

configFileProvider([configFile(fileId: "global-maven-settings", targetLocation: "settings.xml")]){
    sh "cat settings.xml"
}

创建 Dockerfile 文件

configFileProvider([configFile(fileId: "global-dockerfile-file", targetLocation: "Dockerfile")]){
    sh "cat Dockerfile"
}

创建 deployment.yaml 文件

configFileProvider([configFile(fileId: "global-kubernetes-deployment", targetLocation: "deployment.yaml")]){
    sh "cat deployment.yaml"
}

脚本创建文件

在使用 Groovy 写 Pipleline 脚本时候,经常要将变量的文本生成文件,方便在执行流水线过程中操作文本文件使用,如何将文本转换为文件,可以使用 Pipeline Utility Steps 插件的 writeFile 方法,如下:

writeFile encoding: 'UTF-8', file: './test.txt', text: "写入文件的文本内容"

脚本中使用 Http Rrequest 插件

脚本中可以使用 HttpRequest 来对某一地址进行请求,这里简单使用 Get 请求地址,复杂的可以查看 Jenkins 插件的官网查看使用示例。

下面是使用 Http Request 的 Get 请求示例:

result = httpRequest "http:www.baidu.com"

if ("${result.status}" == "200") {
    print "Http 请求成功"
} 

脚本中使用 Kubernetes Cli 插件

在之前说过,在 kubectl 镜像中能够使用 kubectl 命令,不过由于执行 Kubectl 命令一般需要在镜像的 $HOME/.kube/ 目录中存在连接 Kubernetes API 的 config 文件,使其 kubectl 命令有明确请求 kubernetes API 的地址和用户权限,不过将 config 文件挂入镜像内部是一件比较繁琐的事情。

好在 Jenkins 提供的 Kubectl Cli 插件,只要在其中配置连接 Kubernetes 的 Token 凭据,就能够在 Kubectl Cli 提供的 withKubeConfig 方法,拥有类似存在 config 一样的功能,在 kubectl 镜像中的 withKubeConfig 方法块内执行 kubectl 就可以操作配置的 Kubectl Cli 的凭据的 K8S 集群。

container('kubectl') {
    withKubeConfig([credentialsId: "Kubernetes Token 凭据 ID",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
        sh "kubectl get nodes"
    }
}

脚本中操作字符串替换值

在使用 Groovy 语法写 Pipleline 脚本时候,我们经常要替换先前设置好的一些文本的值,这里我们简单示例一下,如何替换字符串。

// 测试的字符串
sourceStr = "这是要替换的值:#value1,这是要替换的值:#value2"
// 替换 #value1 与 #value2 两个值
afterStr = deploy.replaceAll("#value1","AAA").replaceAll("#value2","BBB")
// 输出替换后的字符串
print "${afterStr}"

脚本中读取 pom.xml 参数

在执行 Java 项目的流水线时,我们经常要动态获取项目中的属性,很多属性都配置在项目的 pom.xml 中,还好 Pipeline Utility Steps 插件提供能够读取 pom.xml 的方法,示例如下:

stage('读取pom.xml参数阶段'){
    // 读取 Pom.xml 参数
    pom = readMavenPom file: './pom.xml'
    // 输出读取的参数
    print "${pom.artifactId}"
    print = "${pom.version}"
}

脚本中使用 Docker 插件构建与推送镜像

在流水线脚本中,我们一般不直接使用 Docker 命令,而是使用 Docker 插件提供的 docker.withRegistry("") 方法来构建与推送镜像,并且还能在方法中配置登录凭据信息,来让仓库验证权限,这点是非常方便的。使用示例如下:

docker.withRegistry("http://xxxx Docker 仓库地址", "Docker 仓库凭据 ID") {
        // 构建 Docker 镜像
        def customImage = docker.build("${dockerImageName}")
        // 推送 Docker 镜像
        customImage.push()
    }

七、在 Jenkins 创建模板任务

创建一个 Pipeline Job 来充当各个 Jenkins Job 的模板,方便后续创建 Job 时,直接复制模板项目,然后修改配置就能使用。所以这里我们创建一个模板 Pipeline Job,在 Job 配置中需要添加一些参数和环境变量,方便我们动态替换一些值。

创建 Pipeline 任务

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

配置项目构建基本参数

配置同一时间一个 Job 只能构建一个,不允许多个并发构建。另外需要设置项目构建后,包的保留时间,以防止包过多且大占用大量空间(一个包很可能占 10MB~200MB 大小)导致存储空间不足。

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

配置 Git 变量

在 Job 配置的 参数化构建过程 中,添加下面参数:

Git 项目地址变量

变量名称:GIT_PROJECT_URL
类型:String
描述:项目 Git 地址
默认值:"https://xxxxxxxxxxxx"

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Git 分支变量

变量名称:GIT_BRANCH
类型:Git Parameter
描述:选择 Git 分支
默认值:master

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Git 凭据变量

变量名称:GIT_CREADENTIAL
类型:Credentials
描述:Git 凭据
默认值:global-git-credential

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

配置 Maven 变量

Maven 构建命令变量

变量名称:MAVEN_BUILD_OPTION
类型:Choices
描述:要执行的 Maven 命令选择
可选值:['package', 'install', 'deploy']
默认值:install

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

配置 Docker 变量

Docker 项目地址变量

变量名称:DOCKER_HUB_URL
类型:String
描述:Docker 仓库地址
默认值(默认 Docker 仓库地址):"10.71.164.28:5000"

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Docker 仓库项目组变量

变量名称:DOCKER_HUB_GROUP
类型:String
描述:Docker 仓库项目组名
默认值:""

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Docker 仓库认证凭据变量

变量名称:DOCKER_HUB_CREADENTIAL
类型:Credentials
描述:Docker 仓库认证凭据
默认值:docker-hub-credential

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Docker Dockerfile 文件 ID 变量

变量名称:DOCKER_DOCKERFILE_ID
类型:String
描述:存于 Jenkins "Managed files" 的 Dockerfile 文件的 ID
默认值:"global-dockerfile-file"

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

配置 Kubernetes 变量

Kubernetes 认证凭据变量

变量名称:KUBERNETES_CREADENTIAL
类型:Credentials
描述:Kubernetes 认证 Token
默认值:global-kubernetes-credential

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Kubernetes Namespace 变量

变量名称:KUBERNETES_NAMESPACE
类型:String
描述:Kubernetes 命名空间 Namespace
默认值:""

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Kubernetes 应用实例副本数

变量名称:KUBERNETES_APP_REPLICAS
类型:String
描述:应用实例副本数
默认值:1

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

Kubernetes 应用部署 yaml 文件ID

变量名称:KUBERNETES_DEPLOYMENT_ID
类型:String
描述:存于 Jenkins "Managed files" 的 K8S 部署文件的 ID
默认值:"global-kubernetes-deployment"

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

配置 HTTP 变量

HTTP 健康检查端口

变量名称:HTTP_REQUEST_PORT
类型:String
描述:Http Request 端口(健康检测端口)
默认值:8081

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

HTTP 健康检查地址

变量名称:HTTP_REQUEST_URL
类型:String
描述:Http Request 项目中的相对路径(健康检测路径)
默认值:/actuator/health

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

HTTP 健康检查次数

变量名称:HTTP_REQUEST_NUMBER
类型:Choices
描述:Http Request 请求次数
可选值:['10', '5', '10', '15', '20', '25', '30']
默认值:10

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

HTTP 健康检查时间间隔

变量名称:HTTP_REQUEST_INTERVAL
类型:Choices
描述:Http Request 时间间隔
可选值:['10', '5', '15', '20', '25', '30']
默认值:10

基于 Jenkins 和 K8s 的 CICD 学习随笔(Kubectl)

八、Pipeline 脚本模板

脚本中包含多个阶段,分别为 Git 拉取镜像,Maven 编译 Java 项目,Docker 构建与推送镜像,Kubectl 部署应用到 Kubernetes 中,最后使用 Http 请求进行健康检查。

def label = "jnlp-agent"
timeout(time: 900, unit: 'SECONDS') {
    podTemplate(label: label,cloud: 'kubernetes' ){
        node (label) {
            stage('Git阶段'){
                // 执行 Git 命令进行 Clone 项目
                git changelog: true,
                    branch: "${params.GIT_BRANCH}",
                    credentialsId: "${params.GIT_CREADENTIAL}",
                    url: "${GIT_PROJECT_URL}"
            }
            stage('Maven阶段'){
                container('maven') {  
                    // 创建 Maven 需要的 Settings.xml 文件
                    configFileProvider([configFile(fileId: "global-maven-settings", targetLocation: "settings.xml")]){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${MAVEN_BUILD_OPTION} -Dmaven.test.skip=true --settings settings.xml"
                    }
                }
            }
            stage('读取pom.xml参数阶段'){
                // 读取 Pom.xml 参数
                pom = readMavenPom file: './pom.xml'
                // 设置 appName 和 appVersion 两个全局参数
                appName = "${pom.artifactId}"
                appVersion = "${pom.version}"
            }
            stage('Docker阶段'){
                container('docker') {
                    // 创建 Dockerfile 文件,但只能在方法块内使用
                    configFileProvider([configFile(fileId: "${params.DOCKER_DOCKERFILE_ID}", targetLocation: "Dockerfile")]){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        if ("${params.DOCKER_HUB_GROUP}" == '') {
                            dockerImageName = "${params.DOCKER_HUB_URL}/${appName}:${appVersion}"
                        }
                        // 提供 Docker 环境,使用 Docker 工具来进行 Docker 镜像构建与推送
                        docker.withRegistry("http://${params.DOCKER_HUB_URL}", "${params.DOCKER_HUB_CREADENTIAL}") {
                            def customImage = docker.build("${dockerImageName}")
                            customImage.push()
                        }
                    }
                }
            }
            stage('Kubernetes 阶段'){
                // kubectl 镜像
                container('kubectl') {
                    // 使用 Kubectl Cli 插件的方法,提供 Kubernetes 环境,在其方法块内部能够执行 kubectl 命令
                    withKubeConfig([credentialsId: "${params.KUBERNETES_CREADENTIAL}",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
                        // 使用 configFile 插件,创建 Kubernetes 部署文件 deployment.yaml
                        configFileProvider([configFile(fileId: "${params.KUBERNETES_DEPLOYMENT_ID}", targetLocation: "deployment.yaml")]){
                            // 读取 Kubernetes 部署文件
                            deploy = readFile encoding: "UTF-8", file: "deployment.yaml"
                            // 替换部署文件中的变量,并将替换后的文本赋予 deployfile 变量
                            deployfile = deploy.replaceAll("#APP_NAME","${appName}")
                                           .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}")
                                           .replaceAll("#APP_IMAGE_NAME","${dockerImageName}")
                                           .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString())
                            // 生成新的 Kubernetes 部署文件,内容为 deployfile 变量中的文本,文件名称为 "deploy.yaml"
                            writeFile encoding: 'UTF-8', file: './deploy.yaml', text: "${deployfile}"
                            // 输出新创建的部署 yaml 文件内容
                            sh "cat deploy.yaml"
                            // 执行 Kuberctl 命令进行部署操作
                            sh "kubectl apply -n ${params.KUBERNETES_NAMESPACE} -f deploy.yaml"
                        }
                    }
                }
            }
            stage('应用启动检查'){
                // 设置检测延迟时间 10s,10s 后再开始检测
                sleep 10
                // 健康检查地址
                httpRequestUrl = "http://${appName}.${params.KUBERNETES_NAMESPACE}:${params.HTTP_REQUEST_PORT}${params.HTTP_REQUEST_URL}"
                // 循环使用 httpRequest 请求,检测服务是否启动
                for(n = 1; n <= "${params.HTTP_REQUEST_NUMBER}".toInteger(); n++){
                    try{
                        // 输出请求信息和请求次数
                        print "访问服务:${appName} \n" +
                              "访问地址:${httpRequestUrl} \n" +
                              "访问次数:${n}"
                        // 如果非第一次检测,就睡眠一段时间,等待再次执行 httpRequest 请求
                        if(n > 1){
                            sleep "${params.HTTP_REQUEST_INTERVAL}".toInteger()
                        }
                        // 使用 HttpRequest 插件的 httpRequest 方法检测对应地址
                        result = httpRequest "${httpRequestUrl}"
                        // 判断是否返回 200
                        if ("${result.status}" == "200") {
                            print "Http 请求成功,流水线结束"
                            break
                        } 
                    }catch(Exception e){
                        print "监控检测失败,将在 ${params.HTTP_REQUEST_INTERVAL} 秒后将再次检测。"
                        // 判断检测次数是否为最后一次检测,如果是最后一次检测,并且还失败了,就对整个 Jenkins 任务标记为失败
                        if (n == "${params.HTTP_REQUEST_NUMBER}".toInteger()) {
                            currentBuild.result = "FAILURE"
                        }
                    }
                }
            }
        }
    }
}

变量说明:

    GIT_BRANCH: Git 项目分支变量。
    GIT_PROJECT_URL: Git 项目 URL 变量。
    GIT_CREADENTIAL: Git 凭据 ID 变量。
    MAVEN_BUILD_OPTION: Maven 执行的构建命令,package、install 或 deploy。
    global-maven-settings: 全局 Maven 的 Settings.xml 文件的 ID 值,这里是使用 configFileProvider 插件来创建该文件。
    pom.artifactId: 从 pom.xml 文件中读取的 artifactId 参数值。
    pom.version: 从 pom.xml 文件中读取的 version 参数值。
    DOCKER_DOCKERFILE_ID: Dockerfile 文件的 ID。
    DOCKER_HUB_URL: Docker 仓库 URL 地址。
    DOCKER_HUB_GROUP: Docker 仓库项目组名。
    DOCKER_HUB_CREADENTIAL: Docker 仓库认证凭据。
    appName: 从 pom.xml 中读取的应用名称。
    appVersion: 从 pom.xml 中读取的应用版本号。
    KUBERNETES_DEPLOYMENT_ID: Kubernetes 部署文件的 ID。
    KUBERNETES_CREADENTIAL: Kubernetes API 认证凭据。
    KUBERNETES_NAMESPACE: Kubernetes 部署应用的 Namespace。
    KUBERNETES_APP_REPLICAS: Kubernetes 部署应用的副本数。
    dockerImageName: Docker 镜像名称。
    HTTP_REQUEST_PORT: HTTP 健康检查端口。
    HTTP_REQUEST_URL: HTTP 健康检查 URL 地址。
    HTTP_REQUEST_NUMBER: HTTP 健康检查次数。
    HTTP_REQUEST_INTERVAL: HTTP 健康检查间隔。

将该流水线代码,配置到之前的模板 Job 的流水线脚本中,方便后续项目以此项目为模板。

上一篇:CICD 流水线就该这么玩系列之一


下一篇:Gogs+Drone+Docker自动化部署CICD