凡人丶不烦人 發表於 2022-7-3 22:23:00

Kubernetes 部署 Jenkins + Kubernetes 的 CI CD

<h1 id="jenkins-与-kubernetes-的-ci-与-cd--git--maven--dockerkubectl">Jenkins 与 Kubernetes 的 CI 与 CD &amp; Git + Maven + Docker+Kubectl</h1>
<p>参考:</p>
<p>http://www.mydlq.club/article/47/</p>
<p>https://plugins.jenkins.io/kubernetes/</p>
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>Jenkins 与 Kubernetes 的 CI 与 CD &amp; Git + Maven + Docker+Kubectl<ul><li>一、Kubernetes 部署 Jenkins<ul><li>1.1. 使用StorageClass+NFS创建pv</li><li>1.2. 创建ServiceAccount &amp; ClusterRoleBinding</li><li>1.3. 创建Service &amp; Deployment</li><li>1.4.获取 Jenkins 生成的 Token</li><li>5.启动 Jenkins 进行初始化</li></ul></li><li>二、Jenkins安装相关插件</li><li>三、配置相关凭据<ul><li>3.1、添加 Git 认证凭据</li><li>3.2、添加 Kubernetes Token 凭据</li><li>3.3、添加 Docker 仓库认证凭据</li></ul></li><li>四、Jenkins 配置 Kubernetes 插件<ul><li>4.1、Kubernetes Plugin 基本配置<ul><li>4.1.1、配置连接 Kubernetes 参数</li><li>4.1.2、配置 Jenkins 地址</li></ul></li><li>4.2、Kubernetes 插件配置<ul><li>4.2.1、配置 Pod 名称和标签列表</li><li>4.2.2、配置 Maven<ul><li>4.2.2.1、配置Maven镜像</li><li>4.2.2.2、创建 Maven 存储使用的 PV、PVC</li><li>4.2.2.3、配置 Maven 挂载</li></ul></li><li>4.2.3、配置 Docker In Docker<ul><li>4.2.3.1、配置 Docker In Docker 镜像</li><li>4.2.3.2、配置 Docker 挂载</li></ul></li><li>4.2.4、配置 Kubectl 镜像</li><li>4.2.5、配置 Pod 的原始 yaml</li></ul></li></ul></li><li>五、创建相关文件<ul><li>5.1、新增 Maven 配置文件</li><li>5.2、新增 Dockerfile 文件</li><li>5.3、新增 Kubernetes 部署文件</li></ul></li><li>六、如何写流水线脚本和使用插件<ul><li>6.1、脚本中设置全局超时时间</li><li>6.2、脚本中使用 Git 插件</li><li>6.3、脚本中使用 Kubernetes 插件</li><li>6.4、脚本中使用 Docker 镜像</li><li>6.5、脚本中引入 Jenkins 中预先存储的文件</li><li>6.6、脚本创建文件</li><li>6.7、脚本中使用 Http Rrequest 插件</li><li>6.8、脚本中使用 Kubernetes Cli 插件</li><li>6.9、脚本中操作字符串替换值</li><li>6.10、脚本中读取 pom.xml 参数</li><li>6.11、脚本中使用 Docker 插件构建与推送镜像</li></ul></li><li>七、在 Jenkins 创建模板任务<ul><li>7.1、创建 Pipeline 任务</li><li>7.2、配置项目构建基本参数</li><li>7.3、配置变量<ul><li>7.3.1、配置GIT变量<ul><li>7.3.1.1、Git 项目地址变量</li><li>7.3.1.2、Git 分支变量</li><li>7.3.1.3、Git 凭据变量</li></ul></li><li>7.3.2、配置 Maven 变量<ul><li>7.3.2.1、Maven 构建命令变量</li></ul></li><li>7.3.3、配置 Docker 变量<ul><li>7.3.3.1、Docker 项目地址变量</li><li>7.3.3.2、Docker 仓库项目组变量</li><li>7.3.3.3、Docker 仓库认证凭据变量</li><li>7.3.3.4、Docker Dockerfile 文件 ID 变量</li></ul></li><li>7.3.4、配置 Kubernetes 变量<ul><li>7.3.4.1、Kubernetes 认证凭据变量</li><li>7.3.4.2、Kubernetes Namespace 变量</li><li>7.3.4.3、Kubernetes 应用实例副本数</li><li>7.3.4.4、Kubernetes 应用部署 yaml 文件ID</li></ul></li><li>7.3.5、配置 HTTP 变量<ul><li>7.3.5.1、HTTP 健康检查端口</li><li>7.3.5.2、HTTP 健康检查地址</li><li>7.3.5.3、HTTP 健康检查次数</li><li>7.3.5.4、HTTP 健康检查时间间隔</li></ul></li><li>7.3.6、配置项目参数<ul><li>7.3.6.1、服务端口号</li><li>7.3.6.2、服务环境</li><li>7.3.6.3、Redis地址</li><li>7.3.6.4、Redis端口</li><li>7.3.6.5、Redis密码</li><li>7.3.6.6、数据库地址及端口</li><li>7.3.6.7、数据库名</li><li>7.3.6.8、数据库密码</li><li>7.3.6.9、k8s中维护的拉取镜像的secret凭证</li></ul></li></ul></li></ul></li><li>八、创建 Pipeline 脚本<ul><li>8.1、脚本中使用 Kubernetes 插件及设置超时时间</li><li>8.2、脚本中 Git 拉取项目阶段</li><li>8.3、脚本中 Maven 编译项目阶段</li><li>8.4、脚本中读取 pom.xml 参数阶段</li><li>8.5、脚本中 Docker 镜像构建与推送模块</li><li>8.6、Kubernetes 模块</li><li>8.7、HTTP 健康检查模块</li><li>8.8、发送钉钉通知<ul><li>8.8.1、创建钉钉机器人</li><li>8.8.2、获取<strong>webhook</strong>,并<strong>复制webhook</strong></li><li>8.8.3、配置Jenkins</li><li>8.8.4、脚本中调用dingtalk</li></ul></li><li>9、完整脚本<ul><li>9.1、使用在jenkins中配置的jnlp-agent</li><li>9.2、直接在groovy脚本中指定镜像参数</li></ul></li></ul></li></ul></li></ul></div><p></p>
<h2 id="一kubernetes-部署-jenkins">一、Kubernetes 部署 Jenkins</h2>
<h3 id="11-使用storageclassnfs创建pv">1.1. 使用StorageClass+NFS创建pv</h3>
<p>具体创建StorageClass+NFS方法详见下面链接或自行百度</p>
<p>https://www.cnblogs.com/yg0070/p/16440498.html</p>
<p>创建pvc</p>
<pre><code class="language-yaml">---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
    volume.beta.kubernetes.io/storage-provisioner: master-nfs-storage# 与storageClass中metadata.name保持一致
finalizers:
    - kubernetes.io/pvc-protection
name: jenkins
namespace: jenkins
spec:
accessModes:
    - ReadWriteOnce
resources:
    requests:
      storage: 10Gi    #存储空间大小
storageClassName: master-nfs-storage   # 与storageClass中的provisioner一致
</code></pre>
<h3 id="12-创建serviceaccount--clusterrolebinding">1.2. 创建ServiceAccount &amp; ClusterRoleBinding</h3>
<p>Kubernetes 集群一般情况下都默认开启了 RBAC 权限,所以需要创建一个角色和服务账户,设置角色拥有一定权限,然后将角色与 ServiceAccount 绑定,最后将 ServiceAccount 与 Jenkins 绑定,这样来赋予 Jenkins 一定的权限,使其能够执行一些需要权限才能进行的操作。这里为了方便,将 cluster-admin 绑定到 ServiceAccount 来保证 Jenkins 拥有足够的权限。</p>
<pre><code class="language-yaml">apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin       #ServiceAccount名
namespace: jenkins      #指定namespace,一定要修改成你自己的namespace
labels:
    name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-admin
labels:
    name: jenkins
subjects:
- kind: ServiceAccount
    name: jenkins-admin
    namespace: jenkins
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
</code></pre>
<h3 id="13-创建service--deployment">1.3. 创建Service &amp; Deployment</h3>
<pre><code class="language-yaml">---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
    app: jenkins
name: jenkins
namespace: jenkins
spec:
replicas: 1
selector:
    matchLabels:
      app: jenkins
template:
    metadata:
      labels:
      app: jenkins
    spec:
      serviceAccountName: jenkins-admin
      containers:
      - name: jenkins
          image: 'jenkins/jenkins:lts'
          securityContext:
            privileged: true#拥有特权
            runAsUser: 0      #设置以ROOT用户运行容器
          ports:
            - containerPort: 8080
            name: http
            protocol: TCP
            - containerPort: 50000
            name: jnlp
            protocol: TCP
          resources:
            limits:
            cpu: '2'
            memory: 2Gi
            requests:
            cpu: '1'
            memory: 1Gi
          env:
            - name: LIMITS_MEMORY
            valueFrom:
                resourceFieldRef:
                  divisor: 1Mi
                  resource: limits.memory
            - name: JAVA_OPTS    #设置变量,指定时区和 jenkins slave 执行者设置
            value: &gt;-
                -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm
                -Dhudson.slaves.NodeProvisioner.initialDelay=0
                -Dhudson.slaves.NodeProvisioner.MARGIN=50
                -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
                -Duser.timezone=Asia/Shanghai
                -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai
            - name: JENKINS_OPTS    #设置路径前缀加上 Jenkins
            value: '--prefix=/jenkins'
            - name: JENKINS_JAVA_OPTIONS
            value: '-Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai'
            - name: JAVA_ARGS
            value: &gt;-
                -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm
                -Dhudson.slaves.NodeProvisioner.initialDelay=0
                -Dhudson.slaves.NodeProvisioner.MARGIN=50
                -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
                -Duser.timezone=Asia/Shanghai
                -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai
            - name: JENKINS_JAVA_OPTIONS
            value: &gt;-
                -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm
                -Dhudson.slaves.NodeProvisioner.initialDelay=0
                -Dhudson.slaves.NodeProvisioner.MARGIN=50
                -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
                -Duser.timezone=Asia/Shanghai
          volumeMounts:
            - mountPath: /var/jenkins_home    #设置要挂在的目录
            name: data
      volumes:
      - name: data
          persistentVolumeClaim:
            claimName: jenkins                #设置PVC

---
apiVersion: v1
kind: Service
metadata:
labels:
    app: jenkins
name: jenkins
namespace: jenkins
spec:
ports:
    - name: http
      nodePort: 32001      #NodePort方式暴露 Jenkins 端口
      port: 8080         #服务端口
      protocol: TCP
      targetPort: 8080
    - name: jnlp
      nodePort: 32002
      port: 50000         #代理端口
      protocol: TCP
      targetPort: 50000
selector:
    app: jenkins
type: NodePort
</code></pre>
<p><strong>参数说明:</strong></p>
<ul>
<li><strong>JAVA_OPTS:</strong> JVM 参数设置</li>
<li><strong>JENKINS_OPTS:</strong> Jenkins 参数设置</li>
<li><strong>其它参数:</strong> 默认情况下,Jenkins 生成代理是保守的。例如,如果队列中有两个构建,它不会立即生成两个执行器。它将生成一个执行器,并等待某个时间释放第一个执行器,然后再决定生成第二个执行器。Jenkins 确保它生成的每个执行器都得到了最大限度的利用。如果你想覆盖这个行为,并生成一个执行器为每个构建队列立即不等待,所以在 Jenkins 启动时候添加这些参数:</li>
</ul>
<pre><code class="language-bash">-Dhudson.slaves.NodeProvisioner.initialDelay=0
-Dhudson.slaves.NodeProvisioner.MARGIN=50
-Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
</code></pre>
<h3 id="14获取-jenkins-生成的-token">1.4.获取 Jenkins 生成的 Token</h3>
<p>在安装 Jenkins 时候,它默认生成一段随机字符串在控制台日志中,用于安装时验证。这里需要获取它输出在控制台中的日志信息,来获取 Token 字符串。</p>
<p><strong>查看jenkins pod 启动日志</strong></p>
<ul>
<li>-n:指定应用启动的 namespace</li>
</ul>
<pre><code class="language-bash">$ kubectl log $(kubectl get pods -n mydlqcloud | awk '{print $1}' | grep jenkins) -n mydlqcloud
</code></pre>
<p><strong>在日志中可以看到,默认给的token为:</strong></p>
<pre><code class="language-fallback">*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

96b199612s12sd15s55dfs52dff55db8d

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
</code></pre>
<h3 id="5启动-jenkins-进行初始化">5.启动 Jenkins 进行初始化</h3>
<p>输入 Kubernetes 集群地址和上面设置的 <code>Nodeport</code> 方式的端口号 <code>32001</code>,访问Jenins UI 界面进行初始化。输入从日志中获取的token进入初始化页面。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221405683-2025254699.png" alt="" loading="lazy"></p>
<p><strong>安装插件</strong></p>
<p>安装插件,选择 <code>推荐安装</code> 方式进行安装即可,后续再安装需要的插件。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221422721-913281176.png" alt="" loading="lazy"></p>
<p><strong>设置用户名、密码</strong></p>
<p>在这里输入一个用户名、密码,方便后续登录,如果不设置可能下次登录需要使用之前日志中默认的 Token 串来登录。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221438779-2018323226.png" alt="" loading="lazy"></p>
<p><strong>配置 Jenkins 地址</strong></p>
<p>配置 Jenkins URL 地址,来告知 Jenkins 自己的 URL,在发送邮件、触发钩子等可能用到。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221449475-1021202066.png" alt="" loading="lazy"></p>
<p><strong>进入 Jenkins 界面</strong></p>
<p>到此 Jenkins 初始化就配置完成,成功进入 Jenkins 界面。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221501180-475470690.png" alt="" loading="lazy"></p>
<h2 id="二jenkins安装相关插件">二、Jenkins安装相关插件</h2>
<p>Jenkins 中可以打开 <strong>系统管理-&gt;插件管理-&gt;可选插件</strong> 来安装下面的一些插件:</p>
<ul>
<li><strong>Git:</strong> Jenkins 安装中默认安装 Git 插件,所以不需要单独安装。利用 git 工具可以将 github、gitlab 等等的地址下载源码。</li>
<li><strong>Docker:</strong> Jenkins 安装中默认安装 Docker 插件,所以不需要单独安装。利用 Docker 插件可以设置 Docker 环境,运行 Docker 命令,配置远程 Docker 仓库凭据等。</li>
<li><strong>Kubernetes:</strong> Kubernetes 插件的目的是能够使用 Kubernetes 集群动态配置 Jenkins 代理(使用Kubernetes调度机制来优化负载),运行单个构建,等构建完成后删除该代理。这里我们需要用到这个插件来启动 Jenkins Slave 代理镜像,让代理执行 Jenkins 要执行的 Job。</li>
<li><strong>Kubernetes Cli:</strong> Kubernetes Cli 插件作用是在执行 Jenkins Job 时候提供 kubectl 与 Kubernetes 集群交互环境。可以在 Pipeline 或自由式项目中允许执行 kubectl 相关命令。它的主要作用是提供 kubectl 运行环境,当然也可以提供 helm 运行环境。</li>
<li><strong>Config File Provider:</strong> Config File Provider 插件作用就是提供在 Jenkins 中存储 properties、xml、json、settings.xml 等信息,可以在执行 Pipeline 过程中可以写入存储的配置。例如,存入一个 Maven 全局 Settings.xml 文件,在执行 Pipeline Job 时候引入该 Settings.xml ,这样 Maven 编译用的就是该全局的 Settings.xml。</li>
<li><strong>Pipeline Utility Steps:</strong> 这是一个操作文件的插件,例如读写 json、yaml、pom.xml、Properties 等等。在这里主要用这个插件读取 pom.xml 文件的参数设置,获取变量,方便构建 Docker 镜像。</li>
<li><strong>Git Parameter:</strong> 能够与 Git 插件结合使用,动态获取 Git 项目中分支信息,在 Jenkins Job 构建前提供分支选项,来让项目执行人员选择拉取对应分支的代码。</li>
<li><strong>DingTalk:</strong>钉钉插件,能够推送钉钉机器人消息。</li>
</ul>
<h2 id="三配置相关凭据">三、配置相关凭据</h2>
<p>选择 <strong>凭据-&gt;系统-&gt;全局凭据-&gt;添加凭据</strong> 来新增 Git、Docker Hub、Kubernetes 等认证凭据。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221513919-1929024472.png" alt="" loading="lazy"></p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221519753-1863208906.png" alt="" loading="lazy"></p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221534117-676869129.png" alt="" loading="lazy"></p>
<h3 id="31添加-git-认证凭据">3.1、添加 Git 认证凭据</h3>
<p><strong>配置的参数值:</strong></p>
<ul>
<li>类型:Username with password</li>
<li>范围:全局</li>
<li>用户名(Git 用户名): 略</li>
<li>密码(Git 密码):略</li>
<li>ID:global-git-credential</li>
<li>描述:全局 Git 凭据</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221543258-1758207852.png" alt="" loading="lazy"></p>
<h3 id="32添加-kubernetes-token-凭据">3.2、添加 Kubernetes Token 凭据</h3>
<p><strong>配置的参数值:</strong></p>
<ul>
<li>类型:Secret text</li>
<li>范围:全局</li>
<li>Secret(K8S Token 串):略</li>
<li>ID:global-kubernetes-credential</li>
<li>描述:全局的 K8S Token</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221554124-698255831.png" alt="" loading="lazy"></p>
<h3 id="33添加-docker-仓库认证凭据">3.3、添加 Docker 仓库认证凭据</h3>
<p><strong>配置的参数值:</strong></p>
<ul>
<li>类型:Username with password</li>
<li>范围:全局</li>
<li>用户名(Docker 仓库用户名):略</li>
<li>密码(Docker 仓库密码):略</li>
<li>ID:docker-hub-credential</li>
<li>描述:Docker 仓库认证凭据</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221601597-126040149.png" alt="" loading="lazy"></p>
<h2 id="四jenkins-配置-kubernetes-插件">四、Jenkins 配置 Kubernetes 插件</h2>
<p>进入 <strong>系统管理-&gt;节点管理-&gt;云</strong> 中,点击 <strong>新增一个云</strong> 选项,来新建一个与 Kubernetes 的连接,然后按照下面各个配置项进行配置。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221610610-831414353.png" alt="" loading="lazy"></p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221617003-1863677915.png" alt="" loading="lazy"></p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221622737-1918513450.png" alt="" loading="lazy"></p>
<h3 id="41kubernetes-plugin-基本配置">4.1、Kubernetes Plugin 基本配置</h3>
<h4 id="411配置连接-kubernetes-参数">4.1.1、配置连接 Kubernetes 参数</h4>
<p>配置 Kubernetes API 地址,然后再选择 Kubernetes Token 凭据。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221630897-1424685959.png" alt="" loading="lazy"></p>
<p><strong>注意:</strong> 如果你的 Jenkins 也是安装在 Kubernetes 环境中,那么可以直接使用 Kubernetes 集群内的 Kubernetes API 地址,如果 Jnekins 是在安装在正常物理机或者虚拟机环境中,那么使用集群外的 Kubernetes API 地址,两个地址如下:</p>
<ul>
<li>集群内地址:https://kubernetes.default.svc.cluster.local</li>
<li>集群外地址:https://{Kubernetes 集群 IP}:6443</li>
</ul>
<p>然后点击连接测试,查看是否能成功连通 Kubernetes,如果返回结果 Successful 则代表连接成功,否则失败。</p>
<h4 id="412配置-jenkins-地址">4.1.2、配置 Jenkins 地址</h4>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221651637-871916977.png" alt="" loading="lazy"></p>
<p><strong>注意:</strong> 这里的 Jenkins 地址是供 Slave 节点连接 Jenkins Master 节点用的,所以这里需要配置 Jenkins Master 的 URL 地址。这里和上面一样,也是考虑 Jenkins 是部署在 Kubernetes 集群内还是集群外,两个地址如下:</p>
<ul>
<li>集群内地址:https://{Jenkins Pod 名称}.{Jenkins Pod 所在 Namespace}/</li>
<li>集群外地址:https://{Kubernetes 集群 IP}:{Jenkins NodePort 端口}/</li>
</ul>
<blockquote>
<p>如果 Jnekins 中配置了 /jenkins 前缀,则 URL 后面加上 /jenkins,否则不加,这个地址根据自己的 Jnekins 实际情况来判断。</p>
</blockquote>
<h3 id="42kubernetes-插件配置">4.2、Kubernetes 插件配置</h3>
<p>详细配置见 https://plugins.jenkins.io/kubernetes/</p>
<h4 id="421配置-pod-名称和标签列表">4.2.1、配置 Pod 名称和标签列表</h4>
<p>配置 Pod 模板的名称和标签列表名,Pod 模板名可用于子模板继承,标签列表可用于 Jenkins Job 中指定,使用此 Pod 模板来执行任务。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221707144-94832332.png" alt="" loading="lazy"></p>
<h4 id="422配置-maven">4.2.2、配置 Maven</h4>
<h5 id="4221配置maven镜像">4.2.2.1、配置Maven镜像</h5>
<ul>
<li>名称:maven</li>
<li>Docker 镜像:registry.cn-shanghai.aliyuncs.com/mydlq/maven:3.6.0-jdk8-alpine</li>
<li>其它参数:默认值即可</li>
</ul>
<blockquote>
<p>Maven 镜像可以从官方 Docker Hub 下载,地址:https://hub.docker.com/_/maven</p>
</blockquote>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221717299-1120500240.png" alt="" loading="lazy"></p>
<h5 id="4222创建-maven-存储使用的-pvpvc">4.2.2.2、创建 Maven 存储使用的 PV、PVC</h5>
<blockquote>
<p>Maven需要挂载存储,将中央仓库下载的 Jar 存储到共享目录</p>
</blockquote>
<pre><code class="language-yaml">---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: maven
namespace: jenkins
spec:
accessModes:
    - ReadWriteOnce
resources:
    requests:
      storage: 10Gi
storageClassName: master-nfs-storage
</code></pre>
<h5 id="4223配置-maven-挂载">4.2.2.3、配置 Maven 挂载</h5>
<p>在卷选项中,选择添加卷,选择 <code>Persistent Volume Claim</code> 按如下添加配置:</p>
<ul>
<li>申明值(PVC 名称):maven</li>
<li>挂在路径(容器内的目录):/root/.m2</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221727678-729602656.png" alt="" loading="lazy"></p>
<h4 id="423配置-docker-in-docker">4.2.3、配置 Docker In Docker</h4>
<h5 id="4231配置-docker-in-docker-镜像">4.2.3.1、配置 Docker In Docker 镜像</h5>
<ul>
<li>名称:docker</li>
<li>Docker 镜像:registry.cn-shanghai.aliyuncs.com/mydlq/docker:18.06.3-dind</li>
<li>其它参数:默认值即可</li>
</ul>
<blockquote>
<p>Docker-IN-Docker 镜像可以从官方 Docker Hub 下载,地址:https://hub.docker.com/_/docker</p>
</blockquote>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221736341-695717072.png" alt="" loading="lazy"></p>
<h5 id="4232配置-docker-挂载">4.2.3.2、配置 Docker 挂载</h5>
<p>Kubernetes 中 Pod 的容器是启动在各个节点上,每个节点就是一台宿主机,里面进行了很多 Docker 配置,所以我们这里将宿主机的 Docker 配置挂载进入 Docker 镜像。选择添加卷,选择 <code>Host Path Volume</code> 按如下添加配置:</p>
<p><strong>① 路径 /usr/bin/docker:</strong></p>
<ul>
<li>主机路径(宿主机目录):/usr/bin/docker</li>
<li>挂载路径(容器内的目录):/usr/bin/docker</li>
</ul>
<p><strong>② 路径 /var/run/docker.sock:</strong></p>
<ul>
<li>主机路径(宿主机目录):/var/run/docker.sock</li>
<li>挂载路径(容器内的目录):/var/run/docker.sock</li>
</ul>
<p><strong>③ 路径 /etc/docker:</strong></p>
<ul>
<li>主机路径(宿主机目录):/etc/docker</li>
<li>挂载路径(容器内的目录):/etc/docker</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221747515-290643675.png" alt="" loading="lazy"></p>
<h4 id="424配置-kubectl-镜像">4.2.4、配置 Kubectl 镜像</h4>
<ul>
<li>名称:kubectl</li>
<li>Docker 镜像:registry.cn-shanghai.aliyuncs.com/mydlq/kubectl:1.15.3</li>
<li>其它参数:默认值即可</li>
</ul>
<blockquote>
<p>Kubectl 镜像可以从官方 Docker Hub 下载,地址:https://hub.docker.com/r/bitnami/kubectl</p>
</blockquote>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221810325-1819391638.png" alt="" loading="lazy"></p>
<h4 id="425配置-pod-的原始-yaml">4.2.5、配置 Pod 的原始 yaml</h4>
<p>此项可省略,保存后jenkins会自动生成,这里记录生成后的yaml</p>
<pre><code class="language-yaml">apiVersion: "v1"
kind: "Pod"
metadata:
annotations:
    buildUrl: "http://jenkins.jenkins:8080/jenkins/job/new-test/19/"
    runUrl: "job/new-test/19/"
labels:
    jenkins: "slave"
    jenkins/label: "jnlp"
name: "jnlp-agent"
spec:
containers:
- env:
    - name: "JENKINS_TUNNEL"
      value: "jenkins.jenkins:50000"
    - name: "JENKINS_AGENT_WORKDIR"
      value: "/home/jenkins/agent"
    - name: "JENKINS_URL"
      value: "http://jenkins.jenkins:8080/jenkins/"
    image: "jenkins/inbound-agent:4.3-4"
    name: "jnlp"
    resources:
      limits: {}
      requests:
      memory: "256Mi"
      cpu: "100m"
    volumeMounts:
    - mountPath: "/home/jenkins/agent"
      name: "workspace-volume"
      readOnly: false
nodeSelector:
    kubernetes.io/os: "linux"
restartPolicy: "Never"
volumes:
- emptyDir:
      medium: ""
    name: "workspace-volume"
</code></pre>
<p>完整的配置如下</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221827127-1160203180.png" alt="" loading="lazy"></p>
<h2 id="五创建相关文件">五、创建相关文件</h2>
<p>之前安装了 <strong>Config File Provider</strong> 插件,该插件功能就是可以在 Jenkins 上存储一些配置文件,例如,我们经常使用到的 yaml、properties、Dockerfile、Maven 的 Settings.xml 等文件,都可以存储到 Jenkins 该插件中,也可以将文件存储到项目中从项目中读取文件。</p>
<p>打开 <strong>系统管理-&gt;Managed files</strong> ,在其中新增几个文件:</p>
<ul>
<li><strong>Maven 配置文件:</strong> Maven 的 Settings.xml 配置文件。</li>
<li><strong>Dockerfile 文件:</strong> Dockerfile 脚本。</li>
<li><strong>Kubernetes 部署文件:</strong> 将应用部署到 kubernetes 的 Deployment 文件。</li>
</ul>
<h3 id="51新增-maven-配置文件">5.1、新增 Maven 配置文件</h3>
<p>选择 <strong>Add a new Config—&gt;Global Maven settings.xml</strong> 来新增一个 <strong>Maven</strong> 全局 <strong>Settings.xml</strong> 文件:</p>
<ul>
<li><strong>ID:</strong> global-maven-settings</li>
<li><strong>Name:</strong> MavenGlobalSettings</li>
<li><strong>Comment:</strong> 全局 Maven Settings.xml 文件</li>
<li><strong>Content:</strong> 内容如下↓:</li>
</ul>
<blockquote>
<p>为了加快 jar 包的下载速度,这里将仓库地址指向 aliyun Maven 仓库地址。</p>
</blockquote>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&lt;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"&gt;

&lt;pluginGroups&gt;
&lt;/pluginGroups&gt;

&lt;proxies&gt;
&lt;/proxies&gt;

&lt;servers&gt;
&lt;/servers&gt;

&lt;mirrors&gt;
    &lt;!--Aliyun Maven--&gt;
    &lt;mirror&gt;
      &lt;id&gt;alimaven&lt;/id&gt;
      &lt;name&gt;aliyun maven&lt;/name&gt;
      &lt;url&gt;http://maven.aliyun.com/nexus/content/groups/public/&lt;/url&gt;
      &lt;mirrorOf&gt;central&lt;/mirrorOf&gt;
    &lt;/mirror&gt;
&lt;/mirrors&gt;

&lt;profiles&gt;
&lt;/profiles&gt;

&lt;/settings&gt;
</code></pre>
<h3 id="52新增-dockerfile-文件">5.2、新增 Dockerfile 文件</h3>
<p>选择 <strong>Add a new Config—&gt;Custom file</strong> 来新增一个 <strong>Dockerfile</strong> 文件:</p>
<p>java服务dockerfile</p>
<ul>
<li><strong>ID:</strong> global-dockerfile-file</li>
<li><strong>Name:</strong> Dockerfile</li>
<li><strong>Comment:</strong> 全局 Dockerfile 文件</li>
<li><strong>Content:</strong> 内容如下↓:</li>
</ul>
<pre><code class="language-dockerfile">FROM java:8
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" ]
</code></pre>
<p>vue服务dockerfile</p>
<ul>
<li><strong>ID:</strong> global-dockerfile-file</li>
<li><strong>Name:</strong> Dockerfile</li>
<li><strong>Comment:</strong> 全局 Dockerfile 文件</li>
<li><strong>Content:</strong> 内容如下↓:</li>
</ul>
<pre><code class="language-dockerfile">FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install -g cnpm --registry=https://registry.npm.taobao.org
RUN cnpm install
COPY . ./
RUN npm run build:prod

# production stage
FROM nginx:stable-alpine as production-stage
COPY nginx/nginx.conf /etc/nginx/nginx.conf
COPY --from=build-stage /app/dist /usr/share/nginx/html

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
</code></pre>
<h3 id="53新增-kubernetes-部署文件">5.3、新增 Kubernetes 部署文件</h3>
<p>选择 <strong>Add a new Config—&gt;Custom file</strong> 来新增一个 <strong>Kubernetes 部署文件</strong>:</p>
<ul>
<li><strong>ID:</strong> global-kubernetes-deployment</li>
<li><strong>Name:</strong> deployment.yaml</li>
<li><strong>Comment:</strong> 全局 Kubernetes 部署文件</li>
<li><strong>Content:</strong> 内容如下↓:</li>
</ul>
<pre><code class="language-yaml">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时能够执行
      - name: SPRING_PROFILES_ACTIVE
          value: #SPRING_PROFILES_ACTIVE
      - name: REDIS_HOST
          value: #REDIS_HOST
      - name: REDIS_PORT
          value: "#REDIS_PORT"
      - name: REDIS_PW
          value: #REDIS_PW
      - name: DB_URL
          value: #DB_URL
      - name: DB_NAME
          value: #DB_NAME
      - name: DB_PASSWORD
          value: #DB_PASSWORD
      resources:
          limits:
            cpu: 2000m
            memory: 1024Mi
          requests:
            cpu: 1000m
            memory: 512Mi
      imagePullSecrets:
      - name: #{K8S_PULLIMAGES_SECRET}
</code></pre>
<p>为了模板能够动态替换某些值,上面模板中设置了几个可替换的参数,用 <strong>#变量名称</strong> 来标记,后面我们在执行 Pipeline 时候将里面的 <strong>#xxx变量</strong> 标记替换掉,上面配置的变量有:</p>
<ul>
<li><strong>#APP_NAME:</strong> 应用名称。</li>
<li><strong>#APP_PORT:</strong>应用端口</li>
<li><strong>#APP_REPLICAS:</strong> 应用副本数。</li>
<li><strong>#APP_IMAGE_NAME:</strong> 镜像名称。</li>
<li><strong>#APP_UUID:</strong> 生成的随机值,因为后续 Kubectl 在执行命令时候,如果部署对象中的值没有一点变化的话,将不会执行 kubectl apply 命令,所以这里设置了一个随机值,以确保每次部署文件都不一致。</li>
<li><strong>#SPRING_PROFILES_ACTIVE:</strong>项目参数 - 指定环境</li>
<li><strong>#REDIS_HOST:</strong>项目参数-redis地址</li>
<li><strong>#REDIS_PORT:</strong>项目参数-redis端口</li>
<li><strong>#REDIS_PW:</strong>项目参数-redis密码</li>
<li><strong>#DB_URL:</strong>项目参数-数据库地址及端口</li>
<li><strong>#DB_NAME:</strong>项目参数-数据库名</li>
<li><strong>#DB_USERNAME:</strong>项目参数-数据库用户名</li>
<li><strong>#DB_PASSWORD:</strong>项目参数-数据库密码</li>
<li><strong>#REDIS_HOST:</strong>项目参数-redis地址</li>
<li><strong>#K8S_PULLIMAGES_SECRET:</strong>k8s中维护的拉取镜像的secret凭证</li>
</ul>
<p>并且还有一点就是要注意,设置更新策略为 Recreate(删除再创建) 策略,否则后面的健康检查阶段将不能正常检查更新后的项目。</p>
<blockquote>
<p>Kubernetes 默认为 RollingUpdate 策略,该策略为应用启动时,先将新实例启动,再删除旧的实例,就是因为这样,在后面健康检查阶段,健康检查 URL 地址还是未更新前的旧实例的 URL 地址,会导致健康检查不准确,所以必须改为 Recreate 策略,先删除旧实例,再创建新实例。</p>
</blockquote>
<h2 id="六如何写流水线脚本和使用插件">六、如何写流水线脚本和使用插件</h2>
<p>具体配置参考官网文档:<br>
https://plugins.jenkins.io/kubernetes/<br>
https://www.jenkins.io/doc/pipeline/steps/ssh-steps/<br>
https://plugins.jenkins.io/ssh-steps/#plugin-content-withcredentials</p>
<h3 id="61脚本中设置全局超时时间">6.1、脚本中设置全局超时时间</h3>
<p>设置任务超时时间,如果在规定时间内任务没有完成,则进行失败操作,格式如下:</p>
<pre><code class="language-groovy">timeout(time: 60, unit: 'SECONDS') {
    // 脚本
}
</code></pre>
<h3 id="62脚本中使用-git-插件">6.2、脚本中使用 Git 插件</h3>
<p>Git 插件方法使用格式,及其部分参数:</p>
<ul>
<li><strong>changelog:</strong> 是否检测变化日志</li>
<li><strong>url:</strong> Git 项目地址</li>
<li><strong>branch:</strong> Git 分支</li>
<li><strong>credentialsId:</strong> Jenkins 存的 Git 凭据 ID 值</li>
</ul>
<pre><code class="language-groovy">git changelog: true,
    url: "http://gitlab.xxxx/xxx.git"
    branch: "master",
    credentialsId: "xxxx-xxxx-xxxx-xxxx",
</code></pre>
<h3 id="63脚本中使用-kubernetes-插件">6.3、脚本中使用 Kubernetes 插件</h3>
<p>Kubernetes 插件中存在 PodTemplate 方法,在执行脚本时候,会自动在 Kubernetes 中创建 Pod Template 配置的 Slave Pod,在其中执行 podTemplate 代码块中的脚本。</p>
<pre><code class="language-groovy">def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
      print "在 Slave Pod 中执行任务"
    }
}
</code></pre>
<p><strong>podTemplate 方法参数简介:</strong></p>
<ul>
<li><strong>cloud:</strong> 之前 Kuberntes 插件配置的 Cloud 名称</li>
<li><strong>label:</strong> 之前 Kuberntes 插件配置的 Cloud 中 Pod Template 里面的 Label 标签名称。</li>
</ul>
<h3 id="64脚本中使用-docker-镜像">6.4、脚本中使用 Docker 镜像</h3>
<p>在之前配置了 Kubernetes 插件的 Pod Template 配置中,配置了几个容器,每个容器中都有特定的功能的环境,例如:</p>
<ul>
<li>Maven 容器中能够执行 mvn 命令。</li>
<li>Kuberctl 容器能够执行 kubectl 命令。</li>
<li>Docker In Docker 容器中能够执行 Docker 命令。</li>
</ul>
<p>既然每个容器都能提供特定的环境,那么再执行执行 Pipleline 脚本时候,就可以在不同的镜像中使用不同的环境的命令:</p>
<ul>
<li><strong>Maven 镜像</strong></li>
</ul>
<pre><code class="language-groovy">container('maven') {
    sh "mvn install
}
</code></pre>
<ul>
<li><strong>Docker In Docker 镜像</strong></li>
</ul>
<pre><code class="language-groovy">container('docker') {
    sh "docker build -t xxxxx:1.0.0 .
}
</code></pre>
<ul>
<li><strong>Kubectl 镜像</strong></li>
</ul>
<pre><code class="language-groovy">container('kubectl') {
    sh "kubectl apply -f xxxx.yaml"
}
</code></pre>
<h3 id="65脚本中引入-jenkins-中预先存储的文件">6.5、脚本中引入 Jenkins 中预先存储的文件</h3>
<p>在之前的 <strong>系统设置-&gt;File Manager</strong> 中,存储了很多文件,例如:</p>
<ul>
<li>Docker 的镜像构建脚本文件 Dockerfile。</li>
<li>Maven 的全局设置文件 Settings.xml</li>
<li>Kubernetes 的部署文件 deployment.yaml</li>
</ul>
<p>在使用 Pipleline 脚本时候,我们需要将这些文件文本提取出来,创建在执行任务的流程中,创建这些文件可以使用 Config File Provider 插件提供的 configFileProvider 方法,如下所示:</p>
<ul>
<li><strong>创建 settings.xml 文件</strong></li>
</ul>
<pre><code class="language-groovy">configFileProvider(){
    sh "cat settings.xml"
}
</code></pre>
<ul>
<li><strong>创建 Dockerfile 文件</strong></li>
</ul>
<pre><code class="language-groovy">configFileProvider(){
    sh "cat Dockerfile"
}
</code></pre>
<ul>
<li><strong>创建 Dockerfile 文件</strong></li>
</ul>
<pre><code class="language-groovy">configFileProvider(){
    sh "cat deployment.yaml"
}
</code></pre>
<h3 id="66脚本创建文件">6.6、脚本创建文件</h3>
<p>在使用 Groovy 写 Pipleline 脚本时候,经常要将变量的文本生成文件,方便在执行流水线过程中操作文本文件使用,如何将文件转换为文件,可以使用 Pipeline Utility Steps 插件的 writeFile 方法,如下:</p>
<pre><code class="language-groovy">writeFile encoding: 'UTF-8', file: './test.txt', text: "写入文件的文本内容"
</code></pre>
<h3 id="67脚本中使用-http-rrequest-插件">6.7、脚本中使用 Http Rrequest 插件</h3>
<p>脚本中可以使用 HttpRequest 来对某一地址进行请求,这里简单使用 Get 请求地址,复杂的可以查看 Jenkins 插件的官网查看使用示例。</p>
<p>下面是使用 Http Request 的 Get 请求示例:</p>
<pre><code class="language-groovy">result = httpRequest "http:www.baidu.com"

if ("${result.status}" == "200") {
    print "Http 请求成功"
}
</code></pre>
<h3 id="68脚本中使用-kubernetes-cli-插件">6.8、脚本中使用 Kubernetes Cli 插件</h3>
<p>在之前说过,在 kubectl 镜像中能够使用 kubectl 命令,不过由于执行 Kubectl 命令一般需要在镜像的 <strong>$HOME/.kube/</strong> 目录中存在连接 <strong>Kubernetes API</strong> 的 <strong>config</strong> 文件,使其 <strong>kubectl</strong> 命令有明确请求 <strong>kubernetes API</strong> 的地址和用户权限,不过将 <strong>config</strong> 文件挂入镜像内部是一件比较繁琐的事情。</p>
<p>好在 <strong>Jenkins</strong> 提供的 <strong>Kubectl Cli</strong> 插件,只要在其中配置连接 <strong>Kubernetes 的 Token</strong> 凭据,就能够在 <strong>Kubectl Cli</strong> 提供的 <strong>withKubeConfig</strong> 方法,拥有类似存在 <strong>config</strong> 一样的功能,在 <strong>kubectl</strong> 镜像中的 <strong>withKubeConfig</strong> 方法块内执行 <strong>kubectl</strong> 就可以操作配置的 <strong>Kubectl Cli</strong> 的凭据的 <strong>K8S</strong> 集群。</p>
<pre><code class="language-groovy">container('kubectl') {
    withKubeConfig() {
      sh "kubectl get nodes"
    }
}
</code></pre>
<h3 id="69脚本中操作字符串替换值">6.9、脚本中操作字符串替换值</h3>
<p>在使用 Groovy 语法写 Pipleline 脚本时候,我们经常要替换先前设置好的一些文本的值,这里我们简单示例一下,如何替换字符串。</p>
<pre><code class="language-groovy">// 测试的字符串
sourceStr = "这是要替换的值:#value1,这是要替换的值:#value2"
// 替换#value1与#value2连个值
afterStr = sourceStr.replaceAll("#value1","AAA").replaceAll("#value2","BBB")
// 输出替换后的字符串
print "${afterStr}"
</code></pre>
<h3 id="610脚本中读取-pomxml-参数">6.10、脚本中读取 pom.xml 参数</h3>
<p>在执行 Java 项目的流水线时,我们经常要动态获取项目中的属性,很多属性都配置在项目的 pom.xml 中,还好 Pipeline Utility Steps 插件提供能够读取 pom.xml 的方法,示例如下:</p>
<pre><code class="language-groovy">stage('读取pom.xml参数阶段'){
    // 读取 Pom.xml 参数
    pom = readMavenPom file: './pom.xml'
    // 输出读取的参数
    print "${pom.artifactId}"
    print = "${pom.version}"
}
</code></pre>
<h3 id="611脚本中使用-docker-插件构建与推送镜像">6.11、脚本中使用 Docker 插件构建与推送镜像</h3>
<p>在流水线脚本中,我们一般不直接使用 Docker 命令,而是使用 Docker 插件提供的 docker.withRegistry("") 方法来构建与推送镜像,并且还能在方法中配置登录凭据信息,来让仓库验证权限,这点是非常方便的。使用示例如下:</p>
<pre><code class="language-groovy">docker.withRegistry("http://xxxx Docker 仓库地址", "Docker 仓库凭据 ID") {
      // 构建 Docker 镜像
      def customImage = docker.build("${dockerImageName}")
      // 推送 Docker 镜像
      customImage.push()
    }
</code></pre>
<h2 id="七在-jenkins-创建模板任务">七、在 Jenkins 创建模板任务</h2>
<p>创建一个 Pipeline Job 来充当各个 Jenkins Job 的模板,方便后续创建 Job 时,直接复制模板项目,然后修改配置就能使用。所以这里我们创建一个模板 Pipeline Job,在 Job 配置中需要添加一些参数和环境变量,方便我们动态替换一些值。</p>
<h3 id="71创建-pipeline-任务">7.1、创建 Pipeline 任务</h3>
<ul>
<li><strong>任务名称:</strong> my-template</li>
<li><strong>任务类型:</strong> 流水线项目</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221912965-813783726.png" alt="" loading="lazy"></p>
<h3 id="72配置项目构建基本参数">7.2、配置项目构建基本参数</h3>
<p>配置同一时间一个 Job 只能构建一个,不允许多个并发构建。另外需要设置项目构建后,包的保留时间,以防止包过多且大占用大量空间(一个包很肯能占 10MB~200MB 大小)导致储不足。</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221920105-1147466248.png" alt="" loading="lazy"></p>
<h3 id="73配置变量">7.3、配置变量</h3>
<p>在 Job 配置的 <strong>参数化构建过程</strong> 中,添加下面参数:</p>
<h4 id="731配置git变量">7.3.1、配置GIT变量</h4>
<h5 id="7311git-项目地址变量">7.3.1.1、Git 项目地址变量</h5>
<ul>
<li>变量名称:GIT_PROJECT_URL</li>
<li>类型:String</li>
<li>描述:项目 Git 地址</li>
<li>默认值:"https://xxxxxxxxxxxx"</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221927142-1011665135.png" alt="" loading="lazy"></p>
<h5 id="7312git-分支变量">7.3.1.2、Git 分支变量</h5>
<ul>
<li>变量名称:GIT_BRANCH</li>
<li>类型:Git Parameter</li>
<li>描述:选择 Git 分支</li>
<li>默认值:master</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221934123-1239327565.png" alt="" loading="lazy"></p>
<h5 id="7313git-凭据变量">7.3.1.3、Git 凭据变量</h5>
<ul>
<li>变量名称:GIT_CREADENTIAL</li>
<li>类型:Credentials</li>
<li>描述:Git 凭据</li>
<li>默认值:global-git-credential</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221940553-1096990049.png" alt="" loading="lazy"></p>
<h4 id="732配置-maven-变量">7.3.2、配置 Maven 变量</h4>
<h5 id="7321maven-构建命令变量">7.3.2.1、Maven 构建命令变量</h5>
<ul>
<li>变量名称:MAVEN_BUILD_OPTION</li>
<li>类型:Choices</li>
<li>描述:要执行的执行 Maven 命令选择</li>
<li>可选值:['package', 'install', 'deploy']</li>
<li>默认值:install</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221946747-1076729265.png" alt="" loading="lazy"></p>
<h4 id="733配置-docker-变量">7.3.3、配置 Docker 变量</h4>
<h5 id="7331docker-项目地址变量">7.3.3.1、Docker 项目地址变量</h5>
<ul>
<li>变量名称:DOCKER_HUB_URL</li>
<li>类型:String</li>
<li>描述:Docker 仓库地址</li>
<li>默认值(默认 Docker 仓库地址):"---------------"</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703221953853-1620429736.png" alt="" loading="lazy"></p>
<h5 id="7332docker-仓库项目组变量">7.3.3.2、Docker 仓库项目组变量</h5>
<ul>
<li>变量名称:DOCKER_HUB_GROUP</li>
<li>类型:String</li>
<li>描述:Docker 仓库项目组名</li>
<li>默认值:""</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222016306-576469917.png" alt="" loading="lazy"></p>
<h5 id="7333docker-仓库认证凭据变量">7.3.3.3、Docker 仓库认证凭据变量</h5>
<ul>
<li>变量名称:DOCKER_HUB_CREADENTIAL</li>
<li>类型:Credentials</li>
<li>描述:Docker 仓库认证凭据</li>
<li>默认值:docker-hub-credential</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222024953-54058908.png" alt="" loading="lazy"></p>
<h5 id="7334docker-dockerfile-文件-id-变量">7.3.3.4、Docker Dockerfile 文件 ID 变量</h5>
<ul>
<li>变量名称:DOCKER_DOCKERFILE_ID</li>
<li>类型:String</li>
<li>描述:存于 Jenkins "Managed files" 的 Dockerfile 文件的 ID</li>
<li>默认值:"global-dockerfile-file"</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222031279-1945332304.png" alt="" loading="lazy"></p>
<h4 id="734配置-kubernetes-变量">7.3.4、配置 Kubernetes 变量</h4>
<h5 id="7341kubernetes-认证凭据变量">7.3.4.1、Kubernetes 认证凭据变量</h5>
<ul>
<li>变量名称:KUBERNETES_CREADENTIAL</li>
<li>类型:Credentials</li>
<li>描述:Kubernetes 认证 Token</li>
<li>默认值:global-kubernetes-credential</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222040687-275430037.png" alt="" loading="lazy"></p>
<h5 id="7342kubernetes-namespace-变量">7.3.4.2、Kubernetes Namespace 变量</h5>
<ul>
<li>变量名称:KUBERNETES_NAMESPACE</li>
<li>类型:String</li>
<li>描述:Kubernetes 命名空间 Namespace</li>
<li>默认值:""</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222047967-240240475.png" alt="" loading="lazy"></p>
<h5 id="7343kubernetes-应用实例副本数">7.3.4.3、Kubernetes 应用实例副本数</h5>
<ul>
<li>变量名称:KUBERNETES_APP_REPLICAS</li>
<li>类型:String</li>
<li>描述:应用实例副本数</li>
<li>默认值:1</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222054124-644337717.png" alt="" loading="lazy"></p>
<h5 id="7344kubernetes-应用部署-yaml-文件id">7.3.4.4、Kubernetes 应用部署 yaml 文件ID</h5>
<ul>
<li>变量名称:KUBERNETES_DEPLOYMENT_ID</li>
<li>类型:String</li>
<li>描述:存于 Jenkins "Managed files" 的 K8S 部署文件的 ID</li>
<li>默认值:"global-kubernetes-deployment"</li>
</ul>
<h4 id="735配置-http-变量">7.3.5、配置 HTTP 变量</h4>
<h5 id="7351http-健康检查端口">7.3.5.1、HTTP 健康检查端口</h5>
<ul>
<li>变量名称:HTTP_REQUEST_PORT</li>
<li>类型:String</li>
<li>描述:Http Request 端口(健康检测端口)</li>
<li>默认值:8081</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222103920-535698951.png" alt="" loading="lazy"></p>
<h5 id="7352http-健康检查地址">7.3.5.2、HTTP 健康检查地址</h5>
<ul>
<li>变量名称:HTTP_REQUEST_URL</li>
<li>类型:String</li>
<li>描述:Http Request 项目中的相对路径(健康检测路径)</li>
<li>默认值:/actuator/health</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222109304-323763882.png" alt="" loading="lazy"></p>
<h5 id="7353http-健康检查次数">7.3.5.3、HTTP 健康检查次数</h5>
<ul>
<li>变量名称:HTTP_REQUEST_NUMBER</li>
<li>类型:Choices</li>
<li>描述:Http Request 请求次数</li>
<li>可选值:['10', '5', '10', '15', '20', '25', '30']</li>
<li>默认值:10</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222117019-1192741520.png" alt="" loading="lazy"></p>
<h5 id="7354http-健康检查时间间隔">7.3.5.4、HTTP 健康检查时间间隔</h5>
<ul>
<li>变量名称:HTTP_REQUEST_INTERVAL</li>
<li>类型:Choices</li>
<li>描述:Http Request 时间间隔</li>
<li>可选值:['10', '5', '15', '20', '25', '30']</li>
<li>默认值:10</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222124080-1933764835.png" alt="" loading="lazy"></p>
<h4 id="736配置项目参数">7.3.6、配置项目参数</h4>
<p>全部采用String类型创建</p>
<h5 id="7361服务端口号">7.3.6.1、服务端口号</h5>
<ul>
<li>变量名称:PROJECT_ENV_PORT</li>
<li>类型:String</li>
<li>描述:项目访问端口</li>
<li>默认值:8080</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222131114-1782932211.png" alt="" loading="lazy"></p>
<h5 id="7362服务环境">7.3.6.2、服务环境</h5>
<ul>
<li>变量名称:PROJECT_ENV_SPRING_PROFILES_ACTIVE</li>
<li>类型:String</li>
<li>描述:项目参数 - 环境参数</li>
<li>默认值:dev</li>
</ul>
<h5 id="7363redis地址">7.3.6.3、Redis地址</h5>
<ul>
<li>变量名称:PROJECT_ENV_REDIS_HOST</li>
<li>类型:String</li>
<li>描述:项目参数-redis地址</li>
<li>默认值:</li>
</ul>
<h5 id="7364redis端口">7.3.6.4、Redis端口</h5>
<ul>
<li>变量名称:PROJECT_ENV_REDIS_PORT</li>
<li>类型:String</li>
<li>描述:项目参数-redis端口</li>
<li>默认值:</li>
</ul>
<h5 id="7365redis密码">7.3.6.5、Redis密码</h5>
<ul>
<li>变量名称:PROJECT_ENV_REDIS_PW</li>
<li>类型:String</li>
<li>描述:项目参数-redis密码</li>
<li>默认值:</li>
</ul>
<h5 id="7366数据库地址及端口">7.3.6.6、数据库地址及端口</h5>
<ul>
<li>变量名称:PROJECT_ENV_DB_URL</li>
<li>类型:String</li>
<li>描述:项目参数-数据库地址及端口</li>
<li>默认值:</li>
</ul>
<h5 id="7367数据库名">7.3.6.7、数据库名</h5>
<ul>
<li>变量名称:PROJECT_ENV_DB_NAME</li>
<li>类型:String</li>
<li>描述:项目参数-数据库名</li>
<li>默认值:</li>
</ul>
<h5 id="7368数据库密码">7.3.6.8、数据库密码</h5>
<ul>
<li>变量名称:PROJECT_ENV_DB_PASSWORD</li>
<li>类型:String</li>
<li>描述:项目参数-数据库密码</li>
<li>默认值:</li>
</ul>
<h5 id="7369k8s中维护的拉取镜像的secret凭证">7.3.6.9、k8s中维护的拉取镜像的secret凭证</h5>
<ul>
<li>变量名称:K8S_PULLIMAGES_SECRET</li>
<li>类型:String</li>
<li>描述:k8s中维护的拉取镜像的secret凭证</li>
<li>默认值:</li>
</ul>
<h2 id="八创建-pipeline-脚本">八、创建 Pipeline 脚本</h2>
<p>接下将使用 Groovy 语法创建一个为 SpringBoot 项目准备的 CI/CD 的脚本式的流水线脚本。其中,脚本中包含多个阶段,分别为 Git 拉取镜像,Maven 编译 Java 项目,Docker 构建与推送镜像,Kubectl 部署应用到 Kubernetes 中,最后使用 Http 请求进行健康检查,成功的话发送钉钉消息,下面是各个阶段脚本及其介绍。</p>
<h3 id="81脚本中使用-kubernetes-插件及设置超时时间">8.1、脚本中使用 Kubernetes 插件及设置超时时间</h3>
<p>使用 Kubernetes 插件执行任务,并设置超时时间为 10 分钟,脚本如下:</p>
<pre><code class="language-groovy">// 设置超时时间 600 SECONDS,方法块内的方法执行超时,任务就标记为失败
timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
   
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            print "在 Slave Pod 中执行任务"
      }
    }
}
</code></pre>
<h3 id="82脚本中-git-拉取项目阶段">8.2、脚本中 Git 拉取项目阶段</h3>
<p>接下来接着往整体的脚本中添加 Git 模块,其中需要引用上面配置的变量,将变量填入脚本中的方法,如下:</p>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('get_commit_msg') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                            // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
      }
    }
}
</code></pre>
<p><strong>变量介绍:</strong></p>
<ul>
<li><strong>GIT_BRANCH:</strong> Git 项目分支变量。</li>
<li><strong>GIT_PROJECT_URL:</strong> Git 项目 URL 变量。</li>
<li><strong>GIT_CREADENTIAL:</strong> Git 凭据 ID 变量。</li>
</ul>
<h3 id="83脚本中-maven-编译项目阶段">8.3、脚本中 Maven 编译项目阶段</h3>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('get_commit_msg') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.MAVEN_BUILD_OPTION} -Dmaven.test.skip=true --settings settings.xml"
                  }
                }
            }
      }
    }
}
</code></pre>
<p><strong>变量介绍:</strong></p>
<ul>
<li><strong>MAVEN_BUILD_OPTION:</strong> Maven 执行的构建命令,package、install 或 deploy。</li>
<li><strong>global-maven-settings:</strong> 全局 Maven 的 Settings.xml 文件的 ID 值,这里是使用 configFileProvider 插件来创建该文件。</li>
</ul>
<h3 id="84脚本中读取-pomxml-参数阶段">8.4、脚本中读取 pom.xml 参数阶段</h3>
<p>这里使用 <code>Pipeline Utility Steps</code> 的 <code>readMavenPom</code> 方法读取项目的 <code>pom.xml</code> 文件,并设置 <code>appName</code> 与 <code>appVersion</code> 两个全局参数。</p>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.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}"
            }
      }
    }
}
</code></pre>
<p><strong>变量介绍:</strong></p>
<ul>
<li><strong>pom.artifactId:</strong> 从 pom.xml 文件中读取的 artifactId 参数值。</li>
<li><strong>pom.version:</strong> 从 pom.xml 文件中读取的 version 参数值。</li>
</ul>
<h3 id="85脚本中-docker-镜像构建与推送模块">8.5、脚本中 Docker 镜像构建与推送模块</h3>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                print "开始构建maven"
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.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阶段'){
                print "开始构建docker镜像"
                container('docker') {
                  // 创建 Dockerfile 文件,但只能在方法块内使用
                  configFileProvider(){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        // 判断 DOCKER_HUB_GROUP 是否为空,有些仓库是不设置仓库组的
                        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}")
                            print "docker镜像构建完成"
                            customImage.push()
                            print "docker镜像push完成"
                            sh "docker rmi ${dockerImageName}"
                            print "镜像已删除"
                        }
                  }
                }
            }
      }
    }
}
</code></pre>
<p><strong>变量介绍:</strong></p>
<ul>
<li><strong>DOCKER_DOCKERFILE_ID:</strong> Dockerfile 文件的 ID。</li>
<li><strong>DOCKER_HUB_URL:</strong> Docker 仓库 URL 地址。</li>
<li><strong>DOCKER_HUB_GROUP:</strong> Docker 仓库项目组名。</li>
<li><strong>DOCKER_HUB_CREADENTIAL:</strong> Docker 仓库认证凭据。</li>
<li><strong>appName:</strong> 从 pom.xml 中读取的应用名称。</li>
<li><strong>appVersion:</strong> 从 pom.xml 中读取的应用版本号。</li>
</ul>
<h3 id="86kubernetes-模块">8.6、Kubernetes 模块</h3>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                print "开始构建maven"
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.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阶段'){
                print "开始构建docker镜像"
                container('docker') {
                  // 创建 Dockerfile 文件,但只能在方法块内使用
                  configFileProvider(){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        // 判断 DOCKER_HUB_GROUP 是否为空,有些仓库是不设置仓库组的
                        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}")
                            print "docker镜像构建完成"
                            customImage.push()
                            print "docker镜像push完成"
                            sh "docker rmi ${dockerImageName}"
                            print "镜像已删除"
                        }
                  }
                }
            }
            stage('Kubernetes 阶段'){
                container('kubectl') {
                  // 使用 Kubectl Cli 插件的方法,提供 Kubernetes 环境,在其方法块内部能够执行 kubectl 命令
                  withKubeConfig() {
                        // 使用 configFile 插件,创建 Kubernetes 部署文件 deployment.yaml
                        configFileProvider(){
                            // 读取 Kubernetes 部署文件
                            deploy = readFile encoding: "UTF-8", file: "deployment.yaml"
                            // 替换部署文件中的变量,并将替换后的文本赋予 deployfile 变量
                            deployfile = deploy.replaceAll("#APP_NAME","${appName}")
                                        .replaceAll("#APP_PORT","${params.PROJECT_ENV_PORT}")
                                        .replaceAll("#SPRING_PROFILES_ACTIVE","${params.PROJECT_ENV_SPRING_PROFILES_ACTIVE}")
                                        .replaceAll("#REDIS_HOST","${params.PROJECT_ENV_REDIS_HOST}")
                                        .replaceAll("#REDIS_PORT","${params.PROJECT_ENV_REDIS_PORT}")
                                        .replaceAll("#REDIS_PW","${params.PROJECT_ENV_REDIS_PW}")
                                        .replaceAll("#DB_URL","${params.PROJECT_ENV_DB_URL}")
                                        .replaceAll("#DB_NAME","${params.PROJECT_ENV_DB_NAME}")
                                        .replaceAll("#DB_PASSWORD","${params.PROJECT_ENV_DB_PASSWORD}")
                                        .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}")
                                        .replaceAll("#APP_IMAGE_NAME","${dockerImageName}")
                                        .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString())
                                        .replaceAll("#K8S_PULLIMAGES_SECRET","${params.K8S_PULLIMAGES_SECRET}")
                        // 生成新的 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"
                        }
                  }
                }
            }
      }
    }
}
</code></pre>
<p><strong>变量介绍:</strong></p>
<ul>
<li><strong>KUBERNETES_CREADENTIAL:</strong> Kubernetes API 认证凭据。</li>
<li><strong>KUBERNETES_DEPLOYMENT_ID:</strong> Kubernetes 部署文件的 ID。</li>
<li><strong>appName:</strong> 从 pom.xml 中读取的应用名称。</li>
<li><strong>PROJECT_ENV_PORT:</strong> 项目参数 - 访问端口。</li>
<li><strong>PROJECT_ENV_SPRING_PROFILES_ACTIVE:</strong> 项目参数 - 环境。</li>
<li><strong>PROJECT_ENV_REDIS_HOST:</strong> 项目参数 - redis地址。</li>
<li><strong>PROJECT_ENV_REDIS_PORT:</strong> 项目参数 - redis端口。</li>
<li><strong>PROJECT_ENV_REDIS_PW:</strong> 项目参数 - redis密码。</li>
<li><strong>PROJECT_ENV_DB_URL:</strong> 项目参数 - 数据库地址。</li>
<li><strong>PROJECT_ENV_DB_NAME:</strong> 项目参数 - 数据库用户名。</li>
<li><strong>PROJECT_ENV_DB_PASSWORD:</strong> 项目参数 - 数据库密码。</li>
<li><strong>KUBERNETES_APP_REPLICAS:</strong> Kubernetes 部署应用的副本数。</li>
<li><strong>dockerImageName:</strong> Docker 镜像名称。</li>
<li><strong>K8S_PULLIMAGES_SECRET:</strong> k8s中维护的拉取镜像的secret凭证。</li>
<li><strong>KUBERNETES_NAMESPACE:</strong> Kubernetes 部署应用的 Namespace。</li>
</ul>
<h3 id="87http-健康检查模块">8.7、HTTP 健康检查模块</h3>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                print "开始构建maven"
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.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阶段'){
                print "开始构建docker镜像"
                container('docker') {
                  // 创建 Dockerfile 文件,但只能在方法块内使用
                  configFileProvider(){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        // 判断 DOCKER_HUB_GROUP 是否为空,有些仓库是不设置仓库组的
                        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}")
                            print "docker镜像构建完成"
                            customImage.push()
                            print "docker镜像push完成"
                            sh "docker rmi ${dockerImageName}"
                            print "镜像已删除"
                        }
                  }
                }
            }
            stage('Kubernetes 阶段'){
                container('kubectl') {
                  // 使用 Kubectl Cli 插件的方法,提供 Kubernetes 环境,在其方法块内部能够执行 kubectl 命令
                  withKubeConfig() {
                        // 使用 configFile 插件,创建 Kubernetes 部署文件 deployment.yaml
                        configFileProvider(){
                            // 读取 Kubernetes 部署文件
                            deploy = readFile encoding: "UTF-8", file: "deployment.yaml"
                            // 替换部署文件中的变量,并将替换后的文本赋予 deployfile 变量
                            deployfile = deploy.replaceAll("#APP_NAME","${appName}")
                                        .replaceAll("#APP_PORT","${params.PROJECT_ENV_PORT}")
                                        .replaceAll("#SPRING_PROFILES_ACTIVE","${params.PROJECT_ENV_SPRING_PROFILES_ACTIVE}")
                                        .replaceAll("#REDIS_HOST","${params.PROJECT_ENV_REDIS_HOST}")
                                        .replaceAll("#REDIS_PORT","${params.PROJECT_ENV_REDIS_PORT}")
                                        .replaceAll("#REDIS_PW","${params.PROJECT_ENV_REDIS_PW}")
                                        .replaceAll("#DB_URL","${params.PROJECT_ENV_DB_URL}")
                                        .replaceAll("#DB_NAME","${params.PROJECT_ENV_DB_NAME}")
                                        .replaceAll("#DB_PASSWORD","${params.PROJECT_ENV_DB_PASSWORD}")
                                        .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}")
                                        .replaceAll("#APP_IMAGE_NAME","${dockerImageName}")
                                        .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString())
                                        .replaceAll("#K8S_PULLIMAGES_SECRET","${params.K8S_PULLIMAGES_SECRET}")
                        // 生成新的 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 &lt;= "${params.HTTP_REQUEST_NUMBER}".toInteger(); n++){
                  try{
                        // 输出请求信息和请求次数
                        print "访问服务:${appName} \n" +
                            "访问地址:${httpRequestUrl} \n" +
                            "访问次数:${n}"
                        // 如果非第一次检测,就睡眠一段时间,等待再次执行 httpRequest 请求
                        if(n &gt; 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"
                        }
                  }
                }
            }
      }
    }
}
</code></pre>
<p><strong>变量介绍:</strong></p>
<ul>
<li><strong>HTTP_REQUEST_PORT:</strong> HTTP 健康检查端口。</li>
<li><strong>HTTP_REQUEST_URL:</strong> HTTP 健康检查 URL 地址。</li>
<li><strong>HTTP_REQUEST_NUMBER:</strong> HTTP 健康检查次数。</li>
<li><strong>HTTP_REQUEST_INTERVAL:</strong> HTTP 健康检查间隔。</li>
<li><strong>KUBERNETES_NAMESPACE:</strong> Kubernetes 的 Namespace。</li>
<li><strong>appName:</strong> 从 pom.xml 中读取的应用名称。</li>
</ul>
<h3 id="88发送钉钉通知">8.8、发送钉钉通知</h3>
<h4 id="881创建钉钉机器人">8.8.1、创建钉钉机器人</h4>
<p>在需要推送钉钉消息的群中创建钉钉机器人,设置机器人名字及安全设置(jenkins中会用到)</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222150428-1074967339.png" alt="" loading="lazy"></p>
<h4 id="882获取webhook并复制webhook">8.8.2、获取<strong>webhook</strong>,并<strong>复制webhook</strong></h4>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222159942-1384056511.png" alt="" loading="lazy"></p>
<h4 id="883配置jenkins">8.8.3、配置Jenkins</h4>
<p>选择 <strong>系统管理—&gt;系统配置—&gt;钉钉</strong> 来配置钉钉机器人:</p>
<p><img src="https://img2022.cnblogs.com/blog/2183800/202207/2183800-20220703222206193-2042666657.png" alt="" loading="lazy"></p>
<p>点击测试,可以看是否连通,发送钉钉机器人信息成功,则配置成功</p>
<h4 id="884脚本中调用dingtalk">8.8.4、脚本中调用dingtalk</h4>
<pre><code class="language-groovy">timeout(time: 600, unit: 'SECONDS') {
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  // 获取提交的说明
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                print "开始构建maven"
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.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阶段'){
                print "开始构建docker镜像"
                container('docker') {
                  // 创建 Dockerfile 文件,但只能在方法块内使用
                  configFileProvider(){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        // 判断 DOCKER_HUB_GROUP 是否为空,有些仓库是不设置仓库组的
                        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}")
                            print "docker镜像构建完成"
                            customImage.push()
                            print "docker镜像push完成"
                            sh "docker rmi ${dockerImageName}"
                            print "镜像已删除"
                        }
                  }
                }
            }
            stage('Kubernetes 阶段'){
                container('kubectl') {
                  // 使用 Kubectl Cli 插件的方法,提供 Kubernetes 环境,在其方法块内部能够执行 kubectl 命令
                  withKubeConfig() {
                        // 使用 configFile 插件,创建 Kubernetes 部署文件 deployment.yaml
                        configFileProvider(){
                            // 读取 Kubernetes 部署文件
                            deploy = readFile encoding: "UTF-8", file: "deployment.yaml"
                            // 替换部署文件中的变量,并将替换后的文本赋予 deployfile 变量
                            deployfile = deploy.replaceAll("#APP_NAME","${appName}")
                                        .replaceAll("#APP_PORT","${params.PROJECT_ENV_PORT}")
                                        .replaceAll("#SPRING_PROFILES_ACTIVE","${params.PROJECT_ENV_SPRING_PROFILES_ACTIVE}")
                                        .replaceAll("#REDIS_HOST","${params.PROJECT_ENV_REDIS_HOST}")
                                        .replaceAll("#REDIS_PORT","${params.PROJECT_ENV_REDIS_PORT}")
                                        .replaceAll("#REDIS_PW","${params.PROJECT_ENV_REDIS_PW}")
                                        .replaceAll("#DB_URL","${params.PROJECT_ENV_DB_URL}")
                                        .replaceAll("#DB_NAME","${params.PROJECT_ENV_DB_NAME}")
                                        .replaceAll("#DB_PASSWORD","${params.PROJECT_ENV_DB_PASSWORD}")
                                        .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}")
                                        .replaceAll("#APP_IMAGE_NAME","${dockerImageName}")
                                        .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString())
                                        .replaceAll("#K8S_PULLIMAGES_SECRET","${params.K8S_PULLIMAGES_SECRET}")
                        // 生成新的 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 &lt;= "${params.HTTP_REQUEST_NUMBER}".toInteger(); n++){
                  try{
                        // 输出请求信息和请求次数
                        print "访问服务:${appName} \n" +
                            "访问地址:${httpRequestUrl} \n" +
                            "访问次数:${n}"
                        // 如果非第一次检测,就睡眠一段时间,等待再次执行 httpRequest 请求
                        if(n &gt; 1){
                            sleep "${params.HTTP_REQUEST_INTERVAL}".toInteger()
                        }
                        // 使用 HttpRequest 插件的 httpRequest 方法检测对应地址
                        result = httpRequest "${httpRequestUrl}"
                        // 判断是否返回 200
                        if ("${result.status}" == "200") {
                            print "Http 请求成功,流水线结束"
                            // 发送钉钉通知
                            dingtalk (
                              robot: "jenkins-ding-msg",        //jenkins中设置的钉钉参数中的id
                              type:'ACTION_CARD',
                              atAll: false,
                              title: "${appName}更新成功",
                              messageUrl: 'xxxx',
                              text: [
                                    "### [${appName}](${env.JOB_URL}) ",
                                    '---',
                                    "- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
                                    '- 状态:&lt;font color=#00CD00 &gt;更新成功&lt;/font&gt;',
                                    "- 更新内容:${env.GIT_COMMIT_MSG}",
                                    "- 持续时间:${currentBuild.durationString}".split("and counting"),
                              ]
                            )
                            break
                        }
                  }catch(Exception e){
                        print "监控检测失败,将在 ${params.HTTP_REQUEST_INTERVAL} 秒后将再次检测。"
                        // 判断检测次数是否为最后一次检测,如果是最后一次检测,并且还失败了,就对整个 Jenkins 任务标记为失败
                        if (n == "${params.HTTP_REQUEST_NUMBER}".toInteger()) {
                            currentBuild.result = "FAILURE"
                            dingtalk (
                              robot: "jenkins-ding-msg",        //jenkins中设置的钉钉参数中的id
                              type:'ACTION_CARD',
                              atAll: false,
                              title: "${appName}更新失败",
                              messageUrl: 'xxxx',
                              text: [
                                    "### [${appName}](${env.JOB_URL}) ",
                                    '---',
                                    "- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
                                    '- 状态:&lt;font color=#EE0000 &gt;更新失败&lt;/font&gt;',
                                    "- 持续时间:${currentBuild.durationString}".split("and counting"),
                              ]
                            )
                        }
                  }
                }
            }
      }
    }
}
</code></pre>
<h3 id="9完整脚本">9、完整脚本</h3>
<h4 id="91使用在jenkins中配置的jnlp-agent">9.1、使用在jenkins中配置的jnlp-agent</h4>
<pre><code class="language-groovy">def label = "jnlp-agent"
timeout(time: 900, unit: 'SECONDS') {
    podTemplate(label: label, cloud: 'kubernetes'){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                print "开始构建maven"
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.MAVEN_BUILD_OPTION} -Dmaven.test.skip=true --settings settings.xml"
                  }
                }
                print "maven构建完成"
            }
            stage('读取pom.xml参数阶段'){
                // 读取 Pom.xml 参数
                pom = readMavenPom file: './pom.xml'
                // 设置 appName 和 appVersion 两个全局参数
                appName = "${pom.artifactId}"
                appVersion = "${pom.version}"
            }
            stage('Docker阶段'){
                print "开始构建docker镜像"
                container('docker') {
                  // 创建 Dockerfile 文件,但只能在方法块内使用
                  configFileProvider(){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        // 判断 DOCKER_HUB_GROUP 是否为空,有些仓库是不设置仓库组的
                        if ("${params.DOCKER_HUB_GROUP}" == '') {
                            dockerImageName = "${params.DOCKER_HUB_URL}/${appName}:${appVersion}"
                        }
                        // 提供 Docker 环境,使用 Docker 工具来进行 Docker 镜像构建与推送
                        docker.withRegistry("https://${params.DOCKER_HUB_URL}", "${params.DOCKER_HUB_CREADENTIAL}") {
                            def customImage = docker.build("${dockerImageName}")
                            print "docker镜像构建完成"
                            customImage.push()
                            print "docker镜像push完成"
                            sh "docker rmi ${dockerImageName}"
                            print "镜像已删除"
                        }
                  }
                }
            }
            stage('Kubernetes 阶段'){
                print "重启k8s中的服务"
                container('kubectl') {
                  withKubeConfig() {
                        // 读取 Kubernetes 部署文件
                        deploy = readFile encoding: "UTF-8", file: "./deployment.yaml"
                        deployfile = deploy.replaceAll("#APP_NAME","${appName}")
                                        .replaceAll("#APP_PORT","${params.PROJECT_ENV_PORT}")
                                        .replaceAll("#SPRING_PROFILES_ACTIVE","${params.PROJECT_ENV_SPRING_PROFILES_ACTIVE}")
                                        .replaceAll("#REDIS_HOST","${params.PROJECT_ENV_REDIS_HOST}")
                                        .replaceAll("#REDIS_PORT","${params.PROJECT_ENV_REDIS_PORT}")
                                        .replaceAll("#REDIS_PW","${params.PROJECT_ENV_REDIS_PW}")
                                        .replaceAll("#DB_URL","${params.PROJECT_ENV_DB_URL}")
                                        .replaceAll("#DB_NAME","${params.PROJECT_ENV_DB_NAME}")
                                        .replaceAll("#DB_PASSWORD","${params.PROJECT_ENV_DB_PASSWORD}")
                                        .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}")
                                        .replaceAll("#APP_IMAGE_NAME","${dockerImageName}")
                                        .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString())
                                        .replaceAll("#K8S_PULLIMAGES_SECRET","${params.K8S_PULLIMAGES_SECRET}")
                        // 生成新的 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"
                        print "重启完了"
                  }
                }
            }
            stage('健康检查阶段'){
                // 设置检测延迟时间 10s,10s 后再开始检测
                sleep 10
                // 健康检查地址
                httpRequestUrl = "http://${appName}.${params.KUBERNETES_NAMESPACE}:${params.HTTP_REQUEST_PORT}${params.HTTP_REQUEST_URL}"
                // 循环使用 httpRequest 请求,检测服务是否启动
                for(n = 1; n &lt;= "${params.HTTP_REQUEST_NUMBER}".toInteger(); n++){
                  try{
                        // 输出请求信息和请求次数
                        print "访问服务:${appName} \n" +
                            "访问地址:${httpRequestUrl} \n" +
                            "访问次数:${n}"
                        // 如果非第一次检测,就睡眠一段时间,等待再次执行 httpRequest 请求
                        if(n &gt; 1){
                            sleep "${params.HTTP_REQUEST_INTERVAL}".toInteger()
                        }
                        // 使用 HttpRequest 插件的 httpRequest 方法检测对应地址
                        result = httpRequest "${httpRequestUrl}"
                        // 判断是否返回 200
                        if ("${result.status}" == "200") {
                            print "Http 请求成功,流水线结束"
                            dingtalk (
                              robot: "shawanyi-test",
                              type:'ACTION_CARD',
                              atAll: false,
                              title: "${appName}更新成功",
                              messageUrl: 'xxxx',
                              text: [
                                    "### [${appName}](${env.JOB_URL}) ",
                                    '---',
                                    "- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
                                    '- 状态:&lt;font color=#00CD00 &gt;更新成功&lt;/font&gt;',
                                    "- 更新内容:${env.GIT_COMMIT_MSG}",
                                    "- 持续时间:${currentBuild.durationString}".split("and counting"),
                              ]
                            )
                            break
                        }
                  }catch(Exception e){
                        print "监控检测失败,将在 ${params.HTTP_REQUEST_INTERVAL} 秒后将再次检测。"
                        // 判断检测次数是否为最后一次检测,如果是最后一次检测,并且还失败了,就对整个 Jenkins 任务标记为失败
                        if (n == "${params.HTTP_REQUEST_NUMBER}".toInteger()) {
                            currentBuild.result = "FAILURE"
                            dingtalk (
                              robot: "shawanyi-test",
                              type:'ACTION_CARD',
                              atAll: false,
                              title: "${appName}更新失败",
                              messageUrl: 'xxxx',
                              text: [
                                    "### [${appName}](${env.JOB_URL}) ",
                                    '---',
                                    "- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
                                    '- 状态:&lt;font color=#EE0000 &gt;更新失败&lt;/font&gt;',
                                    "- 持续时间:${currentBuild.durationString}".split("and counting"),
                              ]
                            )
                        }
                  }
                }
            }
      }
    }
}
</code></pre>
<h4 id="92直接在groovy脚本中指定镜像参数">9.2、直接在groovy脚本中指定镜像参数</h4>
<pre><code class="language-groovy">// 使用默认的jnlp
def label = "jnlp"
timeout(time: 900, unit: 'SECONDS') {
    podTemplate(label: label, cloud: 'kubernetes',
    // 手动指定需要用的镜像
    containers: [
      containerTemplate(name: 'maven', image: 'maven:3.3.9-jdk-8-alpine', ttyEnabled: true, command: 'cat'),
      containerTemplate(name: 'docker', image: 'docker:stable', ttyEnabled: true, command: 'cat'),
      containerTemplate(name: 'kubectl', image: 'lachlanevenson/k8s-kubectl:v1.18.20', ttyEnabled: true, command: 'cat')
    ],
    // 挂载卷
    volumes: [
            persistentVolumeClaim(claimName: 'maven', mountPath: '/root/.m2'),
            hostPathVolume(hostPath: '/usr/bin/docker', mountPath: '/usr/bin/docker'),
            hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock'),
            hostPathVolume(hostPath: '/etc/docker', mountPath: '/etc/docker'),
      ],
    ){
      node (label) {
            stage('Git阶段'){
                git changelog: true,
                  url: "${params.GIT_PROJECT_URL}",
                  branch: "${params.GIT_BRANCH}",
                  credentialsId: "${params.GIT_CREADENTIAL}"
            }
            stage('获取git提交信息') {
                script {
                  MAX_MSG_LEN = 100
                  def changeString = ""
                  echo "Gathering SCM changes"
                  def changeLogSets = currentBuild.changeSets
                  for (int i = 0; i &lt; changeLogSets.size(); i++) {
                        def entries = changeLogSets.items
                        for (int j = 0; j &lt; entries.length; j++) {
                            def entry = entries
                            truncated_msg = entry.msg.take(MAX_MSG_LEN)
                            changeString += "--${truncated_msg}[${entry.author}]\n"
                        }
                  }

                  if (!changeString) {
                        changeString = " - 无"
                  }
                  env.GIT_COMMIT_MSG = changeString
                }
            }
            stage('Maven阶段'){
                print "开始构建maven"
                container('maven') {
                  // 创建 Maven 需要的 Settings.xml 文件
                  configFileProvider(){
                        // 执行 Maven 命令构建项目,并且设置 Maven 配置为刚刚创建的 Settings.xml 文件
                        sh "mvn -T 1C clean ${params.MAVEN_BUILD_OPTION} -Dmaven.test.skip=true --settings settings.xml"
                  }
                }
                print "maven构建完成"
            }
            stage('读取pom.xml参数阶段'){
                // 读取 Pom.xml 参数
                pom = readMavenPom file: './pom.xml'
                // 设置 appName 和 appVersion 两个全局参数
                appName = "${pom.artifactId}"
                appVersion = "${pom.version}"
            }
            stage('Docker阶段'){
                print "开始构建docker镜像"
                container('docker') {
                  // 创建 Dockerfile 文件,但只能在方法块内使用
                  configFileProvider(){
                        // 设置 Docker 镜像名称
                        dockerImageName = "${params.DOCKER_HUB_URL}/${params.DOCKER_HUB_GROUP}/${appName}:${appVersion}"
                        // 判断 DOCKER_HUB_GROUP 是否为空,有些仓库是不设置仓库组的
                        if ("${params.DOCKER_HUB_GROUP}" == '') {
                            dockerImageName = "${params.DOCKER_HUB_URL}/${appName}:${appVersion}"
                        }
                        // 提供 Docker 环境,使用 Docker 工具来进行 Docker 镜像构建与推送
                        docker.withRegistry("https://${params.DOCKER_HUB_URL}", "${params.DOCKER_HUB_CREADENTIAL}") {
                            def customImage = docker.build("${dockerImageName}")
                            print "docker镜像构建完成"
                            customImage.push()
                            print "docker镜像push完成"
                            sh "docker rmi ${dockerImageName}"
                            print "镜像已删除"
                        }
                  }
                }
            }
            stage('Kubernetes 阶段'){
                print "重启k8s中的服务"
                container('kubectl') {
                  withKubeConfig() {
                        // 读取 Kubernetes 部署文件
                        deploy = readFile encoding: "UTF-8", file: "./deployment.yaml"
                        deployfile = deploy.replaceAll("#APP_NAME","${appName}")
                                        .replaceAll("#APP_PORT","${params.PROJECT_ENV_PORT}")
                                        .replaceAll("#SPRING_PROFILES_ACTIVE","${params.PROJECT_ENV_SPRING_PROFILES_ACTIVE}")
                                        .replaceAll("#REDIS_HOST","${params.PROJECT_ENV_REDIS_HOST}")
                                        .replaceAll("#REDIS_PORT","${params.PROJECT_ENV_REDIS_PORT}")
                                        .replaceAll("#REDIS_PW","${params.PROJECT_ENV_REDIS_PW}")
                                        .replaceAll("#DB_URL","${params.PROJECT_ENV_DB_URL}")
                                        .replaceAll("#DB_NAME","${params.PROJECT_ENV_DB_NAME}")
                                        .replaceAll("#DB_PASSWORD","${params.PROJECT_ENV_DB_PASSWORD}")
                                        .replaceAll("#APP_REPLICAS","${params.KUBERNETES_APP_REPLICAS}")
                                        .replaceAll("#APP_IMAGE_NAME","${dockerImageName}")
                                        .replaceAll("#APP_UUID",(new Random().nextInt(100000)).toString())
                                        .replaceAll("#K8S_PULLIMAGES_SECRET","${params.K8S_PULLIMAGES_SECRET}")
                        // 生成新的 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"
                        print "重启完了"
                  }
                }
            }
            stage('健康检查阶段'){
                // 设置检测延迟时间 10s,10s 后再开始检测
                sleep 10
                // 健康检查地址
                httpRequestUrl = "http://${appName}.${params.KUBERNETES_NAMESPACE}:${params.HTTP_REQUEST_PORT}${params.HTTP_REQUEST_URL}"
                // 循环使用 httpRequest 请求,检测服务是否启动
                for(n = 1; n &lt;= "${params.HTTP_REQUEST_NUMBER}".toInteger(); n++){
                  try{
                        // 输出请求信息和请求次数
                        print "访问服务:${appName} \n" +
                            "访问地址:${httpRequestUrl} \n" +
                            "访问次数:${n}"
                        // 如果非第一次检测,就睡眠一段时间,等待再次执行 httpRequest 请求
                        if(n &gt; 1){
                            sleep "${params.HTTP_REQUEST_INTERVAL}".toInteger()
                        }
                        // 使用 HttpRequest 插件的 httpRequest 方法检测对应地址
                        result = httpRequest "${httpRequestUrl}"
                        // 判断是否返回 200
                        if ("${result.status}" == "200") {
                            print "Http 请求成功,流水线结束"
                            dingtalk (
                              robot: "shawanyi-test",
                              type:'ACTION_CARD',
                              atAll: false,
                              title: "${appName}更新成功",
                              messageUrl: 'xxxx',
                              text: [
                                    "### [${appName}](${env.JOB_URL}) ",
                                    '---',
                                    "- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
                                    '- 状态:&lt;font color=#00CD00 &gt;更新成功&lt;/font&gt;',
                                    "- 更新内容:${env.GIT_COMMIT_MSG}",
                                    "- 持续时间:${currentBuild.durationString}".split("and counting"),
                              ]
                            )
                            break
                        }
                  }catch(Exception e){
                        print "监控检测失败,将在 ${params.HTTP_REQUEST_INTERVAL} 秒后将再次检测。"
                        // 判断检测次数是否为最后一次检测,如果是最后一次检测,并且还失败了,就对整个 Jenkins 任务标记为失败
                        if (n == "${params.HTTP_REQUEST_NUMBER}".toInteger()) {
                            currentBuild.result = "FAILURE"
                            dingtalk (
                              robot: "shawanyi-test",
                              type:'ACTION_CARD',
                              atAll: false,
                              title: "${appName}更新失败",
                              messageUrl: 'xxxx',
                              text: [
                                    "### [${appName}](${env.JOB_URL}) ",
                                    '---',
                                    "- 任务:[${currentBuild.displayName}](${env.BUILD_URL})",
                                    '- 状态:&lt;font color=#EE0000 &gt;更新失败&lt;/font&gt;',
                                    "- 持续时间:${currentBuild.durationString}".split("and counting"),
                              ]
                            )
                        }
                  }
                }
            }
      }
    }
}
</code></pre><br><br>
来源:https://www.cnblogs.com/yg0070/p/16441188.html
頁: [1]
查看完整版本: Kubernetes 部署 Jenkins + Kubernetes 的 CI CD