运维八一 运维八一
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)

运维八一

运维,运维!
首页
运维杂记
编程浅尝
周积跬步
专栏
生活
关于
收藏
  • 分类
  • 标签
  • 归档
Source (opens new window)
  • 操作系统

  • 域名解析

  • 公有云

  • CI&CD

    • Jenkins安装及常用插件
    • Jenkins配置回滚
    • Jenkins不显示maven
    • Jenkins添加健康检查
    • k8s+jenkins+helm+springboot部署
      • Kubernetes + Jenkins + Helm + Springboot
      • 环境介绍:
      • 一. Kubernetes 环境安装 Jenkins
        • 系统环境:
        • 1. 设置存储目录
        • 1.1 安装 NFS 服务端
        • 1.1.1 服务端配置
        • 配置环境
        • 安装nfs-utils和rpcbind
        • 创建存储的文件夹
        • 配置NFS
        • 设置开机启动并启动
        • 查看是否有可用的NFS地址
        • 1.1.2 客户端配置
        • 安装nfs-utils和rpcbind
        • 创建挂载的文件夹
        • 挂载nfs
        • 1.2 挂载 NFS 并设置存储文件夹
        • 2. 创建 PV & PVC
        • 2.1 准备 PV & PVC 部署文件
        • 2.2 创建 PV & PVC
        • 3. 创建 ServiceAccount & ClusterRoleBinding
        • 4. 创建 Service & Deployment
        • 4.1 创建 Service & Deployment 部署文件
        • 4.2 部署 Jenkins
        • 5. 获取 Jenkins 生成的 Token
        • 6. 启动 Jenkins 进行安装
        • 6.1 进入Jenkins
        • 6.2 安装插件
        • 6.3 设置用户名、密码
        • 6.4 进入 Jenkins 界面
        • 7. 其他
        • 7.1 插件因网络问题安装失败
      • 二. Jenkins 安装插件
      • 三. Jenkins 配置插件
        • 1. Git 插件配置及使用
        • 1.1 配置凭据
        • 1.2 Pipeline 脚本中使用
        • 2. Docker 插件配置及使用
        • 2.1 功能描述
        • 2.2 Pipeline 脚本中使用
        • 3. Kubernetes 插件配置及使用
        • 3.1 配置凭据
        • 3.2 云配置
        • 3.3 Template 模板配置
        • 4. Kubernetes Cli 插件配置及使用
        • 4.1 配置凭据
        • 4.2 Pipeline 脚本中使用
        • 5. Config File Provider 插件
        • 5.1 配置 Maven settings.xml
        • 5.2 Pipeline 脚本中使用
        • 6. Pipeline Utility Steps 插件
        • 6.1 功能描述
        • 6.2 Pipeline 脚本中使用
      • 四. 测试插件
        • 1. 创建流水线任务
        • 2. 配置流水线任务
        • 2.1 常规配置
        • 2.2 流水线脚本
        • 2.3 运行流水线任务
        • 3. 查看流水线日志
      • 五. 部署前准备
        • 1. 配置文件存放位置比较
        • 2. 设置配置文件到项目中
        • 3. 测试运行环境是否可用
      • 六. 开始写 Pipeline 脚本
        • 1. Git 拉取
        • 2. Maven 编译
        • 3. Docker 编译
        • 4. Helm 启动应用
        • 5. 测试接口
      • 七. 完善 Pipeline 脚本
        • 1. 设置超时时间
        • 2. 设置邮箱通知
        • 3. 判断成功失败来发送邮件
        • 3.1 流水线过程判断成功失败
        • 3.2 测试成功失败执行时发送邮件
        • 4. 将脚本放入到项目中
      • 八. 完整代码
  • 数据库

  • 负载均衡&反向代理

  • 存储系统

  • 容器&容器编排

  • 批量管理

  • 邮件系统

  • 监控系统

  • Web服务

  • 虚拟化

  • 防火墙

  • 压测

  • 文件同步

  • 私有云

  • 日志系统

  • 代码仓库&版本管理

  • 安全审计

  • 远程拨号

  • 大数据

  • 统一认证

  • 消息队列

  • Apollo

  • 运维杂记
  • CI&CD
lyndon
2022-10-05
目录

k8s+jenkins+helm+springboot部署

# Kubernetes + Jenkins + Helm + Springboot

参考文档:http://www.mydlq.club/article/8/

# 环境介绍:

Jenkins: 2.172

Helm: 2.13.1

Kubernetes: 1.14.0

chart repo仓库地址: http://chart.mydlq.club

项目Github地址: https://github.com/my-dlq/springboot-helloworld

Helm的chart模板Github地址: https://github.com/my-dlq/springboot-chart

img

# 一. Kubernetes 环境安装 Jenkins

# 系统环境:

kubernetes 版本:1.14.0

jenkins 版本:2.172

jenkins 部署示例文件 Github 地址:https://github.com/my-dlq/blog-example/tree/master/jenkins-deploy

# 1. 设置存储目录

在 Kubenetes 环境下所起的应用都是一个个 Docker 镜像,为了保证应用重启的情况下数据安全,所以需要将 Jenkins 持久化到存储中。这里用的是 NFS 网路存储,方便在 Kubernetes 环境下应用启动节点转义数据一致。当然也可以选择存储到本地,但是为了保证应用数据一致,需要设置 Jenkins 固定到某一 Kubernetes 节点。

# 1.1 安装 NFS 服务端

. 一、服务端配置 . 1、配置环境 . 2、安装nfs-utils和rpcbind . 3、创建存储的文件夹 . 4、配置NFS . 5、设置开机启动并启动 . 6、查看是否有可用的NFS地址 . 二、客户端配置 . 1、安装nfs-utils和rpcbind . 2、创建挂载的文件夹 . 3、挂载nfs 环境: 系统版本:CentOS 7.5

# 1.1.1 服务端配置
# 配置环境

关闭防火墙服务

# 停止并禁用防火墙
$ systemctl stop firewalld
$ systemctl disable firewalld
1
2
3

关闭并禁用SELinux

# 若当前启用了 SELinux 则需要临时设置其当前状态为 permissive
$ setenforce 0

# 编辑/etc/sysconfig selinux 文件,以彻底禁用 SELinux
$ sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
1
2
3
4
5
# 查看selinux状态
$ getenforce

如果selinux状态为permissive,则执行reboot重新启动即可
1
2
3
4
# 安装nfs-utils和rpcbind
$ yum install -y nfs-utils rpcbind
1
# 创建存储的文件夹
# 创建文件夹
$ mkdir /nfs

# 更改归属组与用户
$ chown -R nfsnobody.nfsnobody /nfs
1
2
3
4
5
# 配置NFS
# 编辑exports
$ vi /etc/exports

# 输入以下内容(格式:FS共享的目录 NFS客户端地址1(参数1,参数2,...) 客户端地址2(参数1,参数2,...))
/nfs 192.168.200.0/24(rw,async,no_root_squash)
1
2
3
4
5

#如果设置为 /nfs *(rw,async,no_root_squash) 则对所以的IP都有效

  • 常用选项:

    • ro:客户端挂载后,其权限为只读,默认选项;

    • rw:读写权限;

    • sync:同时将数据写入到内存与硬盘中;

    • async:异步,优先将数据保存到内存,然后再写入硬盘;

    • Secure:要求请求源的端口小于1024

  • 用户映射:

    • root_squash:当NFS客户端使用root用户访问时,映射到NFS服务器的匿名用户;

    • no_root_squash:当NFS客户端使用root用户访问时,映射到NFS服务器的root用户;

    • all_squash:全部用户都映射为服务器端的匿名用户;

    • anonuid=UID:将客户端登录用户映射为此处指定的用户uid;

    • anongid=GID:将客户端登录用户映射为此处指定的用户gid

# 设置开机启动并启动

rpcbind:

$ systemctl restart rpcbind
1

nfs:

$ systemctl enable nfs
$ systemctl restart nfs
1
2
# 查看是否有可用的NFS地址
$ showmount -e 192.168.200.104
1
# 1.1.2 客户端配置
# 安装nfs-utils和rpcbind
$ yum install -y nfs-utils rpcbind
1
# 创建挂载的文件夹
$ mkdir -p /nfs-data
1
# 挂载nfs
$ mount -t nfs -o nolock,vers=4 192.168.200.104:/nfs /nfs-data
1

参数解释:

  • mount:挂载命令
  • -o:挂载选项
  • nfs :使用的协议
  • nolock :不阻塞
  • vers : 使用的NFS版本号
  • IP : NFS服务器的IP(NFS服务器运行在哪个系统上,就是哪个系统的IP)
  • /nfs: 要挂载的目录(Ubuntu的目录)
  • /nfs-data : 要挂载到的目录(开发板上的目录,注意挂载成功后,/mnt下原有数据将会被隐藏,无法找到)

查看挂载

$ df -h
1

卸载挂载

$ umount /nfs-data
1

查看nfs版本

# 查看nfs服务端信息
$ nfsstat -s

# 查看nfs客户端信息
$ nfsstat -c
1
2
3
4
5

# 1.2 挂载 NFS 并设置存储文件夹

如果不能直接操作 NFS 服务端创建文件夹,需要知道 NFS 服务器地址,然后将其挂在到本地目录,进入其中创建 Jenkins 目录空间。

挂载 NFS

$ mount -o vers=4.1 192.168.200.104:/nfs/ /nfs
1

在 NFS 共享存储文件夹下创建存储 Jenkins 数据的文件夹

$ mkdir -p /nfs/data/jenkins
1

# 2. 创建 PV & PVC

创建 PV 绑定 NFS 创建的 Jenkins 目录,然后创建 PVC 绑定这个 PV,将此 PVC 用于后面创建 Jenkins 服务时挂载的存储。

# 2.1 准备 PV & PVC 部署文件

一定要确保 PV 的空间大于 PVC,否则无法关联

jenkins-pv-pvc.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: jenkins
  labels:
    app: jenkins
spec:
  capacity:          
    storage: 10Gi
  accessModes:       
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain  
  mountOptions:   #NFS挂在选项
    - hard
    - nfsvers=4.1    
  nfs:            #NFS设置
    path: /nfs/data/jenkins   
    server: 192.168.2.11
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi			#生产环境空间一定要设置比较大点
  selector:
    matchLabels:
      app: jenkins
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 2.2 创建 PV & PVC

$ kubectl apply -f jenkins-pv-pvc.yaml
1

# 3. 创建 ServiceAccount & ClusterRoleBinding

此 kubernetes 集群用的是 RBAC 安全插件,必须创建权限给一个 ServiceAccount,然后将此 ServiceAccount 绑定到 Jenkins 服务,这样赋予 Jenkins 服务一定权限执行一些操作,为了方便,这里将 cluster-admin 绑定到 ServiceAccount 以保证 Jenkins 能拥有一定的权限。

注意:请提前修改 yaml 中的 namespace

jenkins-rbac.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins-admin       #ServiceAccount名
  namespace: mydlqcloud     #指定namespace,一定要修改成你自己的namespace
  labels:
    name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins-admin
  labels:
    name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins-admin
    namespace: mydlqcloud
    roleRef:
      kind: ClusterRole
      name: cluster-admin
      apiGroup: rbac.authorization.k8s.io
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

创建 RBAC 命令

$ kubectl create -f jenkins-rbac.yaml
1

# 4. 创建 Service & Deployment

这里开始部署 Jenkins 服务,创建 Service 与 Deployment,其中 Service 暴露两个接口 80880 与 50000。而 Deployment 里面要注意的是要设置上面创建的 ServiceAccount ,并且设置容器安全策略为“runAsUser: 0”以 Root 权限运行容器,而且暴露8080、50000两个端口。

# 4.1 创建 Service & Deployment 部署文件

jenkins-deployment.yaml

apiVersion: v1
kind: Service
metadata:
  name: jenkins
  labels:
    app: jenkins
spec:
  type: NodePort
  ports:
  - name: http
    port: 8080          #服务端口
    targetPort: 8080
    nodePort: 32001		#NodePort方式暴露 Jenkins 端口
  - name: jnlp
    port: 50000         #代理端口
    targetPort: 50000
    nodePort: 32002
      selector:
    app: jenkins
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  labels:
    app: jenkins
spec:
  selector:
    matchLabels:
      app: jenkins
  replicas: 1
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      serviceAccountName: jenkins-admin
      containers:
      - name: jenkins
        image: registry.cn-shanghai.aliyuncs.com/mydlq/jenkins:2.172
        securityContext:
          runAsUser: 0       #设置以ROOT用户运行容器
          privileged: true   #拥有特权
        ports:
        - name: http
          containerPort: 8080
        - name: jnlp
          containerPort: 50000
        resources:
          limits:
            memory: 2Gi
            cpu: "1000m"
          requests:
            memory: 1Gi
            cpu: "500m"
        env:
        - name: LIMITS_MEMORY
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
              divisor: 1Mi
        - name: "JAVA_OPTS"  #设置变量,指定时区和 jenkins slave 执行者设置
          value: "
                   -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
                 "
        - name: "JENKINS_OPTS"
          value: "--prefix=/jenkins"         #设置路径前缀加上 Jenkins
        volumeMounts:                        #设置要挂在的目录
        - name: data
          mountPath: /var/jenkins_home
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: jenkins      #设置PVC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

参数说明:

  • JAVA_TOOL_OPTIONS:定义 JVM 环境变量,这里主要是设置编码参数,防止中文乱码

  • JAVA_OPTS: JVM 参数设置

  • JENKINS_OPTS: Jenkins 参数设置

  • 设置执行任务时候不等待:

    • 默认情况下,Jenkins生成代理是保守的。例如,如果队列中有两个构建,它不会立即生成两个执行器。它将生成一个执行器,并等待某个时间释放第一个执行器,然后再决定生成第二个执行器。Jenkins确保它生成的每个执行器都得到了最大限度的利用。如果你想覆盖这个行为,并生成一个执行器为每个构建队列立即不等待,所以在Jenkins启动时候添加这些参数:

      -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85

# 4.2 部署 Jenkins

执行 Kuberctl 命令将 Jenkins 部署到 Kubernetes 集群。

$ kubectl create -f jenkins-deployment.yaml
1

# 5. 获取 Jenkins 生成的 Token

在安装 Jenkins 时候,它默认生成一段随机字符串,用于安装验证。这里访问它的安装日志,获取它生成的 Token 字符串。

查看 Jenkins Pod 启动日志

$ kubectl log $(kubectl get pods | awk '{print $1}' | grep jenkins)
1

查看日志中生成的 Token 字符串,默认给的token为:

*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

81177a7552fe42bb850ee12713e51a70

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
*************************************************************
*************************************************************
1
2
3
4
5
6
7
8
9
10
11
12

# 6. 启动 Jenkins 进行安装

输入集群地址和 Jenkins Service 提供的 NodePort 端口,访问 Jenkins 进行安装步骤,可以按下一步步执行:

# 6.1 进入Jenkins

输入集群地址和上面设置的Nodeport方式的端口 32001,然后输入上面获取的 Token 字符串。

例如,本人的集群IP为 192.168.2.11 ,所以就可以访问 http://192.168.200.101:32001/jenkins,进入后看到以下页面:

img

# 6.2 安装插件

选择自定义插件来进行安装

img

安装一些常用的插件

img

img

img

img

确定后可以看到正在安装插件界面

img

# 6.3 设置用户名、密码

img

# 6.4 进入 Jenkins 界面

img

# 7. 其他

# 7.1 插件因网络问题安装失败

如果 Jenkins 安装插件失败,则可以按一下设置

  • (1)、进入地址 http://192.168.2.11:32001/jenkins/pluginManager/advanced
  • (2)、将最下面的 Update Site 的 URL 地址替换成:http://mirror.esuni.jp/jenkins/updates/update-center.json
  • (3)、点“submit”按钮,然后点右下角角 “check now”
  • (4)、然后输入地址 http://192.168.2.11:32001/jenkins/restart 重启 jenkins 后再重新安装插件

PS:上面地址替换成你们的集群地址及端口。

img

# 二. Jenkins 安装插件

为了方便集成 Maven、Kubernetes、配置文件等等,这里需要安装几个别的插件,这里插件可以在 系统管理—>插件管理—>可选插件 里面安装下面列出的插件:

  • 1、Git 插件

Jenkins 安装中默认安装 Git 插件,所以不需要单独安装。利用 git 工具可以将 github、gitlab 等等的地址下载源码。

  • 2、Docker 插件

Jenkins 安装中默认安装 Docker 插件,所以不需要单独安装。利用 Docker 插件可以设置 Docker 环境,运行 Docker 命令,配置远程 Docker 仓库凭据等。

  • 3、Kubernetes

Kubernetes 插件的目的是能够使用 Kubernetes 集群动态配置 Jenkins 代理(使用Kubernetes调度机制来优化负载),运行单个构建,等构建完成后删除该代理。这里我们需要用到这个插件来启动 Jenkins Slave 代理镜像,让代理执行 Jenkins 要执行的 Job。

  • 4、Kubernetes Cli

Kubernetes Cli 插件作用是在执行 Jenkins Job 时候提供 kubectl 与 Kubernetes 集群交互环境。可以在 Pipeline 或自由式项目中允许执行 kubectl 相关命令。它的主要作用是提供 kubectl 运行环境,当然也可以提供 helm 运行环境。

  • 5、Config File Provider

Config File Provider 插件作用就是提供在 Jenkins 中存储 properties、xml、json、settings.xml 等信息,可以在执行 Pipeline 过程中可以写入存储的配置。 例如,存入一个 Maven 全局 Settings.xml 文件,在执行 Pipeline Job 时候引入该 Settings.xml ,这样 Maven 编译用的就是该全局的 Settings.xml。

  • 6、Pipeline Utility Steps

这是一个操作文件的插件,例如读写 json、yaml、pom.xml、Properties 等等。在这里主要用这个插件读取 pom.xml 文件的参数设置,获取变量,方便构建 Docker 镜像。

# 三. Jenkins 配置插件

# 1. Git 插件配置及使用

# 1.1 配置凭据

如果是私有项目 Git 一般需要配置一个凭据用于验证,如果是公开项目,则无需任何配置。

凭据->系统->全局凭据->添加凭据

img

# 1.2 Pipeline 脚本中使用

利用 Git 插件拉取源码,分别可以设置拉取的“分支”、“显示拉取日志”、“拉取的凭据”、“拉取的地址”,可以将上面设置的凭据ID设置到 credentialsId 参数上

参考:https://jenkins.io/doc/pipeline/steps/git/#-git-%20git

git branch: "master" ,changelog: true , credentialsId: "xxxx-xxxx-xxxx", url: "https://github.com/xxxxxx"
1

# 2. Docker 插件配置及使用

# 2.1 功能描述

此插件将提供以下功能:

  • ​ 记录FROM中使用的Docker镜像的跟踪
  • ​ 记录在容器中运行的Docker镜像的跟踪
  • ​ 在Docker容器中运行构建步骤
  • ​ 设置Docker注册表端点,用于推送镜像验证
  • ​ 设置Docker服务器端点,用于执行远程Docker API

# 2.2 Pipeline 脚本中使用

安装 Jenkins 时候默认会安上此插件,这里主要是利用插件提供一个 docker 登录了的环境,以及执行一些 Docker 命令,具体请看参考,下面将写一个简单的执行例子来描述 Docker 镜像的构建过程。

参考:https://jenkins.io/doc/pipeline/steps/docker-workflow/

// 此方法是设置docker仓库地址,然后选择存了用户名、密码的凭据ID进行验证。注意,只有在此方法之中才生效。
docker.withRegistry("https://hub.docker.com/", "xxxxx-xxxx-xxxx-xxxx") {
    echo "构建镜像"
    def customImage = docker.build("hub.mydlq.club/myproject/springboot-helloworld:0.0.1")
    echo "推送镜像"
    customImage.push()
    echo "删除镜像"
    sh "docker rmi hub.mydlq.club/myproject/springboot-helloworld:0.0.1"
}
1
2
3
4
5
6
7
8
9

# 3. Kubernetes 插件配置及使用

# 3.1 配置凭据

配置连接 kubernetes 集群的凭据(Kubernetes ServiceAccount token),此凭据的账户权限最好设置较大点,避免出现未知问题。配置完成后,需要在后面的 Cloud 云配置中设置这个凭据。

凭据->系统->全局凭据->添加凭据,类型选择secret text

img

# 3.2 云配置

系统管理—>系统设置—>云

img

参考:https://github.com/jenkinsci/kubernetes-plugin

这里是配置连接Kubernetes集群,启动 Jenkins Slave 代理的相关配置。

  • 名称: kubernetes

  • Kubernetes 地址: https://kubernetes.default.svc.cluster.local (默认集群内调用 k8s api 地址)

  • 禁用 HTTPS 证书检查: 勾选 (不验证https)

  • 凭据: 新增凭据—>Secret text—>Secret 设置 kubernetes 的 Token (进入 k8s dashboard 的 token 等都行,设置之后测试连接是否成功)

  • Jenkins地址: http://jenkins.mydlqcloud:8080/jenkins (用于代理与 Jenkins 连接的地址,用的是 k8s 集群中 jenkins 服务的地址为“http://jenkins服务名.jenkins所在namespace:jenkins端口号/jenkins后缀”)

  • 其他: 默认即可

# 3.3 Template 模板配置

这里配置 Jenkins Slave 在 kubernetes 集群中启动的 Pod 的配置。

这里将设置四个镜像,分别是:

  • Jenkins Slave: 用于执行 Jenkins Job 命令。
  • Helm-Kuberctl: 用于执行 Helm 命令。
  • Docker:用于编译、推送 Docker 镜像
  • Maven: 用于Maven编译、打包。

这里将这四个镜像融入到一个 Pod 之中,方便执行各种命令来完成持续部署交互过程。

系统管理—>系统设置—>云—>添加pod模板

Template 基本配置:

image-20221005003052829

  • 名称:jnlp-agent (必须以jnlp开头)

  • 命名空间:mydlqcloud (指定jekins slave启动的namespace)

  • 标签列表:jnlp-agent (执行jenkins slave代理的label,方便jenkins任务中调用)

  • 用法:只允许运行绑定到这台机器的job

  • 连接jenkins的超时时间(秒):100 (这个值可以设大点,防止代理连接超时)

  • pod的原始 Yaml 设置:

    • 在 Pod 的原始 yaml 那栏中,填写下面的 yaml 文件内容进行配置,将会以下面的 yaml 配置作为 Jenkins Slave Pod 的基本配置,如果上面界面上配置了某些信息,会自动替换 yaml 中设置的值,相当于此 yaml 文件作为了一个默认(缺省)配置了。

      - - apiVersion: v1
          kind: Pod
          metadata:
            labels:
              app: jenkins-slave
          spec:
            serviceAccountName: jenkins-admin
            securityContext:                  #容器安全设置
              runAsUser: 0                    #以ROOT用户运行容器
              privileged: true                #赋予特权执行容器
            containers:
          
        - name: jnlp                      #Jenkins Slave镜像
          image: registry.cn-shanghai.aliyuncs.com/mydlq/jenkins-jnlp-slave:3.27-1
          #设置工作目录
          workingDir: /home/jenkins
          tty: true
        - name: docker                    #Docker镜像
          image: registry.cn-shanghai.aliyuncs.com/mydlq/docker:18.06.2-dind
          command: ['cat']
          tty: true
          volumeMounts:
          - name: docker
            mountPath: /usr/bin/docker
          - name: docker-sock
            mountPath: /var/run/docker.sock
          - name: docker-config
            mountPath: /etc/docker
        - name: maven                     #Maven镜像
          image: registry.cn-shanghai.aliyuncs.com/mydlq/maven:3.6.0-jdk8-alpine
          command:
          - cat
            tty: true
            volumeMounts:
          - name: maven-m2
            mountPath: /root/.m2
        - name: helm-kubectl              #Kubectl & Helm镜像
          image: registry.cn-shanghai.aliyuncs.com/mydlq/helm-kubectl:2.13.1
          command:
          - cat
            tty: true
            volumes:
        - name: docker                    #将宿主机 Docker 文件夹挂进容器,方便存储&拉取本地镜像
          hostPath:
            path: /usr/bin/docker
        - name: docker-sock               #将宿主机 Docker.sock 挂进容器
          hostPath:
            path: /var/run/docker.sock
        - name: docker-config             #将宿主机 Docker 配置挂在进入容器
          hostPath:
            path: /etc/docker
        - name: maven-m2                  #Maven 本地仓库挂在到 NFS 共享存储,方便不同节点能同时访问与存储
          nfs:
            server: 192.168.2.11
            path: "/nfs/data/maven-m2"
          
      #  nodeSelector:
          
      #    kubernetes.io/hostname: node-2-12
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59

# 4. Kubernetes Cli 插件配置及使用

# 4.1 配置凭据

配置连接 kubernetes 集群的凭据,这个凭据可以和上面 kubernetes 插件的凭据一致,都是用于连接 Kubernetes 集群。

img

# 4.2 Pipeline 脚本中使用

此插件主要功能就是提供执行 kubectl 的环境设置,在此插件方法中相当于有 kubectl、helm 等环境设置,然后用相关镜像就可以执行相关命令。

参考:https://jenkins.io/doc/pipeline/steps/kubernetes-cli/

// 提供 kubectl 执行的环境,其中得设置存储了 token 的凭据ID和 kubernetes api 地址
withKubeConfig([credentialsId: "xxxx-xxxx-xxxx-xxxx",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
    sh "kubectl get nodes"
}
1
2
3
4

# 5. Config File Provider 插件

# 5.1 配置 Maven settings.xml

在 Jenkins 安装时候安装了“config File Provider”插件,这个插件的作用就是提供在 Jenkins 中存储properties、xml、json、settings.xml 等信息,这里打开下面列表,配置一个全局的 Maven 的 settings.xml 文件。

系统管理—>Managed files—>Add a new Config—>Global Maven settings.xml

img

在里面添加一个全局的 setting.xml 设置,为了加快 jar 包的下载速度,这里将仓库地址指向 aliyun Maven 仓库地址。

<mirror>
    <id>alimaven</id>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>
</mirror>
1
2
3
4
5
6

# 5.2 Pipeline 脚本中使用

参考:https://jenkins.io/doc/pipeline/steps/config-file-provider/

可以在 Pipeline 脚本中,用于生成上面设置的文件,用法如下:

// 生成 settings.xml 文件,这个方法第一个参数是引用文件ID,第二个是生成的文件名
configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]) {
    // 只有在方法里面该文件才存在
    echo "cat settings.xml"
}
1
2
3
4
5

# 6. Pipeline Utility Steps 插件

# 6.1 功能描述

此插件将提供以下功能:

  • 提取Zip文件;
  • 创建Zip文件;
  • 创建一个普通文件;
  • 生成一个Yaml文件;
  • 编写maven项目文件;
  • 在工作区中查找文件;
  • 读取 properties 文件参数;
  • 从工作区中的文件中读取JSON;
  • 读取 maven 项目的 pom.xml 文件
  • ……

# 6.2 Pipeline 脚本中使用

这里主要是用此插件读取 pom.xml 的项目有关的参数,用于 docker 编译镜像时使用。另一个功能是在脚本进行时候用于生成文件,例如 yaml 文件、helm 证书等。

参考:https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/

// 读取 pom.xml 文件
pom = readMavenPom file: "./pom.xml"
echo "${pom.artifactId}:${pom.version}"
1
2
3

# 四. 测试插件

为了保证插件配置正确且值执行,在 kubernetes 环境下启动 Jenkins 代理执行任务,这里将进行测试。

# 1. 创建流水线任务

创建一个名为 “k8s-test” 的任务,类型选择“流水线”。

img

# 2. 配置流水线任务

# 2.1 常规配置

  • 为了安全,不允许并发构建。
  • 为了提升效率,这里设置 流水线效率,持久保存设置覆盖。

img

# 2.2 流水线脚本

这里写一个简单的脚本,将 Kubernetes 插件提供的 Pipeline 的方法引入,如下:

img

脚本内容:

// 代理名称,填写系统设置中设置的 Cloud 中 Template 模板的 label
def label = "jnlp-agent"

// 调用Kubernetes提供的方法
podTemplate(label: label,cloud: 'kubernetes' ){
    // 在代理节点上运行脚本
    node (label) {
        echo "测试 kubernetes 中 jenkins slave 代理!~"
    }
}
1
2
3
4
5
6
7
8
9
10

# 2.3 运行流水线任务

回到任务界面,点击立即构造来执行任务。

img

# 3. 查看流水线日志

然后点击执行历史栏中点击,查看控制台输出的日志信息。

img

img

# 五. 部署前准备

# 1. 配置文件存放位置比较

以下仅是个人看法,有更好的方式,希望告知。

这里涉及到一个问题,在 Jenkins 中,我们的 Jenkinsfile 脚本存放在哪比较方便,这里本人想到三种:

  • 1、新建 Git 项目,专门存放不同的 jenkinsfile 脚本,Jenkins 创建任务时候指定脚本存放的 Git 地址;
  • 2、放到各个项目中,当在执行 Jenkins 任务时候读取 Git项目,从中检测 jenkinsfile 脚本从而执行;
  • 3、每个脚本都放置到 Jenkins 每个任务的配置中,每次都执行配置中设置的脚本;

比较三者:

  • 第1种方式方便统一管理,一改动git上的配置,jenkins 任务的流水线脚本都会跟着变化;
  • 第2种方式可以针对每个项目单独设置,更灵活,就是不方便统一管理,维护需要各个项目组;
  • 第3种方式需要每次都新建项目时候在配置中设置脚本,比较费力不方便维护,不太推荐;

# 2. 设置配置文件到项目中

这里需要将将一些配置文件存入项目源码中,用于在执行流水线中读取对应的配置参数,比如:

  • SpringBoot源码: 用于测试的 helloworld 的SpringBoot项目。
  • Dockerfile: 用于 Docker 编译镜像的文件,比如打包的基础镜像等等。
  • values.yaml: 用于 Helm 启动的chart的配置文件,里面设置了一些chart的配置信息,告知该如何启动应用程序。

项目 Github 地址:https://github.com/my-dlq/springboot-helloworld

img

Dockerfile

FROM registry.cn-shanghai.aliyuncs.com/mydlq/openjdk:8u201-jdk-alpine3.9
VOLUME /tmp
ADD target/*.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS="-Xmx512M -Xms256M -Xss256k -Duser.timezone=Asia/Shanghai"
ENV APP_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
1
2
3
4
5
6
7

values.yaml

kind: Deployment
image:
  pullPolicy: "Always"
replicas: 1
resources:
  limits:
    memory: 512Mi
    cpu: 1000m
  requests:
    memory: 256Mi
    cpu: 500m

#***java && app 环境变量设置
env:

  - name: "JAVA_OPTS"
    value: "-Xmx512M -Xms355M -Xss256k -Duser.timezone=Asia/Shanghai"
  - name: "APP_OPTS"
    value: ""

envFrom:
  #- configMapRef:

#  name: env-config

service:
  type: NodePort                     #Service type设置 (可以设置为ClusterIP、NodePort、None)
  labels:
    svcEndpoints: actuator
  annotations: {}
  ports:
    - name: server
      port: 8080
      targetPort: 8080
      protocol: TCP
      nodePort: 30080
    - name: management
      port: 8081
      targetPort: 8081
      protocol: TCP
      nodePort: 30081
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 3. 测试运行环境是否可用

这里先写一个简单的脚本,用于测试各个环境是否都能用,例如slave镜像的git命令能否执行、maven镜像的mvn命令是否能用等等。

这里可以用 container(‘docker’) 方式,来引用 kubernetes 插件中设置的容器,利用各个容器不同的客户端功能,来执行对应的命令。

将之前创建的任务配置中的 pipeline 脚本改成下面:

def label = "jnlp-agent"

podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        stage('Git阶段'){
            echo "1、开始拉取代码"
            sh "git version"
        }
        stage('Maven阶段'){
            container('maven') {
                echo "2、开始Maven编译、推送到本地库"
                sh "mvn -version"
            }
        }
        stage('Docker阶段'){
            container('docker') {
                echo "3、开始读取Maven pom变量,并执行Docker编译、推送、删除"
                sh "docker version"
            }
        }
         stage('Helm阶段'){
            container('helm-kubectl') {
                echo "4、开始检测Kubectl环境,测试执行Helm部署,与执行部署"
                sh "helm version"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

jenkins slave 容器中默认集成 git 客户端,该整体流水线执行就在 Jenkins slave 容器中,任务默认在 Jenkins Slave 执行,所以不需要设置容器名称。

然后执行查看日志,日志内容如下:

Running on jnlp-agent-g7qk5 in /home/jenkins/workspace/k8s-test
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Git阶段)
[Pipeline] echo
1、开始拉取代码
[Pipeline] sh
+ git version
git version 2.11.0
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Maven阶段)
[Pipeline] container
[Pipeline] {
[Pipeline] echo
2、开始Maven编译、推送到本地库
[Pipeline] sh
+ mvn -version
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T18:41:47Z)
Maven home: /usr/share/maven
Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-1.8-openjdk/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-957.1.3.el7.x86_64", arch: "amd64", family: "unix"
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Docker阶段)
[Pipeline] container
[Pipeline] {
[Pipeline] echo
3、开始读取Maven pom变量,并执行Docker编译、推送、删除
[Pipeline] sh
+ docker version
Client:
Version:           18.06.2-ce
API version:       1.38
Go version:        go1.10.4
Git commit:        6d37f41
Built:             Sun Feb 10 03:43:40 2019
OS/Arch:           linux/amd64
Experimental:      false
Server: Docker Engine - Community
Engine:
Version:          18.09.3
API version:      1.39 (minimum version 1.12)
Go version:       go1.10.8
Git commit:       774a1f4
Built:            Thu Feb 28 06:02:24 2019
OS/Arch:          linux/amd64
Experimental:     false
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Helm阶段)
[Pipeline] container
[Pipeline] {
[Pipeline] echo
4、开始检测Kubectl环境,测试执行Helm部署,与执行部署
[Pipeline] sh
+ helm version
Client: &version.Version{SemVer:"v2.13.1", GitCommit:"618447cbf203d147601b4b9bd7f8c37a5d39fbb4", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.13.1", GitCommit:"79d07943b03aea2b76c12644b4b54733bc5958d6", GitTreeState:"clean"}
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

最后看见执行状态为 SUCCESS 则证明环境可用,否则有问题,请检测问题所在。

# 六. 开始写 Pipeline 脚本

这里进行分阶段性的脚本编写,然后一步步测试,最后合并在一起。这里新建一个名称为 k8s-pipeline 的任务,然后在配置项脚本框汇总输入 Pipleline 脚本。

# 1. Git 拉取

这里拉取本人 Github 上的一个简单的 SpringBoot Demo 项目进行实践。

Groovy脚本

def label = "jnlp-agent"

podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        stage('Git阶段'){
            echo "Git 阶段"
            git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
        }
    }
}
1
2
3
4
5
6
7
8
9
10

查看执行日志

......
Running on jnlp-agent-dhr1h in /home/jenkins/workspace/k8s-pipeline
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Git阶段)
[Pipeline] echo
1、开始拉取代码
[Pipeline] sh
+ git clone https://github.com/my-dlq/springboot-helloworld.git
  Cloning into 'springboot-helloworld'...
  [Pipeline] sh
+ ls -l
  total 0
  drwxr-xr-x 4 root root 79 Apr 28 07:00 springboot-helloworld
  [Pipeline] }
  ......
  Finished: SUCCESS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

可以通过控制台输出的日志看到,已经拉取成功。继续进行下一步,Maven 阶段。

# 2. Maven 编译

这里将进行 Maven 编译,将 Java 源码编译成一个 Jar 项目,方便后续打包进入 Docker 镜像。( Maven 中也可以进行单元测试,由于某些原因,这里不进行阐述,可以自己执行测试命令进行测试 )

Groovy脚本

def label = "jnlp-agent"

podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        stage('Git阶段'){
            echo "Git 阶段"
            git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
        }
        stage('Maven阶段'){
            container('maven') {
                //这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
                configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
                    sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

查看执行日志

......
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ springboot-helloword ---
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ springboot-helloword ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ springboot-helloword ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/jenkins/workspace/k8s-pipeline/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ springboot-helloword ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ springboot-helloword ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ springboot-helloword ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ springboot-helloword ---
[INFO] Building jar: /home/jenkins/workspace/k8s-pipeline/target/springboot-helloword-0.0.1.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.4.RELEASE:repackage (repackage) @ springboot-helloword ---
[INFO] Replacing main artifact with repackaged archive
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ springboot-helloword ---
[INFO] Installing /home/jenkins/workspace/k8s-pipeline/target/springboot-helloword-0.0.1.jar to /root/.m2/repository/club/mydlq/springboot-helloword/0.0.1/springboot-helloword-0.0.1.jar
[INFO] Installing /home/jenkins/workspace/k8s-pipeline/pom.xml to /root/.m2/repository/club/mydlq/springboot-helloword/0.0.1/springboot-helloword-0.0.1.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  7.989 s
[INFO] Finished at: 2019-04-28T09:38:03Z
[INFO] ------------------------------------------------------------------------
......
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 3. Docker 编译

Groovy脚本

def label = "jnlp-agent"

podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        stage('Git阶段'){
            echo "Git 阶段"
            git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
        }
        stage('Maven阶段'){
            echo "Maven 阶段"
            container('maven') {
                //这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
                configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
                    sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
                }
            }
        }
        stage('Docker阶段'){
            echo "Docker 阶段"
            container('docker') {
                // 读取pom参数
                echo "读取 pom.xml 参数"
                pom = readMavenPom file: './pom.xml'
                // 设置镜像仓库地址
                hub = "registry.cn-shanghai.aliyuncs.com"
                // 设置仓库项目名
                project_name = "mydlq"
                echo "编译 Docker 镜像"
                docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") {
                    echo "构建镜像"
                    // 设置推送到aliyun仓库的mydlq项目下,并用pom里面设置的项目名与版本号打标签
                    def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}")
                    echo "推送镜像"
                    customImage.push()
                    echo "删除镜像"
                    sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}"
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

查看执行日志

编译 Docker 镜像
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withDockerRegistry
Executing shell script inside container [docker] of pod [jnlp-agent-v6f1f]
Executing command: "docker" "login" "-u" "3******7@qq.com" "-p" ******** "http://registry.cn-shanghai.aliyuncs.com"  /home/jenkins/workspace/k8s-pipeline2@tmp/b52e213b-a730-4120-b004-decd8e16b246/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[Pipeline] {
[Pipeline] echo
构建镜像
[Pipeline] sh

+ docker build -t registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 .
  Sending build context to Docker daemon  18.37MB

Step 1/7 : FROM registry.cn-shanghai.aliyuncs.com/mydlq/openjdk:8u201-jdk-alpine3.9
 ---> 3675b9f543c5
Step 2/7 : VOLUME /tmp
 ---> Running in 7fc4af80e6ce
Removing intermediate container 7fc4af80e6ce
 ---> 4e4224d3b50b
Step 3/7 : ADD target/*.jar app.jar
 ---> 0c24118d522f
Step 4/7 : RUN sh -c 'touch /app.jar'
 ---> Running in 8836cb91e1ca
Removing intermediate container 8836cb91e1ca
 ---> 389e604851b6
Step 5/7 : ENV JAVA_OPTS="-Xmx512M -Xms256M -Xss256k -Duser.timezone=Asia/Shanghai"
 ---> Running in 5126902b1e6b
Removing intermediate container 5126902b1e6b
 ---> 055ad2b9c49d
Step 6/7 : ENV APP_OPTS=""
 ---> Running in cf8ea4b61eea
Removing intermediate container cf8ea4b61eea
 ---> 07dd4fdda44a
Step 7/7 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
 ---> Running in 93c4d0d859e1
Removing intermediate container 93c4d0d859e1
 ---> d29e092f2c17
Successfully built d29e092f2c17
Successfully tagged registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
[Pipeline] dockerFingerprintFrom
[Pipeline] echo
推送镜像
[Pipeline] sh

+ docker tag registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
  [Pipeline] sh
+ docker push registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
  The push refers to repository [registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword]
  8d45ad1172aa: Preparing
  aba126d6a94c: Preparing
  a464c54f93a9: Mounted from mydlq/openjdk
  dee6aef5c2b6: Mounted from mydlq/openjdk
  aba126d6a94c: Pushed
  8d45ad1172aa: Pushed
  0.0.1: digest: sha256:2c661931a3c08a1cad1562ec4936c68f06b4b3ffcec5de14c390ae793cf5b53b size: 1371
  [Pipeline] echo
  删除镜像
  [Pipeline] sh
+ docker rmi registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
  Untagged: registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
  Untagged: registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword@sha256:2c661931a3c08a1cad1562ec4936c68f06b4b3ffcec5de14c390ae793cf5b53b
  Deleted: sha256:d29e092f2c175a3662af0175e62462238583330ba7d46b84d89134056ba14027
  Deleted: sha256:07dd4fdda44a12d7749e5e7f60b1943c83e6d0a3da2e4f279ce4d53f3b04f27e
  ......
  Finished: SUCCESS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 4. Helm 启动应用

创建Helm执行方法

这里提前创建好执行 helm 的方法,将其简单封装一下用于执行流水线时候,调用此方法,执行对应的 Helm 操作。

  • 方法名:helmDeploy()

  • 可配参数:

    • init: 是否为执行 helm 初始化

    • url: 初始化 chart 仓库地址

    • dry: 是否为尝试部署

    • name: 部署的应用 Release 名

    • namespace: 应用启动到哪个Namespace

    • image: 镜像名

    • tag: 镜像标签

    • template: 选用的chart模板

// 执行Helm的方法
def helmDeploy(Map args) {
    // Helm 初始化
    if(args.init){
        sh "helm init --client-only --stable-repo-url ${args.url}"
    }
    // Helm 尝试部署
    else if (args.dry_run) {
        println "尝试 Helm 部署,验证是否能正常部署"
        sh "helm upgrade --install ${args.name} --namespace ${args.namespace} -f values.yaml --set ${args.repository},${args.tag} stable/${args.template} --dry-run --debug"
    }
    // Helm 正式部署
    else {
        println "正式 Helm 部署"
        sh "helm upgrade --install ${args.name} --namespace ${args.namespace} -f values.yaml --set ${args.repository},${args.tag} stable/${args.template}"
    }
}

// 方法调用
stage() {
    echo "Helm 初始化 http://chart.mydlq.club"
    helmDeploy(init: true ,url: "Helm 仓库地址");
    echo "Helm 尝试执行部署"
    helmDeploy(init: false ,dry: true ,name: "应用名" ,namespace: "应用启动的Namespace" ,image: "镜像名",tag: "镜像标签" ,template: "选用的chart模板")
    echo "Helm 正式执行部署"
    helmDeploy(init: false ,dry: false ,name: "应用名" ,namespace: "应用启动的Namespace" ,image: "镜像名",tag: "镜像标签" ,template: "选用的chart模板")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

完整Groovy脚本:

def label = "jnlp-agent"

// 执行Helm的方法
def helmDeploy(Map args) {
    if(args.init){
        println "Helm 初始化"
        sh "helm init --client-only --stable-repo-url ${args.url}"
    } else if (args.dry_run) {
        println "尝试 Helm 部署,验证是否能正常部署"
        sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template} --dry-run --debug"
    } else {
        println "正式 Helm 部署"
        sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template}"
    }
}

// jenkins slave 执行流水线任务
podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        stage('Git阶段'){
            echo "Git 阶段"
            git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
        }
        stage('Maven阶段'){
            echo "Maven 阶段"
            container('maven') {
                //这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
                configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
                    sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
                }
            }
        }
        stage('Docker阶段'){
            echo "Docker 阶段"
            container('docker') {
                // 读取pom参数
                echo "读取 pom.xml 参数"
                pom = readMavenPom file: './pom.xml'
                // 设置镜像仓库地址
                hub = "registry.cn-shanghai.aliyuncs.com"
                // 设置仓库项目名
                project_name = "mydlq"
                echo "编译 Docker 镜像"
                docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") {
                    echo "构建镜像"
                    // 设置推送到aliyun仓库的mydlq项目下,并用pom里面设置的项目名与版本号打标签
                    def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}")
                    echo "推送镜像"
                    customImage.push()
                    echo "删除镜像"
                    sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}"
                }
            }
        }
        stage('Helm阶段'){
            container('helm-kubectl') {
                withKubeConfig([credentialsId: "8510eda6-e1c7-4535-81af-17626b9575f7",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
                    // 设置参数
                    image = "image.repository=${hub}/${project_name}/${pom.artifactId}"
                    tag = "image.tag=${pom.version}"
                    template = "spring-boot"
                    repo_url = "http://chart.mydlq.club"
                    app_name = "${pom.artifactId}"
                    // 检测是否存在yaml文件
                    def values = ""
                    if (fileExists('values.yaml')) {
                        values = "-f values.yaml"
                    }
                    // 执行 Helm 方法
                    echo "Helm 初始化"
                    helmDeploy(init: true ,url: "${repo_url}");
                    echo "Helm 执行部署测试"
                    helmDeploy(init: false ,dry_run: true ,name: "${app_name}" ,namespace: "mydlqcloud" ,image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
                    echo "Helm 执行正式部署"
                    helmDeploy(init: false ,dry_run: false ,name: "${app_name}" ,namespace: "mydlqcloud",image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

查看执行日志

.....
Executing shell script inside container [helm-kubectl] of pod [jnlp-agent-r3c8h]
Executing command: "kubectl" "config" "set-cluster" "k8s" "--server=https://kubernetes.default.svc.cluster.local" "--insecure-skip-tls-verify=true"
exit
Cluster "k8s" set.
Executing shell script inside container [helm-kubectl] of pod [jnlp-agent-r3c8h]
Executing command: "kubectl" "config" "set-credentials" "cluster-admin" ********
Switched to context "k8s".
[Pipeline] {
[Pipeline] fileExists
[Pipeline] echo
Helm 初始化
[Pipeline] echo
Helm 初始化
[Pipeline] sh

+ helm init --client-only --stable-repo-url http://chart.mydlq.club
  Creating /root/.helm/repository/repositories.yaml
  Adding stable repo with URL: http://chart.mydlq.club
  Adding local repo with URL: http://127.0.0.1:8879/charts
  $HELM_HOME has been configured at /root/.helm.
  Not installing Tiller due to 'client-only' flag having been set
  Happy Helming!
  [Pipeline] echo
  Helm 执行部署测试
  [Pipeline] echo
  尝试 Helm 部署,验证是否能正常部署
  [Pipeline] sh
+ helm upgrade --install springboot-helloworld --namespace mydlqcloud -f values.yaml --set 'image.repository=registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloworld,image.tag=0.0.1' stable/spring-boot --dry-run --debug
  [debug] Created tunnel using local port: '37001'

[debug] SERVER: "127.0.0.1:37001"

[debug] Fetched stable/spring-boot to /root/.helm/cache/archive/spring-boot-1.0.4.tgz

Release "springboot-helloworld" does not exist. Installing it now.
[debug] CHART PATH: /root/.helm/cache/archive/spring-boot-1.0.4.tgz
[Pipeline] echo
Helm 执行正式部署
[Pipeline] echo
正式 Helm 部署
[Pipeline] sh

+ helm upgrade --install springboot-helloworld --namespace mydlqcloud -f values.yaml --set 'image.repository=registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloworld,image.tag=0.0.1' stable/spring-boot
  Release "springboot-helloworld" does not exist. Installing it now.
  NAME:   springboot-helloworld
  LAST DEPLOYED: Mon Apr 29 07:31:39 2019
  NAMESPACE: mydlqcloud
  STATUS: DEPLOYED

RESOURCES:
==> v1/Pod(related)
NAME                                    READY  STATUS             RESTARTS  AGE
springboot-helloworld-7cd66cf74d-vfjr6  0/1    ContainerCreating  0         0s

==> v1/Service
NAME                   TYPE      CLUSTER-IP   EXTERNAL-IP  PORT(S)                        AGE
springboot-helloworld  NodePort  10.10.87.61  <none>       8080:30080/TCP,8081:30081/TCP  0s

==> v1beta1/Deployment
NAME                   READY  UP-TO-DATE  AVAILABLE  AGE
springboot-helloworld  0/1    1           0          0s
......
Finished: SUCCESS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# 5. 测试接口

上面的 Helm步骤执行完成后,就可以进行简单测试了,其中此项目引用的chart是一个简单的 SpringBoot 项目,其中用 NodePort 方式暴露了两个端口,30080 & 30081,分别对应8080、8081俩个端口,提供了一个 Hello World 接口为“/hello”,所以我们这里访问一下这个接口地址:

http://192.168.2.11:30080/hello

img

# 七. 完善 Pipeline 脚本

# 1. 设置超时时间

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

格式:

timeout(time: 20, unit: 'SECONDS') {
    // 流水线代码
}
1
2
3

例子:设置超时时间为 60s 来让 Jenkins Slave 节点执行任务。

def label = "jnlp-agent"
timeout(time: 60, unit: 'SECONDS') {
    podTemplate(label: label,cloud: 'kubernetes' ){
        node (label) {
            stage('Git阶段'){
                echo "Git 阶段"
            }
            stage('Maven阶段'){
                echo "Maven 阶段"
            }
            stage('Docker阶段'){
                echo "Docker 阶段"
            }
            stage('Helm阶段'){
                echo "Helm 阶段"
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 2. 设置邮箱通知

设置邮箱开启 POP3/SMTP/IMAP 设置

img

安装 Email Extension Template 插件

img

配置系统默认邮件参数

系统管理->系统设置:配置“Jenkins Location”和“Extended E-mail Notification”,其中系统管理员邮件地址一定要和“User Name”值一致。

Jenkins Location 设置:

  • Jenkins URL: Jenkins 地址,用于发送邮件时写入内容之中
  • 系统管理员邮件地址: 邮件服务器账户

img

Extended E-mail Notification 设置:

  • SMTP server: smtp 邮箱服务的地址
  • Default user E-mail suffix: 邮件服务器后缀
  • User Name: 邮件服务器账户
  • Password: 邮件服务器 SMTP 授权码
  • Default Content Type: 设置邮件文本格式HTML
  • Enable Debug Mode: 启用 Debug 模式

img

创建一个流水线项目,用于写 Pipeline 脚本测试邮件发送,并配置 Pipeline 脚本。

这里写一个简单的 Pipeline 脚本,调用 emailext 方法执行发送邮件。

img

脚本内容:

def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
    node (label) {
        stage('Git阶段'){
            echo "Git 阶段"
        }
        stage('Maven阶段'){
            echo "Maven 阶段"
        }
        stage('Docker阶段'){
            echo "Docker 阶段"
        }
        stage('Helm阶段'){
            echo "Helm 阶段"
        }
        stage('email'){
            echo "测试发送邮件"
            emailext(subject: '任务执行失败',to: '324******47@qq.com',body: '''测试邮件内容...''')
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

运行项目查看日志,看是否执行发送操作以及运行状况。

Started by user admin
Running in Durability level: PERFORMANCE_OPTIMIZED
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/email-test
[Pipeline] {
[Pipeline] stage
[Pipeline] { (email)
[Pipeline] echo
测试发送邮件
[Pipeline] emailext
Sending email to: 32*****47@qq.com
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

查看邮件,可以看到已经收到设置邮箱发来的邮件信息。

img

# 3. 判断成功失败来发送邮件

# 3.1 流水线过程判断成功失败

这里加 try、catch、finally 进行流水线,当执行 finally 时候,进行判断此任务执行到此是否成功构建,如果成功,则发送成功邮件通知。如果失败,则发送失败邮件通知。

try{
    def label = "jnlp-agent"
    podTemplate(label: label,cloud: 'kubernetes' ){
        node (label) {
            stage('Git阶段'){
                echo "Git 阶段"
            }
            stage('Maven阶段'){
                echo "Maven 阶段"
            }
            stage('Docker阶段'){
                echo "Docker 阶段"
            }
            stage('Helm阶段'){
                echo "Helm 阶段"
            }
        }
    }
}catch(Exception e) {
    currentBuild.result = "FAILURE"
}finally {
    // 获取执行状态
    def currResult = currentBuild.result ?: 'SUCCESS'
    // 判断执行任务状态,根据不同状态发送邮件
    stage('email'){
        if (currResult == 'SUCCESS') {
            echo "发送成功邮件"
            emailext(subject: '任务执行成功',to: '32*****7@qq.com',body: '''任务已经成功构建完成...''')
        }else {
            echo "发送失败邮件"
            emailext(subject: '任务执行失败',to: '32*****7@qq.com',body: '''任务执行失败构建失败...''')
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 3.2 测试成功失败执行时发送邮件

成功:

让其正常成功跑完流程后发送邮件。

img

失败:

模拟故意执行错误发送邮件。

img

# 4. 将脚本放入到项目中

将脚本放入项目之中,方便后续调用时直接设置项目所在的Git地址即可。

img

# 八. 完整代码

完整代码如下:

// 执行Helm的方法
def helmDeploy(Map args) {
    if(args.init){
        println "Helm 初始化"
        sh "helm init --client-only --stable-repo-url ${args.url}"
    } else if (args.dry_run) {
        println "尝试 Helm 部署,验证是否能正常部署"
        sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template} --dry-run --debug"
    } else {
        println "正式 Helm 部署"
        sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template}"
    }
}

// jenkins slave 执行流水线任务
timeout(time: 600, unit: 'SECONDS') {
    try{
        def label = "jnlp-agent"
        podTemplate(label: label,cloud: 'kubernetes' ){
            node (label) {
                stage('Git阶段'){
                    echo "Git 阶段"
                    git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
                }
                stage('Maven阶段'){
                    echo "Maven 阶段"
                    container('maven') {
                        //这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
                        configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
                            sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
                        }
                    }
                }
                stage('Docker阶段'){
                    echo "Docker 阶段"
                    container('docker') {
                        // 读取pom参数
                        echo "读取 pom.xml 参数"
                        pom = readMavenPom file: './pom.xml'
                        // 设置镜像仓库地址
                        hub = "registry.cn-shanghai.aliyuncs.com"
                        // 设置仓库项目名
                        project_name = "mydlq"
                        echo "编译 Docker 镜像"
                        docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") {
                            echo "构建镜像"
                            // 设置推送到aliyun仓库的mydlq项目下,并用pom里面设置的项目名与版本号打标签
                            def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}")
                            echo "推送镜像"
                            customImage.push()
                            echo "删除镜像"
                            sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}"
                        }
                    }
                }
                stage('Helm阶段'){
                    container('helm-kubectl') {
                        withKubeConfig([credentialsId: "8510eda6-e1c7-4535-81af-17626b9575f7",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
                            // 设置参数
                            image = "image.repository=${hub}/${project_name}/${pom.artifactId}"
                            tag = "image.tag=${pom.version}"
                            template = "spring-boot"
                            repo_url = "http://chart.mydlq.club"
                            app_name = "${pom.artifactId}"
                            // 检测是否存在yaml文件
                            def values = ""
                            if (fileExists('values.yaml')) {
                                values = "-f values.yaml"
                            }
                            // 执行 Helm 方法
                            echo "Helm 初始化"
                            helmDeploy(init: true ,url: "${repo_url}");
                            echo "Helm 执行部署测试"
                            helmDeploy(init: false ,dry_run: true ,name: "${app_name}" ,namespace: "mydlqcloud" ,image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
                            echo "Helm 执行正式部署"
                            helmDeploy(init: false ,dry_run: false ,name: "${app_name}" ,namespace: "mydlqcloud",image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
                        }
                    }
                }
            }
        }
    }catch(Exception e) {
        currentBuild.result = "FAILURE"
    }finally {
        // 获取执行状态
        def currResult = currentBuild.result ?: 'SUCCESS'
        // 判断执行任务状态,根据不同状态发送邮件
        stage('email'){
            if (currResult == 'SUCCESS') {
                echo "发送成功邮件"
                emailext(subject: '任务执行成功',to: '32******7@qq.com',body: '''任务已经成功构建完成...''')
            }else {
                echo "发送失败邮件"
                emailext(subject: '任务执行失败',to: '32******7@qq.com',body: '''任务执行失败构建失败...''')
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
上次更新: 2022/10/05, 02:02:36
Jenkins添加健康检查
mysql

← Jenkins添加健康检查 mysql→

最近更新
01
ctr和crictl显示镜像不一致
03-13
02
alpine镜像集成常用数据库客户端
03-13
03
create-cluster
02-26
更多文章>
Theme by Vdoing | Copyright © 2015-2024 op81.com
苏ICP备18041258号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式