案例描述

本案例主要使用Kubernetes+Jenkins+GitLab构建持续集成项目,具体如下:

(1)掌握Jenklins和GitLab的基本使用。

(2)了解Blue Ocean的使用和管理。

(3)掌握Jenkinsfile的语法和编写方式。

规划节点

节点规划见表1-1。

表1-1节点规划

IP 主机名 节点
10.26.15.244 master Kubernetes单节点集群
10.26.15.243 桌面化的测试节点

基础准备

确保Kubernetes集群已部署完成。

案例实施

概述

Kubernetes构建CI/CD的流程拓扑图如下:

127
图1

涉及到的工具与技术包括:

  • GitLab:常用的源代码管理系统。
  • Jenkins、Jenkins Pipeline:常用的自动化构建、部署工具,Pipeline以流水线的方式将构建、部署的各个步骤组织起来。
  • Docker、Dockerfile:容器引擎,所有应用最终都要以Docker容器运行,Dockerfile是Docker镜像定义文件。
  • Kubernetes:Google开源的容器编排管理系统。

部署Harbor

(1)基础准备

下载软件包到本地:

1
2
[root@master ~]# ls
BlueOcean.tar.gz

解压软件包:

1
[root@master ~]# tar -zxf BlueOcean.tar.gz

(2)部署Harbor

安装Docker Compose:

1
2
3
4
5
6
[root@master ~]# cp BlueOcean/tools/docker-compose-Linux-x86_64 /usr/bin/docker-compose
[root@master ~]# docker-compose version
docker-compose version 1.25.0, build 0a186604
docker-py version: 4.1.0
CPython version: 3.7.4
OpenSSL version: OpenSSL 1.1.0l 10 Sep 2019

安装Harbor仓库:

1
2
[root@master ~]# tar -zxf BlueOcean/harbor-offline-installer.tar.gz -C /opt/
[root@master ~]# sh /opt/harbor/install.sh

部署完成后Harbor镜像仓库默认用户为admin,密码为Harbor12345。

(3)访问Harbor

在Web端使用火狐浏览器登录Harbor(http://master),登录成功后如图2所示:

128

图2

新建springcloud项目,访问级别设置为公开,如图3所示:

129

图3

创建完成后如图4所示:

130

图4

上传镜像到Harbor(IP为master节点地址):

1
2
3
4
5
6
7
[root@master ~]# docker login -uadmin -pHarbor12345 10.26.15.244
[root@master ~]# docker load -i BlueOcean/images/maven_latest.tar
[root@master ~]# docker tag maven 10.26.15.244/library/maven
[root@master ~]# docker push 10.26.15.244/library/maven
[root@master ~]# docker load -i BlueOcean/images/java_8-jre.tar
[root@master ~]# docker load -i BlueOcean/images/jenkins_jenkins_latest.tar
[root@master ~]# docker load -i BlueOcean/images/gitlab_gitlab-ce_latest.tar

部署Jenkins

(1)安装Jenkins

新建命名空间:

1
[root@master ~]# kubectl create ns devops

部署Jenkins需要使用到一个拥有相关权限的serviceAccount,名称为jenkins-admin,可以给jenkins-admin赋予一些必要的权限,也可以直接绑定一个cluster-admin的集群角色权限,此处选择给予集群角色权限。编写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
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
[root@master ~]# vi jenkins-deploy.yaml 
apiVersion: v1
kind: Service
metadata:
name: jenkins
labels:
app: jenkins
spec:
type: NodePort
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 30880
- name: agent
port: 50000
targetPort: agent
nodePort: 30850
selector:
app: jenkins

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins
labels:
app: jenkins
spec:
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccountName: jenkins-admin
containers:
- name: jenkins
image: jenkins/jenkins:latest
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
privileged: true
ports:
- name: http
containerPort: 8080
volumeMounts:
- mountPath: /var/jenkins_home
name: jenkinshome
- mountPath: /usr/bin/docker
name: docker
- mountPath: /var/run/docker.sock
name: dockersock
- mountPath: /usr/bin/kubectl
name: kubectl
- mountPath: /root/.kube
name: kubeconfig
volumes:
- name: jenkinshome
hostPath:
path: /home/jenkins_home
- name: docker
hostPath:
path: /usr/bin/docker
- name: dockersock
hostPath:
path: /var/run/docker.sock
- name: kubectl
hostPath:
path: /usr/bin/kubectl
- name: kubeconfig
hostPath:
path: /root/.kube
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkinshome
annotations:
volume.beta.kubernetes.io/storage-class: local-path
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1024Mi
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-admin
labels:
name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-admin
labels:
name: jenkins
subjects:
- kind: ServiceAccount
name: jenkins-admin
namespace: default
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

这里通过NodePort的形式来暴露了Jenkins的8080端口,另外还需要暴露一个agent的端口,这个端口主要用于Jenkins的Master和Slave之间的通信。

部署Jenkins:

1
[root@master ~]# kubectl -n devops apply -f jenkins-deploy.yaml

查看Pod:

1
2
3
[root@master ~]# kubectl -n devops get pods
NAME READY STATUS RESTARTS AGE
jenkins-cc97fd4fc-v5dh2 1/1 Running 0 21s

(2)访问Jenkins

查看Jenkins Service端口:

1
2
3
[root@master ~]# kubectl -n devops get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
jenkins NodePort 192.96.215.194 <none> 8080:30880/TCP,50000:30850/TCP 47s

在Web端使用浏览器访问Jenkins(http://master:30880),如图5所示:

131

图5

获取Jenkins密码:

1
2
[root@master ~]# kubectl -n devops exec deploy/jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
a3ac7ba3812746d0bc8ed40e122ba20b

输入密码后单击“继续”按钮,如图6所示:

132

图6

将离线插件包拷贝到Jenkins:

1
[root@master ~]# kubectl -n devops cp BlueOcean/plugins/ jenkins-cc97fd4fc-v5dh2:/var/jenkins_home

重启Jenkins:

1
[root@master ~]# kubectl -n devops rollout restart deployment jenkins

刷新Jenkins页面,选择“跳过插件安装”,安装完成后进入用户创建页面,创建一个用户jenkins,密码000000,如图7所示:
133

图7

单击“保存并完成”按钮,如图8所示:

134

图8

单击“保存并完成”,如图9所示:

135

图9

单击“开始使用Jenkins”按钮并使用新创建的用户登录Jenkins,如图10所示:

136

图10

部署GitLab

GitLab是利用Ruby on Rails一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。与GitHub类似,GitLab能够浏览源代码,管理缺陷和注释,可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库,团队成员可以利用内置的简单聊天程序(Wall)进行交流。GitLab还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找。

本项目GitLab与Harbor共用一台服务器。

(1)部署GitLab

编写GitLab资源清单文件:

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
[root@master ~]# vi gitlab-deploy.yaml 
apiVersion: v1
kind: Service
metadata:
name: gitlab
spec:
type: NodePort
ports:
- port: 443
nodePort: 30443
targetPort: 443
name: gitlab-443
- port: 80
nodePort: 30888
targetPort: 80
name: gitlab-80
selector:
app: gitlab
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab
spec:
selector:
matchLabels:
app: gitlab
revisionHistoryLimit: 2
template:
metadata:
labels:
app: gitlab
spec:
containers:
- image: gitlab/gitlab-ce:latest
name: gitlab
imagePullPolicy: IfNotPresent
env:
- name: GITLAB_ROOT_PASSWORD # 设置root用户密码
value: admin@123
- name: GITLAB_PORT
value: "80"
ports:
- containerPort: 443
name: gitlab-443
- containerPort: 80
name: gitlab-80

部署GitLab:

1
[root@master ~]# kubectl -n devops apply -f gitlab-deploy.yaml

查看Pod:

1
2
3
4
[root@master ~]# kubectl -n devops get pods
NAME READY STATUS RESTARTS AGE
gitlab-645dd88cd7-6vv2q 1/1 Running 0 29s
jenkins-cc97fd4fc-kmjtl 1/1 Running 0 7m20s

查看GitLab Service:

1
2
3
4
[root@master ~]# kubectl -n devops get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
gitlab NodePort 192.104.250.77 <none> 443:30443/TCP,80:30888/TCP 57s
jenkins NodePort 192.98.107.152 <none> 8080:30880/TCP,50000:30850/TCP 7m48s

GitLab启动较慢,可以通过“kubectl logs”查看其启动状态。启动完成后,在Web端访问GitLab(http://master:30888),如图11所示:

137

图11

登录GitLab,如图12所示:

138

图12

(2)创建项目

单击“New project”按钮,如图13所示:

139

图13

单击“Create blank project”按钮创建项目springcloud,可见等级选择“Public”,如图14所示:

140

图14

单击“Create project”按钮,进入项目,如图15所示:

141

图15

push源代码到GitLab的springcloud项目:

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
[root@master ~]# cd BlueOcean/springcloud/
[root@master springcloud]# git config --global user.name "administrator"
[root@master springcloud]# git config --global user.email "admin@example.com"
[root@master springcloud]# git remote remove origin
[root@master springcloud]# git remote add origin http://10.26.10.143:30888/root/springcloud.git
[root@master springcloud]# git add .
[root@master springcloud]# git commit -m "initial commit"
# On branch master
nothing to commit, working directory clean
[root@master springcloud]# git push -u origin master
Username for 'http://10.26.10.143:30888': root
Password for 'http://root@10.26.15.244:30888':
Counting objects: 3192, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (1428/1428), done.
Writing objects: 100% (3192/3192), 1.40 MiB | 1.70 MiB/s, done.
Total 3192 (delta 1233), reused 3010 (delta 1207)
remote: Resolving deltas: 100% (1233/1233), done.
remote:
remote: To create a merge request for master, visit:
remote: http://gitlab-6778c45f9-xx5gs/root/springcloud/-/merge_requests/new?merge_request%5Bsource_branch%5D=master
remote:
To http://10.26.15.244:30888/root/springcloud.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.

刷新网页,springcloud项目master分支中的文件已经更新了(使用火狐浏览器打开网页),如图16所示:

142

图16

配置Jenkins连接GitLab

(1)设置Outbound requests

登录Gitlab管理员界面(http://master:30888/admin),如图17所示:

143
图17

在左侧导航栏选择“Settings→Network”,设置“Outbound requests”,勾选“Allow requests to the local network from web hooks and services”复选框,如图18所示:

144

图18

配置完成后保存。

(2)创建GitLab API Token

单击GitLab用户头像图标,如图19所示:

145

图19

在左侧导航栏选择“Preferences”,如图20所示:
146
图20

在左侧导航栏选择“Access Tokens”添加Token,如21图所示:
147
图21

单击“Create personal access token”按钮生成Token,如图22所示:
148
图22

记录下Token(U6p_ubRixGSdRvs6MGft),后面配置Jenkins时会用到。

(3)设置Jenkins

登录Jenkins首页,选择“系统管理→系统配置”,配置GitLab信息,取消勾选“Enable authentiviion for ‘/project’ end-point”,输入“Connection name”和“Gitlab host URL”,如图23所示:
149
图23

添加Credentials,单击“添加”→“Jenkins”按钮添加认证信息,将Gitlab API Token填入,如图24所示:
150
图24

选择新添加的证书,然后单击“Test Connection”按钮,如图25所示:
25.png
图25

返回结果为Success,说明Jenkins可以正常连接GitLab。

Jenkinsfile

(1)新建任务

登录Jenkins首页,新建任务springcloud,任务类型选择“流水线”,如图26所示:
26.png
图26

单击“确定”按钮,配置构建触发器,如图27所示:
153
图27

记录下GitLab webhook URL的地址(http://10.26.15.244:30880/project/springcloud),后期配置webhook需要使用。

配置流水线,在定义域中选择“Pipeline script from SCM”,此选项指示Jenkins从源代码管理(SCM)仓库获取流水线。在SCM域中选择“Git”,然后输入“Repository URL”,如图28所示:
154
图28

在Credentials中选择“添加”,凭据类型选择“Username with password”,然后输入对应信息,如图29所示:

155

图29

单击“保存”按钮,回到流水线中,在Credentials域选择刚才添加的凭证,如图30所示:

156
图30

保存任务。

(2)编写流水线

Pipeline有两种创建方法——可以直接在Jenkins的Web UI界面中输入脚本;也可以通过创建一个Jenkinsfile脚本文件放入项目源码库中。

一般推荐在Jenkins中直接从源代码控制(SCMD)中直接载入Jenkinsfile Pipeline这种方法。

登录GitLab进入springcloud项目,选择新建文件,如图31所示:

157
图31

将流水线脚本输入到Jenkinsfile中,如图32所示:

158
图32

Pipeline包括声明式语法和脚本式语法。声明式和脚本式的流水线从根本上是不同的。声明式是Jenkins流水线更友好的特性。脚本式的流水线语法,提供更丰富的语法特性。声明式流水线使编写和读取流水线代码更容易设计。

此处选择声明式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
pipeline{
agent none
stages{
stage('mvn-build'){
agent {
docker {
image '10.26.15.244/library/maven'
args '-v /root/.m2:/root/.m2'
}
}
steps{
sh 'cp -rfv /opt/repository /root/.m2/ && ls -l /root/.m2/repository'
sh 'mvn package -DskipTests'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
}
stage('image-build'){
agent any
steps{
sh 'cd gateway && docker build -t 10.26.15.244/springcloud/gateway -f Dockerfile .'
sh 'cd config && docker build -t 10.26.15.244/springcloud/config -f Dockerfile .'
sh 'docker login 10.26.15.244 -u=admin -p=Harbor12345'
sh 'docker push 10.26.15.244/springcloud/gateway'
sh 'docker push 10.26.15.244/springcloud/config'
}
}
stage('cloud-deploy'){
agent any
steps{
sh 'sed -i "s/sqshq\\/piggymetrics-gateway/10.26.15.244\\/springcloud\\/gateway/g" yaml/deployment/gateway-deployment.yaml'
sh 'sed -i "s/sqshq\\/piggymetrics-config/10.26.15.244\\/springcloud\\/config/g" yaml/deployment/config-deployment.yaml'
sh 'kubectl create ns springcloud'
sh 'kubectl apply -f yaml/deployment/gateway-deployment.yaml'
sh 'kubectl apply -f yaml/deployment/config-deployment.yaml'
sh 'kubectl apply -f yaml/svc/gateway-svc.yaml'
sh 'kubectl apply -f yaml/svc/config-svc.yaml'
}
}
}
}

(3)开启Jenkins匿名访问

登录Jenkins首页,选择“系统管理→全局安全配置”,授权策略选择“任何用户可以做任何事(没有任何限制)”,如图33所示。
159
图33

构建CI/CD

(1)触发构建

在GitLab的项目中,通常会使用Webhook的各种事件来触发对应的构建,通常配置好后会向设定好的URL发送post请求。

登录GitLab,进入springcloud项目,现在左侧导航栏“Settings→Webhooks”,将前面记录的GitLab webhook URL地址填入URL处,禁用SSL认证,如图34所示。
160
图34

单击“Add webhook”按钮添加webhook,完成后如图35所示:
161
图35

单击“Test→Push events”按钮进行测试,如图36所示:
162
图36

结果返回HTTP 200则表明Webhook配置成功。

(2)Jenkins查看

登录Jenkins,可以看到springcloud项目已经开始构建,如图37所示:

163
图37

若是执行 kubectl create ns springcloud 报错

Unable to connect to the server: dial tcp: lookup apiserver.cluster.local on 10.96.0.10:53: no such host

修改 /root/.kube/config

把 apiserver.cluster.local 改为 master 节点 IP

选择左侧导航栏“打开Blue Ocean”,如图38所示:
164
图38

Blue Ocean是pipeline的可视化UI,同时兼容经典的自由模式的job。Jenkins Pipeline从头开始设计,但仍与自由式作业兼容,Blue Ocean减少了经典模式下的混乱并为团队中的每个成员增加了清晰度。

单击项目名称springcloud,如图39所示:
165
图39

单击正在构建的pipeline可以查看阶段视图,如图40所示:
166
图40

单击任意“>”符号可查看每个Step的构建详情,如图41所示:
167
图41

若构建成功,Blue Ocean界面会变为绿色。构建完成后如图42所示:
168
图42

退出阶段试图界面,如图43所示:
169
图43

返回Jenkins首页,如图44所示:
170
图44

(3)Harbor查看

进入Harbor仓库springcloud项目查看镜像列表,可以看到已自动上传了一个gateway镜像,如图45所示:
171
图45

(4)Kubernetes查看

Pod的启动较慢,需等待3–5分钟。在命令行查看Pod:

1
2
3
4
[root@master ~]# kubectl -n springcloud get pods
NAME READY STATUS RESTARTS AGE
config-6b6875fffd-p2g7j 1/1 Running 0 3m6s
gateway-5d5f8cc944-vstgm 1/1 Running 0 3m6s

查看service:

1
2
3
4
[root@master ~]# kubectl -n springcloud get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
config NodePort 192.109.170.192 <none> 8888:30015/TCP 3m18s
gateway NodePort 192.110.243.17 <none> 4000:30010/TCP 3m18s

通过端口30010访问服务,如图46所示:
172
图46

至此,完整的CI/CD流程就完成了。