上篇文章我们部署测试了下Argocd,不过Argocd是基于Git声明式管理,需要将Deployment,Service 等YAML都放到git中,研发自助上线时需要修改YAML,上线时会覆盖到生产环境,如若生产环境对应用的的资源分配或副本等做过调整将会被覆盖,而且也有修改错误的风险。

这次调研测试Jenkins主要是想通过Jenkins 让研发自助上线时仅需关注Image版本,无法修改其他内容,这样就避免了很多风险

控制台效果

dashboard

流程介绍

如上图,项目界面点击Build with Parameters后会展示出可选择界面
主要有以下功能

  1. 当有多集群时,可选择需发布应用所属的集群(将多集群应用统一一个jenkins上线控制台)
  2. 选择对应集群中的命名空间
  3. 选择对应命名空间中需发布的应用
  4. 展示出需发布应用当前正在使用的镜像版本
  5. 选择需要发布的镜像版本
    以上单选框的值均是逐层依赖关系,采用的参数化构建Active Choices Reactive Parameter功能

环境信息

  • Kubernetes: 阿里云ACK 托管集群

    • 因k8s集群在阿里云,Jenkins在公司内网,所以需要为k8s apiserver 绑定公网访问入口

      • 点击”集群信息”->”基本信息”,”API Server 公网连接端点”处点击”绑定公网IP”按钮,创建弹性IP后将会绑定到APIserver 的SLB上
      • 绑定后需为APIserver 的SLB添加IP白名单限制,仅允许以下网段访问(防止被攻击)
        1. 放通VPC网段,pod网段,节点网段,Service网段和100.64.0.0/10地址(这样保证阿里云内部访问正常)
        2. 放通Jenkins出口网段(这样Jenkins可以通过kubelet命令调用集群数据)
  • Jenkins 服务器脚本准备

    • 将各集群访问Apiserver的KubeConfig存放到/opt/kubeconf/目录下,文件名写为集群名称,下面参数化构建和k8s.sh脚本需要调用

    • 需要在服务器创建一个脚本/opt/kubeconf/k8s.sh

      脚本内容如下, HarborAliyun Cli 部分账户密码需要修改为自己的

      action=$1
      cluster=$2
      namespace=$3
      deploy=$4

      case $action in
      GetNs)
      # Get NS List
      kubectl --kubeconfig /opt/kubeconf/$cluster get ns |awk '{print $1}'|egrep -v "NAME|kube-|arms-"| tr "\n" ","
      ;;

      GetDeploy)
      # Get Deploy List
      kubectl --kubeconfig /opt/kubeconf/$cluster -n $namespace get deploy |awk '{print $1}'|egrep -v "NAME" | tr "\n" ","
      ;;

      GetNowImage)
      # Get Deploy Image
      kubectl --kubeconfig /opt/kubeconf/$cluster -n $namespace get deploy $deploy -o jsonpath="{.spec.template.spec.containers[*].image}" | tr "\n" ","
      ;;

      GetRepoTagsList)
      #Get Repositories Image lists
      NowImage=$2
      RepoAddress=$(echo $NowImage |awk -F '/' '{print $1}')
      RepoName=$(echo $NowImage | awk -F'/|:' '{print $2"/"$3}')
      if [[ $RepoAddress == 'harbor.roobo.net' ]]
      then
      curl -s -u "需填写用户名:密码" -X GET -H "Content-Type: application/json" "https://需填写Harobor域名/api/repositories/${RepoName}/tags" | | jq .[].name | tr "\n" "," |sed 's/"//g'
      elif [[ $RepoAddress =~ aliyuncs.com ]]
      then
      /usr/local/bin/aliyun cr --force --version 2016-06-07 --region cn-beijing --access-key-id 须填写accesskeyid --access-key-secret 须填写accesskeysecret GET /repos/${RepoName}/tags |jq .data.tags[].tag | tr "\n" "," |sed 's/"//g'
      fi
      ;;

      SetImage)
      # Update Deployment Image
      NowImage=$5
      NewImage=$6
      RepoAddress=$(echo $NowImage | awk -F':' '{print $1}')
      OldImageVersion=$(echo $NowImage | awk -F':' '{print $2}')
      kubectl --kubeconfig /opt/kubeconf/$cluster -n $namespace set image deploy $deploy $deploy=$RepoAddress:$NewImage

      while :
      do
      Status=$(kubectl --kubeconfig /opt/kubeconf/$cluster -n $namespace rollout status deploy $deploy --watch=false)
      if [[ $Status =~ "successfully" ]]
      then
      echo "==== Successful Deployment ===="
      echo "Deployment details: $deploy from $OldImageVersion to $NewImage"
      echo "OldImage=$NowImage"
      echo "NewImage=$RepoAddress:$NewImage"
      break
      else
      echo "$Status"
      sleep 5
      fi
      done

      ;;

      esac

Jenkins项目创建

实现Build with Parameters

分步骤实现,注意以下参数过程中名称列需固化,每层参数构建都有变量依赖

1. 集群列表

在参数化构建过程中添加选项参数

  • 名称列填写Cluster
  • 选项列填写各个kubernetes集群名称(需与/opt/kubeconf/目录下KubeConfig集群名称一致)

cluster

2. 命名空间

在参数化构建过程中添加Active Choices Reactive Parameter

  • Name列填写NameSpace
  • 选择Groovy Script脚本
    text = "/opt/kubeconf/k8s.sh GetNs $Cluster".execute().text
    myList = text.split(",") as List
    return myList
  • Choice Type 选择Single Select 即单选项
  • Referenced parameters 填写 Cluster (获取Namespace时需要依赖$Cluster)

namespace

3. 应用

在参数化构建过程中添加Active Choices Reactive Parameter

  • Name列填写Deployment
  • 选择Groovy Script脚本
    text = "/opt/kubeconf/k8s.sh GetDeploy $Cluster $NameSpace".execute().text
    myList = text.split(",") as List
    return myList
  • Choice Type 选择 Single Select 即单选项
  • Referenced parameters填写 Cluster,NameSpace (获取Deployment时时需要依赖$Cluster,$Namespace)

deployment

4. 当前镜像

在参数化构建过程中添加Active Choices Reactive Parameter

  • Name列填写NowImage
  • 选择Groovy Script脚本
    text = "/opt/kubeconf/k8s.sh GetNowImage $Cluster $NameSpace $Deployment".execute().text
    myList = text.split(",") as List
    return myList
  • Choice Type 选择 Single Select 即单选项
  • Referenced parameters 填写 Cluster,NameSpace,Deployment (获取NowImage时时需要依赖$Cluster,$Namespace,$Deployment)

nowimage

5. 选择新镜像

在参数化构建过程中添加Active Choices Reactive Parameter

  • Name列填写NewImage
  • 选择Groovy Script脚本
    text = "/opt/kubeconf/k8s.sh GetRepoTagsList $NowImage".execute().text
    myList = text.split(",") as List
    return myList
  • Choice Type 选择 Single Select 即单选项
  • Referenced parameters 填写 NowImage (获取NewImage时需要依赖$NowImage, 根据NowImage去对应镜像仓库里查询所有的tags)

newimage

实现构建过程

添加 执行 Shell

增加构建步骤中选择执行shell
脚本内容为:

/opt/kubeconf/k8s.sh SetImage $Cluster $NameSpace $Deployment $NowImage $NewImage

cmdshell

此时在Dashboard界面即可选择对应Deployment 和 新的镜像进行构建了。

Build History 展示内容优化

默认的展示内容仅显示次数和时间,不显示任何版本相关信息,在查看构建历史时非常不明朗

设置构建名称

  • 构建环境中勾选Set Build Name,并将Build Name设置为
    #${BUILD_NUMBER}-${BUILD_USER}-${Cluster}
  • 构建环境中勾选Set jenkins user build variables
    setbuildname

设置构建描述

增加构建步骤中选择Set build descriptionRegular expression内容填写为

Deployment details:(.*)

在构建脚本/opt/kubeconf/k8s.sh执行结束后脚本会输出以下log,此正则将会匹配内容并设置到构建描述信息中

Deployment details: cicd-test from v1.0.5-4d37c0b6 to v1.0.2-b0d24b86

setbuilddesc

控制台输出Log

consolelog

构建钉钉/邮件通知和Groovy脚本调试

构建钉钉通知

  1. jenkins 安装插件 DingTalk
  2. 参考官方文档添加钉钉机器人并配置到Jenkins全局设置中
  3. 项目配置界面勾选钉钉机器人通知并设置自定义内容
    - 集群: $Cluster
    - **应用: $Deployment**
    - **新版本: $NewImage**
    - 旧版本: $NowImage
    # 生产环境 $Deployment 上线成功
    # 请及时对该服务API接口验证

配置界面

dingdingconfig

效果展示

dingdingtalk

邮件通知

安装插件

项目配置

设置收件人

Project Recipient List处指定收件人邮件,多个账户以逗号分隔

设置邮件内容格式

Content Type处设置为HTML(text/html)

设置邮件内容

Default Content处填写以下内容

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
</head>

<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
offset="0">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<b><font color="#0B610B">上线通知</font></b>
<hr size="2" width="100%" align="center" />

<font color="#CC0000">生产环境 ${Deployment}服务 上线完成,上线结果:${BUILD_STATUS}</font></br>
请及时关注服务运行状况,如有问题,请联系执行人 ${BUILD_USER} 进行回滚</br>

</tr>
<tr>
<td><br />
<b><font color="#0B610B">构建信息</font></b>
<hr size="2" width="100%" align="center" /></td>
</tr>
<tr>
<td>
<ul>
<li>任务 : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
<li>状态 : ${BUILD_STATUS}</li>
<li>执行人 : ${BUILD_USER}</li>
<li>集群 : ${Cluster}</li>
<li><font color="#CC0000">应用 : ${Deployment}</font></li>
<li><font color="#CC0000">新版本 : ${NewImage}</font></li>
<li>旧版本 : ${NowImage}</li>
</ul>
</td>
</tr>
<tr>
<td><br />
<hr size="2" width="100%" align="center" />
<b><font color="#0B610B">本邮件由系统自动发出,无需回复!</font></b>
</td>
</tr>

</table>
</body>
</html>

email-config

邮件效果

email-status

Groovy脚本调试

  • Groovy Script脚本可以在 Jenkins -> 系统管理 -> 脚本命令行 工具中进行调试

debuggroovy