Jenkins集成Kubernetes实现动态Agent构建机制
之前agent都是运行在k8s node节点上,一般项目更新都是在晚上或者中午,agent只会在更新的时候才会去用,平时一直运行agent也会造成系统的资源浪费,因此可以在Jenkins上集成Kubernetes插件,通过定义Pod模板,当任务开始执行后动态创建一个agent程序,去运行任务,当任务执行完成后,agent也会自动消失。
由于是容器版的agent,肯定有很多工具没有集成在容器里面,因此我们换需要自己制作一个容器,集成丰富的插件
1.Jenkins集成Kubernetes
主要就是安装一个Kubernetes插件,然后配置Kubernetes集群信息,定义agent容器Pod模板,实现创建任务时自动运行一个agent容器
1.1.安装Kubernetes插件
系统管理—>插件管理—>搜索Kubernetes—>直接安装

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

1.2.进入配置k8s信息以及Pod模板信息页面
1)跳转至Kubernetes插件配置页面
点击系统管理—>cloud—>点击超链接即可调整到配置Kubernetes信息的页面

2)配置集群列表选择Kubernetes

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

1.3.配置Kubernetes集群地址信息
填写kubernetes信息:
kubernetes地址:https://kubernetes.default //访问api的地址,由于Jenkins部署在k8s中可以通过服务发现方式访问api
kubernetes命名空间:jenkins //将来agent运行后存放于哪个命名空间下

填写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 <none> 8080:38080/TCP,50000:50000/TCP 14d

最终样子

1.4.添加pod模板
pod模板就是运行agent pod的模板信息,定义一写agent pod的参数
1)点击Pod Templates

2)点击添加Pod模板

3)填写pod模板名称
名称:jnlp-slave

1.5.配置pod模板信息
这个pod模板其实就是agent容器,无需配置agent镜像地址,他会自己从官方拉取,新版的镜像地址为:jenkins/inbound-agent:4.3-4
1)点击 Pod Template details配置pod模板信息

2)配置pod模板信息
名称:jenkins-slave //jenkins agent pod的名称
命名空间:jenkins //jenkins agent pod所在的命名空间
标签列表:jenkins-slave //jenkins任务可以指定agent的标签,最终运行在这个agent上
用法:尽可能的使用这个节点

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

配置完数据卷后,即可点击保存
由于我们将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的名称—>挂载路径就是要持久化容器的那个路径数据

在找到最下面的工作空间卷,类型选择Persistent Volume Claim Workspace volume,工作空间卷只能添加一个,主要是为agent进行数据持久化的
填写声明值:jenkins-agent-pvc,这个声明值也就是pvc的名称

配置完成后点击保存即可生效
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中

2.3.构建任务观察效果
1)填写更新信息点击开始构建

2)观察jenkins输出日志

3)查看k8s中jenkins命名空间是否有agent pod产生
可以看到在jenkins名称空间下多了一个jenkins-slave-s3vh2 pod资源,jenkins任务由这个agent去完成

4)查看jenkins任务的构建状态
我们的agent不再是部署在k8s node节点上,因此不再具备集成docker、k8s的命令
jenkins agent使用的容器是 jenkins/inbound-agent:4.3-4,这个容器仅仅只是与k8s master建立了连接,其内部并没有集成docker命令、k8s命令,因此就会导致我们任务构建失败

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命令,因此就会导致我们任务构建失败

我们想要解决此种问题,不光单单针对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    <none>   30d   v1.19.6
k8s-node2     Ready    <none>   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容器保持一致
运行的命令和命令参数都设置为空,否则会覆盖我们制作的启动命令

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

4.3.添加卷为数据提供持久化
我们需要把docker.sock文件挂载到pod中,也需要为/home/jenkins/agent路径进行数据持久化
/home/jenkins/agent下的数据使用在1.6中定义的pvc进行存储即可,docker.sock文件通过hostpath的方式挂载本地的文件到pod中

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)填写构建信息,然后点击构建任务

2)观察任务构建日志查看agent容器是否启动
看到jenkins在k8s上启动了一个名为jenkins-slave-00nh9的容器

3)agent容器在k8s集群成功启动
只要agent pod成功运行,pod中的build容器就开始构建任务了

4)点击运维确认信息

5)任务构建成功

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

7)钉钉消息发送成功

                  
                        
                        
                      
                                            
                                    
baron
pipeline里的很多stage都是在一个容器里执行的吧,都是串行的还好,我其实想请教下如何实现并行的几个stage,用同一个容器模板生成多个容器
麦小轮
使用--- 隔开在一个文件里面继续写下一个容器