前言
这里会做整体CICD的思路和流程的介绍,会给出核心的Jenkins pipeline脚本,最后会演示一下 实验/实操 结果
 由于整体内容较多,所以不打算在这里做每一步的详细演示 - 本文仅作自己的实操记录和日后回顾用
 要看保姆式教学的可以划走了,没有K8S基础的也可以划走了
正文
1. 整体实现思路
- 在K8S集群中部署Jenkins服务(我这里实验用的是 Jenkins 2.486版本),给Jenkins分配对应的NFS、PV、PVC、SA、NS、SVC等资源,挂载Jenkins配置文件到服务器
 - 在Jenkins中安装 kubernetes、blue ocean 插件
 - 配置Jenkins和K8S集群正常通信
 - 配置 pod template - 这里的 pod template 实际上就是对 Jenkins agent(以前叫 Jenkins slave) 的配置,用于完成git代码拉取、mvn打包、docker镜像上传等操作
 - 新建流水线任务,加入自己的pipeline脚本
 
CICD流程:
2. 插件简介
2.1 Kubernetes插件简介
Kubernetes插件使用 - Kubernetes Plugin的配置主要有两部分组成:
- kubernetes的连接参数:主要用于配置如果连接kubernetes
 - kubernetes的pod模板:调用kubernetes创建jenkins slave使用的pod模板
 

2.2 Blue Ocean 插件简介
Blue Ocean 是 Jenkins 的一款现代化 UI 插件,旨在为持续集成/持续交付 (CI/CD) 提供更加直观、用户友好的界面和体验。
- 全新的可视化流水线体验 
  
- Blue Ocean 提供了流水线运行过程的直观可视化,用户可以清晰地查看流水线的各个阶段(stage)、步骤(step)及其状态(成功、失败、运行中)。
 - 支持图形化展示流水线的分支、并行任务及错误点,便于快速诊断和调试。
 
 - 简化的流水线创建和编辑 
  
- 内置交互式流水线编辑器,支持通过拖放操作创建 Jenkinsfile,降低了学习曲线。
 - 用户无需编写复杂的脚本即可快速配置和生成流水线代码。
 
 - 强大的分支支持 
  
- 专为 Git 和其他分布式版本控制系统的多分支流水线设计。
 - 自动检测代码仓库中的分支和 Pull Request,独立运行每个分支的流水线任务。
 
 - 高效的协作能力 
  
- 在失败的任务或构建上添加评论,方便团队成员进行问题协作和修复。
 - 支持通过直观的界面查看构建日志、失败原因以及历史记录。
 
 - 插件生态集成 
  
- 与 Jenkins 的现有插件(如 Git、GitHub、Bitbucket、Docker 等)无缝集成。
 - 支持各种通知机制(如邮件、Slack、Webhook)来增强 CI/CD 流程。
 
 - 跨平台友好 
  
- 适配桌面端和移动端,用户可以随时随地查看流水线状态。

 
 - 适配桌面端和移动端,用户可以随时随地查看流水线状态。
 
3. pipeline脚本示例
env.BRANCH_NAME = 'master'
node('devops') {
    stage('Clone') {
        echo "1.下载代码"
        withCredentials([usernamePassword(credentialsId: 'github', usernameVariable: 'gitHubUser', passwordVariable: 'gitHubToken')]) {
            git url: "https://${gitHubUser}:${gitHubToken}@github.com/cqweiheng/myspringboot.git"
        }
        script {
            build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
        }
    }
    stage('Test') {
        echo "2.代码扫描"
    }
    stage('Build') {
        echo "3.构建docker镜像"	
        // 执行 Maven 构建,生成 jar 文件
        sh 'mvn clean package -Dmaven.test.skip=true'
        // 打包成镜像
        sh "docker build -t cqweiheng/test:myspringboot-${build_tag} ."
    }
    stage('Push') {
        echo "4.推送镜像"
        withCredentials([usernamePassword(credentialsId: 'dockerhub', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
            sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"
            // 这里可以改用 harbor
            //sh "docker login 192.168.86.100 -u ${harborUser} -p ${harborPassword}"
            sh "docker push cqweiheng/test:myspringboot-${build_tag}"
        }
    }
    stage('Deploy to dev') {
        echo "5.发布到 dev 环境"
    		sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev.yaml"
        sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev.yaml"
        sh "cat k8s-dev.yaml"
        sh "kubectl apply -f k8s-dev.yaml"
        sh "sleep 10"
        sh "kubectl get pods -n dev"
	}	
	stage('Deploy to test') {	
		def userSelected = input(
            id: 'userSelected',
            message: '确认发布到【测试】环境?',
            parameters: [
                [
                    $class: 'ChoiceParameterDefinition',
                    choices: "确认\n取消",
                    name: '请选择'
                ]
            ]
        )
        echo "用户选择:${userSelected}"
        if (userSelected == "确认") {
            sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-test.yaml"
            sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-test.yaml"
            sh "cat k8s-test.yaml"
            sh "kubectl apply -f k8s-test.yaml"
            sh "sleep 10"
            sh "kubectl get pods -n test"
        } else {
            //exit
            echo "用户选择不发布到测试环境,本次部署流程终止。"
            currentBuild.result = 'ABORTED'
            error("用户选择取消发布到测试环境。")
        }
    }
	stage('Deploy to pro') {	
		def userSelected = input(
            id: 'userSelected',
            message: '确认发布到【生产】环境?',
            parameters: [
                [
                    $class: 'ChoiceParameterDefinition',
                    choices: "确认\n取消",
                    name: '请选择'
                ]
            ]
            // 仅允许指定用户操作 - 通过授权,仅测试团队可以操作
            //,submitter: 'test-user,test-group'
        )
        echo "用户选择:${userSelected}"
        if (userSelected == "确认") {
            sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod.yaml"
            sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod.yaml"
            sh "cat k8s-prod.yaml"
            sh "kubectl apply -f k8s-prod.yaml --record"
            sh "sleep 10"
            sh "kubectl get pods -n prod"
        }
    }
}
 
4. 演示

 
 
 在 deploy to test 这里就停住了,等待授权
 开发人员在 dev 环境自测完毕后,确认发布到测试环境,由测试团队进行验证
 
发布到生产这里可以通过添加 submitter 来控制用户权限,比如仅测试团队可以提交
 
 
 到这里整个流程就结束了
 从下图中可以看到Jenkins slave这个 pod 生命周期结束
 在命名空间 dev, test, prod 中分别生成了新的 pod
 
访问一下,自己写的一个springboot 应用 -> OK的
 
5. 闲聊
- 这里 svc 用的 NodePort,实际生产中如果做四层负载的话,应该会用 LoadBalance
 - 做版本回滚的话,可以新起一个pipeline,调用shell脚本,选择版本号后进行回滚操作
参考:#查看历史版本 kubectl rollout history deployment/<deployment_name> #回退到指定版本 kubectl rollout undo deployment/<deployment_name> --to-revision=<revision_number> - 这里是用的单个集群,将开发、测试和生产环境部署在 同一个 K8S 集群中,并通过 Namespace 来区分不同环境。这种方式简单高效,但需要合理的规划和配置,确保资源隔离和环境安全性。
比如按 namespace 做资源配额,按 单个pod 做资源限制#按 namespace 做资源限制 apiVersion: v1 kind: ResourceQuota metadata: name: resource-quota namespace: dev spec: hard: pods: "20" # 最大允许 20 个 Pod requests.cpu: "4" # 总 CPU 请求限制 4 核 requests.memory: "8Gi" # 总内存请求限制 8GB limits.cpu: "8" # 总 CPU 限制 8 核 limits.memory: "16Gi" # 总内存限制 16GB #按 单个pod 做资源限制 - 防止单个 Pod 占用过多资源 apiVersion: v1 kind: LimitRange metadata: name: limits namespace: dev spec: limits: - default: cpu: "500m" memory: "512Mi" defaultRequest: cpu: "200m" memory: "256Mi" type: Container - Jenkins 的 Pipeline 可以通过不同的 kubeconfig 文件操作不同的集群。再直白点就是,可以通过 kubeconfig 文件 做服务器隔离、集群隔离的CICD 发布到dev、test、prod环境 - 当然这个我还没实际操作过
顺带搜了一下,大约是这样做的,下面的内容仅供参考#1.为每个集群(dev 和 test)生成独立的 kubeconfig 文件 kubectl config view --raw > kubeconfig-dev.yaml kubectl config view --raw > kubeconfig-test.yaml #2.将这些 kubeconfig 文件上传到 Jenkins 的凭据管理系统,确保安全性 #在 Jenkins 中添加 kubeconfig 凭据 # 打开 Jenkins Dashboard。 # 进入 "Manage Jenkins" > "Credentials"。 # 添加凭据: # Kind:Secret file # File:上传 kubeconfig-dev.yaml 和 kubeconfig-test.yaml。 # ID:分别命名为 kubeconfig-dev 和 kubeconfig-test。 #3.在 Jenkins Pipeline 中使用 kubeconfig 文件 #利用 withCredentials 加载对应的 kubeconfig 文件,动态切换集群。 pipeline { agent any stages { stage('Deploy to Dev') { steps { script { echo "Deploying to Dev Cluster..." withCredentials([file(credentialsId: 'kubeconfig-dev', variable: 'KUBECONFIG')]) { // 使用 kubeconfig-dev 操作 dev 集群 sh 'kubectl apply -f k8s-dev.yaml' sh 'kubectl get pods -n dev' } } } } stage('Deploy to Test') { steps { script { input "Confirm deployment to Test environment?" echo "Deploying to Test Cluster..." withCredentials([file(credentialsId: 'kubeconfig-test', variable: 'KUBECONFIG')]) { // 使用 kubeconfig-test 操作 test 集群 sh 'kubectl apply -f k8s-test.yaml' sh 'kubectl get pods -n test' } } } } } 
}
 ```
 这种就是各集群的资源、权限完全独立,不会相互影响。即使某一环境出现问题,其他环境仍可正常工作。
适合场景
 - 多集群部署:开发、测试、生产环境分布在不同的 Kubernetes 集群中。
 - 跨区域服务器管理:如不同集群位于不同的云平台(阿里云、腾讯云、AWS 等)。
 - 严格隔离需求:需要通过不同的 kubeconfig 文件实现权限和资源隔离。
6. 踩过的坑
6.1 FailedCreatePodSandbox - error getting clusterInfomation: connection is unauthorized

 这个错误纠结了好久,最开始解题思路有误
 看日志一直以为是授权出了问题
 做了如下尝试,又检查了calico插件,发现全都是正常的
 第二天Google搜了一下:
 https://devopscube.com/clusterinformation-connection-unauthorized/
 重启calico后OK,气不气?
#授权
kubectl create clusterrolebinding calico-access-jenkins-admin \
  --clusterrole=calico-kube-controllers \
  --serviceaccount=devops:jenkins-admin
#绑定token - vim jenkins-admin-token.yaml
apiVersion: v1
kind: Secret
metadata:
  name: jenkins-admin-token
  namespace: devops
  annotations:
    kubernetes.io/service-account.name: jenkins-admin
type: kubernetes.io/service-account-token
#kubectl apply -f jenkins-admin-token.yaml
#验证是否生效
kubectl -n devops describe secret $(kubectl -n devops get secret | grep jenkins-admin | awk '{print $1}')
curl -k -H "Authorization: Bearer <your-token>" https://<kube-apiserver>:6443/apis/crd.projectcalico.org/v1/clusterinformations
#能正常访问说明权限没有问题
kubectl --token=<your-token> get clusterinformation -o yaml
 
6.2 jenkins svc 没有暴露50000 端口
这个好像没什么可说的,加上就好了
 
6.3 Jenkins slave 构建镜像时提示 docker: not found

 这个东西也搞了一阵子,后来自己基于
 FROM jenkins/agent:latest
 添加了 docker.io、maven-3.9.5、kubectl v1.31.2
 然后打包成自己的Jenkins slave镜像使用,整个流程才正常跑通
 这个镜像我推送到了 dockerhub 上,有需要的同学可以直接拿来用(不过由于版本问题,估计大概率也只有我自己用,哈哈)
 镜像:cqweiheng/test:jenkins-agent-with-docker-v4










