[TOC]

0x01 Jenkins之K8s集群部署单war包应用

实验目的
描述: 采用Jenkins从Gitlab中拉取测试项目以及代码质量检测,然后进行编译构建并利用K8s集群进行WAR包应用部署实践,最后将生成的制品进行归档到Gitlab的Release之中;

动态配置文件或者脚本:

  • setting.xml (Maven编译构建配置文件)
  • HelloWorld.yaml (应用部署资源清单)
  • options.sh (部署脚本)


前期准备

描述: 假定你已经按照我前面几篇文章中安装好基础环境。

(0) HelloWorld - Tomcat 应用服务器

  • Step 1.基础镜像拉取于上传到私有仓库中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [email protected]:~/k8s/sonarqube$ docker tag tomcat:8.5.61-jdk8-corretto harbor.weiyigeek.top/devops/tomcat:8.5.61-jdk8-corretto
    [email protected]:~/k8s/sonarqube$ docker push harbor.weiyigeek.top/devops/tomcat:8.5.61-jdk8-corretto
    The push refers to repository [harbor.weiyigeek.top/devops/tomcat]
    f13244e4918b: Pushed
    536f15f78828: Pushed
    1c98b6d16fad: Pushed
    fdef502bf282: Pushed
    cee8d35c645b: Pushed
    8.5.61-jdk8-corretto: digest: sha256:110d7391739e868daf8c1bdd03fcb7ffe9eaf2b134768b9162e2cd47d58f7255 size: 1368
  • Step 2.资源清单文件

    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
    cat > HelloWorld.yaml <<'END'
    apiVersion: v1
    kind: Service
    metadata:
    name: deploy-maven-svc
    spec:
    type: NodePort # Service 类型
    selector:
    app: java-maven # 【注意】与deployment资源控制器创建的Pod标签进行绑定;
    release: stabel # Service 服务发现不能缺少Pod标签,有了Pod标签才能与之SVC对应
    ports: # 映射端口
    - name: http
    port: 8080 # cluster 访问端口
    targetPort: 8080 # Pod 容器内的服务端口
    nodePort: 30089
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
    name: deploy-java-maven
    spec:
    serviceName: "deploy-maven-svc"
    replicas: 3 # 副本数
    selector: # 选择器
    matchLabels:
    app: java-maven # 匹配的Pod标签非常重要
    release: stabel
    template:
    metadata:
    labels:
    app: java-maven # 模板标签
    release: stabel
    spec:
    volumes: # 关键点
    - name: webapps # 卷名称
    hostPath: # 采用hostPath卷
    type: DirectoryOrCreate # 卷类型DirectoryOrCreate: 如果子节点上没有该目录便会进行创建
    path: /nfsdisk-31/test/ # 各主机节点上已存在的目录此处是NFS共享
    - name: timezone # 容器时区设置
    hostPath:
    path: /usr/share/zoneinfo/Asia/Shanghai
    containers:
    - name: java-maven
    image: harbor.weiyigeek.top/devops/tomcat:8.5.61-jdk8-corretto # 拉取的镜像
    imagePullPolicy: IfNotPresent
    command: ["bash","-c","cp /tmp/${APPNAME} /usr/local/tomcat/webapps/ROOT.war && catalina.sh run"]
    env:
    - name: APPNAME
    value: HelloWorld-v1.40.war
    ports:
    - name: http # 此端口在服务中的名称
    containerPort: 8080 # 容器暴露的端口
    volumeMounts: # 挂载指定卷目录
    - name: webapps # Tomcat 应用目录
    mountPath: /tmp/
    - name: logs # Tomcat 日志目录
    mountPath: /usr/local/tomcat/logs
    - name: timezone # 镜像时区设置
    mountPath: /usr/share/zoneinfo/Asia/Shanghai
    volumeClaimTemplates: # 卷的体积要求模板此处采用StorageClass存储类主要针对于应用日志的存储;
    - metadata: # 根据模板自动创建PV与PVC并且进行一一对应绑定;
    name: logs
    spec:
    accessModes: [ "ReadWriteOnce" ]
    storageClassName: managed-nfs-storage # StorageClass存储类
    resources:
    requests:
    storage: 1Gi
    END


(1) Nginx - Web服务器

  • Step 1.Nginx 配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    [email protected]:/# grep -v "#" /etc/nginx/conf.d/default.conf
    server {
    listen 80;
    listen [::]:80;
    server_name rel.weiyigeek.top;
    location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    }
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root /usr/share/nginx/html;
    }
    }
  • Step 2.将Jenkins构建的Release进行上传在Nginx 中进行归档所以需要资源清单进行部署;

    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
    cat > jenkins-release-nginx.yaml <<'END'
    # apiVersion: extensions/v1beta1
    # kind: Ingress
    # metadata:
    # annotations:
    # kubernetes.io/ingress.class: nginx
    # labels:
    # app: release
    # name: jenkins-release
    # namespace: devops
    # spec:
    # rules:
    # - host: rel.weiyigeek.top
    # http:
    # paths:
    # - backend:
    # serviceName: release-svc
    # servicePort: 80
    # path: /
    # ---
    apiVersion: v1
    kind: Service
    metadata:
    name: release-svc
    namespace: devops
    spec:
    type: NodePort
    selector:
    app: release
    ports:
    - name: http
    port: 80
    targetPort: 80
    nodePort: 30003
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: nginxconf
    namespace: devops
    data:
    default.conf: |
    server {
    listen 80;
    listen [::]:80;
    server_name rel.weiyigeek.top;
    location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    }
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root /usr/share/nginx/html;
    }
    }

    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: release-deployment
    namespace: devops
    labels:
    app: release
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: release
    template:
    metadata:
    labels:
    app: release
    spec:
    containers:
    - name: release-nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
    containerPort: 80
    volumeMounts:
    - name: conf
    mountPath: /etc/nginx/conf.d/
    readOnly: false
    - name: data
    mountPath: /usr/share/nginx/html/
    readOnly: false
    volumes:
    - name: conf
    configMap:
    name: nginxconf
    defaultMode: 0755
    - name: data
    hostPath:
    type: DirectoryOrCreate
    path: /nfsdisk-31/release
    END
  • Step 3.访问验证

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 1.测试文件
    ~$ cd /nfsdisk-31/release && touch index.html
    # echo "Hello World, Boy or Girl;" > index.html

    # 2.deployments状态查询与SVC
    ~$ kubectl get deployments.apps -n devops release-deployment -o wide
    # NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
    # release-deployment 1/1 1 1 4d11h release-nginx nginx:latest app=release
    ~$ kubectl get svc -n devops release-svc
    # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
    # release-svc NodePort 10.106.107.201 <none> 80:30003/TCP 4d11h

    # 3.访问验证
    ~/k8s/nginx$ curl rel.weiyigeek.top/index.html
    ~/k8s/nginx$ curl http://192.168.12.108:30003/index.html
    # Hello World, Boy or Girl;


(2) Jenkins Pipeline 脚本

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
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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
// @ ssh 登陆链接函数
def getSSH() {
def remote = [:]
remote.name = "K8S#${DEPLOY_HOST}"
remote.user = "${DEPLOY_USER}"
remote.host = "${DEPLOY_HOST}"
remote.port = 20211
remote.allowAnyHosts = true
withCredentials([string(credentialsId: "${DEPLOY_CREDENTIALSID}", variable: 'sshpassword')]) {
remote.password = sshpassword
}
return remote
}

// @ Secure 密钥获取
def getSecure(Ticket_Token) {
def token = ""
withCredentials([string(credentialsId: Ticket_Token, variable: 'secure_token')]) {
token = secure_token
}
println token
return token
}

pipeline {
/* Job 工作节点的绑定 */
agent {
// 1) 采用K8s动态生成Jnlp的Jenkins 节点
kubernetes {
cloud 'kubernetes'
namespace 'devops'
inheritFrom 'jenkins-slave'
workingDir '/home/jenkins/agent'
// yamlFile 'KubernetesPod.yaml' // 指定 yaml 资源清单其内容包含在yaml对象中
yaml """\
apiVersion:
kind: Pod
metadata:
labels:
jenkins: "slave"
jenkins/label: 'k8s-slave'
spec:
containers:
- name: 'jnlp'
image: 'harbor.weiyigeek.top/devops/jenkins-jnlp:3.13.6-alpine'
imagePullPolicy: 'IfNotPresent'
command: ["/bin/sh","-c","/usr/local/bin/jenkins-agent.sh && cat"]
tty: true
volumeMounts:
- mountPath: "/home/jenkins/.m2"
name: "volume-0"
- mountPath: "/var/run/docker.sock"
name: "volume-1"
""".stripIndent()
}
}

/* 全局选项 */
options {
// Gitlab 相关设置(连接凭据与Pipeline步骤)
gitLabConnection('Gitlab-weiyigeek')
gitlabBuilds(builds: ['代码拉取', '代码检测', '项目构建','项目部署','成品归档','消息通知'])

// 全局超时设置 30分钟
timeout(time: 30, unit: 'MINUTES')
}

/* Job 自动触发构建参数 */
triggers {
// gitlab 项目进行 git Push或者是Comment 触发构建
gitlab(
triggerOnPush: false,
triggerOnMergeRequest: true,
triggerOpenMergeRequestOnPush: "never",
noteRegex: "jenkins build",
branchFilterType: 'All'
)
}

/*
全局参数, 在shell可通过变量名访问,而在script pipeline脚本中通过params.参数名称访问. ${params.RELEASE_VERSION}
*/
parameters {
string(name: 'RELEASE_VERSION', defaultValue: "master", description: 'Message: 请选择部署的Tags版本?',trim: 'True')
choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')
choice(name: 'SONARQUBE', choices: ['False','True'],description: 'Message: 是否进行代码质量检测?')
choice(name: 'GITLAB_RELEASE', choices: ['False','True'],description: 'Message: 是否进行编译成品发布到Gitlab开发项目Release中?')
}


/*
环境变量设置, 在shell可通过变量名访问,而在script pipeline脚本中通过env.参数名称访问. ${env.GITLAB_URL}
*/
environment {
// # GitLab 仓库相关信息(修改点1)
GITLAB_URL = 'ssh://[email protected]:2222/weiyigeek/helloworld.git'
GITLAB_PROJECTID = '45'
GITLAB_PUB = '5e2f46d9-6725-4399-847f-dafb3f29d0ce'
GITLAB_TOKEN = 'ce228573-ad3d-40e8-a3bf-b9de510080db'
GITLAB_REL = "http://gitlab.weiyigeek.top/api/v4/projects/${GITLAB_PROJECTID}/releases"
GITLAB_DOWNLOAD = "http://192.168.12.107:30003/${JOB_NAME}/${APP_NAME}"

// # 应用名称&后缀以及集成结果(修改点2)
APP_NAME = "${JOB_NAME}-${RELEASE_VERSION}${APP_SUFFIX}"
APP_SUFFIX = ".war"
APP_FILE = 'target/*.war'
APP_DIR = '/nfsdisk-31/test/'
// # Kubernetes 部署应用相关名称空间以及标签和SVC地址
K8S_NAMESPACE = "default"
K8S_SELECTOR = "app=java-maven"
KS8_ADDRESS = "http://192.168.12.107:30089"

// # 应用部署主机(修改点3)
def SERVER = ''
DEPLOY_CREDENTIALSID = "b846141f-55c2-4b46-98a7-29b9e76af3cc" //12.107
DEPLOY_HOST = "192.168.12.107"
DEPLOY_USER = "root"
DEPLOY_PORT = 20211

// # 代码质量检测项目名称和检测反馈超时时间
SONARQUBE_CREDENTIALSID = "18d24c8f-9772-4b3e-934c-f80ed0e96cde"
SONARQUBE_PROJECTKEY = "${JOB_NAME}"
SONARQUBE_TIMEOUT = '10';

// # 企业微信WebHookURL 1150-曾老师
QYWX_WEBHOOK = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=7d7f46fa-28c6-40b3-9c45-de243ec51150'
}


/* Job 步骤状态 */
stages {
stage ('代码拉取') {
steps {
// (1) 代码超时时间设置 5 分钟
timeout(time: 5, unit: 'MINUTES') {
script {
try {
// (2) Gitlab 项目拉取
checkout([$class: 'GitSCM', branches: [[name: "${params.RELEASE_VERSION}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${env.GITLAB_PUB}", url: "${env.GITLAB_URL}"]]])
// (3) 流水线状态同步
updateGitlabCommitStatus name: '代码拉取', state: 'success'
} catch(Exception err) {
updateGitlabCommitStatus name: '代码拉取', state: 'failed'
// (4) 显示错误信息但还是会进行下一阶段操作(err.toString() == unstable '拉取代码失败')
echo err.toString()
// 将会停止构建
error "[-Error] : 代码拉取失败\n [-Msg] : ${err.getMessage()} "
}

// (5) 显示当前版本Commit信息以及可用Tag版本
// def INFO = sh label: 'release_info',returnStdout: true, returnStatus: true, script: """\
// echo "Git Commit:" && \
// git branch && \
// git log --oneline | grep "${params.RELEASE_VERSION}" && \
// echo "Available Tag Version:" && \
// git tag -l --column;
// """
// """
// git tag -l --column | tr -d ' ' > tag ;
// export a="\$(sed "s#v#, v#g" tag)'";
// echo [\${a#,*}]
// """

// (6) 输出预定变量值
echo "[-Info] : RELEASE_VERSION: ${params.RELEASE_VERSION} , PREJECT_OPERATION: ${params.PREJECT_OPERATION}, SONARQUBE: ${params.SONARQUBE}, GITLAB_RELEASE: ${params.GITLAB_RELEASE}"

// (7) 切换数据库版本(Jenkinsfile中使用)
//sh label: 'checkout_version', script: '[ -n "${RELEASE_VERSION}" ] && git checkout ${RELEASE_VERSION} || { echo -e "切换至指定的tag的版本 tag:${RELEASE_VERSION} 不存在或为空,请检查输入的tag!" && exit 1; }'
}
}
}
}

stage ('代码检测') {
// (8) 判断是否进行代码质量检测(满足则继续否则跳过)
when {
// 如果项目是回滚操作则不进行代码质量检测,然后当SONARQUBE参数为True时进行质量检测,否则不进行。
expression { return params.PREJECT_OPERATION != "rollback" }
anyOf {
environment name: 'SONARQUBE', value: 'True'
}
}
steps {
script {
// (9) Tool 后的名称一定是对应全局工具配置中sonar扫描器的名字进行代码质量检测
def sonarqubeScanner = tool 'Sonar-Scanner';
withSonarQubeEnv(credentialsId: "${SONARQUBE_CREDENTIALSID}") {
// 注意: 可以将sonarQube的属性定义在这里,也可以定义在项目文件中然后在这里引用配置文件
sh "${sonarqubeScanner}/bin/sonar-scanner " +
"-Dsonar.projectName=${JOB_NAME} " +
"-Dsonar.projectKey=${SONARQUBE_PROJECTKEY} " +
"-Dsonar.projectVersion=${RELEASE_VERSION} " +
"-Dsonar.sourceEncoding=utf8 " +
"-Dsonar.sources=. " +
"-Dsonar.language=java " +
"-Dsonar.java.binaries=."

// (10) 暂停xxs等待sonarqube扫描反馈,根据实际时间进行调整;
// NANOSECONDS SECONDS
sleep time: "${SONARQUBE_TIMEOUT}", unit: 'SECONDS'
// sh "sleep ${SONARQUBE_TIMEOUT}"
}

try {
// 方式1
// timeout(1) {
// def qg = waitForQualityGate()
// if (qg.status != 'OK') {
// error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
// } else {
// echo "successful"
// }
// }

// 方式2 (根据实际时间进行调整)
timeout(time: 1, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
updateGitlabCommitStatus name: '代码检测', state: 'success'
} catch(Exception err) {
updateGitlabCommitStatus name: '代码检测', state: 'failed'
unstable '代码检测失败'
}
}
}
}

// (11) 项目构建打包处理
stage ("项目构建") {
// 当操作为 PREJECT_OPERATION 为回滚(rollback) 时不进行构建打包
when {
expression { return params.PREJECT_OPERATION != "rollback" }
}
steps {
script {
try {
// Maven 配置依赖(动态配置)
sh script: "curl -s -o settings.xml http://gitlab.weiyigeek.top/weiyigeek/helloworld/-/raw/master/settings.xml"
sh script: '/usr/local/maven/bin/mvn -s "settings.xml" clean install -Dmaven.test.skip=true'
// 补充还可以采用此种方式进行更新流水线状态
// gitlabCommitStatus(connection: gitLabConnection('Gitlab 用户认证凭据'),name: "项目构建") { ... }
updateGitlabCommitStatus name: '项目构建', state: 'success'
} catch(Exception err) {
updateGitlabCommitStatus name: '项目构建', state: 'failed'
unstable '项目构建失败'
}
}
}
}

// (12) 进行开发测试部署
stage ("项目部署") {
steps {
script {
// ssh 连接函数调用
SERVER = getSSH()
try {
// 项目部署所用得资源清单以及
sh script: """\
curl -s http://gitlab.weiyigeek.top/weiyigeek/helloworld/-/raw/master/${JOB_NAME}.yaml | sed s#__APPNAME__#${APP_NAME}#g > ./${JOB_NAME}.yaml && \
wget http://gitlab.weiyigeek.top/weiyigeek/helloworld/-/raw/master/options.sh -O options.sh
"""
// ssh 插件调用
sshPut remote: SERVER, from: "./${JOB_NAME}.yaml" , into: "${APP_DIR}${JOB_NAME}.yaml"
// 部署脚本
sh script: "chmod +x options.sh && sh -x ./options.sh"
updateGitlabCommitStatus name: '测试部署', state: 'success'
}catch(Exception err) {
updateGitlabCommitStatus name: '测试部署', state: 'failed'
unstable '部署失败' // echo err.toString()
error "[-Error] : 项目部署失败 \n[-Msg] : ${err.getMessage()} "
}
}
}
}

// (13) 进行成品归档 archiveArtifacts
stage('成品归档') {
when {
// 当操作为 PREJECT_OPERATION 为rollback(回滚)时 且当 params.GITLAB_RELEASE 为False 时不进行成品归档
expression { return params.PREJECT_OPERATION != "rollback" && params.GITLAB_RELEASE }
}
steps {
script {
if ( ! params.RELEASE_VERSION.contains("master") ) {
try {
// Jenkins 归档
archiveArtifacts artifacts: "${APP_FILE}", onlyIfSuccessful: true

// Gitlab Token 用于 Gitlab - Release 版本分发
def gitlab_token = getSecure("${GITLAB_TOKEN}")

// 判断 Gitlab - Release 版本是否存在
def gitlab_release = sh(returnStatus: true, script: "sudo apk add jq && curl -s --header 'PRIVATE-TOKEN: ${gitlab_token}' ${GITLAB_REL} | jq .[].tag_name | grep -c '${params.RELEASE_VERSION}'")

// 上传得 Release Nginx web 根路径
def gitlab_release_dir = "/nfsdisk-31/release/${JOB_NAME}/"

echo gitlab_token + " --- " + gitlab_release + "=====" + gitlab_release_dir + "---"

// 根据 gitlab_release 变量判断是否进行release-cli 发布指定版本得 `Release`
if ( gitlab_release != 0 ) {
sshCommand remote: SERVER, command: "mkdir -p ${gitlab_release_dir}"
sshPut remote: SERVER, from: "./${APP_NAME}" ,into: "${gitlab_release_dir}${APP_NAME}"
sh script: """
/usr/local/bin/release-cli --server-url "http://gitlab.weiyigeek.top" --private-token "${gitlab_token}" \
--project-id ${GITLAB_PROJECTID} create \
--name "${JOB_NAME}-${params.RELEASE_VERSION}" \
--tag-name "${params.RELEASE_VERSION}" \
--description "此 ${JOB_NAME} 项目 ${params.RELEASE_VERSION}版本 来自 Jenkins Build 编译构建上传。" \
--assets-link '{"name": "${APP_NAME}","url":"${GITLAB_DOWNLOAD}","link_type":"other"}'
"""
} else {
echo "[-Tips] : Gitlab Release 中已存在 ${params.GITLAB_RELEASE} 版本, 将不会提交该Release到Gitlab中"
}
updateGitlabCommitStatus name: '成品归档', state: 'success'
} catch (Exception err) {
updateGitlabCommitStatus name: '成品归档', state: 'failed'
echo err.toString()
}
} else {
echo "# 版本不能为 ${param.RELEASE_VERSION} , 将不会进行归档!"
}
}
}
}
}

// (12) POST阶段当所有任务执行后触发进行工作空间的清理以及消息通知;
post {
// 无论如何,我已经完成了 无论成功与否都将执行
always {
script {
// 企业微信消息通知
def GIT_COMMIT=sh returnStdout: true, script: "git show --oneline --ignore-all-space --text | head -n 1"
qyWechatNotification aboutSend: true, failNotify: true, failSend: true, mentionedMobile: '', startBuild: true, successSend: true, unstableSend: true, webhookUrl: "${env.QYWX_WEBHOOK}"
sh """\
curl -s ${env.QYWX_WEBHOOK} \
-H 'Content-Type: application/json' \
-d '
{
"msgtype": "text",
"text": {
"content": "项目: ${env.SONARQUBE_PROJECTKEY}, 版本: ${params.RELEASE_VERSION}, 操作: ${params.PREJECT_OPERATION} , 信息: ${GIT_COMMIT}, Gitlab-Release 发布: ${params.GITLAB_RELEASE}, 部署地址: ${env.KS8_ADDRESS}"
}
}'
"""
}
deleteDir() /* clean up our workspace */
}
success {
echo '[-Info] : I succeeeded! - 任务成功'
updateGitlabCommitStatus name: '消息通知', state: 'success'
}
unstable {
echo '[-Info] : I am unstable - 不稳定 :/'
}
failure {
echo '[-Info] : I failed - 任务失败 :( '
updateGitlabCommitStatus name: '消息通知', state: 'failed'
}
changed {
echo '[-Info] : Things were different before - 与以前的情况不一样...'
}
}
}


(3) Shell 部署脚本

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
#!/bin/sh
# // 基础变量
export K8sMaster="${DEPLOY_USER}@${DEPLOY_HOST}"
export AppName="${APP_FILE##*/}"
export FindAppName=$(find ${WORKSPACE} -maxdepth 2 -type f -name ${AppName})

# // 若操作值不为rollback则执行该判断代码块中的内容。
if [${PREJECT_OPERATION} != "rollback" ];then
if [ "${FindAppName}" != "" ]; then
echo "[-Msg]: ${FindAppName} ./${APP_NAME} Copying...."
cp -rf ${FindAppName} ./${APP_NAME}
else
echo "[-Error]: FindAppName param error!"
exit 1
fi
fi

# // 判断应用文件是否存在与共享目录中
function judge () {
export ExistResult=$(ssh -p ${DEPLOY_PORT} ${K8sMaster} "ls ${APP_DIR} | grep ${RELEASE_VERSION} | wc -l")
}

# // 部署操作函数
function deploy () {
if [[ ${ExistResult} -eq 0 || "${RELEASE_VERSION}" == "master" ]];then
scp -P ${DEPLOY_PORT} "${APP_NAME}" ${K8sMaster}:${APP_DIR}${APP_NAME}
else
echo "[-Msg]: /${APP_NAME} current Deploy....."
fi

# 部署脚本
if [[ "${RELEASE_VERSION}" == "master" ]];then
ssh -p ${DEPLOY_PORT} ${K8sMaster} "kubectl -n ${K8S_NAMESPACE} delete pod -l ${K8S_SELECTOR}"
else
ssh -p ${DEPLOY_PORT} ${K8sMaster} "kubectl apply -f ${APP_DIR}${JOB_NAME}.yaml"
fi
}

# // 回退操作函数
function rollback () {
if [[ ${ExistResult} -ne 0 ]];then
ssh -p ${DEPLOY_PORT} ${K8sMaster} "kubectl apply -f ${APP_DIR}${JOB_NAME}.yaml"
else
echo "[-Eroor]: /${APP_NAME} version not exsit....."
exit 1
fi
}

# // 重部署操作函数
function redeploy () {
# 如果是以前部署过则删除以前部署的项目目录,否则重新部署;
if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" || ${ExistResult} -gt 0 ]];then
echo -e "[-Message]: ${RELEASE_VERSION} Version Exsit !"
ssh -p ${DEPLOY_PORT} ${K8sMaster} "find ${APP_DIR} -d -maxdepth 1 -type f -name ${APP_NAME}"
scp -P ${DEPLOY_PORT} "${APP_NAME}" ${K8sMaster}:${APP_DIR}${APP_NAME}
fi

# 触发重新部署脚本
deploy
}

# // 判断是否存在历史版本
judge

# 主操作入口判断
case ${PREJECT_OPERATION} in
"deploy")
echo "1.deploy"
deploy
;;
"rollback")
echo "2.rollback"
rollback
;;
"redeploy")
echo "3.redeploy"
redeploy
;;
*)
echo "[-Error] : ${PREJECT_OPERATION} Param Error not in deploy/rollback/redeploy !"
exit 1
;;
esac

(4) 效果预览

  • Step 1.在BlueOcean进行构建部署参数选择。
WeiyiGeek.BlueOcean部署

WeiyiGeek.BlueOcean部署

  • Step 2.在K8s集群中进行查看Pod迭代变化。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /nfsdisk-31/test# kubectl get pod --watch
    # NAME READY STATUS RESTARTS AGE
    # deploy-java-maven-0 1/1 Running 0 83s
    # deploy-java-maven-1 1/1 Running 0 80s
    # deploy-java-maven-2 1/1 Terminating 0 76s
    # deploy-java-maven-2 0/1 Terminating 0 106s
    # deploy-java-maven-2 0/1 Pending 0 0s
    # deploy-java-maven-2 0/1 ContainerCreating 0 0s
    # deploy-java-maven-2 1/1 Running 0 2s
    # deploy-java-maven-1 1/1 Terminating 0 112s
    # deploy-java-maven-1 0/1 Terminating 0 2m26s
    # deploy-java-maven-1 0/1 Pending 0 0s
    # deploy-java-maven-1 0/1 ContainerCreating 0 0s
    # deploy-java-maven-1 1/1 Running 0 3s
    # deploy-java-maven-0 1/1 Terminating 0 2m32s
WeiyiGeek.实验效果

WeiyiGeek.实验效果


0x02 企业实战