基于Jenkins+k8s+Git+Harbor构建企业级DevOps容器云平台

1、k8s助力企业DevOps在企业落地实践

1.1 传统方式部署项目为什么发布慢,效率低?

image-20221026185646953

1.2 上线一个功能,有多少时间被浪费了

image-20221026185947073

1.3 如何解决上述问题

image-20221026190206097

image-20221026190347349

1.3 什么是DevOps

image-20221026190438303

image-20221026190449080

1.3.1 敏捷开发

提高开发效率,及时跟进用户需求,缩短开发周期。

敏捷开发包括编写代码和构建代码两个阶段,可以使用git或者svn来管理代码,用maven对代码进行构建

1.3.2 持续集成

持续集成强调开发人员提交了新代码之后,立刻自动的进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。持续集成过程中很重视自动化测试验证结果,对可能出现的一些问题进行预警,以保障最终合并的代码没有问题。

常见的持续集成工具:

  1. Jenkins

Jenkins是用Java语言编写的,是目前使用最多和最受欢迎的持续集成工具,使用Jenkins,可以自动监测到git或者svn存储库代码的更新,基于最新的代码进行构建,把构建好的源码或者镜像发布到生产环境。Jenkins还有个非常好的功能:它可以在多台机器上进行分布式地构建和负载测试

  1. TeamCity

  2. Travis CI

  3. Go CD

  4. Bamboo

  5. GitLab CI

  6. Codeship

image-20221026191244428

它的好处主要有以下几点:

1)较早的发现错误:每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,哪个环节出现问题都可以较早的发现

2)快速的发现错误:每完成一部分代码的更新,就会把代码集成到主干中,这样就可以快速的发现错误,比较容易的定位错误

3)提升团队绩效:持续集成中代码更新速度快,能及时发现小问题并进行修改,使团队能创造出更好的产品

4)防止分支过多的偏离主干:经常持续集成,会使分支代码经常向主干更新,当单元测试失败或者出现bug,如果开发者需要在没有调试的情况下恢复仓库的代码到没有bug的状态,只有很小部分的代码会丢失

持续集成的目的是提高代码质量,让产品快速的更新迭代。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。

Martin Fowler说过,”持续集成并不能消除Bug,而是让它们非常容易发现和改正。”

马丁·福勒是一个软件开发方面的著作者和国际知名演说家,专注于面向对象分析与设计,统一建模语言,领域建模,以及敏捷软件开发方法,包括极限编程。

1.3.3 持续交付

持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的「类生产环境」(production-like environments)中。交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。

如果所有的代码完成之后一起交付,会导致很多问题爆发出来,解决起来很麻烦,所以持续集成,也就是没更新一次代码,都向下交付一次,这样可以及时发现问题,及时解决,防止问题大量堆积。

1.3.4 持续部署

持续部署是指当交付的代码通过评审之后,自动部署到生产环境中。持续部署是持续交付的最高阶段。

Puppet,SaltStack和Ansible是这个阶段使用的流行工具。容器化工具在部署阶段也发挥着重要作用。 Docker和k8s是流行的工具,有助于在开发,测试和生产环境中实现一致性。 除此之外,k8s还可以实现自动扩容缩容等功能。

image-20221026191831931

2、K8S在DevOps中的核心作用

Docker和K8S的出现使DevOps变得更加普及,更加容易实现。在传统运维中我们服务时需要针对不同的环境去安装不同的版本,部署方式也杂、多。那么有了docker之后,一次构建、到处运行,我们只需要要构建一次镜像,那么只要有docker的主机,就可以基于镜像把应用跑起来。

互动:docker可以实现DevOps的这个思想,但是存在一个问题,什么问题呢?

在众多微服务中,我们每天可能需要去处理各种服务的崩溃,而服务间的依赖调用关系也及其复杂,这对我们解决问题带来了很大的复杂度。要很好的解决这个问题。我们就需要用到容器编排工具。

Kubernetes 的出现主宰了容器编排的市场,也进化了过去的运维方式,将开发与运维联系的更加紧密。而且让 DevOps 这一角色变得更加清晰,它是目前可用的很流行的容器解决方案之一。

2.1 自动化

敏捷开发->持续集成->持续交付->持续部署

2.2 多集群管理

可以根据客户需求对开发、测试,生产环江部署多套kubernetes集群,每个环境使用独立的物理资源,避免相互之间互相影响

2.3 多环境一致性

Kubernetes是基于docker的容器编排工具,因为容器的镜像是不可变的,所以镜像把OS、业务代码、运行环境、程序库、目录结构都包含在内,镜像保存在我们的私有仓库,只要用户从我们提供的私有仓库拉取镜像,就能保证环境的一致性。

2.4 实时反馈和智能化报表

每次集成或交付,都会第一时间将结果通过多途径的方式反馈给你,也可以定制适合企业专用的报表平台。

3、安装Jenkins

3.1 安装和配置nfs

1
2
3
4
yum install nfs-utils -y 
exportfs -arv
exporting *:/root/nfs_data
chown 1000:1000 -R nfs_data/

3.2 创建名称空间

1
2
[root@master1 ~]# kubectl create ns jenkins-k8s
namespace/jenkins-k8s created

3.3 创建pv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@master1 k8s_jenkins]# cat create_pv.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-k8s-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.106.30
path: /root/nfs_data/


[root@master1 k8s_jenkins]# kubectl apply -f create_pv.yaml
persistentvolume/jenkins-k8s-pv created


[root@master1 k8s_jenkins]# kubectl get pv | grep jenkins-k8s-pv
jenkins-k8s-pv 10Gi RWX Retain Available 108s

3.4 创建pvc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@master1 k8s_jenkins]# cat create_pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-k8s-pvc
namespace: jenkins-k8s
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi


[root@master1 k8s_jenkins]# kubectl apply -f create_pvc.yaml
persistentvolumeclaim/jenkins-k8s-pvc created

3.5 创建一个sa账号

1
2
[root@master1 k8s_jenkins]# kubectl create sa jenkins-k8s-sa -n jenkins-k8s
serviceaccount/jenkins-k8s-sa created

3.6 授权新创建的sa账号

1
2
[root@master1 k8s_jenkins]# kubectl create clusterrolebinding jenkins-k8s-sa-cluster -n jenkins-k8s  --clusterrole=cluster-admin --serviceaccount=jenkins-k8s:jenkins-k8s-sa
clusterrolebinding.rbac.authorization.k8s.io/jenkins-k8s-sa-cluster created

3.7 通过deployment部署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
[root@master1 k8s_jenkins]# cat jenkins_deploy.yaml 
kind: Deployment
apiVersion: apps/v1
metadata:
name: jenkins
namespace: jenkins-k8s
spec:
replicas: 1
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccount: jenkins-k8s-sa
containers:
- name: jenkins
image: jenkins/jenkins:2.328
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkins-volume
subPath: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-volume
persistentVolumeClaim:
claimName: jenkins-k8s-pvc


[root@master1 k8s_jenkins]# kubectl apply -f jenkins_deploy.yaml
deployment.apps/jenkins created


#在nfs服务器上进行操作
chown 1000:1000 -R nfs_data/


[root@master1 k8s_jenkins]# kubectl get pods -n jenkins-k8s
NAME READY STATUS RESTARTS AGE
jenkins-99fd4d66f-2v5m4 1/1 Running 0 2m8s

3.8 创建Jenkins的svc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@master1 k8s_jenkins]# cat jenkins_svc.yaml 
apiVersion: v1
kind: Service
metadata:
name: jenkins-service
namespace: jenkins-k8s
labels:
app: jenkins
spec:
selector:
app: jenkins
type: NodePort
ports:
- name: web
port: 8080
targetPort: web
nodePort: 30002
- name: agent
port: 50000
targetPort: agent


[root@master1 k8s_jenkins]# kubectl apply -f jenkins_svc.yaml
service/jenkins-service created

3.9 登录Jenkins web界面

1
2
/var/jenkins_home/secrets/initialAdminPassword
8e789fe428d0400f9f6aa72555eb615a

image-20221026234126042

3.10 在jenkins中安装kuberbetes、blueocean插件

image-20221027094342631

image-20221027094418466

3.11 配置jenkins访问k8s集群

image-20221027094602524

image-20221027094822096

点击连接测试,如果显示Connection test successful或者Connected to Kubernetes v1.20.6,说明测试成功,Jenkins可以和k8s进行通信

image-20221028194154084

1
http://jenkins-service.jenkins-k8s.svc.cluster.local:8080

配置k8s集群的时候jenkins地址需要写上面域名的形式,配置之后执行如下:

应用——>保存

3.12 配置pod-template

1
http://192.168.106.23:30002/configureClouds/

image-20221028194626063

image-20221028194631844

image-20221028194637970

image-20221028194644981

在上面的pod template下添加容器

添加容器——>Container Template——>按如下配置——>

image-20221028194721283

在每一个pod template右下脚都有一个高级,点击高级

image-20221028194824480

在Service Account处输入jenkins-k8s-sa,这个sa就是我们最开始安装jenkins时的sa

(3)给上面的pod template添加卷

添加卷——>选择Host Path Volume

image-20221028202846790

/var/run/docker.sock

/var/run/docker.sock

/root/.kube

/home/jenkins/.kube

上面配置好之后,Apply(应用)——>Save(保存)

image-20221028202913853

添加自己的dockerhub凭据

首页——>系统管理àManage Credentials(管理凭据)——>点击Stores scoped to Jenkins下的第一行jenkins后的全局,显示如下

image-20221028203102062

image-20221028203159841

3.13 测试通过Jenkins部署应用发布到k8s开发环境、测试环境、生产环境

开发提交代码到代码仓库gitlab->jenkins检测到代码更新-à调用k8s api在k8s中创建jenkins slave pod:

Jenkins slave pod拉取代码—à通过maven把拉取的代码进行构建成war包或者jar包—>上传代码到Sonarqube,进行静态代码扫描- –>基于war包构建docker image–>把镜像上传到harbor镜像仓库–>基于镜像部署应用到开发环境–>部署应用到测试环境—>部署应用到生产环境。

image-20221028203513024

3.13.1 创建名称空间

1
2
3
4
5
6
[root@master1 ~]# kubectl create ns devlopment
namespace/development created
[root@master1 ~]# kubectl create ns production
namespace/production created
[root@master1 ~]# kubectl create ns qatest
namespace/qatest created

开始创建一个新任务—>流水线

image-20221028212906124

3.13.2 Pipeline script

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
node('test') {
stage('Clone') {
echo "1.Clone Stage"
git url: "https://github.com/vorabend/jenkins-sample.git"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
stage('Test') {
echo "2.Test Stage"

}
stage('Build') {
echo "3.Build Docker Image Stage"
sh "docker build -t vorabend/jenkins-demo:${build_tag} ."
}
stage('Push') {
echo "4.Push Docker Image Stage"
withCredentials([usernamePassword(credentialsId: 'dockerhub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"
sh "docker push vorabend/jenkins-demo:${build_tag}"
}
}
stage('Deploy to dev') {
echo "5. Deploy DEV"
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev.yaml"
// sh "bash running-devlopment.sh"
sh "kubectl apply -f k8s-dev.yaml --validate=false"
}
stage('Promote to qa') {
def userInput = input(
id: 'userInput',

message: 'Promote to qa?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-qa.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-qa.yaml"
// sh "bash running-qa.sh"
sh "kubectl apply -f k8s-qa.yaml --validate=false"
sh "sleep 6"
sh "kubectl get pods -n qatest"
} else {
//exit
}
}
stage('Promote to pro') {
def userInput = input(

id: 'userInput',
message: 'Promote to pro?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod.yaml"
// sh "bash running-production.sh"
sh "cat k8s-prod.yaml"
sh "kubectl apply -f k8s-prod.yaml --record --validate=false"
}
}
}

3.13.3 jenkins-variable-test-deploy-rollout

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
node('test') {
stage('git clone') {
git url: "https://github.com/vorabend/jenkins-rollout"
sh "ls -al"
sh "pwd"
}
stage('select env') {
def envInput = input(
id: 'envInput',
message: 'Choose a deploy environment',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "devlopment\nqatest\nproduction",
name: 'Env'
]
]
)
echo "This is a deploy step to ${envInput}"
sh "sed -i 's/<namespace>/${envInput}/' getVersion.sh"
sh "sed -i 's/<namespace>/${envInput}/' rollout.sh"
sh "bash getVersion.sh"
// env.WORKSPACE = pwd()
// def version = readFile "${env.WORKSPACE}/version.csv"
// println version
}
stage('select version') {
env.WORKSPACE = pwd()
def version = readFile "${env.WORKSPACE}/version.csv"
println version
def userInput = input(id: 'userInput',
message: '选择回滚版本',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "$version\n",
name: 'Version'
]
]
)
sh "sed -i 's/<version>/${userInput}/' rollout.sh"
}
stage('rollout deploy') {
sh "bash rollout.sh"
}
}

4、jenkins pipeline介绍

Jenkins pipeline (流水线)是一套运行于jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排与可视化。它把持续提交流水线(Continuous Delivery Pipeline)的任务集成到Jenkins中。

pipeline 是jenkins2.X 最核心的特性, 帮助jenkins 实现从CI到CD与DevOps的转变。

持续提交流水线(Continuous Delivery Pipeline)会经历一个复杂的过程: 从版本控制、向用户和客户提交软件,软件的每次变更(提交代码到仓库)到软件发布(Release)。这个过程包括以一种可靠并可重复的方式构建软件,以及通过多个测试和部署阶段来开发构建好的软件(称为Build)。

总结:

1.Jenkins Pipeline是一组插件,让Jenkins可以实现持续交付管道的落地和实施。
2.持续交付管道(CD Pipeline)是将软件从版本控制阶段到交付给用户或客户的完
整过程的自动化表现。
3.软件的每一次更改(提交到源代码管理系统)都要经过一个复杂的过程才能被发布。

4.1 为什么用Jenkins Pipeline?

本质上,Jenkins 是一个自动化引擎,它支持许多自动模式。 Pipeline向Jenkins中添加了一组强大的工具, 支持简单的CI到全面的CD pipeline。通过对一系列的相关任务进行建模, 用户可以利用pipeline的很多特性:

1、代码:Pipeline以代码的形式实现,使团队能够编辑,审查和迭代其CD流程。
2、可持续性:Jenkins重启或者中断后都不会影响Pipeline Job。
3、停顿:Pipeline可以选择停止并等待人工输入或批准,然后再继续Pipeline运行。
4、多功能:Pipeline支持现实复杂的CD要求,包括循环和并行执行工作的能力。
5、可扩展:Pipeline插件支持其DSL的自定义扩展以及与其他插件集成的多个选项。

DSL 是什么?

DSL 其实是 Domain Specific Language 的缩写,中文翻译为领域特定语言(下简称 DSL);而与 DSL相对的就是GPL,这里的GPL并不是我们知道的开源许可证,而是 General Purpose Language的简称,即通用编程语言,也就是我们非常熟悉的 Objective-C、Java、Python 以及 C 语言等等。

4.2 jenkins pipeline入门

pipeline脚本是由groovy 语言实现的

但无需专门学习 groovy

pipeline支持两种语法:

 -Declarative:声明式

 -Scripted pipeline :脚本式

4.2.1 声明式pipeline语法:

官网:

1
https://www.jenkins.io/doc/book/pipeline/syntax/

声明式语法包括以下核心流程:

1.pipeline : 声明其内容为一个声明式的pipeline脚本
2.agent: 执行节点(job运行的slave或者master节点)
3.stages: 阶段集合,包裹所有的阶段(例如:打包,部署等各个阶段)
4.stage: 阶段,被stages包裹,一个stages可以有多个stage
5.steps: 步骤,为每个阶段的最小执行单元,被stage包裹
6.post: 执行构建后的操作,根据构建结果来执行对应的操作

根据上面流程创建一个简单的pipeline

Pipeline:

作用域:应用于全局最外层,表明该脚本为声明式pipeline
是否必须:必须

agent:

作用域:可用在全局与stage内

agent表明此pipeline在哪个节点上执行
是否必须:是
参数:any,none, label, node,docker,dockerfile

agent any

#运行在任意的可用节点上

agent none

#全局不指定运行节点,由各自stage来决定

agent { label ‘master’ }

#运行在指定标签的机器上,具体标签名称由agent配置决定

1
2
3
4
5
6
agent { 
node {
label 'my-defined-label'
customWorkspace 'xxxxxxx'
}
}

#node{ label ‘master’} 和agent { label ‘master’ }一样,但是node可以扩展节点信息,允许额外的选项 (比如 customWorkspace )。

agent { docker ‘python’ }

#使用指定的容器运行流水线

1
2
3
4
5
6
7
agent {
docker {
image 'maven:3-alpine'
label 'my-defined-label'
args '-v /tmp:/tmp'
}
}

#定义此参数时,执行Pipeline或stage时会动态的在具有label ‘my-defined-label’标签的node提供docker节点去执行Pipelines。 docker还可以接受一个args,直接传递给docker run调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pipeline{   
agent any
stages{
stage("This is first stage"){
steps("This is first step"){
echo "This is a test"
}
}
}
post{
always{
echo "The process is ending"
}
}
}

4.2.2 Pipeline 声明式语法:environment

environment指令指定一系列键值对,这些键值对将被定义为所有step或stage-specific step的环境变量,具体取决于environment指令在Pipeline中的位置。该指令支持一种特殊的方法**credentials()**,可以通过其在Jenkins环境中的标识符来访问预定义的凭据。对于类型为“Secret Text”的凭据,该 **credentials()**方法将确保指定的环境变量包含Secret Text内容;对于“标准用户名和密码”类型的凭证,指定的环境变量将被设置为username:password并且将自动定义两个附加的环境变量:MYVARNAME_USRMYVARNAME_PSW

image-20221031101047102

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pipeline {
agent any
environment {
CC = 'clang' #此变量属于全局变量,可以在所有的step中使用
}
stages {
stage('Example') {
steps {
sh 'printenv'
sh 'hostname'
sh 'df -Th'
}
}
}
}

在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
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/pipeline-dec
[Pipeline] {
[Pipeline] withEnv
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Example)
[Pipeline] sh
+ printenv
JENKINS_HOME=/var/jenkins_home
KUBERNETES_PORT=tcp://10.10.0.1:443
KUBERNETES_SERVICE_PORT=443
JENKINS_UC_EXPERIMENTAL=https://updates.jenkins.io/experimental
CI=true
RUN_CHANGES_DISPLAY_URL=http://192.168.106.23:30002/job/pipeline-dec/8/display/redirect?page=changes
HOSTNAME=jenkins-99fd4d66f-2v5m4
SHLVL=0
NODE_LABELS=built-in
HUDSON_URL=http://192.168.106.23:30002/
HOME=/var/jenkins_home
BUILD_URL=http://192.168.106.23:30002/job/pipeline-dec/8/
HUDSON_COOKIE=54c49d57-c51c-43f5-b180-878199a885a4
JENKINS_SERVER_COOKIE=durable-d9414f3be5acaca373a0ff62ab3139106681e82fe79d6f8231fe369bf18c0c27
JENKINS_UC=https://updates.jenkins.io
WORKSPACE=/var/jenkins_home/workspace/pipeline-dec
REF=/usr/share/jenkins/ref
NODE_NAME=built-in
RUN_ARTIFACTS_DISPLAY_URL=http://192.168.106.23:30002/job/pipeline-dec/8/display/redirect?page=artifacts
STAGE_NAME=Example
EXECUTOR_NUMBER=0
JENKINS_SERVICE_PORT_50000_TCP_ADDR=10.10.78.99
RUN_TESTS_DISPLAY_URL=http://192.168.106.23:30002/job/pipeline-dec/8/display/redirect?page=tests
BUILD_DISPLAY_NAME=#8
JENKINS_VERSION=2.328
KUBERNETES_PORT_443_TCP_ADDR=10.10.0.1
JENKINS_INCREMENTALS_REPO_MIRROR=https://repo.jenkins-ci.org/incrementals
HUDSON_HOME=/var/jenkins_home
JOB_BASE_NAME=pipeline-dec
PATH=/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
JENKINS_SERVICE_PORT_8080_TCP_ADDR=10.10.78.99
JENKINS_SERVICE_SERVICE_HOST=10.10.78.99
JENKINS_SERVICE_PORT_50000_TCP_PORT=50000
BUILD_ID=8
JENKINS_SERVICE_SERVICE_PORT_AGENT=50000
KUBERNETES_PORT_443_TCP_PORT=443
BUILD_TAG=jenkins-pipeline-dec-8
JENKINS_SERVICE_PORT_50000_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PROTO=tcp
JENKINS_URL=http://192.168.106.23:30002/
JENKINS_SERVICE_PORT_8080_TCP_PORT=8080
LANG=C.UTF-8
JOB_URL=http://192.168.106.23:30002/job/pipeline-dec/
JENKINS_SERVICE_PORT_8080_TCP_PROTO=tcp
BUILD_NUMBER=8
JENKINS_NODE_COOKIE=f71b460a-a136-400f-8335-9446defdd1da
RUN_DISPLAY_URL=http://192.168.106.23:30002/job/pipeline-dec/8/display/redirect
JENKINS_SERVICE_SERVICE_PORT=8080
JENKINS_SERVICE_PORT=tcp://10.10.78.99:8080
JENKINS_SLAVE_AGENT_PORT=50000
HUDSON_SERVER_COOKIE=d9f003d95652fd08
JOB_DISPLAY_URL=http://192.168.106.23:30002/job/pipeline-dec/display/redirect
JENKINS_SERVICE_PORT_50000_TCP=tcp://10.10.78.99:50000
KUBERNETES_PORT_443_TCP=tcp://10.10.0.1:443
KUBERNETES_SERVICE_PORT_HTTPS=443
JOB_NAME=pipeline-dec
COPY_REFERENCE_FILE_LOG=/var/jenkins_home/copy_reference_file.log
PWD=/var/jenkins_home/workspace/pipeline-dec
JENKINS_SERVICE_PORT_8080_TCP=tcp://10.10.78.99:8080
JENKINS_SERVICE_SERVICE_PORT_WEB=8080
JAVA_HOME=/opt/java/openjdk
KUBERNETES_SERVICE_HOST=10.10.0.1
WORKSPACE_TMP=/var/jenkins_home/workspace/pipeline-dec@tmp
CC=clang
[Pipeline] sh
+ hostname
jenkins-99fd4d66f-2v5m4
[Pipeline] sh
+ df -Th
Filesystem Type Size Used Avail Use% Mounted on
overlay overlay 508G 8.5G 500G 2% /
tmpfs tmpfs 64M 0 64M 0% /dev
tmpfs tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
192.168.106.30:/root/nfs_data/jenkins-home nfs4 500G 3.0G 498G 1% /var/jenkins_home
/dev/mapper/centos-root xfs 508G 8.5G 500G 2% /etc/hosts
shm tmpfs 64M 0 64M 0% /dev/shm
tmpfs tmpfs 3.9G 12K 3.9G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs tmpfs 3.9G 0 3.9G 0% /proc/acpi
tmpfs tmpfs 3.9G 0 3.9G 0% /proc/scsi
tmpfs tmpfs 3.9G 0 3.9G 0% /sys/firmware
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

4.2.3 options

​ options指令允许在Pipeline本身内配置Pipeline专用选项。Pipeline本身提供了许多选项,例如buildDiscarder,但它们也可能由插件提供,例如 timestamps。

image-20221101105436896

可用选项

buildDiscarder: pipeline保持构建的最大个数。用于保存Pipeline最近几次运行的数据,例如:options { buildDiscarder(logRotator(numToKeepStr: ‘1’)) }
disableConcurrentBuilds: 不允许并行执行Pipeline,可用于防止同时访问共享资源等。例如:options { disableConcurrentBuilds() }
 skipDefaultCheckout:跳过默认设置的代码check out。例如:options { skipDefaultCheckout() }
 skipStagesAfterUnstable: 一旦构建状态进入了“Unstable”状态,就跳过此stage。例如:options { skipStagesAfterUnstable() }
 timeout: 设置Pipeline运行的超时时间,超过超时时间,job会自动被终止,例如:options { timeout(time: 1, unit: ‘HOURS’) }
 retry: 失败后,重试整个Pipeline的次数。例如:options { retry(3) }
 timestamps: 预定义由Pipeline生成的所有控制台输出时间。例如:options { timestamps() }

1
2
3
4
5
6
7
8
9
10
11
12
13
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}

测试脚本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/pipeline-options
[Pipeline] {
[Pipeline] timeout
Timeout set to expire in 1 hr 0 min
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Example)
[Pipeline] echo
Hello World
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // timeout
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

4.2.4 parameters

parameters指令提供用户在触发Pipeline时的参数列表。这些参数值通过该params对象可用于Pipeline stage中,具体用法如下:

image-20221101112541329

作用域:被最外层pipeline所包裹,并且只能出现一次,参数可被全局使用

好处:使用parameters好处是能够使参数也变成code,达到pipeline as code,pipeline中设置的参数会自动在job构建的时候生成,形成参数化构建

可用参数
 string
   A parameter of a string type, for example: parameters { string(name: ‘DEPLOY_ENV’, defaultValue: ‘staging’, description: ‘’) }
 booleanParam
   A boolean parameter, for example: parameters { booleanParam(name: ‘DEBUG_BUILD’, defaultValue: true, description: ‘’) }
 目前只支持[booleanParam, choice, credentials, file, text, password, run, string]这几种参数类型,其他高级参数化类型还需等待社区支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pipeline{
agent any
parameters {
string(name: 'zy', defaultValue: 'my name is zy', description: 'My name is zy')
booleanParam(name: 'zhangyu', defaultValue: true, description: 'This is my name')
}
stages{
stage("stage1"){
steps{
echo "$zy"
echo "$zhangyu"
}
}
}
}

测试脚本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/pipeline-paramters
[Pipeline] {
[Pipeline] stage
[Pipeline] { (stage1)
[Pipeline] echo
my name is zy
[Pipeline] echo
true
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

4.2.5 triggers

triggers指令定义了Pipeline自动化触发的方式。目前有三个可用的触发器:cron和pollSCM和upstream。

image-20221101202815685

用域:被pipeline包裹,在符合条件下自动触发pipeline

cron
 接受一个cron风格的字符串来定义Pipeline触发的时间间隔,例如:

triggers { cron(‘H 4/* 0 0 1-5’) }

pollSCM
 接受一个cron风格的字符串来定义Jenkins检查SCM源更改的常规间隔。如果存在新的更改,则Pipeline将被重新触发。例如:triggers { pollSCM(‘H 4/* 0 0 1-5’) }

1
2
3
4
5
6
7
8
9
10
11
12
13
pipeline {
agent any
triggers {
cron('H 4/* 0 0 1-5')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}

4.2.6 tools

通过tools可自动安装工具,并放置环境变量到PATH。如果agent none,这将被忽略。

image-20221101203251616

Supported Tools(Global Tool Configuration)
 maven
 jdk
 gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pipeline {
agent any
tools {
#工具名称必须在Jenkins 管理Jenkins → 全局工具配置中预配置。
maven 'apache-maven-3.0.1'
}
stages {
stage('Example') {
steps {
sh 'mvn --version'
}
}
}
}
# The tool name must be pre-configured in Jenkins under Manage Jenkins → Global Tool Configuration

4.2.7 input

stage 的 input 指令允许你使用 input step提示输入。 在应用了 options 后,进入 stage 的 agent 或评估 when 条件前,stage 将暂停。 如果 input 被批准, stage 将会继续。 作为 input 提交的一部分的任何参数都将在环境中用于其他 stage

配置项

message

必需的。 这将在用户提交 input 时呈现给用户。

id

input 的可选标识符, 默认为 stage 名称。

ok

input表单上的”ok” 按钮的可选文本。

submitter

可选的以逗号分隔的用户列表或允许提交 input 的外部组名。默认允许任何用户。

submitterParameter

环境变量的可选名称。如果存在,用 submitter 名称设置。

parameters

提示提交者提供的一个可选的参数列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pipeline {
agent any
stages {
stage('Example') {
input {
message "Should we continue?"
ok "Yes, we should."
submitter "zy,lucky"
parameters {
string(name: 'PERSON', defaultValue: 'zy', description: 'Who should I say hello to?')
}
}
steps {
echo "Hello, ${PERSON}, nice to meet you."
}
}
}
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/pipeline-input
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Example)
[Pipeline] input
Input requested
Approved by admin
[Pipeline] withEnv
[Pipeline] {
[Pipeline] echo
Hello, zy, nice to meet you.
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

4.2.8 when

 when指令允许Pipeline根据给定的条件确定是否执行该阶段。该when指令必须至少包含一个条件。如果when指令包含多个条件,则所有子条件必须为stage执行返回true。这与子条件嵌套在一个allOf条件中相同(见下面的例子)。
更复杂的条件结构可使用嵌套条件建:not,allOf或anyOf。嵌套条件可以嵌套到任意深度。

image-20221101204403707

内置条件

​ branch
   当正在构建的分支与给出的分支模式匹配时执行,例如:when { branch ‘master’ }。请注意,这仅适用于多分支Pipeline。
 environment
   当指定的环境变量设置为给定值时执行,例如: when { environment name: ‘DEPLOY_TO’, value: ‘production’ }
 expression
   当指定的Groovy表达式求值为true时执行,例如: when { expression { return params.DEBUG_BUILD } }
 not
   当嵌套条件为false时执行。必须包含一个条件。例如:when { not { branch ‘master’ } }
 allOf
   当所有嵌套条件都为真时执行。必须至少包含一个条件。例如:when { allOf { branch ‘master’; environment name: ‘DEPLOY_TO’, value: ‘production’ } }
 anyOf
   当至少一个嵌套条件为真时执行。必须至少包含一个条件。例如:when { anyOf { branch ‘master’; branch ‘staging’ } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
allOf {
branch 'production'
environment name: 'DEPLOY_TO', value: 'production'
}
}
steps {
echo 'Deploying'
}
}
}
}

4.2.9 Parallel

Declarative Pipeline近期新增了对并行嵌套stage的支持,对耗时长,相互不存在依赖的stage可以使用此方式提升运行效率。除了parallel stage,单个parallel里的多个step也可以使用并行的方式运行。

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
pipeline {
agent any
stages {
stage('Non-Parallel Stage') {
steps {
echo 'This stage will be executed first.'
}
}
stage('Parallel Stage') {
when {
branch 'master'
}
parallel {
stage('Branch A') {
agent {
label "for-branch-a"
}
steps {
echo "On Branch A"
}
}
stage('Branch B') {
agent {
label "for-branch-b"
}
steps {
echo "On Branch B"
}
}
}
}
}

4.3 Pipeline scripted 语法

Groovy脚本不一定适合所有使用者,因此jenkins创建了Declarative pipeline,为编写Jenkins管道提供了一种更简单、更有主见的语法。但是由于脚本化的pipeline是基于groovy的一种DSL语言,所以与Declarative pipeline相比为jenkins用户提供了更巨大的灵活性和可扩展性。

4.3.1 流程控制

pipeline脚本同其它脚本语言一样,从上至下顺序执行,它的流程控制取决于Groovy表达式,如if/else条件语句,举例如下:

1
2
3
4
5
6
7
8
9
node {
stage('Example') {
if (env.BRANCH_NAME == 'master') {
echo 'I only execute on the master branch'
} else {
echo 'I execute elsewhere'
}
}
}

4.4 Declarative pipeline和Scripted pipeline的比较

共同点:
 两者都是pipeline代码的持久实现,都能够使用pipeline内置的插件或者插件提供的stage,两者都可以利用共享库扩展。
区别:
 两者不同之处在于语法和灵活性。Declarative pipeline对用户来说,语法更严格,有固定的组织结构,更容易生成代码段,使其成为用户更理想的选择。但是Scripted pipeline更加灵活,因为Groovy本身只能对结构和语法进行限制,对于更复杂的pipeline来说,用户可以根据自己的业务进行灵活的实现和扩展。

5、jenkins+k8s+harbor实现DevOps

5.1 在Jenkins中添加harbor凭据

1
http://192.168.106.23:30002/credentials/store/system/domain/_/newCredentials

image-20221101210454133

5.2 编写Jenkins pipeline

因为镜像要上传到harbor私有镜像仓库,所以需要在harbor上创建一个项目,项目名称是jenkins-demo,如下所示:

image-20221101212138745

新建一个任务——>输入一个任务名称处输入jenkins-harbor——>流水线——>确定——>在Pipeline script处输入如下内容

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
node('test') {
stage('Clone') {
echo "1.Clone Stage"
git url: "https://github.com/vorabend/jenkins-sample.git"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
stage('Test') {
echo "2.Test Stage"

}
stage('Build') {
echo "3.Build Docker Image Stage"
sh "docker build -t 192.168.106.21/jenkins-demo/jenkins-demo:${build_tag} ."
}
stage('Push') {
echo "4.Push Docker Image Stage"
withCredentials([usernamePassword(credentialsId: 'dockerharbor', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
sh "docker login 192.168.106.21 -u ${dockerHubUser} -p ${dockerHubPassword}"
sh "docker push 192.168.106.21/jenkins-demo/jenkins-demo:${build_tag}"
}
}
stage('Deploy to dev') {
echo "5. Deploy DEV"
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev-harbor.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev-harbor.yaml"
// sh "bash running-devlopment.sh"
sh "kubectl apply -f k8s-dev-harbor.yaml --validate=false"
}
stage('Promote to qa') {
def userInput = input(
id: 'userInput',

message: 'Promote to qa?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-qa-harbor.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-qa-harbor.yaml"
// sh "bash running-qa.sh"
sh "kubectl apply -f k8s-qa-harbor.yaml --validate=false"
sh "sleep 6"
sh "kubectl get pods -n qatest"
} else {
//exit
}
}
stage('Promote to pro') {
def userInput = input(

id: 'userInput',
message: 'Promote to pro?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod-harbor.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod-harbor.yaml"
// sh "bash running-production.sh"
sh "cat k8s-prod-harbor.yaml"
sh "kubectl apply -f k8s-prod-harbor.yaml --record --validate=false"
}
}
}

测试代码

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
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Still waiting to schedule task
‘test-915fx’ is offline
Agent test-kklcw is provisioned from template test
---
apiVersion: "v1"
kind: "Pod"
metadata:
labels:
jenkins: "slave"
jenkins/label-digest: "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
jenkins/label: "test"
name: "test-kklcw"
namespace: "jenkins-k8s"
spec:
containers:
- env:
- name: "JENKINS_SECRET"
value: "********"
- name: "JENKINS_AGENT_NAME"
value: "test-kklcw"
- name: "JENKINS_NAME"
value: "test-kklcw"
- name: "JENKINS_AGENT_WORKDIR"
value: "/home/jenkins/agent"
- name: "JENKINS_URL"
value: "http://jenkins-service.jenkins-k8s.svc.cluster.local:8080/"
image: "xianchao/jenkins-jnlp:v1"
imagePullPolicy: "IfNotPresent"
name: "jnlp"
resources:
limits: {}
requests: {}
tty: true
volumeMounts:
- mountPath: "/home/jenkins/.kube"
name: "volume-1"
readOnly: false
- mountPath: "/var/run/docker.sock"
name: "volume-0"
readOnly: false
- mountPath: "/home/jenkins/agent"
name: "workspace-volume"
readOnly: false
workingDir: "/home/jenkins/agent"
hostNetwork: false
nodeSelector:
kubernetes.io/os: "linux"
restartPolicy: "Never"
serviceAccountName: "jenkins-k8s-sa"
volumes:
- hostPath:
path: "/run/docker.sock"
name: "volume-0"
- hostPath:
path: "/root/.kube"
name: "volume-1"
- emptyDir:
medium: ""
name: "workspace-volume"

Running on test-kklcw in /home/jenkins/agent/workspace/jenkins-harbor
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Clone)
[Pipeline] echo
1.Clone Stage
[Pipeline] git
The recommended git tool is: NONE
No credentials specified
Cloning the remote Git repository
Cloning repository https://github.com/vorabend/jenkins-sample.git
> git init /home/jenkins/agent/workspace/jenkins-harbor # timeout=10
Fetching upstream changes from https://github.com/vorabend/jenkins-sample.git
> git --version # timeout=10
> git --version # 'git version 2.11.0'
> git fetch --tags --progress -- https://github.com/vorabend/jenkins-sample.git +refs/heads/*:refs/remotes/origin/* # timeout=10
Avoid second fetch
Checking out Revision fa8fe9a9fe430e8b01d6eeba8ec8748df8485fa9 (refs/remotes/origin/master)
> git config remote.origin.url https://github.com/vorabend/jenkins-sample.git # timeout=10
> git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
> git rev-parse refs/remotes/origin/master^{commit} # timeout=10
> git config core.sparsecheckout # timeout=10
> git checkout -f fa8fe9a9fe430e8b01d6eeba8ec8748df8485fa9 # timeout=10
> git branch -a -v --no-abbrev # timeout=10
> git checkout -b master fa8fe9a9fe430e8b01d6eeba8ec8748df8485fa9 # timeout=10
Commit message: "Update k8s-prod-harbor.yaml"
> git rev-list --no-walk fa8fe9a9fe430e8b01d6eeba8ec8748df8485fa9 # timeout=10
[Pipeline] script
[Pipeline] {
[Pipeline] sh
+ git rev-parse --short HEAD
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] echo
2.Test Stage
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Build)
[Pipeline] echo
3.Build Docker Image Stage
[Pipeline] sh
+ docker build -t 192.168.106.21/jenkins-demo/jenkins-demo:fa8fe9a .
Sending build context to Docker daemon 182.8kB

Step 1/5 : FROM golang:1.10.4-alpine
---> bd36346540f3
Step 2/5 : ADD . /go/src/app
---> 98a8e12316ef
Step 3/5 : WORKDIR /go/src/app
---> Running in 7347102f918d
Removing intermediate container 7347102f918d
---> 3e592ccb3e95
Step 4/5 : RUN go build -v -o /go/src/app/jenkins-app
---> Running in 05b4398d4924
app
Removing intermediate container 05b4398d4924
---> 3aaed6e21425
Step 5/5 : CMD ["./jenkins-app"]
---> Running in c8af5e8d68d0
Removing intermediate container c8af5e8d68d0
---> 52bc7b4f0384
Successfully built 52bc7b4f0384
Successfully tagged 192.168.106.21/jenkins-demo/jenkins-demo:fa8fe9a
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Push)
[Pipeline] echo
4.Push Docker Image Stage
[Pipeline] withCredentials
Masking supported pattern matches of $dockerHubPassword
[Pipeline] {
[Pipeline] sh
Warning: A secret was passed to "sh" using Groovy String interpolation, which is insecure.
Affected argument(s) used the following variable(s): [dockerHubPassword]
See https://jenkins.io/redirect/groovy-string-interpolation for details.
+ docker login 192.168.106.21 -u admin -p ****
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/jenkins/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[Pipeline] sh
+ docker push 192.168.106.21/jenkins-demo/jenkins-demo:fa8fe9a
The push refers to repository [192.168.106.21/jenkins-demo/jenkins-demo]
458946453feb: Preparing
18a2205f79a7: Preparing
ac1c7fa88ed0: Preparing
cc5fec2c1edc: Preparing
93448d8c2605: Preparing
c54f8a17910a: Preparing
df64d3292fd6: Preparing
df64d3292fd6: Waiting
c54f8a17910a: Waiting
18a2205f79a7: Pushed
93448d8c2605: Pushed
ac1c7fa88ed0: Pushed
c54f8a17910a: Pushed
458946453feb: Pushed
df64d3292fd6: Pushed
cc5fec2c1edc: Pushed
fa8fe9a: digest: sha256:47642efdfa34bdde22be762db8ff7c3f969eacaba4a788ddc891acac41204b66 size: 1785
[Pipeline] }
[Pipeline] // withCredentials
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Deploy to dev)
[Pipeline] echo
5. Deploy DEV
[Pipeline] sh
+ sed -i s/<BUILD_TAG>/fa8fe9a/ k8s-dev-harbor.yaml
[Pipeline] sh
+ sed -i s/<BRANCH_NAME>/null/ k8s-dev-harbor.yaml
[Pipeline] sh
+ kubectl apply -f k8s-dev-harbor.yaml --validate=false
deployment.apps/jenkins-demo configured
service/demo unchanged
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Promote to qa)
[Pipeline] input
Input requested
Approved by admin
[Pipeline] echo
This is a deploy step to YES
[Pipeline] sh
+ sed -i s/<BUILD_TAG>/fa8fe9a/ k8s-qa-harbor.yaml
[Pipeline] sh
+ sed -i s/<BRANCH_NAME>/null/ k8s-qa-harbor.yaml
[Pipeline] sh
+ kubectl apply -f k8s-qa-harbor.yaml --validate=false
deployment.apps/jenkins-demo configured
service/demo unchanged
[Pipeline] sh
+ sleep 6
[Pipeline] sh
+ kubectl get pods -n qatest
NAME READY STATUS RESTARTS AGE
jenkins-demo-575cd7896b-df4jk 0/1 Terminating 5 3d5h
jenkins-demo-74fd584b5f-zdlmh 1/1 Running 0 8s
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Promote to pro)
[Pipeline] input
Input requested
Approved by admin
[Pipeline] echo
This is a deploy step to YES
[Pipeline] sh
+ sed -i s/<BUILD_TAG>/fa8fe9a/ k8s-prod-harbor.yaml
[Pipeline] sh
+ sed -i s/<BRANCH_NAME>/null/ k8s-prod-harbor.yaml
[Pipeline] sh
+ cat k8s-prod-harbor.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-demo
namespace: production
spec:
replicas: 1
selector:
matchLabels:
app: jenkins-demo
template:
metadata:
labels:
app: jenkins-demo
spec:
containers:
- image: 192.168.106.21/jenkins-demo/jenkins-demo:fa8fe9a
imagePullPolicy: IfNotPresent
name: jenkins-demo
env:
- name: branch
value: null
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-demo
namespace: production
spec:
selector:
app: jenkins-demo
type: NodePort
ports:
- port: 18888
targetPort: 18888
nodePort: 31890
[Pipeline] sh
+ kubectl apply -f k8s-prod-harbor.yaml --record --validate=false
deployment.apps/jenkins-demo configured
service/jenkins-demo configured
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

6、Jenkins接入Sonarqube

6.1 在node2上安装Sonarqube

1
2
3
4
5
6
7
8
9
10
11
#注意:修改 vm.max_map_count = 655360
[root@node2 ~]# cat /etc/sysctl.conf | grep vm.max_map_count
vm.max_map_count = 655360
[root@node2 ~]# sysctl -p



[root@node2 ~]# docker run -dt --name postgres10 -p 5432:5432 -e POSTGRES_USER=sonar -e POSTGRES_PASSWORD=123456 postgres


[root@node2 ~]# docker run -d --name sonarqube7.9 -p 9000:9000 --link postgres10 -e SONARQUBE_JDBC_URL=jdbc:postgresql://postgres10:5432/sonar -e SONARQUBE_JDBC_USERNAME=sonar -e SONARQUBE_JDBC_PASSWORD=123456 -v sonarqube_conf:/opt/sonarqube/conf -v sonarqube_extensions:/opt/sonarqube/extensions -v sonarqube_logs:/opt/sonarqube/logs -v sonarqube_data:/opt/sonarqube/data sonarqube

6.2 在Jenkins中安装sonarqube插件

安装插件Sonarqube Scanner

image-20221102224607073

image-20221102230334595

6.3 访问Sonarqube web界面创建token

1
http://192.168.106.22:9000/admin/users

image-20221103213622191

image-20221103213705831

image-20221103213736958

把copy后面的一串token记录下来

1
24282ae327541a70abf505f108d88fa64ff7bd6d

6.4 连接Sonarqube web

在控制节点上解压压缩包microservic-test.zip,以下命令成不成功,取决于版本是否合适

1
[root@master1 microservic-test]# mvn sonar:sonar   -Dsonar.host.url=http://192.168.106.22:9000 -Dsonar.login=24282ae327541a70abf505f108d88fa64ff7bd6d

7、Jenkins+k8s+nexus+gitlab+harbor+sonarqube+springloud构建DevOps

7.1 安装nexus

概念:

Nexus服务器是一个代码包管理的服务器,可以理解 Nexus 服务器是一个巨大的 Library 仓库。Nexus 可以支持管理的工具包括 Maven , npm 等,对于 JAVA 开发来说,只要用到 Maven 管理就可以了。

Nexus服务器作用:因为传统的中央仓库在国外,其地理位置比较远,下载速度比较缓慢。因此,当公司开发人员数量越来越多时,如果不架设一台自己的Nexus服务器,会产生大量的流量阻塞带宽,并且在出现一些不可抗原因(光缆被挖断)导致无法连接到中央仓库时,开发就会因为无法下载相关依赖包而进度停滞。因此在本地环境部署一台私有的Nexus服务器来缓存所有依赖包,并且将公司内部开发的私有包也部署上去,方便其他开发人员下载,是非常有必要的。因为 Nexus 有权限控制,因此外部人员是无法得到公司内部开发的项目包的。

1
[root@node2 ~]# docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 -v /etc/localtime:/etc/localtime --name nexus3   sonatype/nexus3

在日志中,会看到一条消息: Started Sonatype Nexus OSS 3.20.1-01 这意味着Nexus Repository Manager可以使用了。

1
2
[root@node2 ~]# docker logs nexus3 | grep -i "Started Sonatype Nexus OSS 3.37.3-02"
Started Sonatype Nexus OSS 3.37.3-02

image-20221104160127232