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的日志的持久化、构建完成后的邮件推送通知等

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐