jenkins 初体验

  |  

全部的 K8S学习笔记总目录,请点击查看。

CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。作为一种面向开发和运维团队的解决方案,CI/CD 主要针对在集成新代码时所引发的问题。这里主要介绍jenkins的使用。

ci/cd常用的持续集成工具:

  • travis-ci
  • circleci
  • bamboo
  • teamcity
  • gitlabci
  • jenkins
  • tekton
  • argo
  • spinnaker
  • drone
  • concourse
  • 等等……

本次要实现的效果是基于k8s集群部署gitlab、sonarQube、Jenkins等工具,并把上述一些工具集成到Jenkins中,以Django项目和SpringBoot项目为例,通过多分支流水线及Jenkinsfile实现项目代码提交到不同的仓库分支,实现自动代码扫描、单元测试、docker容器构建、k8s服务的自动部署。

其中包含的内容有:

  • DevOps、CI、CD介绍
  • Jenkins、sonarQube、gitlab的快速部署
  • Jenkins初体验
  • 流水线入门及Jenkinsfile使用
  • Jenkins与Kubernetes的集成
  • sonarQube代码扫描与Jenkins的集成
  • 实践Django项目的基于Jenkinsfile实现开发、测试环境的CI/CD

DevOps、CI、CD介绍

CI/CD就是持续集成 Continuous Integration (CI) / 持续交付Continuous Delivery (CD)

一般的软件交付流程如下:

devops-roles

软件交付发展历程

一个软件从零开始到最终交付,大概包括以下几个阶段:规划、编码、构建、测试、发布、部署和维护,基于这些阶段,我们的软件交付模型大致经历了几个阶段:

瀑布式流程

软件交付的早期阶段,大家都是采用瀑布式流程,如下图所示:

devops-waterfall

前期需求确立之后,软件开发人员花费数周和数月编写代码,把所有需求一次性开发完,然后将代码交给QA(质量保障)团队进行测试,然后将最终的发布版交给运维团队去部署。

瀑布模型,简单来说,就是等一个阶段所有工作完成之后,再进入下一个阶段。这种模式的问题也很明显,产品迭代周期长,灵活性差。一个周期动辄几周几个月,适应不了当下产品需要快速迭代的场景。

敏捷开发

为了解决瀑布模型的问题,以及更好地适应当下快速迭代的场景,敏捷开发模式越来越多地被采用。敏捷开发模式的核心是任务由大拆小,开发、测试协同工作,注重开发敏捷,不重视交付敏捷。

devops-agile

DevOps

敏捷开发模式注重开发敏捷,不重视交付敏捷。那么如何解决交付敏捷呢?DevOps就是为了解决这个问题而诞生的。

devops-compire

开发、测试、运维协同工作, 持续开发+持续交付。

DevOps的工具链

我们是否可以认为DevOps = 提倡开发、测试、运维协同工作来实现持续开发、持续交付的一种软件交付模式?为什么最初的开发模式没有直接进入DevOps的时代?

原因是:沟通成本。

各角色人员去沟通协作的时候都是手动去做,交流靠嘴,靠人去指挥,很显然会出大问题。所以说不能认为DevOps就是一种交付模式,因为解决不了沟通协作成本,这种模式就不具备可落地性。

那DevOps时代如何解决角色之间的成本问题?DevOps的核心就是自动化。自动化的能力靠什么来支撑,工具和技术。

DevOps工具链

devops-tools

靠这些工具和技术,才实现了自动化流程,进而解决了协作成本,使得devops具备了可落地性。因此我们可以大致给devops一个定义:

devops = 提倡开发、测试、运维协同工作来实现持续开发、持续交付的一种软件交付模式 + 基于工具和技术支撑的自动化流程的落地实践。

因此devops不是某一个具体的技术,而是一种思想+自动化能力,来使得构建、测试、发布软件能够更加地便捷、频繁和可靠的落地实践。本次核心内容就是要教会大家如何利用工具和技术来实现完整的DevOps平台的建设。我们主要使用的工具有:

  1. gitlab,代码仓库,企业内部使用最多的代码版本管理工具。
  2. Jenkins, 一个可扩展的持续集成引擎,用于自动化各种任务,包括构建、测试和部署软件。
  3. robotFramework, 基于Python的自动化测试框架
  4. sonarqube,代码质量管理平台
  5. maven,java包构建管理工具
  6. Kubernetes
  7. Docker

Kubernetes环境中部署jenkins

本次部署使用helm部署的方式,也可以使用yaml文件部署,参考其他部署方式

注意点:

  • 第一次启动很慢
  • 因为后面Jenkins会与kubernetes集群进行集成,会需要调用kubernetes集群的api,因此安装的时候创建了ServiceAccount并赋予了cluster-admin的权限
  • 初始化容器来设置权限
  • ingress来外部访问
  • 数据存储通过pvc挂载到宿主机中

安装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
# 新建chart仓库
$ helm repo add jenkins https://charts.jenkins.io
$ helm repo update
# 搜索jenkins
$ helm search repo jenkins
# 下载chart包
$ helm pull jenkins/jenkins
# 解压
$ tar -zxvf jenkins-4.9.1.tgz
$ vim jenkins/values.yaml

......
ingress:
# 启用ingress
enabled: true
......
# 配置ingressClassName
ingressClassName: nginx
# 配置ingress的hosts
hostName: jenkins.test.com
......
# 不进行测试
testEnabled: false
......
persistence:
enabled: true
# 配置storageClass
storageClass: "nfs-client"
annotations: {}
labels: {}
# 设置pvc的模式
accessMode: "ReadWriteMany"
# 设置pvc的大小
size: "200Gi"
......
......

# 安装
$ helm -n jenkins upgrade --create-namespace -i jenkins ./jenkins
# 等待安装完成
$ kubectl -n jenkins get all

# 获取登录密码
$ kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/cat /run/secrets/additional/chart-admin-password && echo

# 使用浏览器访问jenkins,输入账号密码就可以登录了

k8s devops jenkins index

由于默认的插件地址安装非常慢,我们可以替换成国内清华的源,进入 jenkins 工作目录,目录下面有一个 updates 的目录,下面有一个 default.json 文件,我们执行下面的命令替换插件地址:

1
2
3
4
$ kubectl -n jenkins exec -it jenkins-0 -c jenkins -- bash
jenkins@jenkins-0:/$ cd /var/jenkins_home/updates
jenkins@jenkins-0:~/updates$ sed -i 's/https:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json
jenkins@jenkins-0:~/updates$ sed -i 's/https:\/\/updates.jenkins.io\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json

暂时先不用重新启动pod,汉化后一起重启。

配置升级站点的URL:

  1. 进入 web ui 的界面,点击 Manage Jenkins -> Plugins 按钮,进入插件管理页面:http://jenkins.test.com/manage/pluginManager/advanced
  2. 选择最后一项 Advanced settings
  3. 在 Update Site 中将原有url替换为:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
  4. 点击 Submit 按钮,保存设置

接下来我们就可以重启服务了。重启jenkins服务有两种方式:

  1. 删除pod,kubernetes会自动重启
  2. 访问下面这个url重启:http://jenkins.test.com/restart/ 这样重启的话,pod不会重建

安装插件

分别点击以下菜单 Jenkins -> Manage Jenkins -> Plugins -> Avaliable plugins

主要安装以下插件:

  • GitLab
  • Pipeline: Multibranch
  • Blue Ocean
  • Localization: Chinese (Simplified)
  • Gogs (太老了,没有维护,但是gitlab比较重,这里先安装gogs,仅实验使用,之后webhook和gitlab用法相同)

如果安装超时,需要查看之前更改的数据源是否生效,如果没有生效,需要重新配置。

选中后,选择[Install],等待下载完成,然后点击[ Restart Jenkins when installation is complete and no jobs are running ],让Jenkins自动重启

启动后,界面默认变成中文。

Kubernetes环境中部署gogs

这里我们需要一个git仓库,因为实验使用的环境比较轻量,因此选用gogs来部署。

安装gogs

gogs的helm仓库已经很老了,因此整理了以下的资源清单,使用k8s部署gogs。

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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# postgres secret
apiVersion: v1
kind: Secret
metadata:
name: postgres
namespace: gogs
type: Opaque
data:
password: cG9zdGdyZXM=
user: cG9zdGdyZXM=
dbname: Z29ncw==
---
# postgres service
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: gogs
spec:
ports:
- name: postgres
port: 5432
targetPort: 5432
protocol: TCP
type: ClusterIP
selector:
app: postgres
---
# postgres persistent volume claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres
namespace: gogs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: nfs-client
---
# postgres deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: gogs
labels:
app: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres
key: password
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: postgres
key: user
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: postgres
key: dbname
volumeMounts:
- name: postgres
mountPath: /var/lib/postgresql/data
volumes:
- name: postgres
persistentVolumeClaim:
claimName: postgres
---
# memcache service
apiVersion: v1
kind: Service
metadata:
name: memcache
namespace: gogs
spec:
ports:
- name: memcache
port: 11211
targetPort: 11211
protocol: TCP
type: ClusterIP
selector:
app: memcache
---
# memcache deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: memcache
namespace: gogs
labels:
app: memcache
spec:
replicas: 1
selector:
matchLabels:
app: memcached
template:
metadata:
labels:
app: memcached
spec:
containers:
- name: memcached
image: memcached:1.6.22
imagePullPolicy: IfNotPresent
command: ["memcached", "-m", "64", "-p", "11211", "-u", "memcache", "-v", "-c", "1024", "-o", "modern"]
ports:
- containerPort: 11211
name: memcached
livenessProbe:
tcpSocket:
port: memcached
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 10
failureThreshold: 3
readinessProbe:
tcpSocket:
port: memcached
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 10
failureThreshold: 3
securityContext:
runAsUser: 1000
---
# gogs persistent volume claim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gogs
namespace: gogs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: nfs-client
---
# gogs configmap
apiVersion: v1
kind: ConfigMap
metadata:
name: gogs-config
namespace: gogs
data:
app.ini: |-
BRAND_NAME = TestGogs

[server]
EXTERNAL_URL = http://gogs.test.com/
DOMAIN = gogs.test.com
HTTP_PORT = 3000

[database]
TYPE = postgres
HOST = postgres:5432
NAME = gogs
USER = postgres
PASSWORD = postgres

[cache]
ADAPTER = memcache
INTERVAL = 60
HOST = memcached:11211

[ui]
EXPLORE_PAGING_NUM = 50
ISSUE_PAGING_NUM = 50

[ui.user]
REPO_PAGING_NUM = 50
COMMITS_PAGING_NUM = 50

[prometheus]
ENABLED = false

[other]
SHOW_FOOTER_BRANDING = true

[security]
INSTALL_LOCK = true
SECRET_KEY = sHyaED0SMrLCR7x
LOCAL_NETWORK_ALLOWLIST = *
---
# gogs service
apiVersion: v1
kind: Service
metadata:
name: gogs
namespace: gogs
spec:
ports:
- name: http
port: 3000
targetPort: 3000
protocol: TCP
- name: ssh
port: 22
targetPort: 22
protocol: TCP
type: ClusterIP
selector:
app: gogs
---
# gogs deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: gogs
namespace: gogs
labels:
app: gogs
spec:
replicas: 1
selector:
matchLabels:
app: gogs
template:
metadata:
labels:
app: gogs
spec:
containers:
- name: gogs
image: gogs/gogs:0.13.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 3000
- name: ssh
containerPort: 22
livenessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 30
timeoutSeconds: 5
periodSeconds: 10
failureThreshold: 3
volumeMounts:
- name: gogs
mountPath: /data
- name: gogs-config
mountPath: /data/gogs/conf/app.ini
subPath: app.ini
volumes:
- name: gogs
persistentVolumeClaim:
claimName: gogs
- name: gogs-config
configMap:
name: gogs-config
---
# ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gogs
namespace: gogs
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
ingressClassName: nginx
rules:
- host: gogs.test.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gogs
port:
number: 3000

有了资源配置清单,我们就可以部署gogs了。

1
2
3
4
5
6
7
# 创建命名空间
$ kubectl create namespace gogs
# 部署gogs
$ kubectl apply -f gogs.yaml

# 查看pod
$ kubectl get pod -n gogs

全部容器启动成功后,我们就可以配置host然后通过浏览器访问gogs了。

k8s devops gogs index

这时可以点击右上角的注册按钮,注册一个账户,在gogs中id=0,也就是第一个注册的账户,是管理员账户,可以创建组织、创建仓库等操作。

k8s devops gogs register

注册完成后,我们就可以使用这个账户登录gogs了。

k8s devops gogs login

创建仓库

安装完成gogs我们就可以创建一个仓库了。这里创建一个django项目的仓库,仓库名称为moonlight。创建方法与github类似,这里就不再赘述。

创建完成后的仓库如下图所示:

k8s devops gogs repo

然后就可以创建django项目了,然后将远程仓库地址添加到django项目中,合并后提交到远程仓库。

1
2
3
4
5
6
7
8
$ django-admin startproject moonlight
$ cd moonlight
$ git init
$ git add .
$ git commit -m "first commit"
$ git remote add origin http://gogs.test.com/administrator/moonlight.git
$ git pull --rebase origin master
$ git push origin master

通过gogs的网页,可以看到代码已经提交到了仓库中。

k8s devops gogs repo commit

配置jenkins

在gogs中,我们可以配置webhook,当代码提交到仓库后,会触发webhook,然后调用jenkins的接口,从而触发jenkins的流水线。

webhook触发的是jenkins的流水线,因此我们需要先创建一个流水线,然后再配置webhook。

因此我们先创建一个流水线

k8s devops jenkins create a job

然后选择自由风格的软件项目,名字叫free-style-demo,然后点击确定。

k8s devops jenkins create free style job

然后我们就可以配置流水线了。首先我们先配置源码管理。选择git,然后填写仓库地址,这里填写的是gogs的仓库地址。然后当焦点离开仓库地址的时候,会发现git报错了,提示指定一个git仓库。

k8s devops jenkins git repo error

这是因为我们使用的是url地址,而k8s内部无法解析这个地址,要想解决这个问题,有两种方式:

  1. 在容器内配置hosts
  2. 配置coredns的静态解析

这里我们使用第二种方式,配置coredns的静态解析。

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
$ kubectl -n kube-system edit configmap coredns

......
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
# hosts字段为新加内容,将解析地址填入。我们将git仓库和jenkins的地址都做了解析,是因为之后使用git的webhook的时候还得用jenkins的url,因此也需要解析。
hosts {
192.168.100.1 gogs.test.com jenkins.test.com
fallthrough
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
......

然后保存即可。保存后我们再回到jenkins的配置页面,重新刷新配置,发现git的报错已经消失了。

k8s devops jenkins git repo success

因为这里我们的仓库是公有的,因此不需要配置认证信息,如果是私有仓库,需要配置认证信息。

这里我们直接配置认证信息,做一个演示。点击添加,选择用户名密码,然后填写用户名密码,id保证内部唯一即可,然后点击添加。

k8s devops jenkins git auth username

k8s devops jenkins git auth password

然后认证选择刚刚添加的认证信息。

k8s devops jenkins git auth

往下走,构建触发器。这里我们选择”Build when a change is pushed to GoGS”,也就是当代码提交到gogs的时候,触发构建。

k8s devops jenkins build trigger

再往下,Build Steps,我们选择执行shell命令。这个就是说,当代码提交到gogs的时候,jenkins会下载代码,然后执行Buile Steps中的命令。

k8s devops jenkins build steps

这里我们主要是测试,先写一个简单点的echo success,点击”可用的环境变量”,可以看到jenkins提供了很多环境变量,这些环境变量可以在shell中使用,这里也打印一个环境变量。

k8s devops jenkins build steps env

然后点击保存即可。

k8s devops jenkins build steps save

配置webhook

jenkins的流水线配置完成后,我们就可以配置webhook了。点击仓库的设置按钮,然后选择管理Web钩子,点击添加Web钩子,选择Gogs

k8s devops gogs repo webhook

然后填写推送地址,推送地址为 http://jenkins.test.com/gogs-webhook/?job=free-style-demo ,这里的free-style-demo为jenkins的流水线名称。

gogs 配置的推送地址格式为 http(s)://<Jenkins地址>/gogs-webhook/?job=<Jenkins任务名>

k8s devops gogs repo webhook url

然后点击添加Web钩子即可。

k8s devops gogs repo webhook add

添加成功后能看到Web钩子列表中多了一个Web钩子。

k8s devops gogs repo webhook list

然后我们点进去,点击测试按钮,测试一下是否能够触发jenkins的流水线。然后就能看到测试已经推送了。

k8s devops gogs repo webhook test

测试按钮底下也有一个成功的推送记录。

k8s devops gogs repo webhook test success

此时,去jenkins的流水线页面,可以看到流水线已经触发了。

k8s devops jenkins build success

触发构建

moonlight项目中,创建一个test.md文件,然后提交到远程仓库,触发jenkins的流水线。

1
2
3
4
$ echo "test" > test.md
$ git add .
$ git commit -m "test"
$ git push origin master

然后我们可以看到jenkins的流水线已经触发了。

k8s devops jenkins gogs push build success

等待build完成后,点击序号,可以看到build的日志。

k8s devops jenkins gogs push build info

点击控制台输出,可以看到整个流水线的执行过程以及日志。

k8s devops jenkins gogs push build console

拉倒最后可以看到整体是先把git仓库拉取到本地,然后执行了我们配置的shell命令。

k8s devops jenkins gogs push build console success

Jenkins 的 Master-Slave 模式

工作节点

在 Jenkins 页面中,点击系统管理, 然后点击节点和云管理,可以看到 Jenkins 的工作节点列表。

k8s devops jenkins node list button

可以看到在 Jenkins 中,只有一个节点,这个节点就是 Jenkins 的 Master 节点,也就是 Jenkins 的主节点。

k8s devops jenkins node list

如果有多个任务都在master节点执行,对master节点的性能就会造成一定影响。因此我们可以添加多个工作节点,让工作节点来执行任务,这样就可以减轻master节点的压力。

Jenkins 的 Master-Slave 模式是指 Jenkins 的 Master 节点负责分发任务,Slave 节点负责执行任务。这样做的好处是可以将任务分发到不同的节点上执行,从而提高任务的执行效率。

添加工作节点

因为目前这个环境已经接入了k8s集群,因此我们使用k8s集群中的节点作为jenkins的工作节点。以下内容仅作为演示,最后还是会切换回k8s集群中的节点。
多个k8s管理和多个slave节点的添加方式大致相同,这里不再赘述,可以尝试添加基本就会了。

点击新建节点,进入新建节点页面。

k8s devops jenkins node create

定义一个节点名称,这里我们定义为slave,然后选择固定节点,然后点击确定。

k8s devops jenkins node create name

之后是填写更详细的信息

  • Number of executors:这个是指这个节点可以同时执行多少个任务,这里我们填写5,也就是这个节点可以同时执行5个任务。
  • 远程工作目录:这个是指这个节点的工作目录,这里我们填写/home/jenkins,也就是这个节点的工作目录为/home/jenkins
  • 标签:这个是指这个节点的标签,这里我们填写slave,也就是这个节点的标签为slave。之后我们可以在任务中指定任务执行在哪个节点上,就是通过标签来进行控制。
  • 启动方法:这个是指这个节点的启动方式,也就是通过什么方式实现Master-Slave的通信。这里我们选择通过Java Web启动代理,也就是通过Java Web启动代理的方式来实现Master-Slave的通信。
    • 自定义工作目录:这个是指这个节点的工作目录,这里我们填写/home/jenkins,也就是和上面的远程工作目录一样。
    • Tunnel连接位置:点开”高级”按钮就可以看到了。
      • 这个是指这个节点连接到Master节点的时候,通过什么方式来连接。这里我们填入10.99.223.156:50000
      • Jenkins 默认就会开放50000端口供Slave节点连接、通信。
      • 因为我们使用的node节点,因此ClusterIP就可以在集群内部访问,因此填写Jenkins的ClusterIP即可。

k8s devops jenkins node create info

k8s devops jenkins node create jave web

填写完成后,点击保存即可。

保存后,我们可以看到节点列表中多了一个节点。但是节点的状态是离线的,因为我们还没有启动这个节点。

k8s devops jenkins node create success

启动工作节点

点击节点列表中的节点名称,进入节点详情页面。可以看到连接节点的命令,这个命令就是启动节点的命令。

k8s devops jenkins slave node command

我们在node节点上执行这个命令,启动节点。

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
# 首先配置hosts确保能够解析jenkins的地址
$ sudo vim /etc/hosts

192.168.100.1 jenkins.test.com gogs.test.com

# 因为命令是java的,因此需要先安装java,注意java的版本,要和jenkins的版本一致,jenkins的pod启动的镜像名称会包含java的版本。比如本次启动的镜像是jenkins/jenkins:2.426.1-jdk17,因此我们安装的java版本也要是jdk17。
$ sudo yum install java-17-openjdk.x86_64 -y

# 保证工作目录存在
$ sudo mkdir -p /home/jenkins
# 修改工作目录的权限,这里修改的用户就是下面执行java命令的用户
$ sudo chown -R jenkins:jenkins /home/jenkins

# 执行命令启动节点
$ curl -sO http://jenkins.test.com/jnlpJars/agent.jar
$ java -jar agent.jar -jnlpUrl http://jenkins.test.com/computer/slave/jenkins-agent.jnlp -secret e74b242a6a58da754a7bdecb0e0511a0fa70f305321974b052a79d6aa8d9c56d -workDir "/home/jenkins"

......
12月 29, 2023 11:56:51 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Agent discovery successful
Agent address: 10.99.223.156
Agent port: 50000
Identity: de:25:39:3d:94:4e:ca:0b:9b:c7:e8:fb:19:e2:92:70
12月 29, 2023 11:56:51 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Handshaking
12月 29, 2023 11:56:51 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Connecting to 10.99.223.156:50000
12月 29, 2023 11:56:51 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Trying protocol: JNLP4-connect
12月 29, 2023 11:56:51 下午 org.jenkinsci.remoting.protocol.impl.BIONetworkLayer$Reader run
信息: Waiting for ProtocolStack to start.
12月 29, 2023 11:56:52 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Remote identity confirmed: de:25:39:3d:94:4e:ca:0b:9b:c7:e8:fb:19:e2:92:70
12月 29, 2023 11:56:52 下午 hudson.remoting.jnlp.Main$CuiListener status
信息: Connected

此时我们再回到jenkins的节点列表中,可以看到节点的状态已经变成在线了。

k8s devops jenkins slave node online

测试工作节点

此时我们进入到free-style-demo任务中,然后点击配置,在Gogs Webhook中,选择限制项目的运行节点,然后填入slave,也就是这个任务只能在slave节点上执行。

k8s devops jenkins slave node test

然后点击保存,保存成功后,我们再次提交代码,触发jenkins的流水线。

1
2
3
$ echo "test again" > test.md
$ git commit -am "test again"
$ git push origin master

然后我们可以看到jenkins的流水线已经触发了。

k8s devops jenkins slave node test build

等待build完成后,点击序号,查看build的日志。可以看到build的时候,是在slave节点上执行的。

k8s devops jenkins slave node test build info

登录到slave节点上,可以看到slave节点上已经有了我们的代码。

1
2
3
4
5
$ ls /home/jenkins/workspace/free-style-demo
LICENSE manage.py moonlight README.md test.md
$ cd /home/jenkins/workspace/free-style-demo
$ cat test.md
test again

Jenkins定制化容器

由于每次新部署Jenkins环境,都需要安装很多必要的插件,因此考虑把插件提前做到镜像中,这样再部署的时候就不需要再在线安装插件了。

首先得知道插件都有哪些,我们可以访问Jenkins的url,然后生成插件列表文件。

1
2
3
# admin:admin@jenkins.test.com 需要替换成Jenkins的用户名、密码及访问地址

curl -sSL "http://admin:admin@jenkins.test.com/pluginManager/api/xml?depth=1&xpath=/*/*/shortName|/*/*/version&wrapper=plugins" | perl -pe 's/.*?<shortName>([\w-]+).*?<version>([^<]+)()(<\/\w+>)+/\1:\2\n/g'|sed 's/ /:/' > plugins.txt

插件列表文件大致格式如下,这个文件中包含了我们需要安装的插件。

1
2
3
4
5
6
7
ace-editor:1.1
allure-jenkins-plugin:2.28.1
ant:1.10
antisamy-markup-formatter:1.6
apache-httpcomponents-client-4-api:4.5.10-1.0
authentication-tokens:1.3
...

下面是一个定制化的Jenkins镜像的Dockerfile文件,主要为了构建一个包含插件的镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM jenkinsci/blueocean:1.25.2
LABEL maintainer="me@example.com"

ENV JENKINS_UC https://updates.jenkins-zh.cn
ENV JENKINS_UC_DOWNLOAD https://mirrors.tuna.tsinghua.edu.cn/jenkins
ENV JENKINS_OPTS="-Dhudson.model.UpdateCenter.updateCenterUrl=https://updates.jenkins-zh.cn/update-center.json"
ENV JENKINS_OPTS="-Djenkins.install.runSetupWizard=false"

## 用最新的插件列表文件替换默认插件文件
COPY plugins.txt /usr/share/jenkins/ref/

## 执行插件安装
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt

执行构建,定制jenkins容器

1
2
$ docker build . -t harbor.test.com/jenkins:v1 -f Dockerfile
$ docker push harbor.test.com/jenkins:v1

至此,我们已经有了定制化的Jenkins镜像,接下来我们就可以使用这个镜像来部署Jenkins了。

1
2
# 编辑现有jenkins的statefulset,将镜像替换成我们定制化的镜像,保存即可
$ kubectl -n jenkins edit statefulset jenkins
文章目录
  1. 1. DevOps、CI、CD介绍
    1. 1.1. 软件交付发展历程
      1. 1.1.1. 瀑布式流程
      2. 1.1.2. 敏捷开发
      3. 1.1.3. DevOps
    2. 1.2. DevOps的工具链
  2. 2. Kubernetes环境中部署jenkins
    1. 2.1. 安装jenkins
    2. 2.2. 安装插件
  3. 3. Kubernetes环境中部署gogs
    1. 3.1. 安装gogs
    2. 3.2. 创建仓库
    3. 3.3. 配置jenkins
    4. 3.4. 配置webhook
    5. 3.5. 触发构建
  4. 4. Jenkins 的 Master-Slave 模式
    1. 4.1. 工作节点
    2. 4.2. 添加工作节点
    3. 4.3. 启动工作节点
    4. 4.4. 测试工作节点
  5. 5. Jenkins定制化容器