目录[-]
环境介绍:
详情请看 Kubernetes 中安装 Jenkins ,这里不过多叙述。
为了方便集成 Maven、Kubernetes、配置文件等等,这里需要安装几个别的插件,这里插件可以在 系统管理—>插件管理—>可选插件 里面安装下面列出的插件。
Jenkins 安装中默认安装 Git 插件,所以不需要单独安装。利用 git 工具可以将 github、gitlab 等等的地址下载源码。
Jenkins 安装中默认安装 Docker 插件,所以不需要单独安装。利用 Docker 插件可以设置 Docker 环境,运行 Docker 命令,配置远程 Docker 仓库凭据等。
Kubernetes 插件的目的是能够使用 Kubernetes 集群动态配置 Jenkins 代理(使用Kubernetes调度机制来优化负载),运行单个构建,等构建完成后删除该代理。这里我们需要用到这个插件来启动 Jenkins Slave 代理镜像,让代理执行 Jenkins 要执行的 Job。
Kubernetes Cli 插件作用是在执行 Jenkins Job 时候提供 kubectl 与 Kubernetes 集群交互环境。可以在 Pipeline 或自由式项目中允许执行 kubectl 相关命令。它的主要作用是提供 kubectl 运行环境,当然也可以提供 helm 运行环境。
Config File Provider 插件作用就是提供在 Jenkins 中存储 properties、xml、json、settings.xml 等信息,可以在执行 Pipeline 过程中可以写入存储的配置。
例如,存入一个 Maven 全局 Settings.xml 文件,在执行 Pipeline Job 时候引入该 Settings.xml ,这样 Maven 编译用的就是该全局的 Settings.xml。
这是一个操作文件的插件,例如读写 json、yaml、pom.xml、Properties 等等。在这里主要用这个插件读取 pom.xml 文件的参数设置,获取变量,方便构建 Docker 镜像。
如果是私有项目 Git 一般需要配置一个凭据用于验证,如果是公开项目,则无需任何配置。
凭据->系统->全局凭据->添加凭据
利用 Git 插件拉取源码,分别可以设置拉取的“分支”、“显示拉取日志”、“拉取的凭据”、“拉取的地址”,可以将上面设置的凭据ID设置到 credentialsId 参数上
参考:https://jenkins.io/doc/pipeline/steps/git/#-git-%20git
git branch: "master" ,changelog: true , credentialsId: "xxxx-xxxx-xxxx", url: "https://github.com/xxxxxx"
此插件将提供一下功能:
安装 Jenkins 时候默认会安上此插件,这里主要是利用插件提供一个 docker 登录了的环境,以及执行一些 Docker 命令,具体请看参考,下面将写一个简单的执行例子来描述 Docker 镜像的构建过程。
参考:https://jenkins.io/doc/pipeline/steps/docker-workflow/
// 此方法是设置docker仓库地址,然后选择存了用户名、密码的凭据ID进行验证。注意,只有在此方法之中才生效。
docker.withRegistry("https://hub.docker.com/", "xxxxx-xxxx-xxxx-xxxx") {
echo "构建镜像"
def customImage = docker.build("hub.mydlq.club/myproject/springboot-helloworld:0.0.1")
echo "推送镜像"
customImage.push()
echo "删除镜像"
sh "docker rmi hub.mydlq.club/myproject/springboot-helloworld:0.0.1"
}
配置连接 kubernetes 集群的凭据(Kubernetes ServiceAccount token),此凭据的账户权限最好设置较大点,避免出现未知问题。配置完成后,需要在后面的 Cloud 云配置中设置这个凭据。
系统管理—>系统设置—>云
参考:https://github.com/jenkinsci/kubernetes-plugin
这里是配置连接Kubernetes集群,启动 Jenkins Slave 代理的相关配置。
这里配置 Jenkins Slave 在 kubernetes 集群中启动的 Pod 的配置,这里将设置四个镜像,分别是:
这里将这四个镜像融入到一个 Pod 之中,方便执行各种命令来完成持续部署交互过程。
在 Pod 的原始 yaml 那栏中,填写下面的 yaml 文件内容进行配置
apiVersion: v1
kind: Pod
metadata:
labels:
app: jenkins-slave
spec:
serviceAccountName: jenkins-admin
securityContext: #容器安全设置
runAsUser: 0 #以ROOT用户运行容器
privileged: true #赋予特权执行容器
containers:
- name: jnlp #Jenkins Slave镜像
image: registry.cn-shanghai.aliyuncs.com/mydlq/jenkins-jnlp-slave:3.27-1
#设置工作目录
workingDir: /home/jenkins
tty: true
- name: docker #Docker镜像
image: registry.cn-shanghai.aliyuncs.com/mydlq/docker:18.06.2-dind
command: ['cat']
tty: true
volumeMounts:
- name: docker
mountPath: /usr/bin/docker
- name: docker-sock
mountPath: /var/run/docker.sock
- name: docker-config
mountPath: /etc/docker
- name: maven #Maven镜像
image: registry.cn-shanghai.aliyuncs.com/mydlq/maven:3.6.0-jdk8-alpine
command:
- cat
tty: true
volumeMounts:
- name: maven-m2
mountPath: /root/.m2
- name: helm-kubectl #Kubectl & Helm镜像
image: registry.cn-shanghai.aliyuncs.com/mydlq/helm-kubectl:2.13.1
command:
- cat
tty: true
volumes:
- name: docker #将宿主机 Docker 文件夹挂进容器,方便存储&拉取本地镜像
hostPath:
path: /usr/bin/docker
- name: docker-sock #将宿主机 Docker.sock 挂进容器
hostPath:
path: /var/run/docker.sock
- name: docker-config #将宿主机 Docker 配置挂在进入容器
hostPath:
path: /etc/docker
- name: maven-m2 #Maven 本地仓库挂在到 NFS 共享存储,方便不同节点能同时访问与存储
nfs:
server: 192.168.2.11
path: "/nfs/data/maven-m2"
# nodeSelector:
# kubernetes.io/hostname: node-2-12
配置连接 kubernetes 集群的凭据,这个凭据可以和上面 kubernetes 插件的凭据一致,都是用于连接 Kubernetes 集群
此插件主要功能就是提供执行 kubectl 的环境设置,在此插件方法中相当于有 kubectl、helm 等环境设置,然后用相关镜像就可以执行相关命令。
参考:https://jenkins.io/doc/pipeline/steps/kubernetes-cli/
// 提供 kubectl 执行的环境,其中得设置存储了 token 的凭据ID和 kubernetes api 地址
withKubeConfig([credentialsId: "xxxx-xxxx-xxxx-xxxx",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
sh "kubectl get nodes"
}
在 Jenkins 安装时候安装了“config File Provider”插件,这个插件的作用就是提供在 Jenkins 中存储properties、xml、json、settings.xml 等信息,这里打开下面列表,配置一个全局的 Maven 的 settings.xml 文件。
系统管理—>Managed files—>Add a new Config—>Global Maven settings.xml
在里面添加一个全局的 setting.xml 设置,为了加快 jar 包的下载速度,这里将仓库地址指向 aliyun Maven 仓库地址。
alimaven
aliyun maven
http://maven.aliyun.com/nexus/content/groups/public/
central
参考:https://jenkins.io/doc/pipeline/steps/config-file-provider/
可以在 Pipeline 脚本中,用于生成上面设置的文件,用法如下:
// 生成 settings.xml 文件,这个方法第一个参数是引用文件ID,第二个是生成的文件名
configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]) {
// 只有在方法里面该文件才存在
echo "cat settings.xml"
}
此插件将提供一下功能:
这里主要是用此插件读取 pom.xml 的项目有关的参数,用于 docker 编译镜像时使用。另一个功能是在脚本进行时候用于生成文件,例如 yaml 文件、helm 证书等。
参考:https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/
// 读取 pom.xml 文件
pom = readMavenPom file: "./pom.xml"
echo "${pom.artifactId}:${pom.version}"
为了保证插件配置正确且值执行,在 kubernetes 环境下启动 Jenkins 代理执行任务,这里将进行测试。
创建一个名为 “k8s-test” 的任务,类型选择“流水线”。
这里写一个简单的脚本,将 Kubernetes 插件提供的 Pipeline 的方法引入,如下:
// 代理名称,填写系统设置中设置的 Cloud 中 Template 模板的 label
def label = "jnlp-agent"
// 调用Kubernetes提供的方法
podTemplate(label: label,cloud: 'kubernetes' ){
// 在代理节点上运行脚本
node (label) {
echo "测试 kubernetes 中 jenkins slave 代理!~"
}
}
回到任务界面,点击立即构造来执行任务。
然后点击执行历史栏中点击,查看控制台输出的日志信息。
以下仅是个人看法,有更好的方式,希望告知。
这里涉及到一个问题,在 Jenkins 中,我们的 Jenkinsfile 脚本存放在哪比较方便,这里本人想到三种:
比较三者:
这里需要将将一些配置文件存入项目源码中,用于在执行流水线中读取对应的配置参数,比如:
项目 Github 地址:https://github.com/my-dlq/springboot-helloworld
FROM registry.cn-shanghai.aliyuncs.com/mydlq/openjdk:8u201-jdk-alpine3.9
VOLUME /tmp
ADD target/*.jar app.jar
RUN sh -c 'touch /app.jar'
ENV JAVA_OPTS="-Xmx512M -Xms256M -Xss256k -Duser.timezone=Asia/Shanghai"
ENV APP_OPTS=""
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
kind: Deployment
image:
pullPolicy: "Always"
replicas: 1
resources:
limits:
memory: 512Mi
cpu: 1000m
requests:
memory: 256Mi
cpu: 500m
#***java && app 环境变量设置
env:
- name: "JAVA_OPTS"
value: "-Xmx512M -Xms355M -Xss256k -Duser.timezone=Asia/Shanghai"
- name: "APP_OPTS"
value: ""
envFrom:
#- configMapRef:
# name: env-config
service:
type: NodePort #Service type设置 (可以设置为ClusterIP、NodePort、None)
labels:
svcEndpoints: actuator
annotations: {}
ports:
- name: server
port: 8080
targetPort: 8080
protocol: TCP
nodePort: 30080
- name: management
port: 8081
targetPort: 8081
protocol: TCP
nodePort: 30081
这里先写一个简单的脚本,用于测试各个环境是否都能用,例如slave镜像的git命令能否执行、maven镜像的mvn命令是否能用等等。
这里可以用 container(‘docker’) 方式,来引用 kubernetes 插件中设置的容器,利用各个容器不同的客户端功能,来执行对应的命令。
将之前创建的任务配置中的 pipeline 脚本改成下面:
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "1、开始拉取代码"
sh "git version"
}
stage('Maven阶段'){
container('maven') {
echo "2、开始Maven编译、推送到本地库"
sh "mvn -version"
}
}
stage('Docker阶段'){
container('docker') {
echo "3、开始读取Maven pom变量,并执行Docker编译、推送、删除"
sh "docker version"
}
}
stage('Helm阶段'){
container('helm-kubectl') {
echo "4、开始检测Kubectl环境,测试执行Helm部署,与执行部署"
sh "helm version"
}
}
}
}
jenkins slave 容器中默认集成 git 客户端,该整体流水线执行就在 Jenkins slave 容器中,任务默认在 Jenkins Slave 执行,所以不需要设置容器名称。
然后执行查看日志,日志内容如下:
Running on jnlp-agent-g7qk5 in /home/jenkins/workspace/k8s-test
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Git阶段)
[Pipeline] echo
1、开始拉取代码
[Pipeline] sh
+ git version
git version 2.11.0
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Maven阶段)
[Pipeline] container
[Pipeline] {
[Pipeline] echo
2、开始Maven编译、推送到本地库
[Pipeline] sh
+ mvn -version
Apache Maven 3.6.0 (97c98ec64a1fdfee7767ce5ffb20918da4f719f3; 2018-10-24T18:41:47Z)
Maven home: /usr/share/maven
Java version: 1.8.0_201, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-1.8-openjdk/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-957.1.3.el7.x86_64", arch: "amd64", family: "unix"
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Docker阶段)
[Pipeline] container
[Pipeline] {
[Pipeline] echo
3、开始读取Maven pom变量,并执行Docker编译、推送、删除
[Pipeline] sh
+ docker version
Client:
Version: 18.06.2-ce
API version: 1.38
Go version: go1.10.4
Git commit: 6d37f41
Built: Sun Feb 10 03:43:40 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.3
API version: 1.39 (minimum version 1.12)
Go version: go1.10.8
Git commit: 774a1f4
Built: Thu Feb 28 06:02:24 2019
OS/Arch: linux/amd64
Experimental: false
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Helm阶段)
[Pipeline] container
[Pipeline] {
[Pipeline] echo
4、开始检测Kubectl环境,测试执行Helm部署,与执行部署
[Pipeline] sh
+ helm version
Client: &version.Version{SemVer:"v2.13.1", GitCommit:"618447cbf203d147601b4b9bd7f8c37a5d39fbb4", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.13.1", GitCommit:"79d07943b03aea2b76c12644b4b54733bc5958d6", GitTreeState:"clean"}
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS
最后看见执行状态为 SUCCESS 则证明环境可用,否则有问题,请检测问题所在。
这里进行分阶段性的脚本编写,然后一步步测试,最后合并在一起。这里新建一个名称为 k8s-pipeline 的任务,然后在配置项脚本框汇总输入 Pipleline 脚本。
这里拉取本人 Github 上的一个简单的 SpringBoot Demo 项目进行实践。
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
}
}
}
......
Running on jnlp-agent-dhr1h in /home/jenkins/workspace/k8s-pipeline
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Git阶段)
[Pipeline] echo
1、开始拉取代码
[Pipeline] sh
+ git clone https://github.com/my-dlq/springboot-helloworld.git
Cloning into 'springboot-helloworld'...
[Pipeline] sh
+ ls -l
total 0
drwxr-xr-x 4 root root 79 Apr 28 07:00 springboot-helloworld
[Pipeline] }
......
Finished: SUCCESS
可以通过控制台输出的日志看到,已经拉取成功。继续进行下一步,Maven 阶段。
这里将进行 Maven 编译,将 Java 源码编译成一个 Jar 项目,方便后续打包进入 Docker 镜像。( Maven 中也可以进行单元测试,由于某些原因,这里不进行阐述,可以自己执行测试命令进行测试 )
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
}
stage('Maven阶段'){
container('maven') {
//这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
}
}
}
}
}
......
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ springboot-helloword ---
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ springboot-helloword ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ springboot-helloword ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/jenkins/workspace/k8s-pipeline/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ springboot-helloword ---
[INFO] Not copying test resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ springboot-helloword ---
[INFO] Not compiling test sources
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ springboot-helloword ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ springboot-helloword ---
[INFO] Building jar: /home/jenkins/workspace/k8s-pipeline/target/springboot-helloword-0.0.1.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.4.RELEASE:repackage (repackage) @ springboot-helloword ---
[INFO] Replacing main artifact with repackaged archive
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ springboot-helloword ---
[INFO] Installing /home/jenkins/workspace/k8s-pipeline/target/springboot-helloword-0.0.1.jar to /root/.m2/repository/club/mydlq/springboot-helloword/0.0.1/springboot-helloword-0.0.1.jar
[INFO] Installing /home/jenkins/workspace/k8s-pipeline/pom.xml to /root/.m2/repository/club/mydlq/springboot-helloword/0.0.1/springboot-helloword-0.0.1.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.989 s
[INFO] Finished at: 2019-04-28T09:38:03Z
[INFO] ------------------------------------------------------------------------
......
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
}
stage('Maven阶段'){
echo "Maven 阶段"
container('maven') {
//这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
}
}
}
stage('Docker阶段'){
echo "Docker 阶段"
container('docker') {
// 读取pom参数
echo "读取 pom.xml 参数"
pom = readMavenPom file: './pom.xml'
// 设置镜像仓库地址
hub = "registry.cn-shanghai.aliyuncs.com"
// 设置仓库项目名
project_name = "mydlq"
echo "编译 Docker 镜像"
docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") {
echo "构建镜像"
// 设置推送到aliyun仓库的mydlq项目下,并用pom里面设置的项目名与版本号打标签
def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}")
echo "推送镜像"
customImage.push()
echo "删除镜像"
sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}"
}
}
}
}
}
编译 Docker 镜像
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withDockerRegistry
Executing shell script inside container [docker] of pod [jnlp-agent-v6f1f]
Executing command: "docker" "login" "-u" "3******[email protected]" "-p" ******** "http://registry.cn-shanghai.aliyuncs.com" /home/jenkins/workspace/k8s-pipeline2@tmp/b52e213b-a730-4120-b004-decd8e16b246/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
[Pipeline] {
[Pipeline] echo
构建镜像
[Pipeline] sh
+ docker build -t registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 .
Sending build context to Docker daemon 18.37MB
Step 1/7 : FROM registry.cn-shanghai.aliyuncs.com/mydlq/openjdk:8u201-jdk-alpine3.9
---> 3675b9f543c5
Step 2/7 : VOLUME /tmp
---> Running in 7fc4af80e6ce
Removing intermediate container 7fc4af80e6ce
---> 4e4224d3b50b
Step 3/7 : ADD target/*.jar app.jar
---> 0c24118d522f
Step 4/7 : RUN sh -c 'touch /app.jar'
---> Running in 8836cb91e1ca
Removing intermediate container 8836cb91e1ca
---> 389e604851b6
Step 5/7 : ENV JAVA_OPTS="-Xmx512M -Xms256M -Xss256k -Duser.timezone=Asia/Shanghai"
---> Running in 5126902b1e6b
Removing intermediate container 5126902b1e6b
---> 055ad2b9c49d
Step 6/7 : ENV APP_OPTS=""
---> Running in cf8ea4b61eea
Removing intermediate container cf8ea4b61eea
---> 07dd4fdda44a
Step 7/7 : ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar $APP_OPTS" ]
---> Running in 93c4d0d859e1
Removing intermediate container 93c4d0d859e1
---> d29e092f2c17
Successfully built d29e092f2c17
Successfully tagged registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
[Pipeline] dockerFingerprintFrom
[Pipeline] echo
推送镜像
[Pipeline] sh
+ docker tag registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1 registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
[Pipeline] sh
+ docker push registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
The push refers to repository [registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword]
8d45ad1172aa: Preparing
aba126d6a94c: Preparing
a464c54f93a9: Mounted from mydlq/openjdk
dee6aef5c2b6: Mounted from mydlq/openjdk
aba126d6a94c: Pushed
8d45ad1172aa: Pushed
0.0.1: digest: sha256:2c661931a3c08a1cad1562ec4936c68f06b4b3ffcec5de14c390ae793cf5b53b size: 1371
[Pipeline] echo
删除镜像
[Pipeline] sh
+ docker rmi registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
Untagged: registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword:0.0.1
Untagged: registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloword@sha256:2c661931a3c08a1cad1562ec4936c68f06b4b3ffcec5de14c390ae793cf5b53b
Deleted: sha256:d29e092f2c175a3662af0175e62462238583330ba7d46b84d89134056ba14027
Deleted: sha256:07dd4fdda44a12d7749e5e7f60b1943c83e6d0a3da2e4f279ce4d53f3b04f27e
......
Finished: SUCCESS
这里提前创建好执行 helm 的方法,将其简单封装一下用于执行流水线时候,调用此方法,执行对应的 Helm 操作。
参数 | 描述 |
---|---|
- init: | 是否为执行 helm 初始化 |
- url: | 初始化 chart 仓库地址 |
- dry: | 是否为尝试部署 |
- name: | 部署的应用 Release 名 |
- namespace: | 应用启动到哪个Namespace |
- image: | 镜像名 |
- tag: | 镜像标签 |
- template: | 选用的chart模板 |
// 执行Helm的方法
def helmDeploy(Map args) {
// Helm 初始化
if(args.init){
sh "helm init --client-only --stable-repo-url ${args.url}"
}
// Helm 尝试部署
else if (args.dry_run) {
println "尝试 Helm 部署,验证是否能正常部署"
sh "helm upgrade --install ${args.name} --namespace ${args.namespace} -f values.yaml --set ${args.repository},${args.tag} stable/${args.template} --dry-run --debug"
}
// Helm 正式部署
else {
println "正式 Helm 部署"
sh "helm upgrade --install ${args.name} --namespace ${args.namespace} -f values.yaml --set ${args.repository},${args.tag} stable/${args.template}"
}
}
// 方法调用
stage() {
echo "Helm 初始化 http://chart.mydlq.club"
helmDeploy(init: true ,url: "Helm 仓库地址");
echo "Helm 尝试执行部署"
helmDeploy(init: false ,dry: true ,name: "应用名" ,namespace: "应用启动的Namespace" ,image: "镜像名",tag: "镜像标签" ,template: "选用的chart模板")
echo "Helm 正式执行部署"
helmDeploy(init: false ,dry: false ,name: "应用名" ,namespace: "应用启动的Namespace" ,image: "镜像名",tag: "镜像标签" ,template: "选用的chart模板")
}
def label = "jnlp-agent"
// 执行Helm的方法
def helmDeploy(Map args) {
if(args.init){
println "Helm 初始化"
sh "helm init --client-only --stable-repo-url ${args.url}"
} else if (args.dry_run) {
println "尝试 Helm 部署,验证是否能正常部署"
sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template} --dry-run --debug"
} else {
println "正式 Helm 部署"
sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template}"
}
}
// jenkins slave 执行流水线任务
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
}
stage('Maven阶段'){
echo "Maven 阶段"
container('maven') {
//这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
}
}
}
stage('Docker阶段'){
echo "Docker 阶段"
container('docker') {
// 读取pom参数
echo "读取 pom.xml 参数"
pom = readMavenPom file: './pom.xml'
// 设置镜像仓库地址
hub = "registry.cn-shanghai.aliyuncs.com"
// 设置仓库项目名
project_name = "mydlq"
echo "编译 Docker 镜像"
docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") {
echo "构建镜像"
// 设置推送到aliyun仓库的mydlq项目下,并用pom里面设置的项目名与版本号打标签
def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}")
echo "推送镜像"
customImage.push()
echo "删除镜像"
sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}"
}
}
}
stage('Helm阶段'){
container('helm-kubectl') {
withKubeConfig([credentialsId: "8510eda6-e1c7-4535-81af-17626b9575f7",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
// 设置参数
image = "image.repository=${hub}/${project_name}/${pom.artifactId}"
tag = "image.tag=${pom.version}"
template = "spring-boot"
repo_url = "http://chart.mydlq.club"
app_name = "${pom.artifactId}"
// 检测是否存在yaml文件
def values = ""
if (fileExists('values.yaml')) {
values = "-f values.yaml"
}
// 执行 Helm 方法
echo "Helm 初始化"
helmDeploy(init: true ,url: "${repo_url}");
echo "Helm 执行部署测试"
helmDeploy(init: false ,dry_run: true ,name: "${app_name}" ,namespace: "mydlqcloud" ,image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
echo "Helm 执行正式部署"
helmDeploy(init: false ,dry_run: false ,name: "${app_name}" ,namespace: "mydlqcloud",image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
}
}
}
}
}
.....
Executing shell script inside container [helm-kubectl] of pod [jnlp-agent-r3c8h]
Executing command: "kubectl" "config" "set-cluster" "k8s" "--server=https://kubernetes.default.svc.cluster.local" "--insecure-skip-tls-verify=true"
exit
Cluster "k8s" set.
Executing shell script inside container [helm-kubectl] of pod [jnlp-agent-r3c8h]
Executing command: "kubectl" "config" "set-credentials" "cluster-admin" ********
Switched to context "k8s".
[Pipeline] {
[Pipeline] fileExists
[Pipeline] echo
Helm 初始化
[Pipeline] echo
Helm 初始化
[Pipeline] sh
+ helm init --client-only --stable-repo-url http://chart.mydlq.club
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: http://chart.mydlq.club
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.
Not installing Tiller due to 'client-only' flag having been set
Happy Helming!
[Pipeline] echo
Helm 执行部署测试
[Pipeline] echo
尝试 Helm 部署,验证是否能正常部署
[Pipeline] sh
+ helm upgrade --install springboot-helloworld --namespace mydlqcloud -f values.yaml --set 'image.repository=registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloworld,image.tag=0.0.1' stable/spring-boot --dry-run --debug
[debug] Created tunnel using local port: '37001'
[debug] SERVER: "127.0.0.1:37001"
[debug] Fetched stable/spring-boot to /root/.helm/cache/archive/spring-boot-1.0.4.tgz
Release "springboot-helloworld" does not exist. Installing it now.
[debug] CHART PATH: /root/.helm/cache/archive/spring-boot-1.0.4.tgz
[Pipeline] echo
Helm 执行正式部署
[Pipeline] echo
正式 Helm 部署
[Pipeline] sh
+ helm upgrade --install springboot-helloworld --namespace mydlqcloud -f values.yaml --set 'image.repository=registry.cn-shanghai.aliyuncs.com/mydlq/springboot-helloworld,image.tag=0.0.1' stable/spring-boot
Release "springboot-helloworld" does not exist. Installing it now.
NAME: springboot-helloworld
LAST DEPLOYED: Mon Apr 29 07:31:39 2019
NAMESPACE: mydlqcloud
STATUS: DEPLOYED
RESOURCES:
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
springboot-helloworld-7cd66cf74d-vfjr6 0/1 ContainerCreating 0 0s
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
springboot-helloworld NodePort 10.10.87.61 8080:30080/TCP,8081:30081/TCP 0s
==> v1beta1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
springboot-helloworld 0/1 1 0 0s
......
Finished: SUCCESS
上面的 Helm步骤执行完成后,就可以进行简单测试了,其中此项目引用的chart是一个简单的 SpringBoot 项目,其中用 NodePort 方式暴露了两个端口,30080 & 30081,分别对应8080、8081俩个端口,切提供了一个 Hello World 接口为“/hello”,所以我们这里访问一下这个接口地址:
http://192.168.2.11:30080/hello
设置任务超时时间,如果在规定时间内任务没有完成,则进行失败操作。
格式:
timeout(time: 20, unit: 'SECONDS') {
// 流水线代码
}
例子:
设置超时时间为 60s 来让 Jenkins Slave 节点执行任务。
def label = "jnlp-agent"
timeout(time: 60, unit: 'SECONDS') {
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
}
stage('Maven阶段'){
echo "Maven 阶段"
}
stage('Docker阶段'){
echo "Docker 阶段"
}
stage('Helm阶段'){
echo "Helm 阶段"
}
}
}
}
这里用的是 163 邮箱
这里安装插件“Email Extension Template”用于设置邮件模板。
系统管理->系统设置:
配置“Jenkins Location”和“Extended E-mail Notification”,其中系统管理员邮件地址一定要和“User Name”值一致。
参数名称 | 描述 |
---|---|
- Jenkins URL: | Jenkins 地址,用于发送邮件时写入内容之中 |
- 系统管理员邮件地址: | 邮件服务器账户 |
参数名称 | 描述 |
---|---|
- SMTP server: | smtp 邮箱服务的地址 |
- Default user E-mail suffix: | 邮件服务器后缀 |
- User Name: | 邮件服务器账户 |
- Password: | 邮件服务器 SMTP 授权码 |
- Default Content Type: | 设置邮件文本格式 |
- Enable Debug Mode: | 启用 Debug 模式 |
创建一个流水线项目,用于写 Pipeline 脚本测试邮件发送,并配置 Pipeline 脚本,这里写一个简单的 Pipeline 脚本,调用 emailext 方法执行发送邮件。
脚本内容:
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
}
stage('Maven阶段'){
echo "Maven 阶段"
}
stage('Docker阶段'){
echo "Docker 阶段"
}
stage('Helm阶段'){
echo "Helm 阶段"
}
stage('email'){
echo "测试发送邮件"
emailext(subject: '任务执行失败',to: '324******[email protected]',body: '''测试邮件内容...''')
}
}
}
查看日志,看是否执行发送操作以及运行状况。
Started by user admin
Running in Durability level: PERFORMANCE_OPTIMIZED
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/email-test
[Pipeline] {
[Pipeline] stage
[Pipeline] { (email)
[Pipeline] echo
测试发送邮件
[Pipeline] emailext
Sending email to: 32*****[email protected]
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS
查看邮件,可以看到已经收到设置邮箱发来的邮件信息。
这里加 try、catch、finally 进行流水线,党执行 finally 时候,进行判断此任务执行到此是否成功构建,如果成功,则发送成功邮件通知。如果失败,则发送失败邮件通知。
try{
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
}
stage('Maven阶段'){
echo "Maven 阶段"
}
stage('Docker阶段'){
echo "Docker 阶段"
}
stage('Helm阶段'){
echo "Helm 阶段"
}
}
}
}catch(Exception e) {
currentBuild.result = "FAILURE"
}finally {
// 获取执行状态
def currResult = currentBuild.result ?: 'SUCCESS'
// 判断执行任务状态,根据不同状态发送邮件
stage('email'){
if (currResult == 'SUCCESS') {
echo "发送成功邮件"
emailext(subject: '任务执行成功',to: '32*****[email protected]',body: '''任务已经成功构建完成...''')
}else {
echo "发送失败邮件"
emailext(subject: '任务执行失败',to: '32*****[email protected]',body: '''任务执行失败构建失败...''')
}
}
}
让其正常成功跑完流程后发送邮件。
模拟故意执行错误发送邮件。
将脚本放入项目之中,方便后续调用时直接设置项目所在的Git地址即可。
完整代码如下:
// 执行Helm的方法
def helmDeploy(Map args) {
if(args.init){
println "Helm 初始化"
sh "helm init --client-only --stable-repo-url ${args.url}"
} else if (args.dry_run) {
println "尝试 Helm 部署,验证是否能正常部署"
sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template} --dry-run --debug"
} else {
println "正式 Helm 部署"
sh "helm upgrade --install ${args.name} --namespace ${args.namespace} ${args.values} --set ${args.image},${args.tag} stable/${args.template}"
}
}
// jenkins slave 执行流水线任务
timeout(time: 600, unit: 'SECONDS') {
try{
def label = "jnlp-agent"
podTemplate(label: label,cloud: 'kubernetes' ){
node (label) {
stage('Git阶段'){
echo "Git 阶段"
git branch: "master" ,changelog: true , url: "https://github.com/my-dlq/springboot-helloworld.git"
}
stage('Maven阶段'){
echo "Maven 阶段"
container('maven') {
//这里引用上面设置的全局的 settings.xml 文件,根据其ID将其引入并创建该文件
configFileProvider([configFile(fileId: "75884c5a-4ec2-4dc0-8d87-58b6b1636f8a", targetLocation: "settings.xml")]){
sh "mvn clean install -Dmaven.test.skip=true --settings settings.xml"
}
}
}
stage('Docker阶段'){
echo "Docker 阶段"
container('docker') {
// 读取pom参数
echo "读取 pom.xml 参数"
pom = readMavenPom file: './pom.xml'
// 设置镜像仓库地址
hub = "registry.cn-shanghai.aliyuncs.com"
// 设置仓库项目名
project_name = "mydlq"
echo "编译 Docker 镜像"
docker.withRegistry("http://${hub}", "ffb3b544-108e-4851-b747-b8a00bfe7ee0") {
echo "构建镜像"
// 设置推送到aliyun仓库的mydlq项目下,并用pom里面设置的项目名与版本号打标签
def customImage = docker.build("${hub}/${project_name}/${pom.artifactId}:${pom.version}")
echo "推送镜像"
customImage.push()
echo "删除镜像"
sh "docker rmi ${hub}/${project_name}/${pom.artifactId}:${pom.version}"
}
}
}
stage('Helm阶段'){
container('helm-kubectl') {
withKubeConfig([credentialsId: "8510eda6-e1c7-4535-81af-17626b9575f7",serverUrl: "https://kubernetes.default.svc.cluster.local"]) {
// 设置参数
image = "image.repository=${hub}/${project_name}/${pom.artifactId}"
tag = "image.tag=${pom.version}"
template = "spring-boot"
repo_url = "http://chart.mydlq.club"
app_name = "${pom.artifactId}"
// 检测是否存在yaml文件
def values = ""
if (fileExists('values.yaml')) {
values = "-f values.yaml"
}
// 执行 Helm 方法
echo "Helm 初始化"
helmDeploy(init: true ,url: "${repo_url}");
echo "Helm 执行部署测试"
helmDeploy(init: false ,dry_run: true ,name: "${app_name}" ,namespace: "mydlqcloud" ,image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
echo "Helm 执行正式部署"
helmDeploy(init: false ,dry_run: false ,name: "${app_name}" ,namespace: "mydlqcloud",image: "${image}" ,tag: "${tag}" , values: "${values}" ,template: "${template}")
}
}
}
}
}
}catch(Exception e) {
currentBuild.result = "FAILURE"
}finally {
// 获取执行状态
def currResult = currentBuild.result ?: 'SUCCESS'
// 判断执行任务状态,根据不同状态发送邮件
stage('email'){
if (currResult == 'SUCCESS') {
echo "发送成功邮件"
emailext(subject: '任务执行成功',to: '32******[email protected]',body: '''任务已经成功构建完成...''')
}else {
echo "发送失败邮件"
emailext(subject: '任务执行失败',to: '32******[email protected]',body: '''任务执行失败构建失败...''')
}
}
}
}