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
# 一. 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
2
3
关闭并禁用SELinux
# 若当前启用了 SELinux 则需要临时设置其当前状态为 permissive
$ setenforce 0
# 编辑/etc/sysconfig selinux 文件,以彻底禁用 SELinux
$ sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config
2
3
4
5
# 查看selinux状态
$ getenforce
如果selinux状态为permissive,则执行reboot重新启动即可
2
3
4
# 安装nfs-utils和rpcbind
$ yum install -y nfs-utils rpcbind
# 创建存储的文件夹
# 创建文件夹
$ mkdir /nfs
# 更改归属组与用户
$ chown -R nfsnobody.nfsnobody /nfs
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)
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
nfs:
$ systemctl enable nfs
$ systemctl restart nfs
2
# 查看是否有可用的NFS地址
$ showmount -e 192.168.200.104
# 1.1.2 客户端配置
# 安装nfs-utils和rpcbind
$ yum install -y nfs-utils rpcbind
# 创建挂载的文件夹
$ mkdir -p /nfs-data
# 挂载nfs
$ mount -t nfs -o nolock,vers=4 192.168.200.104:/nfs /nfs-data
参数解释:
- mount:挂载命令
- -o:挂载选项
- nfs :使用的协议
- nolock :不阻塞
- vers : 使用的NFS版本号
- IP : NFS服务器的IP(NFS服务器运行在哪个系统上,就是哪个系统的IP)
- /nfs: 要挂载的目录(Ubuntu的目录)
- /nfs-data : 要挂载到的目录(开发板上的目录,注意挂载成功后,/mnt下原有数据将会被隐藏,无法找到)
查看挂载
$ df -h
卸载挂载
$ umount /nfs-data
查看nfs版本
# 查看nfs服务端信息
$ nfsstat -s
# 查看nfs客户端信息
$ nfsstat -c
2
3
4
5
# 1.2 挂载 NFS 并设置存储文件夹
如果不能直接操作 NFS 服务端创建文件夹,需要知道 NFS 服务器地址,然后将其挂在到本地目录,进入其中创建 Jenkins 目录空间。
挂载 NFS
$ mount -o vers=4.1 192.168.200.104:/nfs/ /nfs
在 NFS 共享存储文件夹下创建存储 Jenkins 数据的文件夹
$ mkdir -p /nfs/data/jenkins
# 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
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
# 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
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
# 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
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
# 5. 获取 Jenkins 生成的 Token
在安装 Jenkins 时候,它默认生成一段随机字符串,用于安装验证。这里访问它的安装日志,获取它生成的 Token 字符串。
查看 Jenkins Pod 启动日志
$ kubectl log $(kubectl get pods | awk '{print $1}' | grep jenkins)
查看日志中生成的 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
*************************************************************
*************************************************************
*************************************************************
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,进入后看到以下页面:
# 6.2 安装插件
选择自定义插件来进行安装
安装一些常用的插件
确定后可以看到正在安装插件界面
# 6.3 设置用户名、密码
# 6.4 进入 Jenkins 界面
# 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:上面地址替换成你们的集群地址及端口。
# 二. 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 一般需要配置一个凭据用于验证,如果是公开项目,则无需任何配置。
凭据->系统->全局凭据->添加凭据
# 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"
# 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"
}
2
3
4
5
6
7
8
9
# 3. Kubernetes 插件配置及使用
# 3.1 配置凭据
配置连接 kubernetes 集群的凭据(Kubernetes ServiceAccount token),此凭据的账户权限最好设置较大点,避免出现未知问题。配置完成后,需要在后面的 Cloud 云配置中设置这个凭据。
凭据->系统->全局凭据->添加凭据,类型选择secret text
# 3.2 云配置
系统管理—>系统设置—>云
参考: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 基本配置:
名称: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 集群。
# 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"
}
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
在里面添加一个全局的 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>
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"
}
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}"
2
3
# 四. 测试插件
为了保证插件配置正确且值执行,在 kubernetes 环境下启动 Jenkins 代理执行任务,这里将进行测试。
# 1. 创建流水线任务
创建一个名为 “k8s-test” 的任务,类型选择“流水线”。
# 2. 配置流水线任务
# 2.1 常规配置
- 为了安全,不允许并发构建。
- 为了提升效率,这里设置 流水线效率,持久保存设置覆盖。
# 2.2 流水线脚本
这里写一个简单的脚本,将 Kubernetes 插件提供的 Pipeline 的方法引入,如下:
脚本内容:
// 代理名称,填写系统设置中设置的 Cloud 中 Template 模板的 label
def label = "jnlp-agent"
// 调用Kubernetes提供的方法
podTemplate(label: label,cloud: 'kubernetes' ){
// 在代理节点上运行脚本
node (label) {
echo "测试 kubernetes 中 jenkins slave 代理!~"
}
}
2
3
4
5
6
7
8
9
10
# 2.3 运行流水线任务
回到任务界面,点击立即构造来执行任务。
# 3. 查看流水线日志
然后点击执行历史栏中点击,查看控制台输出的日志信息。
# 五. 部署前准备
# 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
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" ]
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
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"
}
}
}
}
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
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"
}
}
}
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
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"
}
}
}
}
}
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
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}"
}
}
}
}
}
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
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模板")
}
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}")
}
}
}
}
}
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
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
# 七. 完善 Pipeline 脚本
# 1. 设置超时时间
设置任务超时时间,如果在规定时间内任务没有完成,则进行失败操作。
格式:
timeout(time: 20, unit: 'SECONDS') {
// 流水线代码
}
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 阶段"
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2. 设置邮箱通知
设置邮箱开启 POP3/SMTP/IMAP 设置
安装 Email Extension Template 插件
配置系统默认邮件参数
系统管理->系统设置:配置“Jenkins Location”和“Extended E-mail Notification”,其中系统管理员邮件地址一定要和“User Name”值一致。
Jenkins Location 设置:
- Jenkins URL: Jenkins 地址,用于发送邮件时写入内容之中
- 系统管理员邮件地址: 邮件服务器账户
Extended E-mail Notification 设置:
- SMTP server: smtp 邮箱服务的地址
- Default user E-mail suffix: 邮件服务器后缀
- User Name: 邮件服务器账户
- Password: 邮件服务器 SMTP 授权码
- Default Content Type: 设置邮件文本格式HTML
- Enable Debug Mode: 启用 Debug 模式
创建一个流水线项目,用于写 Pipeline 脚本测试邮件发送,并配置 Pipeline 脚本。
这里写一个简单的 Pipeline 脚本,调用 emailext 方法执行发送邮件。
脚本内容:
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: '''测试邮件内容...''')
}
}
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
查看邮件,可以看到已经收到设置邮箱发来的邮件信息。
# 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: '''任务执行失败构建失败...''')
}
}
}
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 测试成功失败执行时发送邮件
成功:
让其正常成功跑完流程后发送邮件。
失败:
模拟故意执行错误发送邮件。
# 4. 将脚本放入到项目中
将脚本放入项目之中,方便后续调用时直接设置项目所在的Git地址即可。
# 八. 完整代码
完整代码如下:
// 执行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: '''任务执行失败构建失败...''')
}
}
}
}
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