18.7.3 Jenkins与Kubernetes部署与发布

在Jenkins与Kubernetes的部署与发布中,目标是形成“构建—制品—部署—验证—回滚”的自动化闭环。本节给出架构草图、安装与权限配置、流水线示例、发布与排错流程,并提供可执行练习。

原理草图:Jenkins与Kubernetes发布闭环

文章图片

集成模式与架构选择
- Jenkins主节点 + K8s构建Pod:通过Kubernetes插件动态创建构建Pod,完成后自动回收。
- Jenkins调用K8s API部署:流水线中用kubectl/Helm发布。
- GitOps联动:Jenkins仅构建推送镜像,部署由Argo CD/Flux同步。

安装与基础准备(示例)
1) 安装Kubernetes插件(Jenkins UI)
- Manage Jenkins → Plugins → 安装 KubernetesKubernetes CLIPipeline

2) 创建命名空间与ServiceAccount、RBAC(集群侧)

# file: k8s/jenkins-sa.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: cicd
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: cicd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: jenkins-deployer
  namespace: cicd
rules:
  - apiGroups: ["", "apps", "batch"]
    resources: ["pods", "deployments", "services", "configmaps", "secrets", "jobs"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: jenkins-deployer-binding
  namespace: cicd
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: cicd
roleRef:
  kind: Role
  name: jenkins-deployer
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f k8s/jenkins-sa.yaml

3) 生成kubeconfig并导入Jenkins凭据

# 获取ServiceAccount token
SA_SECRET=$(kubectl -n cicd get sa jenkins -o jsonpath='{.secrets[0].name}')
TOKEN=$(kubectl -n cicd get secret $SA_SECRET -o jsonpath='{.data.token}' | base64 -d)
CA=$(kubectl -n cicd get secret $SA_SECRET -o jsonpath='{.data.ca\.crt}')
SERVER=$(kubectl config view -o jsonpath='{.clusters[0].cluster.server}')

cat > kubeconfig-jenkins <<EOF
apiVersion: v1
kind: Config
clusters:
- name: k8s
  cluster:
    server: ${SERVER}
    certificate-authority-data: ${CA}
contexts:
- name: jenkins@k8s
  context:
    cluster: k8s
    user: jenkins
    namespace: cicd
current-context: jenkins@k8s
users:
- name: jenkins
  user:
    token: ${TOKEN}
EOF

kubeconfig-jenkins 保存为 Jenkins “Secret file” 凭据(ID: kubeconfig-cicd)。

Kubernetes插件Pod模板示例(构建+发布双容器)

# Jenkins Kubernetes Cloud Pod Template (YAML)
apiVersion: v1
kind: Pod
metadata:
  labels:
    jenkins: builder
spec:
  serviceAccountName: jenkins
  containers:
  - name: maven
    image: maven:3.9.6-eclipse-temurin-17
    command: ["cat"]
    tty: true
  - name: kubectl
    image: bitnami/kubectl:1.28
    command: ["cat"]
    tty: true
  - name: kaniko
    image: gcr.io/kaniko-project/executor:v1.20.1
    command: ["/busybox/cat"]
    tty: true
    volumeMounts:
      - name: docker-config
        mountPath: /kaniko/.docker
  volumes:
  - name: docker-config
    secret:
      secretName: regcred

完整流水线示例(构建、推送、部署、回滚)

// file: Jenkinsfile
pipeline {
  agent {
    kubernetes {
      label 'jenkins-k8s'
      defaultContainer 'maven'
      yamlFile 'k8s/jenkins-pod.yaml'
    }
  }
  environment {
    REGISTRY = "registry.example.com"
    IMAGE = "registry.example.com/demo/app"
    TAG = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
    KUBECONFIG_CRED = "kubeconfig-cicd"
    NAMESPACE = "cicd"
  }
  stages {
    stage('Checkout') {
      steps { checkout scm }
    }
    stage('Build') {
      steps {
        sh 'mvn -B -DskipTests package'
      }
    }
    stage('Image Build & Push') {
      steps {
        container('kaniko') {
          sh '''
          /kaniko/executor \
            --context $WORKSPACE \
            --dockerfile $WORKSPACE/Dockerfile \
            --destination $IMAGE:$TAG \
            --insecure \
            --skip-tls-verify
          '''
        }
      }
    }
    stage('Deploy') {
      steps {
        withCredentials([file(credentialsId: env.KUBECONFIG_CRED, variable: 'KUBECONFIG')]) {
          container('kubectl') {
            sh '''
            kubectl --kubeconfig=$KUBECONFIG -n $NAMESPACE set image deploy/demo demo=$IMAGE:$TAG
            kubectl --kubeconfig=$KUBECONFIG -n $NAMESPACE rollout status deploy/demo --timeout=120s
            '''
          }
        }
      }
    }
  }
  post {
    failure {
      withCredentials([file(credentialsId: env.KUBECONFIG_CRED, variable: 'KUBECONFIG')]) {
        container('kubectl') {
          sh '''
          kubectl --kubeconfig=$KUBECONFIG -n $NAMESPACE rollout undo deploy/demo
          '''
        }
      }
    }
  }
}

命令说明
- kubectl set image:更新Deployment镜像。
- rollout status:等待发布完成,超时失败。
- rollout undo:快速回滚到上一个Revision。

Helm发布示例(可替换kubectl)

# 预览渲染结果,避免误发布
helm template demo ./charts/demo -n cicd --set image.tag=${TAG}

# 执行发布
helm upgrade --install demo ./charts/demo -n cicd --set image.tag=${TAG}

# 回滚
helm rollback demo 1 -n cicd

发布策略与关键参数示例

# file: k8s/deploy.yaml
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  • maxSurge=1:滚动更新时临时多一个Pod;
  • maxUnavailable=0:不中断服务。

常见故障排查(命令级)

# 1) Jenkins Pod无法创建
kubectl -n cicd get events --sort-by=.metadata.creationTimestamp | tail -20

# 2) RBAC权限不足(403)
kubectl -n cicd auth can-i create deployments --as=system:serviceaccount:cicd:jenkins

# 3) 镜像拉取失败
kubectl -n cicd describe pod <pod> | grep -A5 "Failed to pull image"

# 4) 发布卡住
kubectl -n cicd rollout status deploy/demo
kubectl -n cicd get pods -l app=demo -o wide
kubectl -n cicd logs deploy/demo -c demo --tail=100

故障处理思路
- 403:检查Role/RoleBinding与namespace是否一致;
- 镜像拉取失败:检查imagePullSecrets、仓库地址与TAG;
- 发布卡住:检查readiness探针或依赖服务不可达。

安全与合规
- 仅为 cicd 命名空间授权,避免集群级权限;
- Jenkins凭据统一使用“Secret file/文本凭据”,禁止硬编码;
- 镜像扫描(Trivy/Grype)未通过则终止流水线。

练习
1) 在测试集群创建 cicd 命名空间并配置ServiceAccount与RBAC,验证 can-i 权限。
2) 将示例Jenkinsfile跑通,成功发布到 cicd,并手动触发一次 rollout undo
3) 故意将镜像TAG写错,观察排错过程与日志输出。