1.Jenkins入门基础介绍与持续化集成部署

2022-09-29 19:09:24 浏览数 (1)

[TOC]

0x00 前言简述

Jenkins 介绍

Q: 什么是Jenkins?

答: Jenkins 是一个开源持续集成的工具(CI&CD)由Java开发而成, 用于自动化各种任务,包括构建、测试和部署软件(自动化服务器); Jenkins 支持各种运行方式,可通过系统包、Docker 或者 通过一个独立的Java程序; 官方介绍 : 全球领先的开源自动化服务器,Jenkins 提供了数以百计的插件来支持构建、部署和自动化任何项目 官方标语 : “Build great things at any scale”-“建造伟大的事情以任何规模”

Tips :个人理解 Jenkins 是一个调度平台,本身不需要处理任何事情,而是通过众多的插件来完成所有的工作;

Q: 为什么要用Jenkins?

答: Jenkins的前身是Hudson, 是基于Java开发的一种持续集成工具,用于监控秩序重复的工作, 它是可以将各个开源的软件进行集成的调度平台,例如( Gitlab/SVN 、Maven、Sonarqube、Shell、钉钉通知、项目监控 )等;

Jenkins 发行线版本说明:

  • TLS 长期支持版本: 每12周从常规版本流中选择,作为该时间段的稳定版本。 每隔 4 周,我们会发布稳定版本,其中包括错误和安全修复反向移植。
  • 每周更新版本: 每周都会发布一个新版本,为用户和插件开发人员提供错误修复和功能。

Jenkins 特性

  • 开源的java语言开发持续集成工具,支持CI,CD;
  • 易于安装部署配置:可通过yum安装,或下载war包以及通过docker容器等快速实现安装部署,可方便web界面配置管理;
  • 消息通知及测试报告:集成RSS/E-mail通过RSS发布构建结果或当构建完成时通过e-mail通知,生成JUnit/TestNG测试报告;
  • 分布式构建:支持Jenkins能够让多台计算机一起构建/测试;
  • 文件识别:Jenkins能够跟踪哪次构建生成哪些jar,哪次构建使用哪个版本的jar等;
  • 丰富的插件支持:支持扩展插件,你可以开发适合自己团队使用的工具,如git,svn,maven,docker

Jenkins 应用场景

  • 1) 创建一个项目,手动构建,完成简单任务,比如拉取代码进行编译(持续集成)。
  • 2) 编译失败通知用户
  • 3) 参数化构建
  • 4) 代码改动自动触发构建或者定时触发构建
  • 5) 一个项目构建完成后自动调用另一个项目的构建,完成一连串任务
  • 6) 并发构建
  • 7) 集群化部署开发(CI/CD)

Jenkins Job 类型

  • 1) Freestyle project : 自由风格项目,主要的项目类型
  • 2) Maven project : maven项目专有,类似freestyle,更简单
  • 3) Multiconfigration project : 多配置项目,适合大量不同配置(环境、平台等)构建
  • 4) Pipeline : 流水线项目,适合使用pipeline 插件功能构建流水线任务,或者使用freestyle project不容易实现的负责任务
  • 5) Multibranch pipeline : 多分支流水线项目,根据SCM仓库中的分支创建多个pipeline项目

0x01 安装配置

安装方式 安装参考: https://www.jenkins.io/zh/doc/book/installing/

  • Windows(Jar 、War)、Linux(yum|rpm 、apt|dpkg)、Mac
  • Docker

PS : Jenkins通常作为一个独立的应用程序在其自己的流程中运行, 内置Java servlet 容器/应用程序服务器(Jetty)。

系统要求 最低推荐配置:

  • 256MB 可用内存
  • 1GB 可用磁盘空间(作为一个Docker容器运行jenkins的话推荐10GB)

小团队推荐的硬件配置:

  • 1 GB 可用内存
  • 50 GB 可用磁盘空间

软件配置: Java 8—​无论是Java运行时环境(JRE)还是Java开发工具包(JDK)都可以

Tips : 安装最低配置:不少于256M内存,不低于1G磁盘,JDK版本>=8(openjdk也可以)。

PS : Jenkins 依赖于 Java 环境, 如果是在不使用Docker安装Jenkins时就需要安装配置Java环境;

代码语言:javascript复制
# Jenkins 版本与 Java版本依赖关系
2.164 (2019-02) and newer: Java 8 or Java 11
2.54 (2017-04) and newer: Java 8
1.612 (2015-05) and newer: Java 7

Ubuntu

Jenkins的Debian Package Repository,以自动化安装和升级。要使用此存储库,请先将键添加到系统: Jenkins Debian Packages:https://pkg.jenkins.io/debian-stable/

官方安装:

代码语言:javascript复制
# 添加  gpg key
wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -

# 在 /etc/apt/sources.list 中添加以下条目 deb https://pkg.jenkins.io/debian-stable binary/
sudo sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
# 更新您的本地包索引,然后最终安装Jenkins:
sudo apt-get update
sudo apt-get install jenkins

国内安装:

代码语言:javascript复制
# Java 环境安装配置 jdk-8u211-linux-x64.tar.gz
# 下载地址: https://www.oracle.com/cn/java/technologies/javase-downloads.html
sudo mkdir /opt/app/ && cd $_
tar -zxvf jdk-8u211-linux-x64.tar.gz -C /usr/local/

# 环境变量设置(临时生效) set oracle jdk environment
ln -s /usr/local/jdk1.8.0_211/bin/java /usr/bin/java
export JAVA_HOME=/usr/local/jdk1.8.0_211 ## 这里要注意目录要换成自己解压的jdk 目录
export JRE_HOME=${JAVA_HOME}/jre  
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib  
export PATH=${JAVA_HOME}/bin:$PATH

# 环境安装测试
root@jenkins:/usr/local/jdk1.8.0_211# java -version
  # java version "1.8.0_211"
  # Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
  # Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)


# Jenkins 下载& 安装
sudo wget https://mirrors.aliyun.com/jenkins/debian-stable/jenkins_2.263.1_all.deb 
sudo dpkg -i jenkins_2.263.1_all.deb

# 更新站点配置 
sed -i "s#updates.jenkins.io#mirrors.tuna.tsinghua.edu.cn/jenkins/updates/#g" /var/lib/jenkins/hudson.model.UpdateCenter.xml

# 启动 Jenkins 并查看状态
systemctl restart jenkins
systemctl status jenkins
  # ● jenkins.service - LSB: Start Jenkins at boot time
  #      Loaded: loaded (/etc/init.d/jenkins; generated)
  #      Active: active (exited) since Wed 2020-12-23 14:10:12 UTC; 6s ago
  #        Docs: man:systemd-sysv-generator(8)
  #     Process: 357567 ExecStart=/etc/init.d/jenkins start (code=exited, status=0/SUCCESS)

安装后设置向导: 参考地址: https://www.jenkins.io/zh/doc/book/installing/#setup-wizard

  • (1) 解锁 Jenkins 当您第一次访问新的Jenkins实例时,系统会要求您使用自动生成的密码对其进行解锁。 浏览到 http://localhost:8080(或安装时为Jenkins配置的任何端口),并等待解锁 Jenkins 页面出现。
代码语言:javascript复制
# Jenkins控制台日志显示可以获取密码的位置(在Jenkins主目录中)
cat /var/lib/jenkins/secrets/initialAdminPassword
# 23092528120a45488a73ff4e7565e06f
  • (2) 自定义jenkins插件 解锁 Jenkins之后,在 Customize Jenkins 页面内, 您可以安装任何数量的有用插件作为您初始步骤的一部分。 两个选项可以设置:
    • 安装推荐的插件 - 安装推荐的一组插件,这些插件基于最常见的用例.
    • 选择插件来安装 - 选择安装的插件集。当你第一次访问插件选择页面时,默认选择建议的插件。(新手首次搭建建议选择此项)

WeiyiGeek.选择安装的插件

PS : 如果您不确定需要哪些插件,请选择安装建议的插件 。 您可以通过Jenkins中的Manage Jenkins > Manage Plugins页面在稍后的时间点安装(或删除)其他Jenkins插件 。

  • (3) 不安装任何插件进入插件列表点击右上角的x关闭窗口,显示如下页面则跳过了管理员用户密码重设;

WeiyiGeek.初始化成功页面

PS : 一旦初始安装完成后,可通过插件管理器安装其他插件

  • (4) 进入 Jenkins 控制台安装成功,然后第一个管理员用户; 点击 Dashboard -> admin -> Configure -> Password 进行管理员密码设置; 从这时起,Jenkins用户界面只能通过提供有效的用户名和密码凭证来访问。

Ubuntu 下 Jenkins 相关文件及其目录(目录结构)一览:

代码语言:javascript复制
# 配置文件
/etc/default/jenkins
# /etc/sysconfig/jenkins # CentOS

# 家目录&插件目录&构建工作目录
/var/lib/jenkins/
/var/lib/jenkins/config.xml  # jenkins root configuration
/var/lib/jenkins/userContent  # 该目录下的文件将在http://server/userContent/下提供
/var/lib/jenkins/plugins   # 插件
/var/lib/jenkins/logs      # 存放jenkins相关的日志
/var/lib/jenkins/fingerprints  # 存储指纹记录
/var/lib/jenkins/secrets   # 密码秘钥所在目录
/var/lib/jenkins/workspace # 各任务的构建工作目录
/var/lib/jenkins/jobs      # 浏览器上面创建的任务都会存放在这里
           - [JOBNAME]          # 每个作业Job的子目录
               - config.xml     # (job configuration file)
               - workspace      # (working directory for the version control system)
               - latest         # (symbolic link to the last successful build)
               - builds
                 - [BUILD_ID]      # (for each build)
                   - build.xml     # (build result summary)
                   - log           # (log file)
                   - changelog.xml  # (change log)

# 启动文件 {start|stop|status|restart|force-reload}
/etc/init.d/jenkins

# systemd 管理的serice
/run/systemd/generator.late/jenkins.service
/run/systemd/generator.late/graphical.target.wants/jenkins.service
/run/systemd/generator.late/multi-user.target.wants/jenkins.service

文件内容&目录结构详细说明

1.config.xml 核心配置文件: 包含了Jenkins的版本信息、权限认证规则、workspace目录定义、builds目录定义、视图信息等等。其他的 xml 文件是 Jekins 服务扩展功能的配置信息文件。

2.plugins 插件目录: 已经安装的Jenkins插件都可以在里面找到对应的文件。每一个插件基本是由一个目录和一个与目录同名的文件配对组成。

3.jobs 执行任务存储目录: 该目录是 Jenkins 管理的所有构建任务的配置细节、构建后的产物和数据。Jenkins 服务所有的 Job 都会在这个目录下,创建一个以 Job 名称命名的文件夹。 job 任务的文件夹中存储的文件有:

代码语言:javascript复制
config.xml 任务的XML格式声明信息。
nextBuildNumber 文件记录下次构建时的 buildNumber
builds 目录存储此 Job 构建的历史。

~ $ ls /var/lib/jenkins/jobs/
HelloWorld  Kubernetes-jenkins-slave  pineline-use-node

4.workspace 工作空间目录: 包含了这个构建作业的源代码, Jenkins存放项目的工作空间。进入这个workspace目录,里面就是你之前创建的项目的目录。在构建过程中Jenkins会根据项目中配置的远程代码仓库的地址去拉取源码到项目目录中并在这里完成打包。之前我们在打包的脚本中用到的$WORKSPACE表示的就是workspace下对应项目的目录。

  • 5.tools 工具目录: Jenkins 服务设置安装 tools ,会安装在这个目录中。安装工具的方式是:【Manage Jenkins】 -> 【Global Tool Configuration】 页面。

6.updates 更新目录: 用来存放可用的插件更新。

7.users 用户信息目录: 存储用户的账号信息。

8.nodes 节点目录: Jenkins 在配置了主从或者工作节点之后会在这里有相应的信息。

9.userContent 用户生成的文件: 用于存储在 Jenkins 管理过程中生成的文件;比如使用Convert To Pipeline 插件可以将 JOB 转换成 Pipeline,生成的 Pipeline 的内容会以文件的形式存储在这个文件夹中。

10.fingerprints 文件指纹目录: 文件指纹(fingerprints)是一个简单的MD5校验和。Jenkins维护了一个md5sum数据库,用于文件指纹校验。对于每个md5sum,Jenkins记录了哪些项目的哪些构建使用了他。在每次构建运行和文件被采集指纹时这个数据库会更新。为了避免过多的磁盘使用,Jenkins不存储实际的文件。相反它只存储md5sum和它的使用记录。

11.logs 日志目录: 用于存储 Jenkins 服务的日志,主要是事件日志和工作日志。

12.war 目录: 如果是以WAR包形式运行的Jenkins,该目录下存放的是解压后的WAR包。

13.Jenkins 服务另外的文件目录:

代码语言:javascript复制
secrets
init.groovy.d
workflow-libs
scriptler
config-history

Tips: jenkins存放数据不依靠数据库所以在移植时只需要拷贝整个程序主目录即可,需要注意的是jobs和plugins目录比较重要

Docker 安装

描述: 使用容器化的方式部署 Jenkins Master 节点,可以选择自行构建镜像,推荐使用 Jenkins 官方提供的镜像。

Jenkins 服务官方在 dockerhub 平台发布官方镜像https://hub.docker.com/u/jenkins

1.Jenkins Master 镜像基础使用

  • Jenkins Web 服务监听的端口为 8080
  • Jenkins 使用JNLP 连接 Agent 节点使用的端口为 5000
  • Jenkins 在 /var/jenkins_home 目录中保存workspace中的数据,包括插件和配置信息
  • Jenkins 服务默认使用jenkins 用户运行,uid为1000;请注意文件权限问题

2.Jenkins Master 使用和升级 使用 Jenkins 的镜像构建容器时,至少要将端口映射出去,将/var/jenkins_home 目录做持久化。

代码语言:javascript复制
# 示例:用 jenkins:2.107.3 创建容器并运行。
docker volume create jenkins-data
docker run --name jenkins-production 
           --detach 
           -p 50000:50000 
           -p 8080:8080 
           -v jenkins-data:/var/jenkins_home 
           jenkins/jenkins:2.275-alpine
# If run for the first time, just run the following to get the admin
# password once it has finished starting
docker exec jenkins-production bash -c 'cat $JENKINS_HOME/secrets/initialAdminPassword'

在需要对 Jenkins 服务升级时,只需要使用新的镜像替换即可;持久化存储和端口映射操作相同。 示例:将 Jenkins 服务升级到 2.121.3

代码语言:javascript复制
docker stop jenkins-production
docker rm jenkins-production 
# just temporarily docker rename it instead if that makes you worried
docker run --name jenkins-production 
           --detach 
           -p 50000:50000 
           -p 8080:8080 
           -v jenkins-data:/var/jenkins_home 
           jenkins/jenkins:2.277-alpine

3.Jenkins Master 节点配置扩展(了解即可)

3.1 设置Master 节点的执行者 executors 数量 描述: 使用groovy脚本指定并设置Jenkins Master 实例的执行程序数。默认情况下,它设置为2个执行者,但是您可以扩展映像并将其更改为所需的执行者数:

代码语言:javascript复制
executors.groovy
import jenkins.model.*
Jenkins.instance.setNumExecutors(5)

# 和 Dockerfile

FROM jenkins/jenkins:lts
COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/executors.groovy

3.2 传递 JVM 的启动参数 描述: 您可能需要自定义运行Jenkins的 JVM,通常是为了传递系统属性(props列表)或调整堆内存设置

代码语言:javascript复制
# 使用JAVA_OPTS环境变量:
docker run --name myjenkins -p 8080:8080 -p 50000:50000 --env JAVA_OPTS=-Dhudson.footerURL=http://mycompany.com jenkins/jenkins:lts

3.3 给 Jenkins 启动器传递参数 描述: 您传递给运行 Jenkins 镜像的 docker 的参数将传递给 jenkins 启动器 ,例如运行 docker run jenkins/jenkins:lts --version 将显示Jenkins版本

代码语言:javascript复制
# 通过定义Jenkins参数 JENKINS_OPTS
示例1.Dockerfile使用此选项来强制将HTTPS与映像中包含的证书一起使用。
FROM jenkins/jenkins:lts
COPY https.pem /var/lib/jenkins/cert
COPY https.key /var/lib/jenkins/pk
ENV JENKINS_OPTS --httpPort=-1 --httpsPort=8083 --httpsCertificate=/var/lib/jenkins/cert --httpsPrivateKey=/var/lib/jenkins/pk
EXPOSE 8083

# 通过定义Jenkins参数 JENKINS_SLAVE_AGENT_PORT
示例2.Dockerfile中定义来更改jenkins的默认从属代理端口。
FROM jenkins/jenkins:lts
ENV JENKINS_SLAVE_AGENT_PORT 50001
# 或作为docker的参数
docker run --name myjenkins -p 8080:8080 -p 50001:50001 --env JENKINS_SLAVE_AGENT_PORT=50001 jenkins/jenkins:lts

Tips :此环境变量将用于设置将系统属性添加jenkins.model.Jenkins.slaveAgentPort到JAVA_OPTS的端口 。

3.4 配置日志记录 描述: 可以通过属性文件和java.util.logging.config.fileJava属性来配置Jenkins日志记录。例如:

代码语言:javascript复制
mkdir data

cat > data/log.properties <<EOF
handlers=java.util.logging.ConsoleHandler
jenkins.level=FINEST
java.util.logging.ConsoleHandler.level=FINEST
EOF

docker run --name myjenkins -p 8080:8080 -p 50000:50000 
--env JAVA_OPTS="-Djava.util.logging.config.file=/var/jenkins_home/log.properties"
 - v `pwd`/data:/var/jenkins_home jenkins/jenkins:lts

3.5 调整Jenkins 浏览器访问的路径path 描述: 如果您需要在浏览器中访问 Jenkins 服务时要添加一个 path 路径,则需要添加环境变量JENKINS_OPTS="--prefix=/jenkins"

Tips : 容器中 Docker 客户端配置

1.构建 NodeJS 任务执行镜像的 Dockerfile 文件内容:

代码语言:javascript复制
FROM docker.io/node:alpine
run apk add --no-cache git docker-cli
RUN yarn config set registry "https://registry.npmmirror.com"

2.配置 Docker 客户端连接远程的 Docker 服务端:使用一个环境变量就可以实现这个配置。

代码语言:javascript复制
env:
- name: DOCKER_HOST
  value: tcp://docker.example.com:2375

Tips : 配置镜像仓库的认证信息,认证信息一般存储在 /root/.docker/config.json 文件中。这里可以模仿前面 Maven 服务的 settings.xml 文件的配置方式配置, 然后就可以实现了连接 Docker Server 端构建镜像,对镜像打 tag 的操作, 然后将制作好的镜像推送到远程的镜像仓库中,例如Harbor。

Kubernetes 安装

请参考后续的 <(2) 集群搭建Jenkins Master 节点> 章节的文章。


0x02 基础知识

Jenkins 环境变量

描述: 环境变量可以被看作是pipeline与Jenkins交互的媒介, 环境变量可以分为Jenkins内置变量自定义变量以及自定义全局环境变量

比如,可以在 pipeline 中通过 BUILD_NUMBER 变量知道构建任务的当前构建次数。

1.内置变量 描述: 在pipeline执行时,Jenkins通过一个名为 env 的全局变量,将Jenkins内置环境变量暴露出来。

代码语言:javascript复制
${env.BUILD_NUMBER} 方式一,推荐使用
$env.BUILD_NUMBER 方式二,
${BUILD_NUMBER} 方式三,不推荐使用

例如:在实际工作中经常用到的变量。

代码语言:javascript复制
BUILD_URL:当前构建的页面URL。
BUILD_NUMBER:构建号,累加的数字。
BRANCH_NAME:多分支pipeline项目支持。
GIT_BRANCH:通过git拉取的源码构建的项目才会有此变量。

2.自定义环境变量 描述: 当 pipeline 变得复杂时,我们就会有定义自己的环境变量的需求。声明式 pipeline 提供了environment 指令,方便自定义变量。

另外 environment 指令可以在pipeline中定义,代表变量作用域为整个 pipeline;也可以在 stage 中定义,代表变量只在该阶段有效。

Tips : 如果在environment中定义的变量与env中的变量重名,那么被重名的变量的值会被覆盖掉。

3.自定义全局环境变量 描述: 如果我们需要定义一些全局的跨pipeline的自定义变量。

我们可以进入 Manage Jenkins→Configure System→Global properties 页,勾选“Environment variables”复选框,单击“Add”按钮,在输入框中输入变量名和变量值即可,

Tips : 自定义全局环境变量会被加入 env 属性列表中,所以,使用自定义全局环境变量与使用Jenkins内置变量的方法无异。

Tips : Jenkins 内置变量参考 请看补充说明中的内置环境变量

Jenkins 参数构建类型

主要缺省参数类型如下几类:

  • Boolean 参数
  • Choice 参数 (常用)
  • String 参数 (常用)
  • File 参数
  • 密码 参数
  • 凭据 参数
  • 其他 参数
  • 运行时 参数

额外的我们需要安装Extended Choice Parameter 和 git Parameter,以便向 Choice 参数添加扩展功能 以及 使用Git参数来获取分支。

使用过程在参数化构建过程 -> 构建参数添加 -> 构建参数的变量 ->通过

WeiyiGeek.构建参数

Tips : 环境变量生效的顺序全局环境变量 < slave配置环境变量 < JOB参数 < JOB injected环境变量

官网地址: https://www.jenkins.io/ 官方文档: https://www.jenkins.io/zh/doc/

Jenkins 并发构建

描述: 我们创建Job可以进行并发构建,每个并发构建使用单独的workspace,默认是<workspace path>@<num>

WeiyiGeek.并发构建

Tips : slave 也可以进行并发构建前提是需要Node and Label parameter plugin插件,并且需要配置node类型参数;

Jenkins 自动化构建

常见的triggers几种:

  • Build periodically : 设定类似cron周期性时间触发构建
  • poll SCM : 设定类似 cron 周期性时间触发检查代码变化,只有代码变动时才触发构建
  • Hooks : 用过SVN的都知道钩子 Github hooks / Gitlab hooks
  • Events : Gerrit event

WeiyiGeek.triggers

build periodically 描述: H 代表 jenkins 自己分配时间不去指定客观时间, 注意一般都是Jenkins根据监控资源利用率算法分配的。

代码语言:javascript复制
# * * * * *
# 第一颗*表示分钟,取值0~59
# 第二颗*表示小时,取值0~23
# 第三颗*表示一个月的第几天,取值1~31
# 第四颗*表示第几月,取值1~12
# 第五颗*表示一周中的第几天,取值0~7,其中0和7代表的都是周日

1.每30分钟构建一次:H/30 * * * *

2.每2个小时构建一次 H H/2 * * *

3.每天早上8点构建一次: 0 8 * * *

4.每天的8点,12点,22点,一天构建3次 0 8,12,22 * * *

# 多个时间点,中间用逗号隔开

Poll SCM 描述: 他会定时检查源码变更(根据SCM软件的版本号),如果有更新就checkout最新code下来然后执行构建动作;

比如: 你可以如果我想每隔30分钟检查一次源码变化有变化就执行;

代码语言:javascript复制
H/30 * * * *

Jenkins 上下游构建

触发下游构建:

  • 1) 其他项目构建后触发: “Build other projects” under “Post build actions” -> 输入项目名称
  • 2) 利用 Parameterized Trigger 插件 参数化构建 -> 在构建后操作步骤中 -> Trigger Parameterized build on other peoject -> Restrict matrix execuson to a subset

WeiyiGeek.上下游构建

Jenkins 调用API

描述: Jenkins 服务提供了很多类型的API;因为工作需要这里只记录一下执行job的API使用CURL调用的方式。

Jenkins API 介绍

1.Jenkins API 从级别上分类

代码语言:javascript复制
* 站点 API:创建Job、复制Job、Build 队列、重启Jenkins等
* Job API:修改Job、删除Job、获取 Build 信息、Build Job、禁用Job、启用Job
* Build Job: 根据 Build Number 获取Build 信息,获取Build 控制台的输出日志

2.传输数据格式: POST 传输数据支持的格式有XML,JSON,PYTHON

3.安全处理: 可以找到一些开发语言编写的API封装包,结合到自己的脚本中,提高开发效率。

代码语言:javascript复制
* CSRF 跨域攻击保护:可以在全局安全中设置。
* 用户验证设置:可以使用用户的密码,也可以为用户创建token。

CURL 调用 API 执行 Job 实例:

代码语言:javascript复制
# Curl Api 调用
CURL -X POST ${JOB_URI} 
--user ${user_name}:${user_password}|${user_token} 
--data-urlencode json="{"parameter":${JSON_DATA},"statusCode":"303","redirectTo":"."}"

参数解析:

  • JOB_URL: 就是Job 的访问URL地址。在Job 页面中点击”Build with Parameters”就可以获取到地址。一般以”build?delay=0sec“结尾; 示例: http://192.168.1.1/job/HelloWorld/build?delay=0sec
  • user_name,user_password,user_token 用户验证信息
  • JSON_DATA:Job 运行时,需要传递的参数,由Json 结构封装;示例:[{"name":"branchName","value":"origin/dev"},{"name":"projectName","value":"helloworld"}]

0x03 基础配置

视图管理

描述: 当Job创建数量达到一定时我们需要在Jenkins中建立视图(分类),可以帮助我们快速找到某个所需Job; 实际上Job的视图类似于我们电脑上的文件夹可以通过一些过滤规则,将已经创建好的Job过滤到视图之中,也可以在视图中直接创建我们的Job;

我们可以采用View或者directory文件两种方式进行管理:

views - 视图方式

代码语言:javascript复制
views视图更加灵活,不改变job的路径
views有多种形式、层级、看板,流水线等多样化

directory - 文件夹方式 (名称空间隔离)

代码语言:javascript复制
文件夹适合多个团队共用Jenkins
性能更好,执行速度更快
支持RBAC权限管理

两则异同说明: 文件夹创建一个可以嵌套存储的容器利用它可以进行分组,而视图仅仅是一个过滤器,其次文件夹则是一个独立的命名空间,因此你可以有多个相同名称的的内容,只要它们在不同的文件夹里即可。

views - 视图方式 创建流程:

  • Step 1.我的视图 -> 点击所有旁边 号 -> 设置视图名称 -> 选择三种类型列表视图/包括全局视图/我的视图 列表视图 : 显示简单列表。你可以从中选择任务来显示在某个视图中 包括全局视图 : Shows the content of a global view. 我的视图 : 该视图自动显示当前用户有权限访问的任务
  • Step 2.此处设置列表视图为例 -> 选择任务列表 -> 增加或者删除指定显示列

WeiyiGeek.views

directory - 文件夹方式 创建流程:

  • Step 1.创建Job -> 选择文件夹 -> 输入任务名称directory-test
  • Step 2.在directory-test文件夹下 -> 可以继续创建视图( ) -> 选择(列表视图/包括全局视图/我的视图)

WeiyiGeek.directory

插件管理

(1) 插件安装加速 Q: 在安装插件时如何进行配置安装加速?

答: 选择国内清华大学的Jenkins插件更新源 https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

配置位置: Dashboard > Plugin Manager > Advanced > Update Site

WeiyiGeek.插件安装加速

(2)插件安装的几种方式

  • Step 1.采用插件更新源搜索进行在线安装,例如下面对Jenkins进行汉化插件的安装, 搜索 Localization: Chinese (Simplified) 插件安装即可;

WeiyiGeek.Jenkins 汉化插件安装

当安装插件安装完成时候可以选择重启 Jenkins 或 跳转到首页, 此处我重启 Jenkins 如下图所示汉化成功;

WeiyiGeek.Jenkins 汉化界面

  • Step 2.通过Web页面上传.hpi文件进行插件安装 清华大学 jenkins 插件下载: https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/ 例如下面进行 git 插件下载 进行上传其 git.hpi 文件进行插件安装,之后上传即可;

WeiyiGeek.Jenkins git 插件安装

Step 3.导入之前服务器已安装的插件进行离线安装;

代码语言:javascript复制
# (1) Jenkins 已安装的插件目录
/var/lib/jenkins/plugins$ ls
  # apache-httpcomponents-client-4-api      display-url-api      jdk-tool                  localization-zh-cn      script-security      trilead-api
  # apache-httpcomponents-client-4-api.jpi  display-url-api.jpi  jdk-tool.jpi              localization-zh-cn.jpi  script-security.jpi  trilead-api.jpi
  # command-launcher                        git                  jsch                      mailer                  ssh-credentials      workflow-scm-step
  # command-launcher.jpi                    git-client           jsch.jpi                  mailer.jpi              ssh-credentials.jpi  workflow-scm-step.jpi
  # credentials                             git-client.jpi       localization-support      scm-api                 structs              workflow-step-api
  # credentials.jpi                         git.jpi              localization-support.jpi  scm-api.jpi             structs.jpi          workflow-step-api.jpi


# (2) 打包插件目录并上传到另外一台同版本的Jenkins服务器(PS:不同版本间可能会出现插件不兼容的情况) 
tar -zcvf jenkins_2.263.1_plugins.tar.gz /var/lib/jenkins/plugins

# (3) 解压插件目录并进行所属者组修改
tar -zxvf jenkins_2.263.1_plugins.tar.gz 
chown -R jenkins:jenkins /var/lib/jenkins/plugins

# (4) 启动 Jenkins 服务
systemctl stop jenkins
systemctl start jenkins

# (5) 启动 日志 查看
tail -f /var/log/jenkins/jenkins.log

权限管理

描述: 由于jenkins默认的权限管理体系不支持用户组或角色的配置,因此需要安装第三发插件来支持角色的配置,我们使用Role-based Authorization Strategy插件,安装请参考前面插件管理章节。

插件功能特点:

  • 按角色分权限
  • 按项目分权限
  • Step 1.插件安装后我们先启用该插件管理Jenkins权限 -> 面板 -> 全局安全配置(Global Security)-> 授权策略 -> 找到并选中 Role-Based Strategy -> 然后应用保存 。
  • Step 2.管理Jenkins -> 安全 -> Manage and Assign Roles -> 可以进行角色权限管理与分配 -> 此处添加一个test角色并设置其权限 -> 应用 -> 保存; ·
  • Step 3.Assign Roles -> Global roles(按角色)、Item roles(按项目)、Node roles(按节点) -> 用户添加(同时您需要在Jenkins用户中新增一个该用户) -> 保存应用
    • 添加用户和组。默认情况下用户/组名是区分大小写的,因此它需要与您在Configure Global Security->Access Control->Security Realm下的用户/组定义匹配。

WeiyiGeek.jenkins权限管理

构建工具管理

在 Jenkins 服务中运行 Pipeline 等任务过程中,需要依赖一些工具(环境需求);比如 JDK,MAVEN或者golang 或者 python 环境。

构建工具的安装方式有三种:

  • 1) 自行安装工具: 即自行在服务器上安装配置,然后在 Jenkins 服务中配置好这些工具的安装信息就可以使用了。配置路径:Manage Jenkins→Global Tool Configuration
  • 2) Jenkins 自动安装工具: 利用 Jenkins 服务提供的【工具自动安装功能】,实现工具的快速配置。
  • 3) tools 指令安装工具:帮助我们自动下载并安装所指定的构建工具,并将其加入 PATH 变量中。这样,我们就可以在sh步骤里直接使用了, 但在agent none的情况下不会生效。

Tips : tools指令默认支持3种工具:JDK、Maven、Gradle。通过安装插件,tools 指令还可以支持更多的工具。


0x04 基础使用

HelloWorld 自由风格软件项目

Q: Jenkins Free Style 基本使用? 目的: 构建一个自由风格的软件项目, 这是Jenkins的主要功能Jenkins将会结合任何SCM和任何构建系统来构建你的项目, 甚至可以构建软件以外的系统.

操作流程:

Step 1.项目创建

(1) 面板 -> 新建一个任务 -> 任务名称 freestyle-helloworld -> 保存 (2) Dashboard -> 项目名称 freestyle-helloworld -> General

代码语言:javascript复制
# 重要的配置项
丢弃旧的构建:(保持构建的天数 / 保持构建的最大个数)
参数化构建过程: 比如添加的环境变量或者API密钥等等

(3) Dashboard -> 项目名称 freestyle-helloworld > 构建 > 执行 Shell 命令 echo "FreeStyle - HelloWorld" touch hello-world.txt

WeiyiGeek.自由风格的软件项目

Step 2.工程 freestyle-helloworld 构建与控制台输出查看;

WeiyiGeek.工程构建与控制台输出

PS : 如果是蓝色圆点表示构建成功, 如果是红色圆点表示构建失败!

Step 3.构建后的工作区存放目录

代码语言:javascript复制
jenkins:/var/lib/jenkins/workspace/freestyle-helloworld$ ls
  # hello-world.txt
jenkins:/var/lib/jenkins/workspace/freestyle-helloworld$ cat hello-world.txt

Gitlab 集成配置与实践

简单说明: Q: Jenkins 为啥要集成Gitlab?

答:由于我们需要依托于Jenkins将Gitlab上的项目获取至本地,为后续网站的代码发布工作做好准备;

Q: Jenkins 如何集成Gitlab?

答: 由于Jenkins 只是一个调度平台,所以需要安装和Gitlab相关的插件即可完成集成;

Jenkins 与 Gitlab 集成思路

  • 1.开发提交代码 至 Gitlab
  • 2.Jenkins 安装 Gitlab 所需插件
代码语言:javascript复制
Credentials Plugin (2.3.14) - This plugin allows you to store credentials in Jenkins.
Git client plugin (3.6.0) - Utility plugin for Git support in Jenkins
Git plugin (4.5.0) - This plugin integrates Git with Jenkins.
Gitlab Authentication plugin (1.10) - This is the an authentication plugin using gitlab OAuth.
Gitlab Hook Plugin (1.4.2) - Enables Gitlab web hooks to be used to trigger SMC polling on Gitlab projects
GitLab Plugin (1.5.13) - This plugin allows GitLab to trigger Jenkins builds and display their results in the GitLab UI.

WeiyiGeek.Gitlab关联插件

  • 3.Jenkins 创建 FreeStyle 项目,然后配置Gitlab仓库对应的地址;

操作流程:

  • Step 1.首先需要将我们所用的项目代码推送到Gitlab(此处假设您已安装Gitlab)之中;

(1) 管理中心 -> 群组 -> 新建群组 -> 完善信息 -> 创建群组 (2) 管理中心 -> 项目 -> 创建项目 -> 完善仓库信息 -> 创建项目 (3) 推送现有文件夹到Gitlab之中

代码语言:javascript复制
cd existing_folder
git init
git remote add origin http://gitlab.weiyigeek.top/ci-cd/blog.git
git add .
git commit -m "Initial commit"
git push -u origin master
  # remote: Resolving deltas: 100% (829/829), done.
  # To http://gitlab.weiyigeek.top/ci-cd/blog.git
  #  * [new branch]      master -> master
  # Branch 'master' set up to track remote branch 'master' from 'origin'.

WeiyiGeek.gitlab

  • Step 2.此处假设您已经按照上面Gitlab 集成配置安装了相应的插件, 并且配置好gitlab域名访问的解析记录;
代码语言:javascript复制
# 此处由于没有自己搭建DNS,则将其写入到Jenkins 服务器的hosts文件中进行相应的IP与域名绑定
cat /etc/hosts
  # 127.0.0.1 localhost
  # 127.0.1.1 gitlab
  # 10.10.107.201 gitlab.weiyigeek.top
  • Step 3.Jenkins 创建 FreeStyle 项目,然后配置Gitlab仓库对应的地址(ssh 的方式)
代码语言:javascript复制
# (1) 在Gitlab中添加当前机器ssh密钥(此处以Jenkins用户为例)
jenkins@jenkins:~$ssh-keygen -t ed25519 -C "jenkins@weiyigeek.top"
~$ ls ~/.ssh/id_ed25519
  # id_ed25519      id_ed25519.pub

# (2) 添加公钥到gitlab中 
# > 用户设置 -> SSH Keys -> jenkins-CI
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINFb2ALOnOpePb7e/tss4/ZKxNHb3srh7NXntW5jAWSf jenkins@weiyigeek.top

# (3) 主机认证指纹绑定
jenkins@jenkins:~$ git ls-remote -h -- git@gitlab.weiyigeek.top:ci-cd/blog.git HEAD
  # The authenticity of host 'gitlab.weiyigeek.top (10.10.107.201)' can't be established.
  # ECDSA key fingerprint is SHA256:cmG5Ces 96qG9EEaU1b/tJuTR1re7XDXUU4jg7asna4.
  # Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

WeiyiGeek.SSH Keys

  • Step 4.Jenkins > FreeStyle 项目 > 源码管理 > Git > Repositories 设置 > Credentials 设置 PS : 如果是非Jenkins用户下公钥对Gitlab该项目有访问权限时,可以通过 Credentials 添加其认证的密钥即可;
代码语言:javascript复制
# (1) Weiyigeek 用户密钥 (此时假设Gitlab已经添加该公钥)
weiyigeek@jenkins:~$ cat /home/weiyigeek/.ssh/id_ed25519
  # -----BEGIN OPENSSH PRIVATE KEY-----
  # b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
  # QyNTUxOQAAACAIxH1a4EivkJOSAXqd6LUE8tUvRfgPwwn1Erb7r728cwAAAJgANcy8ADXM
  # vAAAAAtzc2gtZWQyNTUxOQAAACAIxH1a4EivkJOSAXqd6LUE8tUvRfgPwwn1Erb7r728cw
  # AAAECLw4Wnle7Md7fc5NQN6EohejUx0XMHKfdYXejLAw2/NwjEfVrgSK Qk5IBep3otQTy
  # 1S9F A/DCfUStvuvvbxzAAAAFG1hc3RlckB3ZWl5aWdlZWsudG9wAQ==
  # -----END OPENSSH PRIVATE KEY-----

# (2) 进行 Credentials 相应的设置

WeiyiGeek.Weiyigeek 用户密钥

Step 5.首先手动实现设置NFS共享存储以及 Kubernetes 部署 StatefulSet 资源清单,通过Nginx访问我们的blog; 基础配置:

代码语言:javascript复制
# (1) NFS 服务器配置
root@nfs$ showmount -e
  # /nfs/data4 *
/nfs/data4# ls -alh /nfs/
  # drwxrwxrwx  2 weiyigeek weiyigeek 4.0K Nov 19 13:27 data4
/nfs/data4# mkdir /nfs/data4/tags/
/nfs/data4# ln -s /nfs/data4/tags/ /nfs/data4/web
/nfs/data4# chown -R nobody:weiyigeek /nfs/data4/

# (2) Node 1 && Node 2
sudo mkdir -pv /nfs/data4/
sudo chown -R weiyigeek:weiyigeek /nfs/data4/
sudo mount.nfs -r 10.10.107.202:/nfs/data4/ /nfs/data4/
k8s-node-4:/nfs/data4$ ls -alh  # 权限查看
  # total 12K
  # drwxrwxrwx 3 weiyigeek weiyigeek 4.0K Dec 24 15:44 .
  # drwxr-xr-x 3 root   root 4.0K Dec 24 15:47 ..
  # drwxr-xr-x 2 weiyigeek weiyigeek 4.0K Dec 24 15:44 tags
  # lrwxrwxrwx 1 weiyigeek weiyigeek   16 Dec 24 15:44 web -> /nfs/data4/tags/

资源清单:

代码语言:javascript复制
cat > jenkins-gitlab-ci.yaml <<'EOF'
apiVersion: v1 
kind: Service 
metadata:
  name: deploy-blog-svc
spec:
  type: NodePort         # Service 类型
  selector:
    app: blog-html        # 【注意】与deployment资源控制器创建的Pod标签进行绑定;
    release: stabel       # Service 服务发现不能缺少Pod标签,有了Pod标签才能与之SVC对应
  ports:                  # 映射端口
  - name: http            
    port: 80              # cluster 访问端口
    targetPort: 80        # Pod 容器内的服务端口
    nodePort: 30088
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: deploy-blog-html
spec:
  serviceName: "deploy-blog-svc"
  replicas: 3                # 副本数
  selector:                  # 选择器
    matchLabels:  
      app: blog-html   # 匹配的Pod标签非常重要
      release: stabel
  template:
    metadata:
      labels:
        app: blog-html      # 模板标签
        release: stabel
    spec:
      volumes:                # 关键点
      - name: web 
        hostPath:             # 采用hostPath卷
          type: DirectoryOrCreate   # 卷类型DirectoryOrCreate: 如果子节点上没有该目录便会进行创建
          path: /nfs/data4/web      # 各主机节点上已存在的目录此处是NFS共享
      - name: timezone     # 容器时区设置
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
      containers:
      - name: blog-html
        image: harbor.weiyigeek.top/test/nginx:v3.0  # 拉取的镜像
        imagePullPolicy: IfNotPresent
        ports:
        - name: http         # 此端口在服务中的名称
          containerPort: 80  # 容器暴露的端口
        volumeMounts:        # 挂载指定卷目录
        - name: web 
          mountPath: /usr/share/nginx/html
        - name: logs
          mountPath: /var/log/nginx
        - 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
EOF

部署资源清单:

代码语言:javascript复制
~/K8s/Day12$ kubectl apply -f jenkins-gitlab-ci.yaml
service/deploy-blog-svc unchanged
statefulset.apps/deploy-blog-html configured

~/K8s/Day12$ kubectl get services -o wide
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE    SELECTOR
deploy-blog-svc   NodePort    10.104.74.36   <none>        80:30088/TCP   5m9s   app=blog-html,release=stabel
kubernetes        ClusterIP   10.96.0.1      <none>        443/TCP        49d    <none>

~/K8s/Day12$ kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP             NODE          NOMINATED NODE   READINESS GATES
deploy-blog-html-0                        1/1     Running   0          72s   10.244.0.193   Master 
deploy-blog-html-1                        1/1     Running   0          57s   10.244.1.182   k8s-node-4  
deploy-blog-html-2                        1/1     Running   0          49s   10.244.2.84    k8s-node-5

效果查看:

代码语言:javascript复制
curl http://10.10.107.202:30088/host.html
  # Hostname: deploy-blog-html-0 ,Image Version: 3.0, Nginx Version: 1.19.4
  • Step 6.采用Tag方式发布和回退项目

Q: 为什么要让项目支持Tag版本方式上线?

答: 采用 Tag 的方式可以直观的知道我们部署的项目版本,同时也便于进行回退; 比如: 第一次上线v1.1第二次上线v1.2,如果此时上线的v1.2出现文件,那么我们可以快速回退至上一个版本v1.1;

使用Tag方式发布与回退思路:

1.开发如果需要发布新版本,必须将当前版本打上一个标签。 2.Jenkins需要让其脚本支持传参,比如用户传递v1.1则拉取项目的v1.1标签。

实践操作:

(1) 首先需要安装 Git Parameter 插件(增加了从项目中配置的git存储库中选择分支、标记或修订的能力。),然后配置Jenkins参数化构建过程,让用户在构建时选择对应的Tag版本; 丢弃旧的构建 > 保持构建的最大个数 为 10 个 参数化构建过程 > Git 参数 > git_version (变量) -> 参数类型 Tags -> 默认值 (origin/master) -> 排序方式 (DESCENDING SMART) 倒序 防止 Tags 顺序乱; 选项参数 (选择) > deploy_option (变量) -> 选项 deploy|rollback 源码管理 > Git > Repository URL:git@gitlab.weiyigeek.top:ci-cd/blog.git > Credentials (密钥认证) -> 分支机构建设 (指定分支(为空时代表any))-> 引入 ${git_version} 变量 构建 > 执行shell > /bin/sh -x /tmp/script/blog-script.sh 应用 > 保存

WeiyiGeek.jenkins项目创建

(2) 部署和回退脚本以及自动化发布重复构建的问题处理编写

touch /tmp/script/blog-script.sh && chmod a x $_

代码语言:javascript复制
#!/bin/bash
# Description: Jenkins CI & Kubernetes & Gitlab -> Deploy or Rollback Blog HTML 
DATE=$(date  %Y%m%d-%H%M%S)
TAG_PATH="/nfs/data4/tags"
WEB_PATH="/nfs/data4/web"
WEB_DIR="blog-${DATE}-${git_version}"
K8S_MATER="weiyigeek@10.10.107.202"
K8S_MATER_PORT="20211"


# 部署
deploy () {
  # 1.打包工作目录中的项目
  cd ${WORKSPACE}/ && 
  tar -zcf /tmp/${WEB_DIR}.tar.gz ./*

  # 2.上传打包的项目到master之中
  scp -P ${K8S_MATER_PORT} /tmp/${WEB_DIR}.tar.gz  weiyigeek@10.10.107.202:${TAG_PATH}

  # 3.解压&软链接
  ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "mkdir -p ${TAG_PATH}/${WEB_DIR} &&  
                                         tar -xf ${TAG_PATH}/${WEB_DIR}.tar.gz -C ${TAG_PATH}/${WEB_DIR} && 
                                         rm -rf ${WEB_PATH} && 
                                         ln -s ${TAG_PATH}/${WEB_DIR} ${WEB_PATH} && 
                                         kubectl delete pod -l app=blog-html"
}

# 回退
rollback () {
  Previous_Version=$(ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "find ${TAG_PATH} -maxdepth 1 -type d -name blog-*-${git_version}")
  ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "rm -rf ${WEB_PATH} && 
                                         ln -s ${Previous_Version} ${WEB_PATH} && 
                                         kubectl delete pod -l app=blog-html"
}


# 部署 & 回退 入口(坑-==两边没有空格)
if [[ "${deploy_option}" = "deploy" ]]; then
  # 坑 (防止字符串为空)
  if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" ]];then
    echo -e "您已经部署过 ${git_version} 版本"
    exit 1
  else
    deploy
  fi
elif [[ "${deploy_option}" = "rollback" ]];then
  rollback
else
  exit 127
fi

(3) Jenkins 服务器 与 Kubernetes Master 机器中的普通用户进行 ssh 公钥认证登录;

代码语言:javascript复制
# copy 公钥 到 对端主机
ssh-copy-id -p 20211 weiyigeek@10.10.107.202
  # /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/var/lib/jenkins/.ssh/id_ed25519.pub"
  # authorized only. All activity will be monitored and reported.
  # weiyigeek@10.10.107.202's password:

  # Number of key(s) added: 1

  # Now try logging into the machine, with:   "ssh -p '20211' 'weiyigeek@10.10.107.202'" and check to make sure that only the key(s) you wanted were added.

(4) 为我们的Blog打三个Tag并验证构建脚本

代码语言:javascript复制
$ cd ~/workspace/freestyle-blog
jenkins:~/workspace/freestyle-blog$ git config --global user.email "jenkins@weiygeek.top"
jenkins:~/workspace/freestyle-blog$ git config --global user.name "Weiyigeek"
vim index.html
git add .
git commit -m "v1.1"
git tag v1.1 -m "Jenkins CI test - v1.1"
git push origin HEAD:master
git push origin v1.1
  # Total 0 (delta 0), reused 0 (delta 0)
  # To gitlab.weiyigeek.top:ci-cd/blog.git
  # * [new tag]         v1.1 -> v1.1

(5) 进行项目构建 -> Build With Paramters 查看 部署的版本以及是部署还是回退 -> 如下图所示拉取的指定 Tags ;

WeiyiGeek.项目构建

(6) 项目构建部署验证 访问http://10.10.107.202:30088/博客地址

代码语言:javascript复制
Built Branches
    v1.3: Build #4 of Revision 4f80504ee142cb9866e4a0988cdc1e1693cfd7aa (v1.3)
    v1.2: Build #3 of Revision 4e1dd6eded12d7cad3740d2461c3e784ad712632 (v1.2)
    v1.1: Build #2 of Revision cd2d5778292ff2cbb7d4ac1b82c684223a38761b (v1.1)

# 如果已经部署过该项目则显示:
  #   '[' deploy==deploy ']'
  #   [[ v4f80504ee142cb9866e4a0988cdc1e1693cfd7aa = v4f854ee142cb9866e4a988cdc1e1693cfd7aa ]]
  #   echo -e '您已经部署过 v1.3 版本'
  # 您已经部署过 v1.3 版本

WeiyiGeek.项目构建部署验证

(6) 项目回退验证 > 请选择您要部署的RELEASE版本v1.1, 然后选择部署或是回退;

WeiyiGeek.项目回退验证

Maven 集成配置与实践

基本概述: Q: 什么是Java项目?

答:简单来说就是使用Java编写的代码,我们将其称为Java项目;

Q: 为什么Java项目需要使用Maven编译?

答: 由于Java编写的Web服务代码是无法直接在服务器上运行,需要使用Maven工具进行打包; 简单理解: Java 源代码就像汽车的一堆散件,必须经过工厂的组装才能完成一辆完整的汽车,这里组装汽车可以理解是 Maven 编译过程;

Q: 在实现自动化构建Java项目时,先实现手动构建Java项目;

答: 因为想要实现自动化发布代码,就必须手动进行一次构建,既是熟悉过程又是优化我们的部署脚本; 大致流程: 源码 -> gitlab -> Maven 手动构建 -> 最后推送构建 -> 发布;

代码语言:javascript复制
# Maven 拉取 Jar 的几种途径
                -> 国外 Maven Jar 服务器
Gitlab -> Maven -> 国内 Maven Jar 镜像服务器
                -> 企业 内部 Maven Jar 私服服务器 (可以双向同步)

自动部署 Java 项目至 kubernetes 集群流程步骤:

  • (1) Jenkins 安装 Maven Integration 插件,使Jenkins支持Maven项目的构建
  • (2) Jenkins 配置 JDK 路径以及 Maven 路径
  • (3) Jenkins 创建 Maven 项目然后进行构建
  • (4) 编写自动化上线脚本推送至 kubernetes 集群
  • (5) 优化部署脚本使其支持上线与回滚以及,相同版本重复构建

基础环境配置:

Step 1. Jenkins 服务器中 Java 与 Maven 环境配置与 Maven 插件安装;

代码语言:javascript复制
# Java 环境
~$ ls /usr/local/jdk1.8.0_211/
~$ java -version
  # java version "1.8.0_211"
  # Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
  # Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

# Maven 环境 (两种方式)
# ~$ sudo apt install maven # 方式1
~$ wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz 
~$ sudo tar -zxvf apache-maven-3.6.3-bin.tar.gz -C /usr/local
~$ ls /usr/local/apache-maven-3.6.3/
  # bin  boot  conf  lib  LICENSE  NOTICE  README.txt
~$ sudo ln -s /usr/local/apache-maven-3.6.3/ /usr/local/maven  # 非常值得学习(变版本却不变执行路径)
~$ /usr/local/maven/bin/mvn -v
  # Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
  # Maven home: /usr/local/maven
  # Java version: 1.8.0_211, vendor: Oracle Corporation, runtime: /usr/local/jdk1.8.0_211/jre
  # Default locale: en_US, platform encoding: UTF-8
  # OS name: "linux", version: "5.4.0-46-generic", arch: "amd64", family: "unix"
~$ export PATH=/usr/local/maven/bin/:$PATH  # 临时
~$ export PATH=${JAVA_HOME}/bin:/usr/local/maven/bin/:$PATH # 加入 /etc/Profile永久 
~$ mvn -v

# Maven Integration 插件 安装
# PS : 提供了Jenkins和Maven的深度集成:根据快照在项目之间自动触发,各种Jenkins发布者的自动配置(Junit)。

# Jenkins 中 Maven 集成配置
> 面板 -> 全局工具配置 -> JDK 配置 -> 新增JDK -> 别名 jdk_env -> JAVA_HOME /usr/local/jdk1.8.0_211/
                                -> 新增Maven -> 别名 maven_env -> MAVEN_HOME /usr/local/jdk1.8.0_211/

WeiyiGeek.Maven 集成配置

  • Step 2.手动我们的Java的Maven项目, 测试Jenkins服务器上的Maven是否可以正常编译War包并运行在单机的Tomcat8 之中;

Maven 镜像配置(您也可选择阿里云的Maven仓库地址加快拉取速度):

代码语言:javascript复制
<!-- # (1) 修改官方的Maven仓库地址为本地私服; -->
158   <mirrors>
159 <!-- Maven 私服地址配置 -->
160     <mirror>
161         <id>weiyigeek-maven</id>
162         <mirrorOf>central</mirrorOf>
163         <name>Private maven</name>
164         <url>http://maven.weiyigeek.top:8081/repository/maven-public/</url>
165     </mirror>
166   </mirrors>

<!-- # (2) Default: ${user.home}/.m2/repository 缓存目录 -->
53   <localRepository>/path/to/local/repo</localRepository>

Maven代码项目程序拉取:

代码语言:javascript复制
$ git clone git@gitlab.weiyigeek.top:ci-cd/java-maven.git
  # Cloning into 'java-maven'...
  # The authenticity of host 'gitlab.weiyigeek.top (10.10.107.201)' can't be established.
$ ~/code/java-maven$ ls
pom.xml  src  target

构建与运行测试:

代码语言:javascript复制
~/code/java-maven$ mvn clean package -Dmaven.test.skip=true   # 跳过测试用例
  # [INFO] Building war: /home/weiyigeek/code/java-maven/target/hello-world.war
  # [INFO] WEB-INF/web.xml already added, skipping
  # [INFO] ------------------------------------------------------------------------
  # [INFO] BUILD SUCCESS
  # [INFO] ------------------------------------------------------------------------
  # [INFO] Total time:  7.225 s
  # [INFO] Finished at: 2020-12-25T09:23:20Z
  # [INFO] ------------------------------------------------------------------------

~/code/java-maven$ ls -alh /home/weiyigeek/code/java-maven/target/hello-world.war  # 生成的war
-rw-r--r-- 1 weiyigeek weiyigeek 2.0M Dec 25 09:23 /home/weiyigeek/code/java-maven/target/hello-world.war

# 将生成的 hello-world.war 复制到本地的 Tomcat 的Wabapps目录之中他会自动解压

WeiyiGeek.Maven-hello-world

Step 3.在 Kubernets 上 准备接受 Jenkins 推送过来的war包的项目环境; 基础配置:

代码语言:javascript复制
# (1) NFS 服务器上配置 (PS:由于测试资源有限 则 nfs 与 k8s master 在同一台机器上)
root@nfs$ showmount -e
  # /nfs/data4 *
/nfs/data4# ls -alh /nfs/
  # drwxrwxrwx  2 weiyigeek weiyigeek 4.0K Nov 19 13:27 data4
/nfs/data4# mkdir /nfs/data4/{war,webapps}/
/nfs/data4# ln -s /nfs/data4/war/ROOT /nfs/data4/webapps/ROOT
/nfs/data4# chown -R weiyigeek:weiyigeek /nfs/data4/

# (2) Node 1 && Node 2 都要执行
sudo mkdir -pv /nfs/data4/
sudo chown -R weiyigeek:weiyigeek /nfs/data4/
sudo mount.nfs -r 10.10.107.202:/nfs/data4/ /nfs/data4/

# (3) master 节点拉取 tomcat 此处选择 8.5.61-jdk8-corretto 并上传到内部私有harbor之中
master$ docker pull tomcat:8.5.61-jdk8-corretto
  # 8.5.61-jdk8-corretto: Pulling from library/tomcat
  # ...
  # Status: Downloaded newer image for tomcat:8.5.61-jdk8-corretto
  # docker.io/library/tomcat:8.5.61-jdk8-corretto
master$ docker tag tomcat:8.5.61-jdk8-corretto harbor.weiyigeek.top/test/tomcat:8.5.61-jdk8-corretto
master$ docker push  harbor.weiyigeek.top/test/tomcat:8.5.61-jdk8-corretto
  # The push refers to repository [harbor.weiyigeek.top/test/tomcat]
  # f13244e4918b: Pushed
  # 536f15f78828: Pushed
  # 1c98b6d16fad: Pushed
  # fdef502bf282: Pushed
  # cee8d35c645b: Pushed
  # 8.5.61-jdk8-corretto: digest: sha256:110d7391739e868daf8c1bdd03fcb7ffe9eaf2b134768b9162e2cd47d58f7255 size: 1368

# (4) 节点拉取验证
k8s-node-4:~$ docker pull harbor.weiyigeek.top/test/tomcat:8.5.61-jdk8-corretto
k8s-node-5:~$ docker pull harbor.weiyigeek.top/test/tomcat:8.5.61-jdk8-corretto

# (5) 将 jenkins 中构建好的war包 上传到 Master 节点 的 /nfs/data4/war 目录之中
jenkins$ scp -P 20211 /var/lib/jenkins/workspace/Maven-HelloWorld/target/hello-world.war weiyigeek@10.10.107.202:/nfs/data4/war
# **************WARNING**************
# Authorized only. All activity will be monitored and reported.
# hello-world.war     

# (6) 解压war并建立软链接
master$ /nfs/data4/war$ unzip hello-world.war -d hello-world-v1.0
  # Archive:  hello-world.war
master$ /nfs/data4/war$ ls
  # hello-world-v1.0  hello-world.war
master$ /nfs/data4$ ln -s /nfs/data4/war/hello-world-v1.0/ /nfs/data4/webapps && ls /nfs/data4/webapps # 坑太多该目录不能存在
  # index.jsp  META-INF  WEB-INF

资源清单: (同样此处采用了HostPath的Volumns卷(war项目)与StorageClass存储类(访问日志))

代码语言:javascript复制
cat > jenkins-ci-gitlab-to-kubernetes.yaml <<'EOF'
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: /nfs/data4/webapps  # 各主机节点上已存在的目录此处是NFS共享
      - name: timezone     # 容器时区设置
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
      containers:
      - name: java-maven
        image: harbor.weiyigeek.top/test/tomcat:8.5.61-jdk8-corretto  # 拉取的镜像
        imagePullPolicy: IfNotPresent
        ports:
        - name: http         # 此端口在服务中的名称
          containerPort: 8080  # 容器暴露的端口
        volumeMounts:        # 挂载指定卷目录
        - name: webapps      # Tomcat 应用目录
          mountPath: /usr/local/tomcat/webapps/ROOT
        - 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
EOF

资源清单构建:

代码语言:javascript复制
# (1) svc 与 stafefulset 资源创建
~/K8s/Day12$ kubectl apply -f jenkins-ci-gitlab-to-kubernetes.yaml
  # service/deploy-maven-svc created
  # statefulset.apps/deploy-java-maven created

# (2) 查看创建的资源
~/K8s/Day12$ kubectl get svc | grep "deploy-maven-svc" && kubectl get pod -l app=java-maven -o wide
  # deploy-maven-svc   NodePort    10.101.97.223    8080:30089/TCP  7m24s
  # NAME                  READY   STATUS    RESTARTS   AGE   IP             NODE        
  # deploy-java-maven-0   1/1     Running   0          10s   10.244.0.212   weiyigeek-ubuntu 
  # deploy-java-maven-1   1/1     Running   0          25s   10.244.1.199   k8s-node-4  
  # deploy-java-maven-2   1/1     Running   0          29s   10.244.2.102   k8s-node-5

# (3) 访问验证
master$ curl -s http://10.101.97.223:8080 | grep "<p>"
  # <p> Server : Apache Tomcat/8.5.61  | 10.244.2.102 </p>
  # <p> Client : 10.244.0.0 | 10.244.0.0</p>
  # <p> Document_Root : /usr/local/tomcat/webapps/ROOT/  <br/><br/> URL : 10.101.97.223/index.jsp  </p>
master$ curl -s http://10.101.97.223:8080 | grep "<p>"
  # <p> Server : Apache Tomcat/8.5.61  | 10.244.0.212 </p>
  # <p> Client : 10.244.0.1 | 10.244.0.1</p>
  # <p> Document_Root : /usr/local/tomcat/webapps/ROOT/  <br/><br/> URL : 10.101.97.223/index.jsp  </p>
master$ curl -s http://10.101.97.223:8080 | grep "<p>"
  # <p> Server : Apache Tomcat/8.5.61  | 10.244.1.199 </p>
  # <p> Client : 10.244.0.0 | 10.244.0.0</p>
  # <p> Document_Root : /usr/local/tomcat/webapps/ROOT/  <br/><br/> URL : 10.101.97.223/index.jsp  </p>
  • Step 4.下面正餐开始了,在 Jenkins 控制台 点击新建任务 -> 输入任务名称(Maven-HelloWorld) -> 选择构建一个Maven项目

常规设置: 丢弃旧的构建 10 个、参数化构建过程 Git 参数 git_version | 选项参数 deploy_option (deploy|rollback|redeploy) (设置与上面相同 PS 注意排序方式为智能排序)

关键点: Maven Build -> Goals and options : clean package -Dmaven.test.skip=true

构建后脚本设置: Post Steps -> 执行 Shell 命令 -> /bin/bash -x /tmp/script/maven-jenkins-ci-script.sh-> 应用并保存

构建测试结果:

代码语言:javascript复制
# 项目拉取
Running as SYSTEM
Building in workspace /var/lib/jenkins/workspace/Maven-HelloWorld
The recommended git tool is: NONE
using credential b4c8b4e9-2777-44a1-a1ed-e9dc21d37f4f
Cloning the remote Git repository
Cloning repository git@gitlab.weiyigeek.top:ci-cd/java-maven.git

<===[JENKINS REMOTING CAPACITY]===>channel started
Executing Maven: -B -f /var/lib/jenkins/workspace/Maven-HelloWorld/pom.xml clean package -Dmaven.test.skip=true
# 扫描项目并下载依赖
[INFO] Scanning for projects...
[INFO] 
[INFO] -------------------< com.weiyigeek.main:hello-world >-------------------
[INFO] Building hello-world Maven Webapp 1.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO] Downloading from weiyigeek-maven: http://maven.weiyigeek.top:8081/repository/maven-public/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.pom

# 打包应用
[INFO] Packaging webapp
[INFO] Assembling webapp [hello-world] in [/var/lib/jenkins/workspace/Maven-HelloWorld/target/hello-world]
[INFO] Processing war project
[INFO] Copying webapp resources [/var/lib/jenkins/workspace/Maven-HelloWorld/src/main/webapp]
[INFO] Webapp assembled in [26 msecs]
[INFO] Building war: /var/lib/jenkins/workspace/Maven-HelloWorld/target/hello-world.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  6.756 s
[INFO] Finished at: 2020-12-25T13:55:45Z
[INFO] ------------------------------------------------------------------------
Waiting for Jenkins to finish collecting data
[JENKINS] Archiving /var/lib/jenkins/workspace/Maven-HelloWorld/pom.xml to com.weiyigeek.main/hello-world/1.1-SNAPSHOT/hello-world-1.1-SNAPSHOT.pom
[JENKINS] Archiving /var/lib/jenkins/workspace/Maven-HelloWorld/target/hello-world.war to com.weiyigeek.main/hello-world/1.1-SNAPSHOT/hello-world-1.1-SNAPSHOT.war
channel stopped
Finished: SUCCESS

Step 5.Jenkins CI 部署Shell脚本编写,脚本需求部署、回退、可重复构建; 脚本与权限: su - "jenkins" -c "touch /tmp/script/maven-jenkins-ci-script.sh && chmod a x /tmp/script/maven-jenkins-ci-script.sh"

代码语言:javascript复制
#!/bin/bash
# Description: Jenkins CI & Kubernetes & Gitlab -> Deploy or Rollback or Redeploy Java Maven Project
DATE=$(date  %Y%m%d-%H%M%S)
WAR_PATH="/nfs/data4/war"
WEBROOT_PATH="/nfs/data4/webapps"
WEB_DIR="${JOB_NAME}-${DATE}-${git_version}"
WAR_DIR="${WAR_PATH}/${WEB_DIR}"
WAR_NAME="${WEB_DIR}.war"
K8S_MATER="weiyigeek@10.10.107.202"
K8S_MATER_PORT="20211"

# 部署
deploy () {
  # 1.上传Maven打包的war包到master之中
  scp -P ${K8S_MATER_PORT} ${WORKSPACE}/target/*.war  weiyigeek@10.10.107.202:${WAR_PATH}/${WAR_NAME}

  # 2.解压&软链接
  ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "unzip ${WAR_PATH}/${WAR_NAME} -d ${WAR_DIR} && 
                                         rm -rf ${WEBROOT_PATH} && 
                                         ln -s ${WAR_PATH}/${WEB_DIR} ${WEBROOT_PATH} && 
                                         kubectl delete pod -l app=java-maven"
}

# 回退
rollback () {
  History_Version=$(ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "find ${WAR_PATH} -maxdepth 1 -type d -name ${JOB_NAME}-*-${git_version}")
  ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "rm -rf ${WEBROOT_PATH} && 
                                         ln -s ${History_Version} ${WEBROOT_PATH} && 
                                         kubectl delete pod -l app=java-maven"
}


# 重部署
redeploy () {
  # 如果是以前部署过则删除以前部署的项目目录,否则重新部署;
  if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" ]];then
    echo -e "曾经部署过 ${git_version} 版本,现在正在重新部署!"
    History_Version=$(ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "find ${WAR_PATH} -maxdepth 1 -type d -name ${JOB_NAME}-*-${git_version}")
    ssh -p ${K8S_MATER_PORT} ${K8S_MATER} "rm -rf ${History_Version}"
  fi
  # 物理如何都要重新部署
  deploy
}

# 部署 & 回退 入口(坑-==两边没有空格)
if [[ "${deploy_option}" = "deploy" ]]; then
  # 坑 (防止字符串为空)
  if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" ]];then
    echo -e "您已经部署过 ${git_version} 版本"
    exit 1
  else
    deploy
  fi
elif [[ "${deploy_option}" = "rollback" ]];then
  rollback
elif [[ "${deploy_option}" = "redeploy" ]];then 
  redeploy
else
  echo -e "无任何操作!停止执行脚本"
  exit 127
fi

Step 6.修改h1标签为我们的项目打几个Tag标签然后Push到仓库;

WeiyiGeek.show tags

  • Step 7.验证我们的构建的Maven项目以及推送构建的war到kubernetes集群之中(激动人心的时刻即将到来);

PS: 查看任务控制台输出信息确定脚本部署没有任何问题,之后访问http://10.10.107.202:30089/查看编译的war运行在kunbernets中部署Tomcat应用的结果;

WeiyiGeek.jenkins-ci-deploy

Step 8.回滚测试 构建参数: deploy_option->rollback , git_version -> v1.1

代码语言:javascript复制
# 构建推送反馈
[Maven-HelloWorld] $ /bin/sh -xe /tmp/jenkins6930799554885763742.sh
  /bin/bash -x /tmp/script/maven-jenkins-ci-script.sh
   date  %Y%m%d-%H%M%S
  DATE=20201227-021208
  WAR_PATH=/nfs/data4/war
  WEBROOT_PATH=/nfs/data4/webapps
  WEB_DIR=Maven-HelloWorld-20201227-021208-v1.1
  WAR_DIR='/nfs/data4/war/Maven-HelloWorld-20201227-021208-v1.1'
  WAR_NAME=Maven-HelloWorld-20201227-021208-v1.1.war
  K8S_MATER=weiyigeek@10.10.107.202
  K8S_MATER_PORT=20211
  [[ rollback = deploy ]]
  [[ rollback = rollback ]]  # 进行回滚操作
  rollback
   ssh -p 20211 weiyigeek@10.10.107.202 'find /nfs/data4/war -maxdepth 1 -type d -name Maven-HelloWorld-*-v1.1'
**************WARNING**************
Authorized only. All activity will be monitored and reported.
  History_Version=/nfs/data4/war/Maven-HelloWorld-20201227-020140-v1.1  # 历史版本
  ssh -p 20211 weiyigeek@10.10.107.202 'rm -rf /nfs/data4/webapps &&                                          ln -s /nfs/data4/war/Maven-HelloWorld-20201227-020140-v1.1 /nfs/data4/webapps &&                                          kubectl delete pod -l app=java-maven'
**************WARNING**************
Authorized only. All activity will be monitored and reported.
pod "deploy-java-maven-0" deleted
pod "deploy-java-maven-1" deleted
pod "deploy-java-maven-2" deleted
Finished: SUCCESS

# 应用反馈 
http://10.10.107.202:30089/
  # Maven - Hello World - v1.1
  # 访问时间: Sun Dec 27 2020 10:12:18 GMT 0800 (中国标准时间)
  # Server : Apache Tomcat/8.5.61 | 10.244.1.203
  # Client : 10.244.0.0 | 10.244.0.0
  # Document_Root : /usr/local/tomcat/webapps/ROOT/
  # URL : 10.10.107.202/index.jsp

Step 9.重部署测试 构建参数: deploy_option->redeploy , git_version -> v1.2

代码语言:javascript复制
# (1) 我们先来看看 war 目录的v1.2版本的文件与目录名称
/nfs/data4/war$ ls -alh | grep "-v1.2"
drwxrwxr-x 4 weiyigeek weiyigeek 4.0K Dec 27 10:09 Maven-HelloWorld-20201227-020939-v1.2
-rw-r--r-- 1 weiyigeek weiyigeek 2.0M Dec 27 10:09 Maven-HelloWorld-20201227-020939-v1.2.war

# (2) 项目重部署控制台信息输出
[Maven-HelloWorld] $ /bin/sh -xe /tmp/jenkins8520069315177369772.sh
channel stopped
  /bin/bash -x /tmp/script/maven-jenkins-ci-script.sh
   date  %Y%m%d-%H%M%S
  DATE=20201227-021934
  WAR_PATH=/nfs/data4/war
  WEBROOT_PATH=/nfs/data4/webapps
  WEB_DIR=Maven-HelloWorld-20201227-021934-v1.2
  WAR_DIR='/nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2'
  WAR_NAME=Maven-HelloWorld-20201227-021934-v1.2.war
  K8S_MATER=weiyigeek@10.10.107.202
  K8S_MATER_PORT=20211
  [[ redeploy = deploy ]]
  [[ redeploy = rollback ]]
  [[ redeploy = redeploy ]]
  redeploy   # 进入了重部署脚本选择
  [[ ve8d88cf3e222b79259edcfb7ca48cee7b079ee08 = ve8d88cf3e222b79259edcfb7ca48cee7b79ee8 ]]
  echo -e '曾经部署过 v1.2 版本,现在正在重新部署!'
曾经部署过 v1.2 版本,现在正在重新部署!
   ssh -p 20211 weiyigeek@10.10.107.202 'find /nfs/data4/war -maxdepth 1 -type d -name Maven-HelloWorld-*-v1.2'
  History_Version=/nfs/data4/war/Maven-HelloWorld-20201227-020939-v1.2
  ssh -p 20211 weiyigeek@10.10.107.202 'rm -rf /nfs/data4/war/Maven-HelloWorld-20201227-020939-v1.2' # 删除了原来v1.2版本的war解压目录

# 重新构建
  deploy
  scp -P 20211 /var/lib/jenkins/workspace/Maven-HelloWorld/target/hello-world.war weiyigeek@10.10.107.202:/nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2.war
# 解压上传的war 并创建软连接
  ssh -p 20211 weiyigeek@10.10.107.202 'unzip /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2.war -d  /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2 &&                                          rm -rf /nfs/data4/webapps &&                                          ln -s /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2 /nfs/data4/webapps &&                                          kubectl delete pod -l app=java-maven'
Archive:  /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2.war
   creating: /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2/META-INF/
....
  inflating: /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2/META-INF/maven/com.weiyigeek.main/hello-world/pom.properties  
pod "deploy-java-maven-0" deleted
pod "deploy-java-maven-1" deleted
pod "deploy-java-maven-2" deleted

Finished: SUCCESS

# (3) 应用反馈
Maven - Hello World - v1.2
访问时间: Sun Dec 27 2020 10:20:03 GMT 0800 (中国标准时间)
Server : Apache Tomcat/8.5.61 | 10.244.2.107
Client : 10.244.0.0 | 10.244.0.0
Document_Root : /usr/local/tomcat/webapps/ROOT/
URL : 10.10.107.202/index.jsp

PS : 至此 Maven 与 Jenkins 集成实践完成

补充介绍: 除了上面在项目的Pom.xml中设置内部Maven服务器,我们可以可以采样下面两种方式指定配置文件

方式1.通过 curl 在构建前下载 Git 版本控制的 Maven 自定义的 setting.xml 配置文件(在我后面K8s中构建时会看见)。

方式2.采用 Managed file(可以配置 Maven 的全局配置文件和用户配置文件)Maven Integration 插件

代码语言:javascript复制
// Config File Provider 插件使用配置文件
configFileProvider([configFile(fileId:'maven-global-settings',variable:'MAVEN_GLOBAL_ENV')]) {
  sh "mvn -s $MAVEN_GLOBAL_ENV clean install"
}

// Pipeline Maven Integration 插件使用配置文件
withMaven(
  // Maven installation declared in the Jenkins "Global Tool Configuration"
  maven: 'maven-3',
  // Maven settings.xml file defined with the Jenkins Config File Provider Plugin
  // We recommend to define Maven settings.xml globally at the folder level using 
  // navigating to the folder configuration in the section "Pipeline Maven Configuration / Override global Maven configuration"
  // or globally to the entire master navigating to  "Manage Jenkins / Global Tools Configuration"
  mavenSettingsConfig: 'my-maven-settings') {
    // Run the maven build
    sh "mvn clean verify"

  } // withMaven will discover the generated Maven artifacts, JUnit Surefire & FailSafe & FindBugs & SpotBugs reports...

SonarQube 集成配置与实践

描述: 在Jenkins持续集成中中可以在构建代码前对我们项目进行一个代码质量扫描检测, 此处我们引入SonarQube进行实现;

操作流程:

Step 0.在 SonarQube Web中进行认证 Token 生成:(手工设置)_添加项目 -> 项目标识 -> 创建一个令牌 (Jenkins) -> 得到Token;

代码语言:javascript复制
Jenkins: 755eeb453cb28f96aa9d4ea14334da7287c2e840
# 此令牌用于执行分析时认证时使用,如果这个令牌存在问题,可以随时在你的用户账号下取消这个令牌。

Step 1.插件安装: 系统管理 -> 插件管理 -> SonarQube Scanner for Jenkins()

WeiyiGeek.SonarQube Scanner for Jenkins

Step 2.在Jenkins系统管理->系统设置以及全局工具管理->上配置SonarQube相关配置(服务端地址,以及客户端工具地址);

代码语言:javascript复制
# (1) 服务端
Dashboard -> 配置 -> SonarQube servers -> Add SonarQube 设置名称和SonarQube地址 -> 添加Token凭据(类型:Secret Text) -> Jenklins-Connet-Sonarqube-Token

# (2) 客户端
Dashboard -> 全局工具配置 -> SonarQube Scanner -> 名称(sonarqube-scanner) -> 工具路径 (/usr/local/sonar)-> 保存应用

WeiyiGeek.Jenkins上SonarQube配置

Step 3.应用项目分析实战在分析项目中配置Analysis Properties->保存和应用-> 此时项目中会多一个SonarQube的图标; PS : 下述图中有误应该是在Pre Steps阶段(Execute SonarQube Scanner,其次 Build 构建即可)

代码语言:javascript复制
# 构建前进行分析配置(名称、唯一标识、检测目录)
sonar.projectName=${JOB_NAME}
sonar.projectKey=Jenkis
sonar.source=.


# 已配置不需要了
# sonar.host.url=http://sonar.weiyigeek.top:9000 
# sonar.login=755eeb453cb28f96aa9d4ea14334da7287c2e840

WeiyiGeek.SonarQube Analysis Properties

Step 4.新建立一个V1.7Tag并上传到Gitlab之中,之后进行检测与构建操作->返回工程点击SonarQube 图标进行项目检测结果页面;

代码语言:javascript复制
WeiyiGeek@WeiyiGeek MINGW64 /e/EclipseProject/hello-world (master)
$ git add .
$ git commit -m "v1.7"
  # [master 0f50b10] v1.7
  # 1 file changed, 1 insertion( ), 1 deletion(-)
$ git tag -a "v1.7" -m "v1.7"
$ git push
  # Enumerating objects: 11, done.
  # Counting objects: 100% (11/11), done.
  # .....
  # To http://gitlab.weiyigeek.top/ci-cd/java-maven.git
  #    3bfb942..0f50b10  master -> master

$ git push origin v1.7
  # ...
  # To http://gitlab.weiyigeek.top/ci-cd/java-maven.git
  # * [new tag]         v1.7 -> v1.7

WeiyiGeek.SonarQube项目检测结果

Step 5.项目在K8s集群中部署结果

代码语言:javascript复制
# https://10.10.107.202:30089/
Maven - Hello World - v1.7
访问时间: Sun Jan 03 2021 13:20:34 GMT 0800 (中国标准时间)
Server : Apache Tomcat/8.5.61 | 10.244.2.111
Client : 10.244.0.0 | 10.244.0.0
Document_Root : /usr/local/tomcat/webapps/ROOT/
URL : 10.10.107.202/index.jsp

邮箱&钉钉&微信消息通知 集成配置与实践

描述: 在Jenkins中我们还有最重要的一步还没有完成, 即消息通知(让我们知道是构建成功还是、构建失败)等等, 常规的方式有邮箱通知、Shell自定义脚本通知,WebHook通知等;

通知插件插件安装:

代码语言:javascript复制
DingTalk : 钉钉 Jenkins 插件 2.4.3 (https://github.com/jenkinsci/dingtalk-plugin)
Qy Wechat Notification Plugin : 这个插件是一个机器人,可以发布构建状态和发送消息给qy微信. 1.0.2	 (https://www.jenkins.io/doc/pipeline/steps/qy-wechat-notification/)

(0) 邮箱通知实践配置 描述: 此处以腾讯企业邮箱为例进行配置,首先需要登陆将要被使用的邮箱,注意必须要使用微信绑定后才能正常生成客户端专用密码,然后开启SMTP服务;

代码语言:javascript复制
# 客户端设置方法
接收服务器:
imap.exmail.qq.com(使用SSL,端口号993)
发送服务器:
smtp.exmail.qq.com(使用SSL,端口号465)

WeiyiGeek.腾讯企业邮箱

Step 1.在 Dashboard -> 系统配置 -> Jenkins Location进行配置系统管理员邮件地址,注意此处管理员邮箱地址必须与smtp服务发信地址一致否则将会报出501 mail from address must be same as authorization user错误;

Step 2.设置发信邮箱相关配置,点击 Dashboard -> 系统配置 -> 邮件通知 填入 SMTP 发信服务器地址以及企业邮箱后缀,采用SSL协议并输入认证的账号与客户端专用密码,最后测试发信;

WeiyiGeek.发信邮箱相关配置

Step 3.构建项目通信发信测试,点击 Dashboard -> Maven-HelloWorld -> 构建设置 -> 启用E-mail Notification

代码语言:javascript复制
# 收信人:Recipients
# 什么场景发送信息:
- 构建失败给每一个人发送发送电子邮件 : Send e-mail for every unstable build
- 谁构建失败给谁发送邮件: Send separate e-mails to individuals who broke the build
- 为每个失败的模块发送电子邮件 : Send e-mail for each failed module

WeiyiGeek.项目通信发信测试

补充方式: 描述: 由于Jenkins自带的邮件功能比较鸡肋,因此这里推荐安装专门的邮件插件(Email Extension)并介绍如何配置Jenkins自带的邮件功能作用。

插件安装: 系统管理→管理插件→可选插件选择Email Extension Plugin插件进行安装 系统设置:

1) 通过系统管理→系统设置,进行邮件配置 -> Extended E-mail Notification -> 输入 SMTP Server 相关信息以及Authentication相关设置(注意:密码一般是邮箱授权码)

2) 设置其编码格式以及默认内容类型,以及邮件模板配置在 Extended E-mail的 default content -;

代码语言:javascript复制
<!-- ^s*(?=r?$)n 正则替换空行 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
    <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
        <tr>
            <td>(本邮件由程序自动下发,请勿回复!)</td>
        </tr>
        <tr>
            <td>
                <h2><font color="#FF0000">构建结果 - ${BUILD_STATUS}</font></h2>
            </td>
        </tr>
        <tr>
            <td><br />
                <b><font color="#0B610B">构建信息</font></b>
                <hr size="2" width="100%" align="center" />
            </td>
        </tr>
        <tr><a href="${PROJECT_URL}">${PROJECT_URL}</a>
            <td>
                <ul>
                    <li>项目名称:${PROJECT_NAME}</li>
                    <li>GIT路径:<a href="${GIT_URL}">${GIT_URL}</a></li>                   
                    <li>构建编号:${BUILD_NUMBER}</li>                   
                    <li>触发原因:${CAUSE}</li>  
                    <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
                </ul>
            </td>
        </tr>
        <tr>
            <td>
                <b><font color="#0B610B">变更信息:</font></b>
               <hr size="2" width="100%" align="center" />
            </td>
        </tr>
        <tr>
            <td>
                <ul>
                    <li>上次构建成功后变化 :  ${CHANGES_SINCE_LAST_SUCCESS}</a></li>
                </ul>   
            </td>
        </tr>
 <tr>
            <td>
                <ul>
                    <li>上次构建不稳定后变化 :  ${CHANGES_SINCE_LAST_UNSTABLE}</a></li>
                </ul>   
            </td>
        </tr>
        <tr>
            <td>
                <ul>
                    <li>历史变更记录 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li>
                </ul>   
            </td>
        </tr>
        <tr>
            <td>
                <ul>
                    <li>变更集:${JELLY_SCRIPT,template="html"}</a></li>
                </ul>   
            </td>
        </tr>
        <!--
        <tr>
            <td>
                <b><font color="#0B610B">Failed Test Results</font></b>
                <hr size="2" width="100%" align="center" />
            </td>
        </tr>
        <tr>
            <td>
                <pre style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">$FAILED_TESTS</pre>
                <br />
            </td>
        </tr>
        <tr>
            <td>
                <b><font color="#0B610B">构建日志 (最后 100行):</font></b>
                <hr size="2" width="100%" align="center" />
            </td>
        </tr>-->
        <!-- <tr>
            <td>Test Logs (if test has ran): <a
                href="${PROJECT_URL}ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip">${PROJECT_URL}/ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip</a>
                <br />
            <br />
            </td>
        </tr> -->
        <!--
        <tr>
            <td>
                <textarea cols="80" rows="30" readonly="readonly" style="font-family: Courier New">${BUILD_LOG, maxLines=100,escapeHtml=true}</textarea>
            </td>
        </tr>-->
        <hr size="2" width="100%" align="center" />
    </table>
</body>
</html>

3) 设置Default Triggers触发机制,例如下面是失败时候和成功时候发送;

WeiyiGeek.设置

  • 4) 在Job任务调用中选择构建后操作进行设置Email Notification进行设置通知

WeiyiGeek.Email-Notification

(1) 钉钉消息通知实践配置 Step 0.在钉钉中建立一个群聊并且创建一个群机器人生成一个Webhook地址,操作如下图所示:

代码语言:javascript复制
例如:https://oapi.dingtalk.com/robot/send?access_token=95f707645db08794166ed3aad3eaad363bb1475bf7c91635b7456a0a8c8893c6

WeiyiGeek.钉钉群聊机器人添加

Step 1.设置钉钉消息通知参数, 点击 Dashboard -> 系统配置 -> 钉钉 选择通知时机以及代理通信(当该主机无法正常连接网络时可采用此方法) -> 点击新增(填入唯一的id、以及名称和webhook地址,注意如果在创建机器人时指定了关键字和加密字符串需要填写上)->然后测试发信;

WeiyiGeek.钉钉消息通知参数

Step 2.在FreeStyle风格的项目是可以在通用设置卡点选钉钉消息通知的,而Maven的项目是没有该点选选项,因为该插件只支持FreeStyle和PIPELINE流水线(这里有巨坑所以有的时候还是老版本的插件好用),注意网上博客中关于大多数此问题都是不适用的官方文档才是第一手;

参考连接: https://jenkinsci.github.io/dingtalk-plugin/examples/freestyleAdvanced.html#详细日志

WeiyiGeek.FreeStyle风格构建的钉钉通知

PS : 对于其它项目风格的项目在后面我们将使用流水线PIPEline进行实现钉钉的消息通知;

(2) 企业微信通知实践配置

  • Step 1.设置企业微信通知全局参数,点击 Dashboard -> 系统配置 -> 企业微信通知配置设置构建环境名称(会在信息中显示)以及默认Webhook地址(全局的)、通知用户的Uid(@ALL表示全部)

WeiyiGeek.企业微信全局参数

Step 2.在构建任务中设置相应的通知参数,点击 Dashboard -> Maven-HelloWorld(项目名称) -> 构建后的操作 -> 选择企业微信

代码语言:javascript复制
# PS:此处输入的Webhook优先级高于全局的企业微信Webhook这样做的好处是便于为每个任务分配不同的Webhook;
Webhook地址: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c222f3fc-f645-440a-ad24-0ce8d9626f11
情况通知:
  仅失败才@
  发送开始构建信息
  仅失败才发送
  仅成功才发送
  仅构建中断才发送
  仅不稳定构建才发送
通知UserID: @ALL
通知手机号码: 选填

Step 3.对 Maven-HelloWorld 项目进行构建并查看控制台输出,消息推送;

代码语言:javascript复制
# 构建前发送
> git config core.sparsecheckout # timeout=10
> git checkout -f e8d88cf3e222b79259edcfb7ca48cee7b079ee08 # timeout=10
Commit message: "v1.2"
> git rev-list --no-walk e8d88cf3e222b79259edcfb7ca48cee7b079ee08 # timeout=10
推送通知 {"markdown":{"content":"Jenkins-Notify<font color="info">【Maven-HelloWorld】</font>开始构建n >构建参数:<font color="comment">git_version=v1.2, deploy_option=rollback </font>n >预计用时:<font color="comment">0分钟</font>n >[查看控制台](http://jenkins.weiyigeek.top:8080/job/Maven-HelloWorld/16/console)"},"msgtype":"markdown"}
通知结果 {"errcode":0,"errmsg":"ok"}

# 构建部署后发送
  ssh -p 20211 weiyigeek@10.10.107.202 'rm -rf /nfs/data4/webapps &&  ln -s /nfs/data4/war/Maven-HelloWorld-20201227-021934-v1.2 /nfs/data4/webapps && kubectl delete pod -l app=java-maven'
**************WARNING**************
Authorized only. All activity will be monitored and reported.
pod "deploy-java-maven-0" deleted
pod "deploy-java-maven-1" deleted
pod "deploy-java-maven-2" deleted
推送通知{"markdown":{"content":"Jenkins-Notify<font color="info">【Maven-HelloWorld】</font>构建<font color="info">成功~</font>


	

0 人点赞