基于Jenkins Pipeline流水线实现K8s负载更新
本文介绍了一种基于Jenkins和Kubernetes的CI/CD流水线实现方案。该方案利用Jenkins从Gitee拉取源码,通过动态Slave方式在K8s中创建Maven和Docker容器分别执行代码编译和镜像构建任务。环境准备包括配置Harbor镜像仓库、Jenkins插件安装、K8s命名空间和权限设置。关键步骤包括:1)创建K8s资源(命名空间、RBAC权限);2)配置Jenkins凭证和

Jenkins从gitee拉取源码文件(编译jar),dockerfile(构建镜像),jenkinsfile(CICD步骤),然后用动态slave的方式在k8s中分别创建maven和docker容器去执行对应的构建任务,最后通过调用k8s的Api接口的方式去通知负载到Harbor中拉取新镜像并更新
##此方式是基于服务已经在k8s中成功运行后更新的操作,并不包括初始部署内容
一、环境准备
服务准备
Gitee/Gitlab/git***
Harbor(IP为192.168.175.60)
Jenkins(IP为192.168.175.60:8080)
K8s(IP为192.168.175.20/30)
Kuboard(IP为192.168.175.60:9000)
Harbor上传好环境镜像

Jenkins下载插件
Jenkins的功能大多是依靠插件完成的,所以需要提前下载好一些插件
Pipeline Plugin (流水线)
Git Plugin (拉代码)
Git Parameter Plugin (获取git标签)
Kubernetes Plugin (动态pod)
Credentials Binding Plugin (获取凭证)
通过kuboard获取负载更新命令(调用k8s的API)

K8s创建资源
1、需要在k8s中创建专门供动态pod运行的命名空间
[root@k8snode1 ~]# kubectl create namespace jenkins
[root@k8snode1 ~]# kubectl get namespaces | grep jenkins
jenkins Active 12d
2、需要在k8s创建对上面命名空间有对应权限的SA(用户)和role(权限)
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-role
# 指定命名空间,限定权限范围
namespace: jenkins
rules:
- apiGroups: [""]
#仅针对于pod资源
resources: ["pods", "pods/exec", "pods/log"]
verbs: ["create", "get", "list", "watch", "delete", "update"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jenkins-sa
namespace: jenkins
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jenkins-rb
#和 Role/ServiceAccount 同命名空间
namespace: jenkins
subjects:
- kind: ServiceAccount
name: jenkins-sa
namespace: jenkins
roleRef:
# 对应上面的 Role
kind: Role
name: jenkins-role
apiGroup: rbac.authorization.k8s.io
3、用上面创建的SA生成专属的集群连接配置文件(类似于admin.conf),里面包含集群地址,连接的Token和CA证书
#查看上面创建的ServiceAccount
[root@k8smaster ~]# kubectl get sa -n jenkins
NAME SECRETS AGE
default 1 12d
jenkins-external-sa 1 12d
#查看ServiceAccount对应的secret(储存k8s的令牌和CA证书)
[root@k8smaster ~]# kubectl describe sa jenkins-external-sa -n jenkins
Name: jenkins-external-sa
Namespace: jenkins
...............................................
Mountable secrets: jenkins-external-sa-token-mmljn
Tokens: jenkins-external-sa-token-mmljn
Events: <none>
#查看secret的详细信息
[root@k8smaster ~]# kubectl describe secret jenkins-external-sa-token-mmljn -n jenkins
Name: jenkins-external-sa-token-mmljn
Namespace: jenkins
................................................
Data
====
ca.crt: 1099 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkdtdVBXdHVNaVV4N1BsRWtXalR6UnNZMDdxSENfX3dGMFRmVk9yOWFGZEUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJqZW5raW5zIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImplbmtpbnMtZXh0ZXJuYWwtc2EtdG9rZW4tbW1sam4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiamVua2lucy1leHRlcm5hbC1zYSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjcwN2Q2ZGEwLWMyNDMtNDdmOS1hMjQ2LWE4NDAxN2IxZjE1ZSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpqZW5raW5zOmplbmtpbnMtZXh0ZXJuYWwtc2EifQ.hFr0qMMmJKNpjOrPsmyGKFOLWVskxOO7r7eUPIGqTc6zLo70aW7SC_tpPqL8Q1K6B9Pk20CYRu8tDNksJpogxhpFC-aRP5TG_OXH2n7BD4Dg0ZEXF22hg1M-ebNTMIN50vVLC6R6xW9tylgGYRLIioWpmOF6AHOZEAvtu6LJg_jpnxY8S_o5imzTvNwD0B6PI_xe_fZfeRry1a9lFJZF-i-URcWkeh5kNGQ45LHtADsQHTeV8Eau8JDrpYKdOeykytmqCNkA_ykUPRDW0oJwkV51gEYU5BxpzNlko8BcCYUaqdeMRG6oDUT_1haws8Mi4w-7XiuIOJLx2a2i4yd0bA
####CA证书查看方式:kubectl get secret 【secret名称】 -n jenkins -o jsonpath='{.data.ca\.crt}'
4、集群连接文件模版(将下面内容写入.yaml文件中即可)
apiVersion: v1
kind: Config
###################集群配置####################
clusters:
### 集群名称(自定义,和Jenkins配置对应)
- name: k8s-cluster
cluster:
### 填写上面secret的CA证书
certificate-authority-data: 【CA证书】
### 填写集群API访问地址
server: https://192.168.175.20:6443
###################用户配置####################
users:
### 填写SA名称
- name: 【SA名称】
user:
### 填写上面secret的Token令牌
token: 【Token令牌】
###################关联配置####################
contexts:
### 定义上下文名称,作用是用哪个用户连接哪个集群
- name: jenkins-context
context:
cluster: k8s-cluster
user: 【SA名称】
###################默认使用的关联配置####################
current-context: jenkins-context
###测试访问
在集群外的一台机器上用此文件查看集群内的资源
[root@]# kubectl --kubeconfig=jenken_k8s.yaml get pod
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:jenkins:jenkins-sa" cannot list resource "pods" in API group "" in the namespace "default"
[root@]# kubectl --kubeconfig=jenken_k8s.yaml get pod -n jenkins
No resources found in jenkins namespace.
5、因为本地的Harbor是http协议的,所以需要提前做好docker的配置文件,方便后面docker容器构建完镜像后推送到仓库
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-config
namespace: jenkins
data:
daemon.json: |
{
"insecure-registries":["192.168.175.60:80"]
}
Jenkins凭证配置
从Git上拉取代码,推送镜像到harbor仓库,连接k8s操作集群都需要在Jenkins上配置好凭证(连接账号)
Git
系统管理——凭证管理——添加凭证
类型选择账号密码,填写git仓库的账号、密码、凭证ID、凭证描述

Harbor
系统管理——机器人账户
创建机器人账号并配置相应的权限

凭证配置方式与git相似

K8s
系统管理——凭证管理——添加凭证
类型选择凭证文件,上传上面创建好的集群连接文件,填写凭证ID、凭证描述

连接的凭证添加好后,要在Jenkins中配置集群信息
jenkins——>系统管理——>Clouds——>New cloud——>输入cloud name并勾选类型为kubernetes

填写集群名称,集群访问地址(token和CA证书我们的集群连接文件里都有这里就不需要填写了)

选择上面添加好的凭证并测试连接

Gitee中准备好源码文件,dockerfile,jenkinsfile
源码文件
源码文件可以找开发朋友给写一个,或找AI给生成一个,这里忽略
dockerfile
FROM 192.168.175.60:80/new/openjdk:1.8.0
RUN mkdir /data
COPY ./target/jenkins-demo-1.0.0.jar /data
WORKDIR /data
ENTRYPOINT java -jar jenkins-demo-1.0.0.jar
Jenkinfile(主要内容)
仓库地址,凭证,Harbor镜像,k8s接口调用命令等信息
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
volumes:
- name: docker-build
emptyDir: {}
- name: docker-sock
emptyDir: {}
- name: docker-conf
configMap:
name: docker-config
containers:
# 必须添加的JNLP通信容器(核心,不能少)
- name: jnlp
image: 192.168.102.30:80/new/inbound-agent@sha256:b8ee5ed60769477132712e47c91cd8196b37c89fe5ffc3a1b24ed77b113002d6
resources:
requests:
cpu: "0.2"
memory: "256Mi"
limits:
cpu: "0.5"
memory: "512Mi"
# 用于 Java 编译
- name: maven
image: 192.168.102.30:80/new/maven@sha256:fde3db5003ddce434e48902489084093e74010187aaa79dd4ddb9b25943be714
command:
- sleep
- infinity
volumeMounts:
- mountPath: /docker-build
name: docker-build
resources:
requests:
cpu: "1"
memory: "512Mi"
limits:
cpu: "2"
memory: "1Gi"
# 用于镜像构建推送
- name: docker
image: 192.168.102.30:80/new/docker@sha256:be20acdedb34b6b09b6f6379c84dffb750b5bb0a88c3b0047a790408e5813b3f
securityContext:
privileged: true
volumeMounts:
- mountPath: /docker-build
name: docker-build
- mountPath: /var/run
name: docker-sock
- mountPath: /etc/docker/daemon.json
name: docker-conf
subPath: daemon.json
env:
- name: DOCKER_TLS_CERTDIR
value: ""
resources:
requests:
cpu: "0.5"
memory: "256Mi"
limits:
cpu: "0.5"
memory: "512Mi"
"""
}
}
// 全局环境变量(统一配置,便于修改)
environment {
// 代码仓库配置
GIT_REPO_URL = "https://gitee.com/baoxiaozhang/new_-pipeline.git"
// 镜像仓库地址信息
HARBOR_IP = "192.168.102.30:80"
HARBOR_PROJECT = "new"
HARBOR = credentials('harbor_image_robot')
// 镜像名称
IMAGE_NAME = "hello-jenkins"
}
parameters {
gitParameter(
name: 'TAG', // 变量名
type: 'PT_TAG', // 新版插件核心:用 PT_TAG 替代 TAG
defaultValue: 'v2.0', // 默认标签(需确保仓库存在该标签)
description: '请选择要拉取的Git标签',
sortMode: 'DESCENDING', // 最新标签排在前面
tagFilter: '*', // 匹配所有标签(也可写 v.* 匹配v开头)
selectedValue: 'DEFAULT',// 默认选中 defaultValue
quickFilterEnabled: true // 开启快速筛选(输入关键词过滤标签)
)
}
stages {
stage('拉取代码') {
steps {
echo "===== 开始拉取 Gitee 代码 ====="
//需要有git插件
checkout scmGit(
branches: [[name: "refs/tags/${params.TAG}"]],
userRemoteConfigs: [[url: "${GIT_REPO_URL}", credentialsId: 'gitee_password']]
)
}
}
stage('编译java代码') {
steps {
echo "===== 开始编译代码 ====="
//需要有配置好maven镜像
container('maven') { // 指定在 maven 容器中执行
sh """
# 打印 Maven 版本,验证环境
mvn -v
pwd
# 编译打包(跳过测试加速构建)
mvn clean package -DskipTests
# 验证编译产物
ls -l target/*.jar || echo "未找到编译jar包,请检查maven日志"
# 将jar包同步至共享目录
cp -a target/*.jar /docker-build/
ls /docker-build/ || echo "同步失败"
"""
}
}
}
stage('构建镜像') {
steps {
echo "===== 开始构建镜像 ====="
//需要有配置好docker镜像
container('docker') {
sh """
# 登录Harbor私有仓库
docker login ${HARBOR_IP} -u \${HARBOR_USR} -p \${HARBOR_PSW}
# 构建镜像
docker build -t ${HARBOR_IP}/${HARBOR_PROJECT}/${IMAGE_NAME}:${params.TAG} -f docker/dockerfile .
# 推送镜像到Harbor
docker push ${HARBOR_IP}/${HARBOR_PROJECT}/${IMAGE_NAME}:${params.TAG}
"""
}
}
}
stage('更新镜像') {
steps {
sh """
curl -X PUT \
-H "content-type: application/json" \
-H "Cookie: KuboardUsername=admin; KuboardAccessKey=e47nkyerxs4m.4emntpdzjbjnzszpem22tz8fkn8xpybm" \
-d '{"kind":"deployments","namespace":"helm","name":"hello-jenkins","images":{"192.168.102.30:80/new/hello-jenkins":"192.168.102.30:80/new/hello-jenkins:${params.TAG}"}}' \
"http://192.168.102.30:9000/kuboard-api/cluster/new-k8s/kind/CICDApi/admin/resource/updateImageTag"
"""
}
}
}
}
二、创建流水线项目
新建流水线项目

选择Pipeline script from SCM,填写存放jenkinsfile的仓库地址,选择上面准备好的Git凭证,注意下面的jenkinsfile文件名字要和仓库中的对应

三、执行构建任务并查看结果
模拟开发更新代码内容
#拉取代码
[root@192 git]# git clone https://gitee.com/baoxiaozhang/new_-pipeline.git
正克隆到 'new_-pipeline'...
..............................................................
Unpacking objects: 100% (29/29), done.
[root@192 git]# ls
docker.tar new_-pipeline
[root@192 git]# cd new_-pipeline/
[root@192 new_-pipeline]# ls
docker pom.xml src
#更新内容
[root@192 new_-pipeline]# sed -n 's@hello jenkins V1.0@hello jenkins V2.0@gp' src/main/java/com/demo/JenkinsApplication.java
// 根路径接口,访问后返回 hello jenkins V2.0
return "hello jenkins V2.0";
[root@192 new_-pipeline]# sed -i 's@hello jenkins V1.0@hello jenkins V2.0@g' src/main/java/com/demo/JenkinsApplication.java
#提交代码
[root@192 new_-pipeline]# git add ./
[root@192 new_-pipeline]# git commit -m "更新版本为V2.0" ./
1 file changed, 2 insertions(+), 2 deletions(-)
[root@192 new_-pipeline]# git tag v2.0
[root@192 new_-pipeline]# git push origin master
..............................................................
To https://gitee.com/baoxiaozhang/new_-pipeline.git
14580dc..3bc5c99 master -> master
[root@192 new_-pipeline]# git push origin v2.0
To https://gitee.com/baoxiaozhang/new_-pipeline.git
* [new tag] v2.0 -> v2.0
##第一次构建任务会按照默认的标签去拉取代码,后面再次构建才能选择其他标签的代码

通过控制台日志可以看到pod已经创建好了,里面的容器正在启动

在k8s集群里也可以看到动态POD

最后构建成功可以看到harbor中有新版本的镜像已上传,页面内容也发生了改变


任务执行完后可以看到动态pod已经销毁了
[root@k8snode1 ~]# kubectl get pod -n jenkins
No resources found in jenkins namespace.
备注1:若想要实现完全自动化,可以再配置"钩子"去触发流水线,这样每次只要开发上传代码,就会自动更新到k8s中
备注2:由于本地资源有限,所以舍弃了部分配置,如pod的日志的持久化、构建完成后的邮件推送通知等
更多推荐
所有评论(0)