Jenkins基于k8s容器化构建Pipline

jenkins使用kubernetes插件可以实现,动态创建流水线相关的任务Pod在流水线执行结束后会删除相应的任务Pod以达到资源的释放。

具体kubernetes的agent使用详解

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
pipeline {
agent {
kubernetes {
cloud 'kubernetes' //这里需要指定相关jenkins中创建的kubernetes对接信息的名称
slaveConnectTimeout 1200 //超时配置
workspaceVolume emptyDirWorkspaceVolume() //jenkins的工作目录,必须设置起到一个Pod中不同container的目录共享jenkins工作目录
yaml ''' //这里以下都是Pod定义信息
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.15/kubernetes/jnlp:alpine'
name: jnlp //jnlp容器是必须的他负责连接jenkins,这里保持默认使用即可
imagePullPolicy: IfNotPresent
//以下容器为具体的工作容器,所有流水线中的任何阶段的任务都在容器中执行,可以定义多个在流水线中指定任务使用那个容器进行执行
- command: //所有容器推荐使用cat命令保证容器在启动后保持运行不退出
- "cat"
tty: true //保持tty,起到容器不退出
image: "192.168.10.254:5000/bash/alpine:latest"
imagePullPolicy: "IfNotPresent"
name: "echo" //container的名称
restartPolicy: Never
// 在k8s上的节点上打上label,这里的nodeselector就是选择打了对应标签的node
nodeSelector:
build: true
'''
}
}
//具体流水线配置
stages {
//这里为流水线定义
stage('echo') { //stage名称
steps {
container(name: 'echo') { //这里定义这个步骤使用那个container进行执行,指定container的名称
sh "echo hello word"
}
}
}
}

1.1 相关镜像准备

有些镜像需自己准备,如docker镜像用于构建docker镜像,kubectl镜像用于连接k8s进行服务更新。其余镜像使用官方镜像即可,但是前提是必须要有一个jnlp的镜像,用于连接jenkins的master。

1.docker镜像

镜像构建

1
2
3
4
5
6
#直接使用alpine镜像拷贝docker二进制执行文件到容器即可
[17:38:58 root@nexus docker]#cat Dockerfile
FROM 192.168.10.254:5000/bash/alpine:latest
COPY docker /usr/bin/
#构建
docker build -t 192.168.10.254:5000/kubernetes/docker:alpine .

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
pipeline {
agent {
kubernetes {
cloud 'kubernetes'
slaveConnectTimeout 1200
workspaceVolume emptyDirWorkspaceVolume() // 相当于k8s卷当中的emptdir()
yaml '''
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.15/kubernetes/jnlp:alpine'
name: jnlp
imagePullPolicy: IfNotPresent
- command:
- "cat"
image: "192.168.10.15/kubernetes/docker:alpine"
imagePullPolicy: "IfNotPresent"
name: "docker"
tty: true
// 将本地的docker.sock映射到容器当中,容器当中就能使用docker相关的命令,相当于是操作宿主机本地的docker,构建好的镜像也是本地的。
volumeMounts:
- mountPath: "/var/run/docker.sock"
name: "dockersock"
readOnly: false
volumes:
#注意docker容器必须被调度到存在docker的k8s节点,并且挂载主机的docker.sock文件到容器
- hostPath:
path: "/var/run/docker.sock"
name: "dockersock"
restartPolicy: Never
nodeSelector:
build: true
'''
}
}
stages {
stage('docker info') {
steps {
// 使用k8s 当中的docker 镜像容器进行操作
container(name: 'docker') {
sh "docker info" //执行docker info有正常输出即可
}
}
}
}
}

2.kubectl镜像

镜像构建

1
2
3
4
5
6
[17:46:21 root@nexus kubectl]#cat Dockerfile 
FROM 192.168.10.254:5000/bash/alpine:latest
# 这里复制的是一个kubelet的二进制文件,配合kubeconfig进行使用
COPY kubectl /usr/bin/
#构建
[17:46:01 root@nexus kubectl]#docker build -t 192.168.10.254:5000/kubernetes/kubectl:alpine .

使用验证

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
pipeline {
agent {
kubernetes {
cloud 'kubernetes' //这里需要指定相关jenkins中创建的kubernetes对接信息的名称
slaveConnectTimeout 1200 //超时配置
workspaceVolume emptyDirWorkspaceVolume() //jenkins的工作目录,必须设置起到一个Pod中不同container的目录共享jenkins工作目录
yaml '''
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.15/kubernetes/jnlp:alpine'
name: jnlp
imagePullPolicy: IfNotPresent
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/kubectl:alpine"
imagePullPolicy: "IfNotPresent"
name: "kubectl"
tty: true
restartPolicy: Never
nodeSelector:
build: true
'''
}
}
//具体流水线配置
stages {
//这里为流水线定义
stage('kubectl get') { //stage名称
// 使用credentials加载kubeconfig配置文件
environment {
MY_KUBECONFIG = credentials('kubernetes-cluster')
}
steps {
container(name: 'kubectl') { //这里定义这个步骤使用那个container进行执行,指定container的名称
sh "kubectl get pod -A --kubeconfig $MY_KUBECONFIG"
}
}
}
}
}

二、自动化构建 Java 应用

1.流水线结构

48616-wxdhj40oko.png
注意这里的Jenkinsfile与Dockerfile文件都存放在git仓库中。java应用会使用mvn进行打包,mvn会下载一系列依赖的包,默认会下载到mvn容器的**/root/.m2**目录最好使用volume进行持久化。

2.需要克隆开源代码到自己gitlab仓库

仓库地址:https://github.com/AdlerED/bolo-solo
68302-21ztfgmuost.png

3.Jenkinsfile文件

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
pipeline {
//顶层环境变量设置
environment {
namespace = "bolo" //服务部署在那个namespace中
registries = "192.168.10.15/bolo" //生成镜像存放镜像的仓库地址
GIT = "[email protected]:kubernetes/bolo-solo.git" //代码仓库地址
TAG = "" //镜像tag,会在下面生成,这里只是定义全局变量
NANE = "" //jenkins项目名称,会在下面生成,这里只是定义全局变量
}
//全局配置
options {
timestamps() //所有输出每行都会打印时间戳
buildDiscarder(logRotator(numToKeepStr: '5')) //保留5个历史构建版本
}
//手动构建时选择分支参数
parameters {
gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
}
//agent配置
agent {
kubernetes {
cloud 'kubernetes'
slaveConnectTimeout 1200
workspaceVolume emptyDirWorkspaceVolume() //这里使用临时目录共享jenkins的工作目录默认路径为/home/jenkins/agent
yaml '''
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.254:5000/kubernetes/jnlp:alpine'
name: jnlp #这个容器必须有,保持默认即可
imagePullPolicy: IfNotPresent
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/maven:3.8.5-openjdk-8-slim"
imagePullPolicy: "IfNotPresent"
name: "maven" #maven打包镜像
volumeMounts: #持久化依赖包,重复构建不会进行重复下载
- mountPath: "/root/.m2"
name: mvn-data
tty: true
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/docker:alpine"
imagePullPolicy: "IfNotPresent"
name: "docker" #docker容器需要挂载docker.sock文件,需要调度到有docker的node节点
tty: true
volumeMounts:
- mountPath: "/var/run/docker.sock"
name: "dockersock"
readOnly: false
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/kubectl:apline"
imagePullPolicy: "IfNotPresent"
name: "kubectl" #kubectl镜像
tty: true
volumes:
- name: mvn-data
# 绑定已有的pvc用于加速maven的文件下载,相当于是maven的缓存
persistentVolumeClaim:
claimName: mvn
- hostPath:
path: "/var/run/docker.sock"
name: "dockersock"
restartPolicy: Never
nodeSelector: #这里需要给有docker的node节点打标签调度Pod到这个节点
build: true
'''
}
}
//具体流水线配置
stages {
//克隆代码
stage('git clone') {
//并行执行
failFast true //并行执行的分支只要有一个失败立即结束流水线
parallel {
//手动执行jenkins流水线
stage('git clone by Jenkins') {
when {
expression {
// when表达式,当中环境变量当中的gitbranch为null的时候执行下面的steps
env.gitlabBranch == null
}
}
steps {
// 在parameters 当中指定了对应的branch,使用gitlab-key的拉取凭证。
git branch: "${BRANCH}", credentialsId: 'gitlab-key', url: "${GIT}"
script {
// returnStdout 返回标准输出,也就是执行结果 echo -n 表示不换行
TAG = sh(returnStdout: true, script: "echo -n ${BRANCH}-${env.BUILD_ID}")
}
}
}
//gitlab触发构建
stage('git clone trigger') {
when {
expression {
// 如果在构建的时候给定了branch的构建参数
env.gitlabBranch != null
}
}
steps {
git branch: "${env.gitlabBranch}", credentialsId: 'gitlab-key', url: "${GIT}"
script {
TAG = sh(returnStdout: true, script: "echo -n ${env.gitlabBranch}-${env.BUILD_ID}")
}
}
}
//初始化项目名称,项目名称用于docker镜像名称,不能有大写字母,转义
stage('init env') {
steps {
script {
// 对编译的job名称进行小写
NAME = sh(returnStdout: true, script: "echo -n ${env.JOB_NAME}").toLowerCase()
}
sh "echo ${NAME}"
}
}
}
}
//打包java程序
stage('mvn build') {
steps {
container(name: 'maven') {
sh "mvn package -DskipTests -Pci"
sh "ls -l"
}
}
}
//构建镜像并且推送镜像仓库
stage('docker build') {
environment {
HARBOR_USER = credentials('harbor-account') //获取镜像仓库认证信息
}
steps {
// 本步是使用了上面agent当中初始化好的docker镜像
container(name: 'docker') {
sh "docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${registries}"
// 这里的build 要基于下面的dockerfile进行构建
sh "docker build -t ${registries}/${NAME}:${TAG} ."
sh "docker push ${registries}/${NAME}:${TAG}"
}
}
}
//更新k8s相关应用
stage('update deploy') {
environment {
// 加载kubeconfig到环境变量当中
MY_KUBECONFIG = credentials('kubernetes-cluster')
}
steps {
container(name: 'kubectl') {
// 这里的部署是使用deploy先部署好的,然后进行镜像更新,实现自动构建部署,deploy 文件内容在下面
sh "kubectl get deploy -n ${namespace} -l image=${NAME} --kubeconfig $MY_KUBECONFIG"
// 指定使用新的镜像进行更新
sh "kubectl set image deploy -n ${namespace} -l image=${NAME} ${NAME}=${registries}/${NAME}:${TAG} --kubeconfig $MY_KUBECONFIG"
// 重新启动deployment,让deploy控制器控制的pod加载新打包的镜像。
sh "kubectl rollout status deployment -n ${namespace} ${NAME} --timeout=60s --kubeconfig $MY_KUBECONFIG"
}
}
}
}
}

4.Dockerfile文件

1
2
3
4
5
6
7
FROM 192.168.10.254:5000/kubernetes/openjdk:8-alpine
WORKDIR /opt/bolo/
COPY target/bolo /opt/bolo
COPY src/main/resources/docker /opt/bolo/WEB-INF/classes/
ENV TZ=Asia/Shanghai
EXPOSE 8080
ENTRYPOINT [ "java", "-cp", "WEB-INF/lib/*:WEB-INF/classes", "org.b3log.solo.Starter" ]

5.创建流水线

创建流水线
35816-khesyz80aa.png
进行配置
05790-0xcztgh1phkq.png
第一次需进行构建读取jenkinsfile文件,会报错

6.部署应用进行测试

部署相关应用

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
apiVersion: apps/v1 
kind: Deployment
metadata:
name: bolo
namespace: bolo
labels:
image: bolo #jenkinsfile中更新我使用lable进行筛选,这里必须设置
spec:
replicas: 1
selector:
matchLabels:
app: bolo
template:
metadata:
creationTimestamp: null
labels:
app: bolo
spec:
containers:
- name: bolo
image: 192.168.10.15/bolo/bolo:jenkins-bolo-23
args:
- --listen_port=8080
- --server_scheme=http
- --server_host=192.168.10.13
ports:
- name: http
containerPort: 8080
protocol: TCP
env: #这里如果要正常使用需部署mysql数据库
- name: RUNTIME_DB
value: "MYSQL"
- name: JDBC_DRIVER
value: "com.mysql.cj.jdbc.Driver"
- name: JDBC_URL
value: "jdbc:mysql://mysql:3306/bolo?useUnicode=yes&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true"
- name: JDBC_USERNAME
value: "bolo"
- name: JDBC_PASSWORD
value: "123456"
---
apiVersion: v1
kind: Service
metadata:
name: bolo
namespace: bolo
spec:
ports:
- name: mysql
port: 8080
protocol: TCP
targetPort: http
selector:
app: bolo
type: ClusterIP

测试手动执行流水线
05602-zftwmqilwed.png

三、自动化构建 Vue/H5 前端应用

1.流水线结构

42342-e9ltpyddna5.png
注意这里的Jenkinsfile与Dockerfile文件都存放在git仓库中。Vue应用会使用npm进行打包,npm会下载一系列依赖的包,默认会下载到npm容器的运行npm的目录node_modules中所以为了重复构建不进行重复下载需要持久化,这里推荐直接持久化jenkins工作目录即可。

2.需要克隆开源代码到自己gitlab仓库

源码仓库:https://github.com/SD-Gaming/Vue3-Todo-List-AddName

3.Jenkinsfile文件

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
pipeline {
//顶层环境变量设置
environment {
namespace = "bolo" //服务部署在那个namespace中
registries = "192.168.10.15/vue" //镜像仓库地址
GIT = "[email protected]:root/Vue3-Todo-List-AddName.git" //代码仓库地址
TAG = "" //镜像tag,会在下面生成,这里只是定义全局变量
NANE = "" //jenkins项目名称,会在下面生成,这里只是定义全局变量
}
//全局配置
options {
timestamps() //所有输出每行都会打印时间戳
buildDiscarder(logRotator(numToKeepStr: '5')) //保留5个历史构建版本
}
//手动构建时选择分支参数
parameters {
gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
}
//agent配置
agent {
kubernetes {
cloud 'kubernetes'
slaveConnectTimeout 1200
workspaceVolume persistentVolumeClaimWorkspaceVolume(claimName: "npm-data", mountPath: "/", readOnly: "false")
yaml '''
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.254:5000/kubernetes/jnlp:alpine'
name: jnlp
imagePullPolicy: IfNotPresent
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/node:latest"
imagePullPolicy: "IfNotPresent"
name: "nodejs"
tty: true
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/docker:alpine"
imagePullPolicy: "IfNotPresent"
name: "docker"
tty: true
volumeMounts:
- mountPath: "/var/run/docker.sock"
name: "dockersock"
readOnly: false
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/kubectl:apline"
imagePullPolicy: "IfNotPresent"
name: "kubectl"
tty: true
volumes:
- hostPath:
path: "/var/run/docker.sock"
name: "dockersock"
restartPolicy: Never
nodeSelector:
build: true
'''
}
}
//具体流水线配置
stages {
//克隆代码
stage('git clone') {
//并行执行
failFast true //并行执行的分支只要有一个失败立即结束流水线
parallel {
//手动执行jenkins流水线
stage('git clone by Jenkins') {
when {
expression {
// 手动构建的时候,在环境变量当中,branch为空,所以就走手动构建的部分
env.gitlabBranch == null
}
}
steps {
git branch: "${BRANCH}", credentialsId: 'gitlab-key', url: "${GIT}"
script {
// 标准输出构建tag信息
TAG = sh(returnStdout: true, script: "echo -n ${BRANCH}-${env.BUILD_ID}")
}
}
}
//gitlab触发构建
stage('git clone trigger') {
when {
expression {
env.gitlabBranch != null
}
}
steps {
git branch: "${env.gitlabBranch}", credentialsId: 'gitlab-key', url: "${GIT}"
script {
TAG = sh(returnStdout: true, script: "echo -n ${env.gitlabBranch}-${env.BUILD_ID}")
}
}
}
//初始化项目名称,项目名称用于docker镜像名称,不能有大写字母,转义
stage('init env') {
steps {
script {
NAME = sh(returnStdout: true, script: "echo -n ${env.JOB_NAME}").toLowerCase()
}
sh "echo ${NAME}"
}
}
}
}
stage('npm build') {
steps {
// 在nodejs 容器内
container(name: 'nodejs') {
sh "npm install --registry=https://registry.npm.taobao.org"
sh "npm run build"
sh "ls -l"
}
}
}
stage('docker build') {
environment {
HARBOR_USER = credentials('harbor-account')
}
steps {
container(name: 'docker') {
sh "docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${registries}"
sh "docker build -t ${registries}/${NAME}:${TAG} ."
sh "docker push ${registries}/${NAME}:${TAG}"
}
}
}
stage('update deploy') {
environment {
MY_KUBECONFIG = credentials('kubernetes-cluster')
}
steps {
container(name: 'kubectl') {
sh "kubectl get deploy -n ${namespace} -l image=${NAME} --kubeconfig $MY_KUBECONFIG"
sh "kubectl set image deploy -n ${namespace} -l image=${NAME} ${NAME}=${registries}/${NAME}:${TAG} --kubeconfig $MY_KUBECONFIG"
sh "kubectl rollout status deployment -n ${namespace} ${NAME} --timeout=60s --kubeconfig $MY_KUBECONFIG"
}
}
}
}
}

4.Dockerfile文件

1
2
3
4
5
FROM 192.168.10.254:5000/bash/nginx:latest
WORKDIR /usr/share/nginx/html/
COPY dist/ /usr/share/nginx/html/
ENV TZ=Asia/Shanghai
EXPOSE 80

5.创建流水线

57168-zkvjh6cmzl.png

6.部署应用进行测试

部署应用

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
apiVersion: apps/v1 
kind: Deployment
metadata:
name: vue
namespace: bolo
labels:
image: vue #这里必须定义为镜像名称
spec:
replicas: 1
selector:
matchLabels:
app: vue
template:
metadata:
creationTimestamp: null
labels:
app: vue
spec:
containers:
- name: vue
image: 192.168.10.15/vue/vuevue3-addname:main-6
ports:
- name: http
containerPort: 80
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: vue
namespace: bolo
spec:
ports:
- name: vue
port: 80
protocol: TCP
targetPort: http
selector:
app: vue
type: NodePort

测试流水线
61641-b97abzj01ir.png

四、自动化构建 Golang 项目

1.流水线结构

48032-5u3ropw0if.png
注意这里的Jenkinsfile与Dockerfile文件都存放在git仓库中。go应用会使用go build进行编译,会下载一系列依赖的包,默认会下载到容器的**/opt/pkg**目录中所以为了重复构建不进行重复下载需要持久化。

2.需要克隆开源代码到自己gitlab仓库

源码仓库:https://gitee.com/dukuan/go-project.git

3.Jenkinsfile文件

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
pipeline {
//顶层环境变量设置
environment {
namespace = "bolo" //服务部署在那个namespace中
registries = "192.168.10.15/go" //镜像仓库地址
GIT = "[email protected]:root/go-project.git" //代码仓库地址
TAG = "" //镜像tag,会在下面生成,这里只是定义全局变量
NANE = "" //jenkins项目名称,会在下面生成,这里只是定义全局变量
}
//全局配置
options {
timestamps() //所有输出每行都会打印时间戳
buildDiscarder(logRotator(numToKeepStr: '5')) //保留5个历史构建版本
}
//手动构建时选择分支参数
parameters {
gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
}
//agent配置
agent {
kubernetes {
cloud 'kubernetes'
slaveConnectTimeout 1200
workspaceVolume emptyDirWorkspaceVolume()
yaml '''
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.254:5000/kubernetes/jnlp:alpine'
name: jnlp
imagePullPolicy: IfNotPresent
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/golang:1.18.3"
imagePullPolicy: "IfNotPresent"
name: "go"
# 对应go的依赖
volumeMounts:
# go 容器内的路径
- mountPath: "/go/pkg"
name: go-pkg-data
tty: true
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/docker:alpine"
imagePullPolicy: "IfNotPresent"
name: "docker"
tty: true
volumeMounts:
- mountPath: "/var/run/docker.sock"
name: "dockersock"
readOnly: false
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/kubectl:apline"
imagePullPolicy: "IfNotPresent"
name: "kubectl"
tty: true
volumes:
- name: go-pkg-data
persistentVolumeClaim:
claimName: go
- hostPath:
path: "/var/run/docker.sock"
name: "dockersock"
restartPolicy: Never
# 把创建的容器调度到有标签build:true的节点上
nodeSelector:
build: true
'''
}
}
//具体流水线配置
stages {
//克隆代码
stage('git clone') {
//并行执行
failFast true //并行执行的分支只要有一个失败立即结束流水线
parallel {
//手动执行jenkins流水线
stage('git clone by Jenkins') {
when {
expression {
env.gitlabBranch == null
}
}
steps {
git branch: "${BRANCH}", credentialsId: 'gitlab-key', url: "${GIT}"
script {
TAG = sh(returnStdout: true, script: "echo -n ${BRANCH}-${env.BUILD_ID}")
}
}
}
//gitlab触发构建
stage('git clone trigger') {
when {
expression {
env.gitlabBranch != null
}
}
steps {
git branch: "${env.gitlabBranch}", credentialsId: 'gitlab-key', url: "${GIT}"
script {
TAG = sh(returnStdout: true, script: "echo -n ${env.gitlabBranch}-${env.BUILD_ID}")
}
}
}
//初始化项目名称,项目名称用于docker镜像名称,不能有大写字母,转义
stage('init env') {
steps {
script {
NAME = sh(returnStdout: true, script: "echo -n ${env.JOB_NAME}").toLowerCase()
}
sh "echo ${NAME}"
}
}
}
}
stage('build') {
steps {
container(name: 'go') {
sh "export GO111MODULE=on"
sh "go env -w GOPROXY=https://goproxy.cn,direct"
sh "go mod tidy"
sh "go build"
sh "ls -l"
}
}
}
stage('docker build') {
environment {
HARBOR_USER = credentials('harbor-account')
}
steps {
container(name: 'docker') {
sh "docker login -u ${HARBOR_USER_USR} -p ${HARBOR_USER_PSW} ${registries}"
sh "docker build -t ${registries}/${NAME}:${TAG} ."
sh "docker push ${registries}/${NAME}:${TAG}"
}
}
}
stage('update deploy') {
environment {
MY_KUBECONFIG = credentials('kubernetes-cluster')
}
steps {
container(name: 'kubectl') {
sh "kubectl get deploy -n ${namespace} -l image=${NAME} --kubeconfig $MY_KUBECONFIG"
sh "kubectl set image deploy -n ${namespace} -l image=${NAME} ${NAME}=${registries}/${NAME}:${TAG} --kubeconfig $MY_KUBECONFIG"
sh "kubectl rollout status deployment -n ${namespace} ${NAME} --timeout=60s --kubeconfig $MY_KUBECONFIG"
}
}
}
}
}

4.Dockerfile文件

1
2
3
4
5
6
7
8
FROM 192.168.10.254:5000/bash/alpine-glibc:glibc-2.35
WORKDIR /opt/workdir/
# go-project是jenkins的workspace的名称
COPY go-project /opt/workdir/
COPY conf/ /opt/workdir/conf/
ENV TZ=Asia/Shanghai
EXPOSE 8080
CMD [ "./go-project"]

5.创建流水线

30345-r0khvp1vc9s.png

6.部署应用测试

部署应用

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
apiVersion: apps/v1 
kind: Deployment
metadata:
name: go-project
namespace: bolo
labels:
image: go-project #这里使用lable匹配更新,必须设置这个,因为在jenkinsfile当中写了过去指定标签的pod
spec:
replicas: 1
selector:
matchLabels:
app: go-project
template:
metadata:
creationTimestamp: null
labels:
app: go-project
spec:
containers:
- name: go-project
image: 192.168.10.15/vue/vuevue3-addname:main-6
ports:
- name: http
containerPort: 8080
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: go-project
namespace: bolo
spec:
ports:
- name: go-project
port: 8080
protocol: TCP
targetPort: http
selector:
app: go-project
type: NodePort

测试流水线
81806-cokov9c86p.png

五、配置自动触发构建

之前的构建都是采用手动选择分支进行构建的,实际使用时,项目可能有很多,如果都是手动触发可能比较消耗人力。所以推荐可以按需配置自动触发,即提交代码后自动触发Jenkins进行构建任务。

5.1 配置jenkins

本次用 Java 项目进行演示。首先找到 Java 项目的 Job,点击 Configure

之后选择 Build Triggers,勾选 Build when a change…,记录 webhook URL(新版本[Version 2.452.2]的jenkins汉化后的选项是[触发远程构建 (例如,使用脚本)])

32956-sahjntuh73j.png
选择 Allow all branches,如果不想任何分支都可以触发该流水线,可以选择 Filter 进行条件匹配。之后点击 Generate 生成 Secret token, 最后点击 Save 即可。
64313-z8clhrevg1k.png

5.2 配置gitlab

接下来配置 GitLab,首先点击 Menu→Admin(这个设置是在总体设置里面,不是在单独的项目内)
81636-mc5o7vwrnpp.png
保存后,找到 Java 项目(在项目内进行设置),点击 Settings→WebHooks(JENKINS_URL/job/docker-pipline/build?token=TOKEN_NAME粘贴在url内,下面的secret token就不用填写)
70363-0mlvz2an5q2c.png
确认无误后,点击 Add webhook

保存后没有问题可以进行测试

38054-vf8gly9k2el.png
验证流水线是否触发执行
36937-t4zeinnzjup.png

六、一次构建多次部署

创建一个新的 Job,名字为 go-project-uat,类型 Pipeline

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
pipeline {
environment {
HARBOR_ADDRESS = "192.168.10.15" //镜像仓库地址
NAMESPACE = "bolo" //部署应用的命名空间
IMAGE_NAME = "go-project" //镜像名称
}
parameters {
imageTag(name: 'DOCKER_IMAGE', description: '', image: 'go/go-project', filter: '.*', defaultTag: '', registry: 'http://192.168.10.15', credentialId: 'harbor-account', tagOrder: 'NATURAL') //获取镜像名称与tag,相关参数根据实际情况填写
}
//全局配置
options {
timestamps() //所有输出每行都会打印时间戳
buildDiscarder(logRotator(numToKeepStr: '5')) //保留5个历史构建版本
}
agent {
kubernetes {
cloud 'kubernetes'
slaveConnectTimeout 1200
workspaceVolume emptyDirWorkspaceVolume()
yaml '''
apiVersion: v1
kind: Pod
metadata:
name: jenkins-agent
namespace: jenkins
spec:
containers:
- args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
image: '192.168.10.254:5000/kubernetes/jnlp:alpine'
name: jnlp
imagePullPolicy: IfNotPresent
- command:
- "cat"
image: "192.168.10.254:5000/kubernetes/kubectl:apline"
imagePullPolicy: "IfNotPresent"
name: "kubectl"
tty: true
'''
}
}
stages {
stage('Deploy') {
environment {
MY_KUBECONFIG = credentials('kubernetes-cluster')
}
steps {
container(name: 'kubectl'){
sh "echo ${DOCKER_IMAGE}"
sh "kubectl get deploy -n ${NAMESPACE} -l image=${IMAGE_NAME} --kubeconfig $MY_KUBECONFIG"
sh "kubectl set image deploy -n ${NAMESPACE} -l image=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${DOCKER_IMAGE} --kubeconfig $MY_KUBECONFIG"
sh "kubectl rollout status deployment -n ${NAMESPACE} ${IMAGE_NAME} --timeout=60s --kubeconfig $MY_KUBECONFIG"
}
}
}
}
}

执行流水线
33052-cgm561oyaqf.png

原文来自:zhangzhuo


Jenkins基于k8s容器化构建Pipline
https://blog.t-ao.cn/2025/03/19/Jenkins基于k8s容器化构建Pipline/
作者
TAO
发布于
2025年3月19日
许可协议