0
点赞
收藏
分享

微信扫一扫

Kubernetes 基于 Helm 的 CI/CD Jenkins 详细配置指南

Jenkins 环境准备和配置

1、 安装必要插件

首先在 Jenkins 中安装以下必需插件:

系统管理 → 插件管理 → 可选插件

Kubernetes Plugin (版本 1.31.3+)

Docker Pipeline Plugin (版本 1.28+)

Helm Plugin (版本 1.0.0+)

Git Plugin (版本 4.11.3+)

Pipeline Plugin (版本 2.6+)

Blue Ocean Plugin (可选,用于可视化)

Config File Provider Plugin (用于管理配置文件)

2 、配置 Kubernetes Cloud

系统管理 → 节点管理 → Configure Clouds

# Kubernetes 云配置
名称: kubernetes
Kubernetes 地址: https://kubernetes.default.svc
Kubernetes 命名空间: jenkins
凭据: 添加 kubeconfig 文件或 Service Account
Jenkins 地址: http://jenkins.jenkins.svc.cluster.local:8080

3、 配置凭据

凭据 → 系统 → 全局凭据 → 添加凭据

Docker Registry 凭据

类型: Username with password
用户名: your-docker-username
密码: your-docker-password
ID: docker-registry-creds

Git 凭据
类型: SSH Username with private key
用户名: git
私钥: 粘贴 SSH 私钥
ID: git-ssh-key

Kubernetes 配置
类型: Secret file
文件: 上传 kubeconfig 文件
ID: kubeconfig-file

4 完整 的JenkinsFile

#!/usr/bin/env groovy

// 定义全局参数
properties([
    parameters([
        choice(
            choices: ['dev', 'staging', 'production'],
            description: '选择部署环境',
            name: 'DEPLOY_ENV'
        ),
        string(
            defaultValue: '',
            description: 'Docker 镜像标签 (留空使用 BUILD_NUMBER)',
            name: 'CUSTOM_TAG'
        ),
        booleanParam(
            defaultValue: false,
            description: '是否跳过测试',
            name: 'SKIP_TESTS'
        )
    ]),
    buildDiscarder(logRotator(numToKeepStr: '20')),
    pipelineTriggers([
        pollSCM('H/5 * * * *')
    ])
])

// 环境配置映射
def loadEnvironmentConfig() {
    def configs = [
        dev: [
            namespace: 'development',
            replicas: 1,
            ingressHost: 'dev.myapp.com',
            cpuRequest: '100m',
            memoryRequest: '128Mi',
            cpuLimit: '200m',
            memoryLimit: '256Mi',
            helmTimeout: '5m',
            autoApproval: true
        ],
        staging: [
            namespace: 'staging',
            replicas: 2,
            ingressHost: 'staging.myapp.com',
            cpuRequest: '200m',
            memoryRequest: '256Mi',
            cpuLimit: '500m',
            memoryLimit: '512Mi',
            helmTimeout: '10m',
            autoApproval: false
        ],
        production: [
            namespace: 'production',
            replicas: 3,
            ingressHost: 'myapp.com',
            cpuRequest: '500m',
            memoryRequest: '512Mi',
            cpuLimit: '1000m',
            memoryLimit: '1Gi',
            helmTimeout: '15m',
            autoApproval: false
        ]
    ]
    return configs[params.DEPLOY_ENV]
}

pipeline {
    agent {
        kubernetes {
            label "helm-cicd-${env.BUILD_NUMBER}"
            yaml """
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-agent
spec:
  serviceAccount: jenkins-agent
  containers:
  - name: helm
    image: alpine/helm:3.12.0
    command: ['cat']
    tty: true
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"
  - name: kubectl
    image: bitnami/kubectl:1.27
    command: ['cat']
    tty: true
    env:
    - name: KUBECONFIG
      value: /home/jenkins/.kube/config
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"
  - name: docker
    image: docker:20.10-dind
    command: ['cat']
    tty: true
    env:
    - name: DOCKER_HOST
      value: tcp://localhost:2375
    resources:
      requests:
        memory: "256Mi"
        cpu: "200m"
      limits:
        memory: "512Mi"
        cpu: "500m"
    securityContext:
      privileged: true
  - name: node
    image: node:18-alpine
    command: ['cat']
    tty: true
    resources:
      requests:
        memory: "256Mi"
        cpu: "200m"
      limits:
        memory: "512Mi"
        cpu: "500m"
  volumes:
  - name: docker-sock
    emptyDir: {}
"""
        }
    }
    
    environment {
        // 基础配置
        REGISTRY = "registry.mycompany.com"
        PROJECT = "myproject"
        APP_NAME = "myapp"
        HELM_CHART_PATH = "kubernetes/charts/myapp"
        DOCKERFILE_PATH = "."
        
        // 从凭据中获取
        DOCKER_CREDS = credentials('docker-registry-creds')
        GIT_SSH_KEY = credentials('git-ssh-key')
        
        // 动态设置
        IMAGE_TAG = "${params.CUSTOM_TAG ?: env.BUILD_NUMBER}"
        FULL_IMAGE_NAME = "${REGISTRY}/${PROJECT}/${APP_NAME}:${IMAGE_TAG}"
    }
    
    stages {
        // 阶段 1: 代码检出
        stage('Checkout Code') {
            steps {
                timestamps {
                    echo "开始检出代码,环境: ${params.DEPLOY_ENV}"
                    git branch: 'main', 
                         credentialsId: 'git-ssh-key',
                         url: 'git@github.com:myorg/myrepo.git'
                    
                    sh 'git log -1 --oneline'
                    sh 'ls -la'
                }
            }
        }
        
        // 阶段 2: 代码质量检查
        stage('Code Quality') {
            steps {
                container('node') {
                    timestamps {
                        echo "运行代码质量检查"
                        sh """
                            npm install
                            npm run lint
                            npm run test:unit
                        """
                    }
                }
            }
        }
        
        // 阶段 3: Helm Chart 验证
        stage('Validate Helm Chart') {
            steps {
                container('helm') {
                    timestamps {
                        echo "验证 Helm Chart"
                        script {
                            // 更新依赖
                            sh """
                                helm dependency update ${HELM_CHART_PATH}
                                helm dependency build ${HELM_CHART_PATH}
                            """
                            
                            // Chart 语法检查
                            sh "helm lint ${HELM_CHART_PATH} --strict"
                            
                            // 模板渲染测试
                            sh """
                                helm template ${APP_NAME} ${HELM_CHART_PATH} \
                                    --namespace default \
                                    --set image.repository=${REGISTRY}/${PROJECT}/${APP_NAME} \
                                    --set image.tag=${IMAGE_TAG} \
                                    --debug
                            """
                            
                            // 验证 values 文件
                            if (fileExists("${HELM_CHART_PATH}/values-${params.DEPLOY_ENV}.yaml")) {
                                sh """
                                    helm template ${APP_NAME} ${HELM_CHART_PATH} \
                                        -f ${HELM_CHART_PATH}/values-${params.DEPLOY_ENV}.yaml \
                                        --debug
                                """
                            }
                        }
                    }
                }
            }
        }
        
        // 阶段 4: 构建 Docker 镜像
        stage('Build and Push Docker Image') {
            steps {
                container('docker') {
                    timestamps {
                        echo "构建和推送 Docker 镜像"
                        script {
                            // 登录 Docker Registry
                            sh """
                                docker login -u ${DOCKER_CREDS_USR} -p ${DOCKER_CREDS_PSW} ${REGISTRY}
                            """
                            
                            // 构建镜像
                            sh """
                                docker build \
                                    -t ${FULL_IMAGE_NAME} \
                                    -t ${REGISTRY}/${PROJECT}/${APP_NAME}:latest \
                                    -f ${DOCKERFILE_PATH}/Dockerfile \
                                    ${DOCKERFILE_PATH}
                            """
                            
                            // 推送镜像
                            sh """
                                docker push ${FULL_IMAGE_NAME}
                                docker push ${REGISTRY}/${PROJECT}/${APP_NAME}:latest
                            """
                            
                            // 清理本地镜像
                            sh """
                                docker rmi ${FULL_IMAGE_NAME} || true
                                docker rmi ${REGISTRY}/${PROJECT}/${APP_NAME}:latest || true
                            """
                        }
                    }
                }
            }
        }
        
        // 阶段 5: 部署到开发环境
        stage('Deploy to Development') {
            when {
                expression { params.DEPLOY_ENV == 'dev' }
            }
            steps {
                container('helm') {
                    timestamps {
                        echo "部署到开发环境"
                        script {
                            def config = loadEnvironmentConfig()
                            
                            sh """
                                helm upgrade --install ${APP_NAME} ${HELM_CHART_PATH} \
                                    --namespace ${config.namespace} \
                                    --set image.repository=${REGISTRY}/${PROJECT}/${APP_NAME} \
                                    --set image.tag=${IMAGE_TAG} \
                                    --set replicaCount=${config.replicas} \
                                    --set resources.requests.cpu=${config.cpuRequest} \
                                    --set resources.requests.memory=${config.memoryRequest} \
                                    --set resources.limits.cpu=${config.cpuLimit} \
                                    --set resources.limits.memory=${config.memoryLimit} \
                                    --set ingress.hosts[0].host=${config.ingressHost} \
                                    --wait \
                                    --timeout ${config.helmTimeout} \
                                    --atomic
                            """
                        }
                    }
                }
            }
        }
        
        // 阶段 6: 集成测试
        stage('Integration Tests') {
            when {
                expression { params.DEPLOY_ENV in ['dev', 'staging'] && !params.SKIP_TESTS }
            }
            steps {
                container('node') {
                    timestamps {
                        echo "运行集成测试"
                        script {
                            def config = loadEnvironmentConfig()
                            
                            // 等待应用就绪
                            container('kubectl') {
                                sh """
                                    kubectl wait --for=condition=ready pod -l app=${APP_NAME} \
                                        -n ${config.namespace} \
                                        --timeout=300s
                                """
                            }
                            
                            // 运行测试
                            sh """
                                npm run test:integration \
                                    --baseUrl=http://${APP_NAME}.${config.namespace}.svc.cluster.local
                            """
                        }
                    }
                }
            }
        }
        
        // 阶段 7: 人工审批 (预生产和生产环境)
        stage('Manual Approval') {
            when {
                expression { 
                    params.DEPLOY_ENV in ['staging', 'production'] && 
                    !loadEnvironmentConfig().autoApproval 
                }
            }
            steps {
                timestamps {
                    script {
                        def config = loadEnvironmentConfig()
                        def approvers = params.DEPLOY_ENV == 'production' ? 
                            'production-team' : 'qa-team'
                        
                        input(
                            message: "确认部署到 ${params.DEPLOY_ENV} 环境?",
                            ok: "确认部署",
                            submitterParameter: 'APPROVER',
                            submitter: approvers
                        )
                        
                        echo "部署已由 ${env.APPROVER} 批准"
                    }
                }
            }
        }
        
        // 阶段 8: 部署到预生产/生产环境
        stage('Deploy to Staging/Production') {
            when {
                expression { params.DEPLOY_ENV in ['staging', 'production'] }
            }
            steps {
                container('helm') {
                    timestamps {
                        echo "部署到 ${params.DEPLOY_ENV} 环境"
                        script {
                            def config = loadEnvironmentConfig()
                            
                            // 使用 values 文件进行部署
                            def valuesFiles = ""
                            if (fileExists("${HELM_CHART_PATH}/values-${params.DEPLOY_ENV}.yaml")) {
                                valuesFiles = "-f ${HELM_CHART_PATH}/values-${params.DEPLOY_ENV}.yaml"
                            }
                            
                            sh """
                                helm upgrade --install ${APP_NAME} ${HELM_CHART_PATH} \
                                    --namespace ${config.namespace} \
                                    --set image.repository=${REGISTRY}/${PROJECT}/${APP_NAME} \
                                    --set image.tag=${IMAGE_TAG} \
                                    --set replicaCount=${config.replicas} \
                                    --set resources.requests.cpu=${config.cpuRequest} \
                                    --set resources.requests.memory=${config.memoryRequest} \
                                    --set resources.limits.cpu=${config.cpuLimit} \
                                    --set resources.limits.memory=${config.memoryLimit} \
                                    --set ingress.hosts[0].host=${config.ingressHost} \
                                    ${valuesFiles} \
                                    --wait \
                                    --timeout ${config.helmTimeout} \
                                    --atomic \
                                    --create-namespace
                            """
                        }
                    }
                }
            }
        }
        
        // 阶段 9: 健康检查
        stage('Health Check') {
            steps {
                container('kubectl') {
                    timestamps {
                        echo "执行健康检查"
                        script {
                            def config = loadEnvironmentConfig()
                            
                            // 检查 Pod 状态
                            sh """
                                kubectl get pods -n ${config.namespace} -l app=${APP_NAME}
                                kubectl wait --for=condition=ready pod -l app=${APP_NAME} \
                                    -n ${config.namespace} \
                                    --timeout=600s
                            """
                            
                            // 检查服务端点
                            sh """
                                kubectl get svc -n ${config.namespace} ${APP_NAME}
                                kubectl get ingress -n ${config.namespace} ${APP_NAME}
                            """
                            
                            // 简单的应用健康检查
                            sh """
                                curl -f http://${APP_NAME}.${config.namespace}.svc.cluster.local/health || \
                                curl -f https://${config.ingressHost}/health || \
                                echo "Health check endpoint might be different"
                            """
                        }
                    }
                }
            }
        }
    }
    
    post {
        always {
            timestamps {
                echo "Pipeline 执行完成 - ${currentBuild.result}"
                
                // 收集部署信息
                container('kubectl') {
                    script {
                        if (params.DEPLOY_ENV) {
                            def config = loadEnvironmentConfig()
                            sh """
                                echo "=== 部署状态 ==="
                                kubectl get deployments -n ${config.namespace} ${APP_NAME} -o wide
                                echo ""
                                echo "=== Pod 状态 ==="
                                kubectl get pods -n ${config.namespace} -l app=${APP_NAME}
                                echo ""
                                echo "=== 服务状态 ==="
                                kubectl get svc -n ${config.namespace} -l app=${APP_NAME}
                            """
                        }
                    }
                }
                
                // 清理工作空间
                cleanWs()
            }
        }
        
        success {
            script {
                def config = loadEnvironmentConfig()
                emailext (
                    subject: "SUCCESS: 部署 ${APP_NAME} 到 ${params.DEPLOY_ENV}",
                    body: """
                    <h2>部署成功!</h2>
                    <p><strong>应用:</strong> ${APP_NAME}</p>
                    <p><strong>环境:</strong> ${params.DEPLOY_ENV}</p>
                    <p><strong>镜像:</strong> ${FULL_IMAGE_NAME}</p>
                    <p><strong>构建:</strong> ${env.BUILD_URL}</p>
                    <p><strong>访问地址:</strong> https://${config.ingressHost}</p>
                    """,
                    to: 'team@mycompany.com'
                )
            }
        }
        
        failure {
            script {
                emailext (
                    subject: "FAILED: 部署 ${APP_NAME} 到 ${params.DEPLOY_ENV}",
                    body: """
                    <h2>部署失败!</h2>
                    <p><strong>应用:</strong> ${APP_NAME}</p>
                    <p><strong>环境:</strong> ${params.DEPLOY_ENV}</p>
                    <p><strong>构建:</strong> ${env.BUILD_URL}</p>
                    <p>请检查 Jenkins 日志获取详细信息。</p>
                    """,
                    to: 'devops@mycompany.com'
                )
            }
        }
        
        unstable {
            echo "Pipeline 标记为不稳定"
        }
    }
    
    options {
        timeout(time: 30, unit: 'MINUTES')
        retry(2)
        timestamps()
    }
}

5、配套的 Helm Chart 结构

kubernetes/charts/myapp/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-staging.yaml
├── values-production.yaml
├── charts/
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   ├── secrets.yaml
│   └── hpa.yaml
└── .helmignore


6、values.yaml

# 默认配置
replicaCount: 1

image:
  repository: ""
  tag: "latest"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: true
  className: "nginx"
  hosts:
    - host: "chart-example.local"
      paths:
        - path: /
          pathType: Prefix
  tls: []

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 200m
    memory: 256Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

env:
  - name: ENVIRONMENT
    value: "development"

7、蓝绿发布

stage('Blue-Green Deployment') {
    steps {
        script {
            def currentDeployment = sh(
                script: "helm get values ${APP_NAME} -n ${KUBE_NAMESPACE} -o json | jq -r '.color' 2>/dev/null || echo 'blue'",
                returnStdout: true
            ).trim()
            
            def newColor = currentDeployment == 'blue' ? 'green' : 'blue'
            
            // 部署新版本
            container('helm') {
                sh """
                    helm upgrade --install ${APP_NAME}-${newColor} ${HELM_CHART_PATH} \
                        --namespace ${KUBE_NAMESPACE} \
                        --set image.tag=${env.BUILD_NUMBER} \
                        --set image.repository=${REGISTRY}/${PROJECT}/${APP_NAME} \
                        --set color=${newColor} \
                        --set service.color=${newColor} \
                        --wait --timeout 300s
                """
            }
            
            // 切换流量
            sh """
                kubectl patch svc ${APP_NAME} -n ${KUBE_NAMESPACE} \
                    -p '{"spec":{"selector":{"color":"${newColor}"}}}'
            """
            
            // 清理旧版本
            sh "helm uninstall ${APP_NAME}-${currentDeployment} -n ${KUBE_NAMESPACE} || true"
        }
    }
}

举报

相关推荐

0 条评论