云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制

Jenkins集成Kubernetes实现动态Agent构建机制

文章目录

  • Jenkins集成Kubernetes实现动态Agent构建机制
    • 1.Jenkins集成Kubernetes
      • 1.1.安装Kubernetes插件
      • 1.2.进入配置k8s信息以及Pod模板信息页面
      • 1.3.配置Kubernetes集群地址信息
      • 1.4.添加pod模板
      • 1.5.配置pod模板信息
      • 1.6.使用pvc存储agent pod数据(扩展)
        • 1.6.1.编写pvc资源yaml文件
        • 1.6.2.创建pvc资源
        • 1.6.3.创建pvc存储路径并赋权
        • 1.6.4.配置jenkins pod模板信息使用pvc作为数据存储
    • 2.调整pipeline脚本使用动态agent去执行任务
      • 2.1.编辑pipeline脚本
      • 2.2.将pipeline粘贴到jenkins中
      • 2.3.构建任务观察效果
      • 2.5.查看agent pod产生的数据
    • 3.解决agent容器没有集成部署命令的问题
      • 3.1.问题分析
    • 4.重新配置集群中Pod模板添加容器
      • 4.1.添加工具容器
      • 4.2.添加agent容器(旧版需要这样做)
      • 4.3.添加卷为数据提供持久化
    • 5.优化pipeline脚本指定阶段任务运行在指定的容器
      • 5.1.优化pipeline脚本
      • 5.2.构建任务观察agent pod的状态

之前agent都是运行在k8s node节点上,一般项目更新都是在晚上或者中午,agent只会在更新的时候才会去用,平时一直运行agent也会造成系统的资源浪费,因此可以在Jenkins上集成Kubernetes插件,通过定义Pod模板,当任务开始执行后动态创建一个agent程序,去运行任务,当任务执行完成后,agent也会自动消失。

由于是容器版的agent,肯定有很多工具没有集成在容器里面,因此我们换需要自己制作一个容器,集成丰富的插件

1.Jenkins集成Kubernetes

主要就是安装一个Kubernetes插件,然后配置Kubernetes集群信息,定义agent容器Pod模板,实现创建任务时自动运行一个agent容器

1.1.安装Kubernetes插件

系统管理—>插件管理—>搜索Kubernetes—>直接安装

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第1张图片

安装过程很可能会失败,可以尝试多安装几次,最后看到爆黄,重启Jenkins就好了

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第2张图片

1.2.进入配置k8s信息以及Pod模板信息页面

1)跳转至Kubernetes插件配置页面

点击系统管理—>cloud—>点击超链接即可调整到配置Kubernetes信息的页面

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第3张图片

2)配置集群列表选择Kubernetes

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第4张图片

3)点击Kubernetes cloud details进行k8s集群信息配置

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第5张图片

1.3.配置Kubernetes集群地址信息

填写kubernetes信息:

kubernetes地址:https://kubernetes.default //访问api的地址,由于Jenkins部署在k8s中可以通过服务发现方式访问api

kubernetes命名空间:jenkins //将来agent运行后存放于哪个命名空间下

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第6张图片

填写Jenkins地址信息,声明agent如何连接Jenkins

Jenkins地址:http://jenkins-svc:8080

Jenkins通道:http://jenkins-svc:50000

可以查一下Jenkins的svc资源名称,通过服务发现的方式去连接Jenkins

[root@k8s-master1 ~]# kubectl get svc -n jenkins
NAME          TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                          AGE
jenkins-svc   NodePort   10.99.113.179           8080:38080/TCP,50000:50000/TCP   14d

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第7张图片

最终样子

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第8张图片

1.4.添加pod模板

pod模板就是运行agent pod的模板信息,定义一写agent pod的参数

1)点击Pod Templates

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第9张图片

2)点击添加Pod模板

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第10张图片

3)填写pod模板名称

名称:jnlp-slave

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第11张图片

1.5.配置pod模板信息

这个pod模板其实就是agent容器,无需配置agent镜像地址,他会自己从官方拉取,新版的镜像地址为:jenkins/inbound-agent:4.3-4

1)点击 Pod Template details配置pod模板信息

1622770973598

2)配置pod模板信息

名称:jenkins-slave //jenkins agent pod的名称

命名空间:jenkins //jenkins agent pod所在的命名空间

标签列表:jenkins-slave //jenkins任务可以指定agent的标签,最终运行在这个agent上

用法:尽可能的使用这个节点

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第12张图片

指定一个存储卷,持久化agent数据

卷类型选择nfs,填写nfs服务器的地址、提供存储的路径,jenkins会自动生成k8s yaml文件,这个nfs会自动被挂载到agent pod数据存储路径:/home/jenkins/agent,也可以使用pvc存储agent数据

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第13张图片

配置完数据卷后,即可点击保存

由于我们将agent的数据持久化了,因此也需要把nfs共享路径的权限调整为agent容器的所属用户,否则无法存储数据

[root@k8s-master2 ~]# cd /data2/k8s/
[root@k8s-master2 /data2/k8s]# mkdir jenkins-agent
[root@k8s-master2 /data2/k8s]# chown -R 1000.1000 jenkins-agent

1.6.使用pvc存储agent pod数据(扩展)

1.5中关于agent pod的数据是持久化到nfs中,也可以将agent的数据持久化到pvc里

1.6.1.编写pvc资源yaml文件

apiVersion: v1
kind:  PersistentVolume
metadata:
  name: jenkins-agent-pv
  labels:
    pv: jenkins-agent-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data2/k8s/jenkins-agent-data
    server: 192.168.16.105

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-agent-pvc
  namespace: jenkins
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  selector:
    matchLabels:
      pv: jenkins-agent-pv

1.6.2.创建pvc资源

[root@k8s-master1 jenkins]# kubectl apply -f jenkins-agent-pvc.yaml 
persistentvolume/jenkins-agent-pv create
persistentvolumeclaim/jenkins-agent-pvc create

[root@k8s-master1 jenkins]# kubectl get pv,pvc -n jenkins | grep agent
persistentvolume/jenkins-agent-pv                           10Gi       RWX            Retain           Bound      jenkins/jenkins-agent-pvc                                                    54m
persistentvolumeclaim/jenkins-agent-pvc               Bound    jenkins-agent-pv                           10Gi       RWX                                   54m

1.6.3.创建pvc存储路径并赋权

[root@k8s-master2 ~]# mkdir /data2/k8s/jenkins-agent-data
[root@k8s-master2 ~]# chown -R 1000.1000 /data2/k8s/jenkins-agent-data

1.6.4.配置jenkins pod模板信息使用pvc作为数据存储

jenkins配置k8s集群地址: http://192.168.16.104:38080/configureClouds/

找到pod模板,点击添加卷—>类型为Persistent Volume Claim—>申明值就是pvc的名称—>挂载路径就是要持久化容器的那个路径数据

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第14张图片

在找到最下面的工作空间卷,类型选择Persistent Volume Claim Workspace volume,工作空间卷只能添加一个,主要是为agent进行数据持久化的

填写声明值:jenkins-agent-pvc,这个声明值也就是pvc的名称

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第15张图片

配置完成后点击保存即可生效

2.调整pipeline脚本使用动态agent去执行任务

2.1.编辑pipeline脚本

仅需要调整使用哪个gaent即可

pipeline {
    agent { label 'jenkins-slave' }            //仅需要调整使用哪个gaent即可  

    environment {                                  
        IMAGE_REPO = "harbor.jiangxl.com/project"             
        DINGDING_TOKEN_CREDS = credentials('dingding-token')       
        TAB_STR = "\n                               \n                     "                    
    }

	parameters {                               
		gitParameter(name: 'VERSION',defaultValue: 'master',type: 'BRANCH',description: '选择要更新的分支')            
		string(name: 'project_namespace',defaultValue: 'know-system',description: '项目所在的命名空间',trim: true)               
        string(name: 'project_kind',defaultValue: 'deployment',description: '项目资源所使用的pod控制器',trim: true)            
        string(name: 'container_name',defaultValue: 'nginx',description: '项目所使用的pod容器名称',trim: true)                
        string(name: 'project',defaultValue: 'know-system',description: '项目名称',trim: true)
	}	
    stages {                      
        stage('运维确认信息') {                               
            steps {
                input message: """                          
                jobname: ${project}
                branch: ${VERSION}""", ok: "更新"                       
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')            
                script {
                    env.BUILD_TASKS = env.STAGE_NAME + " ✅ " + env.TAB_STR       
                }
            }
        }
        stage('拉取项目代码') {                               
            steps {
				checkout([$class: 'GitSCM', branches: [[name: '$VERSION']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-root', url: 'http://192.168.16.106:30080/root/know_system.git']]])
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR     
                }
            }
        }  
        stage('Dockerfile构建项目镜像') {			                        
            steps {
				sh """              
				echo "
FROM harbor.jiangxl.com/project/nginx-project:v1-code

RUN mkdir /data/code/know_system
COPY  ./* /data/code/know_system/
EXPOSE 80
				" >Dockerfile
				"""                            
 				retry(2) {sh 'docker build -t ${IMAGE_REPO}/${project}:master-v${BUILD_ID} . '}	   
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')   
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR       
                }           
            }
        }   

        stage('推送镜像到Harbor仓库') {            
            steps {
 				sh 'docker push ${IMAGE_REPO}/${project}:master-v${BUILD_ID}'	
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success') 
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR          
                }
            }
        }           
        stage('更新项目至Kubernetes环境') {                     
            steps {                                    
                sh 'kubectl -n ${project_namespace} set image ${project_kind} ${project} ${container_name}=${IMAGE_REPO}/${project}:master-v${BUILD_ID} --record' 
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR   
                }
            }
        }                  
    }
    post {
    	success {				
    			echo "构建成功,发送消息到钉钉"
    			sh """
    			curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGDING_TOKEN_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"project",
                            "text": "构建成功 \n\n 关键字: jenkins \n\n 项目名称: ${JOB_BASE_NAME} \n\n 更新的分支号: ${VERSION} \n\n  本次构建的镜像版本: ${IMAGE_REPO}/${project}:master-v${BUILD_ID} \n\n 构建地址: ${RUN_DISPLAY_URL} \n\n 阶段任务状态: ${BUILD_TASKS}" 
                            }
                        }'
    			"""             
    	}
    	failure {				
    			echo "构建失败,发送消息到钉钉"
    			sh """
    			curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGDING_TOKEN_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"project",
                            "text": "❌ 构建失败 ❌ \n\n 关键字: jenkins \n\n 项目名称: ${JOB_BASE_NAME} \n\n 更新的分支号: ${VERSION} \n\n  本次构建的镜像版本: ${IMAGE_REPO}/${project}:master-v${BUILD_ID} \n\n 构建地址: ${RUN_DISPLAY_URL} \n\n 阶段任务状态: ${BUILD_TASKS}" 
                            }
                        }'
 				"""
    	}
    	always {					
    		echo "构建流程结束"
    	}
    }
}

2.2.将pipeline粘贴到jenkins中

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第16张图片

2.3.构建任务观察效果

1)填写更新信息点击开始构建

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第17张图片

2)观察jenkins输出日志

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第18张图片

3)查看k8s中jenkins命名空间是否有agent pod产生

可以看到在jenkins名称空间下多了一个jenkins-slave-s3vh2 pod资源,jenkins任务由这个agent去完成

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第19张图片

4)查看jenkins任务的构建状态

我们的agent不再是部署在k8s node节点上,因此不再具备集成docker、k8s的命令

jenkins agent使用的容器是 jenkins/inbound-agent:4.3-4,这个容器仅仅只是与k8s master建立了连接,其内部并没有集成docker命令、k8s命令,因此就会导致我们任务构建失败

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第20张图片

2.5.查看agent pod产生的数据

#jenkins数据都存储在workspace里,这个路径是pvc的挂载路径
[root@k8s-master2 ~]# ll /data2/k8s/jenkins-agent-data/
总用量 0
drwxr-xr-x 3 www www 26 6月   4 14:48 caches
drwxr-xr-x 4 www www 34 6月   4 14:47 remoting
drwxr-xr-x 4 www www 64 6月   4 14:48 workspace

3.解决agent容器没有集成部署命令的问题

3.1.问题分析

jenkins agent使用的容器是 jenkins/inbound-agent:4.3-4,这个容器仅仅只是与k8s master建立了连接,其内部并没有集成docker命令、k8s命令,因此就会导致我们任务构建失败

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第21张图片

我们想要解决此种问题,不光单单针对k8s、docker环境,就要将我们需要用到的命令工具全部集成到pod的容器内

将这些工具全都集成到jnlp容器中显然有些不合理,可以想一下k8s pod的机制,一个pod最多可以有4个容器,我们可以手动制作一个容器放到pod模板中,然后再修改pipeline脚本,哪一个阶段需要用到命令工具了,我们就给他指定这个阶段采用什么容器去执行

将命令工具集成到agent容器内也需要考虑以下因素:

  • 是否可以创建一个新的容器,让新的容器来做具体的任务,agent容器只负责连接jenkins服务
  • 是否可以针对不同的构建环境(java、python、go、nodejs),制作不同的容器,在pipeline中针对阶段任务构建环境的不同就调用不同的容器去执行任务

制作容器注意的点:

动态Agent机制是靠一个Agent容器与Jenkins服务端建立连接,然后所有的构建操作都由一个专门集成构建环境工具的容器去操作。

关于制作容器要考虑的工具范围和注意点:

1)容器底层系统镜像采用centos7,虽然容器体积大了点但是功能及其丰富;

2)配置阿里云yum镜像源,用于安置环境工具;

3)需要使用docker命令构建镜像,安装docker环境,并挂载节点的docker.sock文件;

4)需要拉取Gitlab上的程序代码,安装Git工具;

5)需要使用Maven编译程序代码,安装Maven、Jdk工具;

6)需要将编译好的war包进行测试,安装tomcat环境,如果不需要测试可以不安装;

7)需要将程序部署在K8S集群,需要将kubectl命令复制到容器里;

8)使用kubectl命令需要kubeconfig认证才可使用,需要将Master节点的.kube目录拷贝至容器;

9)由于本容器还需要连接Harbor推送镜像以及使用kubectl的kubeconfig文件,因此需要在容器的hosts文件中增加Harbor仓库的地址解析以及K8S APIServer的域名地址解析。

由于kubectl命令会去找apiserver的域名,因此还需要配一个hosts,在Dockerfile中无法配置hosts,因此可以自己写一个hosts文件,然后在entrypoint脚本中是由于cat方式再重定向到/etc/hosts

3.2.1.准备软件

准备tomcat、kubectl命令、kubectl证书文件、entryporint、Dockerfile容器启动脚本

[root@k8s-node2 ~/docker-image/docker-jnlp]# ll
总用量 51096
-rw-r--r-- 1 root root  9353658 3月  24 2017 apache-tomcat-8.5.12.tar.gz
-rw------- 1 root root     5583 6月   4 16:59 config
-rw-r--r-- 1 root root      867 6月   7 11:40 Dockerfile
-rwxr-xr-x 1 root root       79 6月   7 11:37 entryporint.sh
-rwxr-xr-x 1 root root 42950656 6月   4 16:59 kubectl

3.2.2.准备entrypoint启动脚本

entrypoint.sh脚本中主要组成包括加载/etc/profile、启动Tomcat、将K8S Apiserver、Harbor仓库的域名地址解析写到hosts文件、追踪Tomcat的日志夯筑容器。

关于如何将解析写到hosts文件的思路:自己手动写一个hosts文件,再Dockerfile中定义COPY,将hosts文件拷贝到/tmp目录中,然后在通过cat+重定向的方式将其输出到/etc/hosts

hosts文件里需要增加harbor的地址解析和k8s api地址的解析

1.准备hosts文件
[root@k8s-node2 ~/docker-image/docker-jnlp]# vim hosts 
192.168.16.106 apiserver.cluster.local
192.168.16.106 harbor.jiangxl.com

2.准备entrypoint脚本
[root@k8s-node2 ~/docker-image/docker-jnlp]# vim entrypoint.sh 
#!/bin/bash
source /etc/profile
/data/tomcat/bin/startup.sh
cat /tmp/hosts >> /etc/hosts
tail -f /data/tomcat/logs/catalina.out

3.2.3.编写Dockerfile

注意:Dockerfile关于安装docker命令不要使用epel源去安装,要下载docker官方的源,安装docker-ce,安装完docker-ce之后,docker命令的版本就是高版本,低版本的docker命令在使用docker push的时候会报错

FROM centos:centos7.5.1804

#准备yum源,安装docker、jdk、maven等必要软件
RUN rm -rf /etc/yum.repos.d/* && curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo ;curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && yum -y install git curl tar bash vim java net-tools

#安装docker
RUN curl -o /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo && yum -y install --setopt=obsoletes=0 docker-ce-19.03.0-3.el7

#安装maven jdk
COPY jdk1.8.tar.gz /usr/local
COPY apache-maven-3.3.9.tar /usr/local/
RUN tar xf /usr/local/jdk1.8.tar.gz -C /usr/local/ && tar xf /usr/local/apache-maven-3.3.9.tar -C /usr/local/ && echo -e "export JAVA_HOME=/usr/local/jdk1.8.0_131 \nexport MVN_HOME=/usr/local/apache-maven-3.3.9 \nexport PATH=\$PATH:\$JAVA_HOME/bin:\$MVN_HOME/bin" >> /etc/profile && echo "source /etc/profile" >> ~/.bashrc
COPY settings.xml /usr/local/apache-maven-3.3.9/conf/settings.xml


#准备kubectl认证文件目录
RUN mkdir /root/.kube

#将kubectl认证文件上传到容器里对应的目录
COPY .kube /root/.kube

#将kubectl目录上传至容器
COPY kubectl /usr/local/bin
RUN chmod a+x /usr/local/bin/kubectl

#部署tomcat
RUN mkdir /data/soft -p
COPY apache-tomcat-8.5.12.tar.gz /data/soft
RUN tar xf /data/soft/apache-tomcat-8.5.12.tar.gz -C /data && mv /data/apache-tomcat-8.5.12 /data/tomcat && rm -rf /data/tomcat/webapps/*

COPY hosts /tmp/hosts
COPY entrypoint.sh /data/

ENTRYPOINT ["/bin/bash","/data/entrypoint.sh"]

3.2.4.构建容器并启动查看工具是否可用

1.构建镜像
[root@k8s-node2 ~/docker-image/docker-jnlp]# docker build -t harbor.jiangxl.com/jenkins/build-tools:v1 .

2.启动镜像
[root@k8s-node2 ~/docker-image/docker-jnlp]# docker run -itd  -v /var/run/docker.sock:/var/run/docker.sock harbor.jiangxl.com/jenkins/build-tools:v1 
a2a962d3ead1a7cef29b6ac837a19869c83c609fa953449418b3dbf42bdd01c0

3.进入容器验证工具是否可用

[root@f3adc78e4d3a /]# java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)


[root@f3adc78e4d3a /]# mvn -v
ache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-10T16:41:47+00:00)
Maven home: /usr/local/apache-maven-3.3.9
Java version: 1.8.0_131, vendor: Oracle Corporation
Java home: /usr/local/jdk1.8.0_131/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "3.10.0-862.el7.x86_64", arch: "amd64", family: "unix"

[root@f3adc78e4d3a /]# netstat -lnpt | grep java
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      28/java             
tcp        0      0 127.0.0.1:8005          0.0.0.0:*               LISTEN      28/java             
tcp        0      0 0.0.0.0:8009            0.0.0.0:*               LISTEN      28/java  

[root@a2a962d3ead1 /]# docker -v
Docker version 20.10.7, build f0df350

[root@a2a962d3ead1 /]# kubectl get nodes
NAME          STATUS   ROLES    AGE   VERSION
k8s-master1   Ready    master   30d   v1.19.6
k8s-master2   Ready    master   30d   v1.19.6
k8s-node1     Ready       30d   v1.19.6
k8s-node2     Ready       30d   v1.19.6 

3.2.5.推送镜像到harbor仓库

将工具镜像和agent镜像都推送至harbor仓库

[root@k8s-node2 ~]# docker push  harbor.jiangxl.com/jenkins/build-tools:v1 

4.重新配置集群中Pod模板添加容器

我们刚刚手动制作了一个集成所有工具的docker镜像,需要把这个镜像添加到pod模板中的容器列表,然后让pipeline脚本调用

jenkins中的k8s集群配置,旧版本(默认新加了容器列表就会把原来默认的jnlp agent容器给覆盖掉,所以再添加了工具容器之后还需要再把jnlp容器在容器列表中重新添加一下)新版本(新版本已经没有这个缺陷,只需要增加一个工具容器就可以让pipeline部分阶段任务由指定的工具容器去完成,连接k8s master还是让默认的jnlp agent容器去做)

4.1.添加工具容器

http://192.168.16.104:38080/configureClouds/这个链接下,找到pod模板—>容器列表—>添加容器

名称:jenkins-build

Docker镜像:harbor.jiangxl.com/jenkins/build-tools:v1

工作目录:/home/jenkins/agent #工作目录与agent容器保持一致

运行的命令和命令参数都设置为空,否则会覆盖我们制作的启动命令

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第22张图片

4.2.添加agent容器(旧版需要这样做)

老版的jenkins需要再声明一次agent容器,因为工具容器会将pod模板的容器给覆盖,所以需要在容器列表再声明agent容器,新版本2.277则无需再声明agent镜像

还是在容器列表—>添加容器,agent容器就需要配置命令参数了

名称:jenkins-slave

Docker镜像:harbor.jiangxl.com/jenkins/inbound-agent:4.3-4

运行的命令:/bin/sh -c

命令参数:jenkins-slave

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第23张图片

4.3.添加卷为数据提供持久化

我们需要把docker.sock文件挂载到pod中,也需要为/home/jenkins/agent路径进行数据持久化

/home/jenkins/agent下的数据使用在1.6中定义的pvc进行存储即可,docker.sock文件通过hostpath的方式挂载本地的文件到pod中

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第24张图片

5.优化pipeline脚本指定阶段任务运行在指定的容器

我们在pod模板中定义了容器列表,其中包含一个集成所有构件工具的容器,我们的阶段任务都依赖某些工具,所以要把每个阶段任务让指定的工具容器去运行

5.1.优化pipeline脚本

主要是在stage里增加一个container(‘容器名’){具体的任务},让指定的容器去运行执行的阶段任务

pipeline {
    agent { label 'jenkins-slave' }             
    
    environment {                                  
        IMAGE_REPO = "harbor.jiangxl.com/project"             
        DINGDING_TOKEN_CREDS = credentials('dingding-token')       
        TAB_STR = "\n                               \n                     "           
    }

	parameters {                         
		gitParameter(name: 'VERSION',defaultValue: 'master',type: 'BRANCH',description: '选择要更新的分支')             
		string(name: 'project_namespace',defaultValue: 'know-system',description: '项目所在的命名空间',trim: true)               
        string(name: 'project_kind',defaultValue: 'deployment',description: '项目资源所使用的pod控制器',trim: true)            
        string(name: 'container_name',defaultValue: 'nginx',description: '项目所使用的pod容器名称',trim: true)                 
        string(name: 'project',defaultValue: 'know-system',description: '项目名称',trim: true)                                       
	}	
    stages {                       
        stage('运维确认信息') {                                 
            steps {
                input message: """                          
                jobname: ${project}
                branch: ${VERSION}""", ok: "更新"                      
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')            
                script {
                    env.BUILD_TASKS = env.STAGE_NAME + " ✅ " + env.TAB_STR         
                }
            }
        }
        stage('拉取项目代码') {                                 
            steps {
                container('jenkins-build'){ 
	    			checkout([$class: 'GitSCM', branches: [[name: '$VERSION']], extensions: [], userRemoteConfigs: [[credentialsId: 'gitlab-root', url: 'http://192.168.16.106:30080/root/know_system.git']]])
                }           //由pod模板中的jenkins-build这个容器去运行checkout任务
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR       
                }
            }
        }  
        stage('Dockerfile构建项目镜像') {			                        
            steps {
                container('jenkins-build'){
                sh """              
				echo "
FROM harbor.jiangxl.com/project/nginx-project:v1-code

RUN mkdir /data/code/know_system
COPY  ./* /data/code/know_system/
EXPOSE 80
				" >Dockerfile
				""" 
 				retry(2) {sh 'docker build -t ${IMAGE_REPO}/${project}:master-v${BUILD_ID} . '}		
                }           //工具镜像里继承了docker命令,由pod模板中的jenkins-build这个容器去运行构建镜像的任务

                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')   
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR          
                }           
            }
        }   

        stage('推送镜像到Harbor仓库') {             
            steps {
                container('jenkins-build'){
                    sh 'docker login -u admin -p admin $IMAGE_REPO'                           
                    sh 'docker push ${IMAGE_REPO}/${project}:master-v${BUILD_ID}'
                }               //工具镜像里继承了docker命令,由pod模板中的jenkins-build这个容器去运行推送镜像的任务
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success') 
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR           
                }
            }
        }           
        stage('更新项目至Kubernetes环境') {                     
            steps {      
                container('jenkins-build'){
                    sh 'kubectl -n ${project_namespace} set image ${project_kind} ${project} ${container_name}=${IMAGE_REPO}/${project}:master-v${BUILD_ID} --record'   
                }                 //工具镜像里继承了kubelet命令,由pod模板中的jenkins-build这个容器去运行升级的任务                             
                             
                updateGitlabCommitStatus(name: env.STAGE_NAME, state: 'success')
                script {
                    env.BUILD_TASKS += env.STAGE_NAME + " ✅ " + env.TAB_STR       
                }
            }
        }                  
    }
    post {
    	success {				
    			echo "构建成功,发送消息到钉钉"
    			sh """
    			curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGDING_TOKEN_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"project",
                            "text": "构建成功 \n\n 关键字: jenkins \n\n 项目名称: ${JOB_BASE_NAME} \n\n 更新的分支号: ${VERSION} \n\n  本次构建的镜像版本: ${IMAGE_REPO}/${project}:master-v${BUILD_ID} \n\n 构建地址: ${RUN_DISPLAY_URL} \n\n 阶段任务状态: ${BUILD_TASKS}" 
                            }
                        }'
    			"""            
    	}
    	failure {			
    			echo "构建失败,发送消息到钉钉"
    			sh """
    			curl 'https://oapi.dingtalk.com/robot/send?access_token=${DINGDING_TOKEN_CREDS_PSW}' \
                    -H 'Content-Type: application/json' \
                    -d '{
                        "msgtype": "markdown",
                        "markdown": {
                            "title":"project",
                            "text": "❌ 构建失败 ❌ \n\n 关键字: jenkins \n\n 项目名称: ${JOB_BASE_NAME} \n\n 更新的分支号: ${VERSION} \n\n  本次构建的镜像版本: ${IMAGE_REPO}/${project}:master-v${BUILD_ID} \n\n 构建地址: ${RUN_DISPLAY_URL} \n\n 阶段任务状态: ${BUILD_TASKS}" 
                            }
                        }'
 				"""
    	}
    	always {					
    		echo "构建流程结束"
    	}
    }
}

5.2.构建任务观察agent pod的状态

1)填写构建信息,然后点击构建任务

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第25张图片

2)观察任务构建日志查看agent容器是否启动

看到jenkins在k8s上启动了一个名为jenkins-slave-00nh9的容器

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第26张图片

3)agent容器在k8s集群成功启动

只要agent pod成功运行,pod中的build容器就开始构建任务了

1623225235111

4)点击运维确认信息

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第27张图片

5)任务构建成功

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第28张图片

6)agent pod自动销毁

当jenkins任务构建结束后,agent pod会自动销毁,避免资源浪费

1623225784090

7)钉钉消息发送成功

云原生DevOps篇:Jenkins集成Kubernetes实现动态Agent构建机制_第29张图片

你可能感兴趣的:(#,kubernetes,jenkins,云原生,devops,docker)