欢迎光临
我们一直在努力

【Linux运维大神系列】k8s基于Jenkins实现CICD

目录

k8s集群部署CICD环境

部署GitLab仓库

部署SonarQube

部署Jenkins

Pipeline流水线

Jenkins Slave环境部署

配置Jenkins动态Slave

测试动态Slave Pod

制作Pod模板镜像

测试Pod模板

Pipeline部署SpringBoot项目

CICD流程介绍

上传项目到仓库

添加GitLab域名解析

添加Jenkins凭据

Pipeline拉取项目

添加Sonar及Jenkins域名解析

Jenkins集成SonarQube

Pipeline项目检测

Pipeline获取检测结果

Pipeline项目编译(Maven)

Pipeline制作镜像

Pipeline部署项目到k8s

Jenkins自动发布项目

Pipeline项目回滚

k8s集群部署CICD环境

部署GitLab仓库

GitLab以容器方式运行需要持久化以下几个目录的数据

本地位置容器位置使用
$PVC/data /var/opt/gitlab 用于存储应用程序数据
$PVC/logs /var/log/gitlab 用于存储日志
$PVC/config /etc/gitlab 用于存储GitLab配置文件

参考地址:https://docs.gitlab.com/ee/install/docker.html

[root@k8s-master01 ~]# kubectl create ns ops
[root@k8s-master01 ~]# mkdir cicd
[root@k8s-master01 ~]# cd cicd

#创建文件
[root@master01 ~/cicd]#cat gitlab.yml
apiVersion: v1
kind: Service
metadata:
name: gitlab-svc
namespace: ops
spec:
clusterIP: None
selector:
app: gitlab
ports:
– name: http
port: 80
targetPort: 80
– name: https
port: 443
targetPort: 443

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: gitlab
namespace: ops
spec:
serviceName: "gitlab-svc" # 关联的headlessService
selector:
matchLabels:
app: gitlab
template:
metadata:
labels:
app: gitlab
spec:
containers:
– name: gitlab-ce
image: gitlab/gitlab-ce:14.1.0-ce.0
env:
– name: GITLAB_ROOT_PASSWORD # 设置GitLab的root密码
value: "admin123"
– name: GITLAB_OMNIBUS_CONFIG # 需要修改gitlab.rb文件中的配置可以在这里配置,每个配置一行,需要定义GitLab的URL,关闭无用组件
value: |
external_url "http://web.gitlab.com"
gitlab_rails['time_zone'] = 'Asia/Shanghai'
node_exporter['enable'] = false
redis_exporter['enable'] = false
postgres_exporter['enable'] = false
gitlab_exporter['enable'] = false
grafana['enable'] = false
grafana['reporting_enabled'] = false
prometheus['enable'] = false
prometheus['monitor_kubernetes'] = false
ports:
– name: http
containerPort: 80
– name: https
containerPort: 443
volumeMounts:
– name: data
mountPath: /etc/gitlab
subPath: config
– name: data
mountPath: /var/opt/gitlab
subPath: data
– name: data
mountPath: /var/log/gitlab
subPath: logs

volumeClaimTemplates:
– metadata:
name: data
spec:
accessModes: ["ReadWriteMany"]
storageClassName: "nfs-client"
resources:
requests:
storage: 50Gi

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitlab-ingress
namespace: ops
spec:
ingressClassName: "nginx"
rules:
– host: "web.gitlab.com"
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: gitlab-svc
port:
name: http

创建pod

#创建pod
[root@master01 ~/cicd]#kubectl create -f gitlab.yml
service/gitlab-svc created
statefulset.apps/gitlab created
ingress.networking.k8s.io/gitlab-ingress created

#查看pod
[root@master01 ~/cicd]#kubectl get pod -n ops
NAME READY STATUS RESTARTS AGE
gitlab-0 1/1 Running 0 41s

配置本地解析,访问GitLab:http://web.gitlab.com/

部署SonarQube

官方地址https://docs.sonarsource.com/sonarqube/latest/setup-and-upgrade/deploy-on-kubernetes/deploy-sonarqube-on-kubernetes/

sonarqube需要postgresql存储数据,先部署postgresql

部署postgresql

apiVersion: v1
kind: Service
metadata:
name: pgsql-svc
namespace: ops
spec:
clusterIP: None
selector:
app: pgsql
ports:
– port: 5432


apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql
namespace: ops
spec:
serviceName: "pgsql-svc"
selector:
matchLabels:
app: pgsql
template:
metadata:
labels:
app: pgsql
spec:
containers:
– name: postgresql
image: postgres:13.8
env:
– name: POSTGRES_DB # 数据库
value: sonardb
– name: POSTGRES_USER # 用户
value: sonar
– name: POSTGRES_PASSWORD # 密码
value: "123456"
ports:
– containerPort: 5432
volumeMounts:
– name: db
mountPath: /var/lib/postgresql/data

volumeClaimTemplates:
– metadata:
name: db
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs-client"
resources:
requests:
storage: 50Gi

#创建Pod
kubectl create -f sonar-pgsql.yml

#进入到pgsql容器
kubectl exec -it postgresql-0 -n ops — /bin/bash

#登录pgsql
root@postgresql-0:/# psql -Usonar -d sonardb

#查看库
sonardb-# \\l

List of databases
Name | Owner | Encoding | Collate | Ctype |
———–+——-+———-+————+————+
postgres | sonar | UTF8 | en_US.utf8 | en_US.utf8 |
sonardb | sonar | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | sonar | UTF8 | en_US.utf8 | en_US.utf8 |
| | | | |
template1 | sonar | UTF8 | en_US.utf8 | en_US.utf8 |
| | | | |

部署sonarqube

sonarqube以容器方式运行需要持久化以下几个目录的数据

本地位置容器位置使用
$PVC/data /opt/sonarqube/data 用于存储应用程序数据
$PVC/logs /opt/sonarqube/logs 用于存储日志
$PVC/config /opt/sonarqube/extensions 用于存储第三方插件

cat sonarqube.yml

apiVersion: v1
kind: Service
metadata:
name: sonarqube-svc
namespace: ops
spec:
clusterIP: None
selector:
app: sonarqube
ports:
– name: web
port: 9000
targetPort: 9000

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sonarqube
namespace: ops
spec:
serviceName: "sonarqube-svc"
selector:
matchLabels:
app: sonarqube
template:
metadata:
labels:
app: sonarqube
spec:
initContainers: # 通过初始容器设置SonarQube的建议值
– name: set-kernel # 容器名称
image: busybox # 镜像
command: ["sh", "-c", "sysctl -w vm.max_map_count=524288 ; sysctl -w fs.file-max=131072 ; ulimit -n 131072 ; ulimit -u 8192"]
securityContext: # 用来控制容器的权限,允许容器在主机操作系统上执行更高权限的操作
privileged: true # 当 privileged 属性设置为 true 时,容器将被授予主机操作系统的完全权限,包括对底层硬件和内核的访问
containers:
– name: sonarqube
image: sonarqube:10.1.0-community
env:
– name: JAVA_OPTS # 设置时区
value: -Duser.timezone=Asia/Shanghai
– name: SONARQUBE_JDBC_USERNAME # 连接pgsql用户名
value: sonar
– name: SONARQUBE_JDBC_PASSWORD # 连接pgsql密码
value: "123456"
– name: SONARQUBE_JDBC_URL # 连接pgsql地址/数据库
value: jdbc:postgresql://pgsql-svc.ops.svc.cluster.local:5432/sonardb
ports:
– name: web
containerPort: 9000
volumeMounts:
– name: data
mountPath: /opt/sonarqube/data
subPath: data
– name: data
mountPath: /opt/sonarqube/logs
subPath: logs
– name: data
mountPath: /opt/sonarqube/extensions
subPath: extensions

volumeClaimTemplates:
– metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs-client"
resources:
requests:
storage: 50Gi

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sonarqube-ingress
namespace: ops
spec:
ingressClassName: "nginx"
rules:
– host: "web.sonarqube.com"
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: sonarqube-svc
port:
name: web
#创建Pod
kubectl create -f sonarqube.yml

#查看pod
[root@master01 ~/cicd]#kubectl get pod -n ops
NAME READY STATUS RESTARTS AGE
gitlab-0 1/1 Running 0 41m
postgresql-0 1/1 Running 0 13m
sonarqube-0 1/1 Running 0 3m33s

配置本地解析,访问sonarqube:http://web.sonarqube.com/,用户名admin,密码admin(新密码123456)

部署Jenkins

参考地址:https://www.jenkins.io/doc/book/installing/kubernetes/

先创建RBAC, 后期Jenkins需要创建 Slave Pod来完成流水线的执行,Slave Pod需要集群一些权限;

cat jenkins-rbac.yml

# serviceaccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins
namespace: ops


# clusterRole
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins
rules:
– apiGroups: ["extensions", "apps"]
resources: ["deployments", "ingresses"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
– apiGroups: [""]
resources: ["services"]
verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
– apiGroups: [""]
resources: ["pods"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
– apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
– apiGroups: [""]
resources: ["pods/log", "events"]
verbs: ["get", "list", "watch"]
– apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]


# clusterrolebinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: jenkins
namespace: ops
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jenkins
subjects:
– kind: ServiceAccount
name: jenkins
namespace: ops

#创建RBAC
kubectl create -f jenkins-rbac.yml

部署jenkins

[root@master01 ~/cicd]#cat jenkins.yml
apiVersion: v1
kind: Service
metadata:
name: jenkins-svc
namespace: ops
spec:
clusterIP: None
selector:
app: jenkins
ports:
– name: http
port: 8080
targetPort: 8080
– name: agent
port: 50000
targetPort: 50000

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: jenkins
namespace: ops
spec:
serviceName: "jenkins-svc"
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccount: jenkins
containers:
– name: jenkins
image: jenkins/jenkins:2.401.3-lts
securityContext: # 用来控制容器的权限,允许容器在主机操作系统上执行更高权限的操作
privileged: true # 当 privileged 属性设置为 true 时,容器将被授予主机操作系统的完全权限,包括对底层硬件和内核的访问
runAsUser: 0 # root身份运行
env:
– name: JAVA_OPTS
value: -Duser.timezone=Asia/Shanghai
ports:
– name: http
containerPort: 8080
– name: agent
containerPort: 50000
readinessProbe: # 就绪探针
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60 # 首次探测时间
timeoutSeconds: 5 # 超时时间
volumeMounts:
– name: data
mountPath: /var/jenkins_home # Jenkins的家目录

volumeClaimTemplates:
– metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "nfs-client"
resources:
requests:
storage: 50Gi

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jenkins-ingress
namespace: ops
spec:
ingressClassName: "nginx"
rules:
– host: "web.jenkins.com"
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: jenkins-svc
port:
name: http

#创建Pod
kubectl create -f jenkins.yml

#查看日志获取jenkins密码
kubectl logs jenkins-0 -n ops

1fc79449abbb472baf25b9cba7e4b6d7

配置本地解析,访问Jenkins:http://web.jenkins.com/

点击选择安装插件

安装Jenkins插件

  • 中文插件: Localization: Chinese

  • Git插件: git、gitlab(实现Jenkins与GitLab的webhook)

  • Sonar插件: SonarQube Scanner(支持与sonarqube集成插件)

  • Pipeline插件: pipeline、pipeline Stage View、Blue Ocean(查看流水线阶段视图插件)

  • Kubernetes插件: Kubernetes(支持与k8s集成的插件)

通过左侧【Manage Jenkins】【插件管理】【Available】安装以上插件,当插件安装完成后,重启Jenkins。

Pipeline流水线

Pipeline 就是通过“ 代码的方式 ” 将多个步骤的任务连接起来共同完成一件事;比如:获取代码->代码扫描->代码编译->制作镜像->推送仓库->部署应用 ,将这些步骤通过代码的方式组织在一起完成这次应用发布,这个就可以称为流水线;

流水线语法:https://www.jenkins.io/zh/doc/book/pipeline/syntax/

pipeline {
//指定任务在哪个集群节点上执行(any表示任意节点)
agent any
//声明全局环境变量,方便后面使用
environment {
key='value'
}

//一个流水线任务的集合,所有任务步骤都在stages中定义
stages {
//一个stage就是一个步骤
stage('拉取git仓库代码') {
//在steps中定义执行的命令
steps {
echo '拉取git仓库代码-SUCCESS'
}
}
stage('通过sonarqube做代码质量检测') {
steps {
echo '代码质量检测-SUCCESS'
}
}
stage('通过maven构建项目') {
steps {
echo '通过maven构建项目-SUCCESS'
}
}
stage('通过docker制作自定义镜像') {
steps {
echo 'docker制作自定义镜像-SUCCESS'
}
}
stage('将自定义镜像推送到harbor仓库中') {
steps {
echo '推送到harbor仓库-SUCCESS'
}
}
stage('在kubernetes执行部署') {
steps {
echo '在kubernetes执行部署-SUCCESS'
}
}
}
}

新建一个流水线任务,点击配置,将流水线语句复制进去,点击立即构建

Jenkins Slave环境部署

所谓 Jenkins Master/Slave 架构,及在Master上进行任务分配。然后由Slave来完成,不过Slave运行方式有两种:

  • 静态SLave:Jenkins Master 和 Jenkins Slave 部署在物理机或者VM中,手动注册到 Master,然后执行任务,任务完成节点处于空闲等待状态;

  • 动态Slave:Jenkins Master 和 Jenkins Slave 以容器形式运行在 Kubernetes 集中,由Master动态创建Slave的Pod,自动注册到 Master,然后执行任务,任务结束Pod自动销毁;

这种方式带来的好处有很多:

  • 服务高可用,当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。

  • 动态伸缩,合理使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。

配置Jenkins动态Slave

在Jenkins的【系统管理】【节点和云管理】【Configure Clouds】

其他配置无需定义,点击下方【Save】即可。

测试动态Slave Pod

在jenkins创建一个流水线,测试能否在k8s集群中拉起一个动态的Slave Pod

进入配置,点击流水线语法

#编写测试流水线
pipeline{
agent {
kubernetes {
cloud 'kubernetes'
}
}

stages{
stage('输出主机名'){
sh 'hostname'
}
stage('等待20秒'){
sh 'sleep 20'
}
}
}

制作Pod模板镜像

jnlp镜像是用来连接Jenkins Master以及共享Master的 WORKSPACE,但该镜像并没有maven、docker、kubectl等常用命令,为此我们需要准备几个镜像,后期通过Pipeline将不同的任务交由同一个Pod的不同的容器来执行。

在harbor主机准备以下镜像

准备maven镜像(需要修改镜像里的一些配置)

[root@harbor ~]# mkdir maven
[root@harbor maven]# cat dockerfile

#基础镜像
FROM maven:3.8.6-openjdk-8

#添加maven配置文件,该文件指向的maven仓库地址是阿里云地址
ADD ./settings_docker.xml /usr/share/maven/conf/settings.xml

#调整系统时区
COPY /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

构建镜像

#构建镜像
[root@harbor maven]# docker build -f dockerfile -t maven:3.8.6 .

#上传镜像到仓库(提前在harbor创建一个ops公开仓库)
[root@harbor ~]# docker tag maven:3.8.6 49.235.163.3/ops/maven:3.8.6
[root@harbor ~]# docker push 49.235.163.3/ops/maven:3.8.6

准备sonar-scanner客户端镜像

sonar-scanner的作用是将拉取过来的代码推送给服务端进行检测(该镜像无需修改,下载后直接上传到harbor仓库即可)

#拉取镜像
[root@harbor ~]# docker pull emeraldsquad/sonar-scanner:2.3.0

#推送仓库
[root@harbor ~]# docker tag 'emeraldsquad/sonar-scanner:2.3.0' '49.235.163.3/ops/sonar-scanner:2.3.0'
[root@harbor ~]# docker push '49.235.163.3/ops/sonar-scanner:2.3.0'

准备nodejs镜像

用于构建java前端项目

[root@harbor ~]# mkdir nodejs
[root@harbor nodejs]# cat dockerfile

#基础镜像
FROM centos:7

#调整时区
COPY /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

#准备nodejs仓库(–silent不显示进度条,–location遇到HTTP重定向时自动跟随,bash – 直接执行前边访问的脚本)
RUN curl –silent –location https://rpm.nodesource.com/setup_14.x |bash –

#安装node.js
RUN yum install nodejs gcc-c++ make vim -y && \\
yum clean all

构建镜像并上传到仓库

[root@harbor nodejs]# docker build -f dockerfile -t 49.235.163.3/ops/nodejs:14.20 .
[root@harbor nodejs]# docker push 49.235.163.3/ops/nodejs:14.20

准备docker客户端镜像

用于构建项目镜像等功能,如:build、push等(该镜像无需修改)

[root@harbor ~]# docker pull docker:20.10
[root@harbor ~]# docker tag docker:20.10 49.235.163.3/ops/docker:20.10
[root@harbor ~]# docker push 49.235.163.3/ops/docker:20.10

准备kubectl镜像

[root@harbor ~]# mkdir kubectl
[root@harbor kubectl]# cat dockerfile

#基础镜像
FROM centos:7
#调整系统时区
COPY ./Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

#添加kubernetes源
ADD ./kubernetes.repo /etc/yum.repos.d/kubernetes.repo

#安装Kubectl
RUN yum makecache && yum install kubectl-1.23.0 -y && \\
yum clean all

构建镜像并上传到仓库

[root@harbor kubectl]# docker build -f dockerfile -t 49.235.163.3/ops/kubectl:1.23.0 .

[root@harbor kubectl]# docker push 49.235.163.3/ops/kubectl:1.23.0

测试Pod模板

在Jenkins中运行一个流水线任务,定义Pod模板中的容器名称以及容器镜像地址,然后定义不同的任务由不同的容器来执行。

pipeline{
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
#定义两个存储卷
volumes:
#使用nfs存储maven下载的依赖
– name: maven_data
nfs:
server: 192.168.0.15
path: /data/maven
#使用hostpath,让docker容器客户端能够使用命令
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
#五个容器
– name: maven
image: 192.168.0.98/ops/maven:3.8.6
#使用cat命令让容器不关闭
command: ["cat"]
tty: true
#挂载
volumeMounts:
– name: maven_data
mountPath: /root/.m2
– name: sonar
image: 192.168.0.98/ops/sonar-scanner:2.3.0
command: ["cat"]
tty: true
– name: nodejs
image: 192.168.0.98/ops/nodejs:14.20
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.98/ops/docker:20.10
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.98/ops/kubectl:1.23.0
command: ["cat"]
tty: true
'''
}
}
stages{
stage('maven测试'){
steps{
container('maven'){
sh 'mvn –version'
}
}
}
stage('nodejs测试'){
steps{
container('nodejs'){
sh 'node -v'
}
}
}
stage('docker测试'){
steps{
container('docker'){
sh 'docker ps'
}
}
}
}
}

在任务中执行流水线,验证结果

Pipeline部署SpringBoot项目

CICD流程介绍

  • 获取代码

  • 漏洞扫描

  • 检测漏洞扫描结果,如果正常则继续,否则就终止

  • 使用maven进行编译、打包

  • 通过dockerfile制作镜像,推送到镜像仓库

  • 手动交付应用到k8s

  • 自动的交付应用到k8s

上传项目到仓库

在GitLab提前创建一个名称为springboot项目仓库

Git全局配置

git config –global user.name "joren"
git config –global user.email "joren@qq.com"

在Harbor主机上传项目到仓库

项目地址:https://pan.baidu.com/s/1Y3RtP6yG3Jm7jbtRC_wuZw?pwd=ycei

#需要配置本地解析(因为harbor独立于k8s集群)
[root@harbor ~]# vim /etc/hosts
#…
192.168.0.13 web.gitlab.com #地址是Ingress代理节点的IP

#解压项目
[root@harbor ~]# unzip spring-boot-helloWorld.zip
[root@harbor ~]# cd spring-boot-helloWorld

#初始化本地仓库并上传项目
git init
git add . #将代码添加到暂存区
git commit -m '测试1' #提交到版本库
git remote add origin http://web.gitlab.com/root/sbringboot.git #添加远程仓库
git remote #查看远程仓库
git push -u origin master #推送到master分支

添加GitLab域名解析

首先在k8s集群添加一个DNS解析记录,用于让Slave Pod可以方便访问GitLab仓库,默认Slave Pod无法访问GitLab

#查看coredns的configMap
[root@k8s-master01 ~]# kubectl get cm -n kube-system

coredns

#查看Ingress的地址及对应的GitLab域名,并将该地址及域名添加到coredns中解析
[root@k8s-master01 ~]# kubectl get ing -n ops
NAME CLASS HOSTS ADDRESS PORTS
gitlab-ingress nginx web.gitlab.com 10.96.104.68 80

#查看Ingress的地址及对应的GitLab域名,并将该地址及域名添加到coredns中解析
[root@k8s-master01 ~]# kubectl get ing -n ops
NAME CLASS HOSTS ADDRESS PORTS
gitlab-ingress nginx web.gitlab.com 10.96.104.68 80

提示:如果添加失败,就删除当前的coredns重新部署。

添加Jenkins凭据

在Jenkins中添加一个全局凭据,用于让Slave Pod可以访问GitLab的私有仓库,在:【系统管理】【凭据管理】

Pipeline拉取项目

在Jenkins中新建任务,通过流水线发布Spring-boot项目

点击配置——流水线语法——片段生成器——选择checkout

定义拉取代码的流水线

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
}
}

在Jenkins的 pipeline-deploy-springboot-k8s 项目中测试语法

到此为止代拉取项目测试成功。

添加Sonar及Jenkins域名解析

在k8s集群添加SonarQube与Jenkins的DNS解析记录,用于让Slave Pod可以方便访问SonarQube进行项目漏洞检查,后续Jenkins需要与Sonarqube集成也需要相互访问

#查看Ingress的地址及对应的GitLab域名,并将该地址及域名添加到coredns中解析
[root@k8s-master01 ~]# kubectl get ing -n ops
NAME CLASS HOSTS ADDRESS PORTS
sonarqube-ingress nginx web.sonarqube.com 10.96.104.68 80
jenkins-ingress nginx web.jenkins.com 10.96.104.68 80

#编辑coredns的configMap,增加DNS解析,添加到hosts中
[root@k8s-master01 ~]# kubectl edit cm coredns -n kube-system
#…
hosts {
10.96.104.68 web.sonarqube.com
10.96.104.68 web.jenkins.com
fallthrough
}
#…

Jenkins集成SonarQube

Jenkins将获取的代码交给SonarQube检测,需要在jenkins中配置集成SonarQube,在【系统管理】【系统配置】

回到jenkins

到此为止,Jenkins就可以集成Sonarqube进行代码检测。

Pipeline项目检测

接下来通过SonarQube进行代码检测,需要让sonar-scanner客户端可以访问SonarQube的服务端,将代码推送到服务端进行检测

定义项目检测的流水线

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=.'
}
}
}
}
}
}

withSonarQubeEnv的值在jenkins中设置的地方如下

在Jenkins的 pipeline-deploy-springboot-k8s 项目中测试语法

在SonarQube的pipeline中添加 -Dsonar.java.binaries=src ,src是源码目录名称

stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=. \\
-Dsonar.java.binaries=src'
}
}
}
}

登录SonarQube查看检测结果

Pipeline获取检测结果

当SonarQube检测完成后,Jenkins需要获取检测的结果是否合格,如果检测不合格,则不执行后续的部署,合格则继续执行后续的流水线任务。

现在SonarQube中配置webhook,通过webhook将检测结果发送至Jenkins

注意!格式为:http://jenkins账号:jenkins密码@jenkins地址/sonarqube-webhook

最后面的sonarqube-webhook是固定的

定义项目检测的流水线

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=. \\
-Dsonar.java.binaries=src'
}
}
}
}
stage('检测结果'){
steps{
container('sonar'){
script{ #定义script脚本
timeout(5){#超时时间
def qg = waitForQualityGate()
if (qg.status != 'OK'){
error "检测失败,失败的原因: ${qg.status}"
}
}
}
}
}
}
}
}

Pipeline项目编译(Maven)

定义编译项目的流水线

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=. \\
-Dsonar.java.binaries=src'
}
}
}
}
stage('检测结果'){
steps{
container('sonar'){
script{
timeout(5){
def qg = waitForQualityGate()
if (qg.status != 'OK'){
error "检测失败,失败的原因: ${qg.status}"
}
}
}
}
}
}
stage('项目编译'){
steps{
container('maven'){
sh 'pwd && ls -l'
sh 'mvn package -Dmaven.test.skip=true' #跳过单元检测,编译打包
}
}
}
}
}

Pipeline制作镜像

提示1:如果Harbor仓库是通过IP地址访问,先进到容器中测试容器是否可以访问到Harbor仓库地址。

提示2:如果Harbor仓库是通过域名访问,在k8s集群中添加私有仓库的域名解析,后续Slave Pod才可以通过域名访问到仓库。

#编辑coredns的configMap,增加DNS解析,添加到hosts中
[root@k8s-master01 ~]# kubectl edit cm coredns -n kube-system
#…
hosts {
192.168.0.25 web.harbor.com
10.96.104.68 web.gitlab.com
10.96.104.68 web.jenkins.com
10.96.104.68 web.sonarqube.com
fallthrough
}
#…

进到容器中验证是否可以访问

定义制作项目镜像的流水线

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=. \\
-Dsonar.java.binaries=src'
}
}
}
}
stage('检测结果'){
steps{
container('sonar'){
script{
timeout(5){
def qg = waitForQualityGate()
if (qg.status != 'OK'){
error "检测失败,失败的原因: ${qg.status}"
}
}
}
}
}
}
stage('项目编译'){
steps{
container('maven'){
sh 'pwd && ls -l'
sh 'mvn package -Dmaven.test.skip=true'
}
}
}
stage('制作镜像'){
steps{
container('docker'){
//登录仓库
sh 'docker login 192.168.0.25 -u admin -p 12345'
//构建镜像,提前创建好springboot公开仓库,构建镜像时在项目仓库有提前准备好的Dockerfile文件
sh 'docker build -f Dockerfile -t 192.168.0.25/springboot/springboot:v1 .'
//上传镜像到仓库,提前在harbor中创建好对应的仓库
sh 'docker push 192.168.0.25/springboot/springboot:v1'
//删除镜像,镜像是在节点上构建,上传到仓库后删除即可
sh 'docker rmi 192.168.0.25/springboot/springboot:v1'
}
}
}
}
}

提示:每次发布项目,镜像的标签都是固定的怎么办

在流水线中增加一个步骤,用于生成镜像的tag

stage('生成镜像Tag'){
steps{
container('maven'){
script{
//获取本次git提交的commit的ID(git log -n1 –pretty=format:%h),并把ID存储到变量中
env.COMMITID = sh(returnStdout: true, script: "git log -n1 –pretty=format:%h").trim()
//获取本次镜像的构建时间(date +%Y%m%d_%H%M),并把时间存储到变量中
env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M").trim()
//完整的镜像Tag
env.ImageTag = COMMITID + "_" + BuildTime
}
}
}
}
stage('制作镜像'){
steps{
container('docker'){
//登录仓库
sh 'docker login 192.168.0.25 -u admin -p 12345'
//标签名字改为定义的变量名
sh 'docker build -f Dockerfile -t 192.168.0.25/springboot/springboot:${ImageTag} .'
//上传镜像到仓库,提前在harbor中创建好对应的仓库
sh 'docker push 192.168.0.25/springboot/springboot:${ImageTag}'
//删除镜像,镜像是在节点上构建,上传到仓库后删除即可
sh 'docker rmi 192.168.0.25/springboot/springboot:${ImageTag}'
}
}
}
}

Pipeline部署项目到k8s

首先要让Slave Pod在集群中可以执行kubectl命令来部署应用,这个我们在前边Pod模板中已经准备了kubectl命令行工具,但是Slave Pod没有管理集群的权限,需要先将集群管理员的配置文件~/.kube/config 在jenkins中创建成凭据,然后在流水线中调用这个凭据

在jenkins中的【系统管理】【凭据管理】添加凭据

在【流水线语法】中将该凭据存储到变量中,然后在流水线中引用变量即可,在【片段生成器】生成语法

将生成的语法定义到流水线中

定义项目的流水线,通过kubectl部署项目

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=. \\
-Dsonar.java.binaries=src'
}
}
}
}
stage('检测结果'){
steps{
container('sonar'){
script{
timeout(5){
def qg = waitForQualityGate()
if (qg.status != 'OK'){
error "检测失败,失败的原因: ${qg.status}"
}
}
}
}
}
}
stage('项目编译'){
steps{
container('maven'){
sh 'pwd && ls -l'
sh 'mvn package -Dmaven.test.skip=true'
}
}
}
stage('生成镜像Tag'){
steps{
container('maven'){
script{
//获取本次git提交的commit的ID(git log -n1 –pretty=format:%h),并把ID存储到变量中
env.COMMITID = sh(returnStdout: true, script: "git log -n1 –pretty=format:%h").trim()
//获取本次镜像的构建时间(date +%Y%m%d_%H%M),并把时间存储到变量中
env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M").trim()
//完整的镜像Tag
env.ImageTag = COMMITID + "_" + BuildTime
}
}
}
}
stage('制作镜像'){
steps{
container('docker'){
//登录仓库
sh 'docker login 192.168.0.25 -u admin -p 12345'
//构建镜像,提前创建好springboot公开仓库,构建镜像时在项目仓库有提前准备好的Dockerfile文件
sh 'docker build -f Dockerfile -t 192.168.0.25/springboot/springboot:${ImageTag} .'
//上传镜像到仓库,提前在harbor中创建好对应的仓库
sh 'docker push 192.168.0.25/springboot/springboot:${ImageTag}'
//删除镜像,镜像是在节点上构建,上传到仓库后删除即可
sh 'docker rmi 192.168.0.25/springboot/springboot:${ImageTag}'
}
}
}
stage('项目部署'){
steps{
container('kubectl'){
withCredentials([file(credentialsId: 'kubernetes-admin-config', variable: 'KUBECONFIG')]) {
sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
//替换deploy.yaml文件中的{NameSpace}部分,替换成实际的名称空间,名称空间提前创建好
sh 'sed -i "s#{NameSpace}#springboot#g" deploy.yaml'
//替换deploy.yaml文件中的{Image}部分,替换成实际的镜像名称
sh 'sed -i "s#{Image}#192.168.0.25/springboot/springboot:${ImageTag}#g" deploy.yaml'
//部署应用
sh 'kubectl apply -f deploy.yaml'
}
}
}
}
}
}

在Jenkins的 pipeline-deploy-springboot-k8s 项目中测试语法

在k8s集群确认项目

[root@k8s-master01 ~]# kubectl get pod -n springboot
NAME READY STATUS RESTARTS AGE
springboot-84fd4dbdc5-xjdlb 1/1 Running 0 4m17s

#查看ingress
[root@k8s-master01 ~]# kubectl get ing -n springboot
NAME CLASS HOSTS ADDRESS PORTS AGE
springboot-ingress nginx web.springboot-demo.com 10.96.104.68 80 9m48s

配置域名解析后,访问项目验证:http://web.springboot-demo.com/

Jenkins自动发布项目

本实验实现只要GitLab的master分支有新代码提交,Jenkins自动触发Build Now(立即构建)功能,实现代码自动发布。

在任务中,增加 构建触发器 的功能,选择它并设置对应触发事件,具体配置如下图:

在GitLab中设置webhook通知,webhook 是一种程序之间进行实时通信的方式,它能在某个事件发生时,一个程序可以立即通知另一个程序。回到GitLab的 springboot 项目中点击左侧 设置→webhooks

解决方法:通过 Menu→Admin→设置→网络→外发请求 ,勾选 允许Webhook和服务对本地网络的请求 如下图

设置好后,再重新添加webhook就好了

接下来可以将本地代码修改后push到GitLab仓库,检测是否会触发自动构建

[root@harbor controller]# pwd
/root/spring-boot-helloWorld/src/main/java/com/neo/controller

#修改代码
[root@harbor controller]# vim HelloWorldController.java
package com.neo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {

@RequestMapping("/")
public String index() {
return "Hello Spring Boot 2.3!";
}
}

#推送gitlab
[root@harbor spring-boot-helloWorld]# git add .
[root@harbor spring-boot-helloWorld]# git commit -m '测试3'
[root@harbor spring-boot-helloWorld]# git push -u origin master

确认项目推送成功后,在Jenkins中查看是否自动触发构建动作,并访问确认是否发布成功

提示:后续部署应用到其他名称空间,只需要在Pipeline中修改名称空间即可(删除重复的Ingress解析)。

Pipeline项目回滚

对于部署的到生产环境的项目,一旦出现问题,我们可以在流水线中增加一个快速回滚的步骤,这样即使出现问题,我们可以快速回滚到上一个版本。

任务——配置——流水线语法——片段生成器步骤如下:

然后还要增加一个交互的步骤

定义项目的流水线,增加版本回退功能

pipeline {
//在名为kubernetes的Agent中执行流水线
agent {
kubernetes {
cloud 'kubernetes'
yaml '''
apiVersion: v1
kind: Pod
spec:
volumes:
– name: data
nfs:
server: 192.168.0.15
path: /data/maven
– name: dockersocket
hostPath:
path: /run/docker.sock
containers:
– name: maven
image: 192.168.0.25/ops/maven:3.8.6
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: data
mountPath: /root/.m2
– name: nodejs
image: 192.168.0.25/ops/nodejs:14.20
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: sonar
image: 192.168.0.25/ops/sonar-scanner:2.3.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
– name: docker
image: 192.168.0.25/ops/docker:20.10
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
volumeMounts:
– name: dockersocket
mountPath: /run/docker.sock
– name: kubectl
image: 192.168.0.25/ops/kubectl:1.23.0
imagePullPolicy: IfNotPresent
command: ["cat"]
tty: true
'''
}
}

//定义全局变量
environment{
//把GitLab仓库地址定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_URL = 'http://web.gitlab.com/root/sbringboot.git'

//把认证的token定义成变量更加灵活一些,注意引用变量时用双引号
Gitlab_token = 'gitlab-root-token'
}

stages {
stage('拉取项目') {
steps {
container('maven'){
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "$Gitlab_token", url: "$Gitlab_URL"]])

//查看项目文件
sh 'ls -l'

//查看当前项目所在路径
sh 'pwd'
}
}
}
stage('项目检测'){
steps{
//withSonarQubeEnv是集成SonarQube属性,用于设置一组环境变量,并引用前边在jenkins中创建的sonar-k8s令牌就可以访问SonarQube,并通过以下方式对项目进行检测
withSonarQubeEnv('sonar-k8s'){
container('sonar'){
sh 'sonar-scanner \\
-Dsonar.projectKey=Pipeline-pipeline-deploy-springboot-k8s \\
-Dsonar.sources=. \\
-Dsonar.java.binaries=src'
}
}
}
}
stage('检测结果'){
steps{
container('sonar'){
script{
timeout(5){
def qg = waitForQualityGate()
if (qg.status != 'OK'){
error "检测失败,失败的原因: ${qg.status}"
}
}
}
}
}
}
stage('项目编译'){
steps{
container('maven'){
sh 'pwd && ls -l'
sh 'mvn package -Dmaven.test.skip=true'
}
}
}
stage('生成镜像Tag'){
steps{
container('maven'){
script{
//获取本次git提交的commit的ID(git log -n1 –pretty=format:%h),并把ID存储到变量中
env.COMMITID = sh(returnStdout: true, script: "git log -n1 –pretty=format:%h").trim()
//获取本次镜像的构建时间(date +%Y%m%d_%H%M),并把时间存储到变量中
env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M").trim()
//完整的镜像Tag
env.ImageTag = COMMITID + "_" + BuildTime
}
}
}
}
stage('制作镜像'){
steps{
container('docker'){
//登录仓库
sh 'docker login 192.168.0.25 -u admin -p 12345'
//构建镜像,提前创建好springboot公开仓库,构建镜像时在项目仓库有提前准备好的Dockerfile文件
sh 'docker build -f Dockerfile -t 192.168.0.25/springboot/springboot:${ImageTag} .'
//上传镜像到仓库,提前在harbor中创建好对应的仓库
sh 'docker push 192.168.0.25/springboot/springboot:${ImageTag}'
//删除镜像,镜像是在节点上构建,上传到仓库后删除即可
sh 'docker rmi 192.168.0.25/springboot/springboot:${ImageTag}'
}
}
}
stage('项目部署'){
steps{
container('kubectl'){
withCredentials([file(credentialsId: 'kubernetes-admin-config', variable: 'KUBECONFIG')]) {
sh 'mkdir -p ~./kube && cp ${KUBECONFIG} ~./kube/config'
//替换deploy.yaml文件中的{NameSpace}部分,替换成实际的名称空间,名称空间提前创建好
sh 'sed -i "s#{NameSpace}#springboot#g" deploy.yaml'
//替换deploy.yaml文件中的{Image}部分,替换成实际的镜像名称
sh 'sed -i "s#{Image}#192.168.0.25/springboot/springboot:${ImageTag}#g" deploy.yaml'
//部署应用
sh 'kubectl apply -f deploy.yaml'
}
}
}
}
stage('快速回滚'){
steps{
container('kubectl'){
withCredentials([file(credentialsId: 'kubernetes-admin-config', variable: 'KUBECONFIG')]) {
sh 'mkdir -p ~./kube && cp ${KUBECONFIG} ~./kube/config'
//在步骤中定义脚本
script{
//定义回滚的超时时间为1小时,意为1小时之内可以执行回滚动作
timeout(time:1 ,unit: 'HOURS'){
//def用于定义函数,函数名是自定义的,函数里的参数是流水线语法中生成的
def UserInput = input message: '是否回退到上一个版本', parameters: [choice(choices: ['No', 'Yes'], name: 'rollback')]
//增加条件判断,如果输入Yes则执行回滚动作
if (UserInput == 'Yes'){
//如果选择Yes则执行回滚到上一个版本的命令
sh 'kubectl rollout undo deploy springboot -n springboot'
}
//如果没有输入Yes,则不执行回滚
else{
sh 'echo 没有选择回滚'
}
}
}
}
}
}
}
}
}

在Jenkins的 pipeline-deploy-springboot-k8s 项目中测试语法

然后可以在一个小时之内,选择是否回滚

赞(0)
未经允许不得转载:171主机测评 » 【Linux运维大神系列】k8s基于Jenkins实现CICD
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址