Jenkinsfile 13.4 KB
pipeline {
    agent {
        label 'jenkins-agent'
    }
    
    tools {
        maven 'maven-3.9.10'
    }
    
    environment {
        AWS_ACCOUNT_ID = '319998871902'
        AWS_REGION = 'us-east-1'
        ECR_REPO = "${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/user-management"
        CLUSTER_NAME = 'comic-website-prod'
        KUBE_NAMESPACE = 'user-management'
        DOCKER_IMAGE = "${ECR_REPO}:${env.BUILD_NUMBER}"
        APP_NAME = "user-management-system"
    }
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timeout(time: 45, unit: 'MINUTES')  // 增加总超时到45分钟
        disableConcurrentBuilds()
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
                sh '''
                    echo "=== 代码信息 ==="
                    echo "仓库: ${GIT_URL}"
                    echo "分支: ${GIT_BRANCH}"
                    echo "Commit: ${GIT_COMMIT}"
                    git log -1 --oneline
                '''
            }
        }
        
        stage('Build') {
            steps {
                sh '''
                    echo "=== 开始构建应用 ==="
                    echo "Maven 版本:"
                    mvn --version
                    mvn clean package -DskipTests=true
                    echo "构建完成!"
                '''
            }
        }
        
        stage('Docker Build') {
            steps {
                script {
                    echo "=== 构建 Docker 镜像 ==="
                    sh 'docker --version'
                    docker.build("${DOCKER_IMAGE}")
                }
            }
        }
        
        stage('Docker Push to ECR') {
            steps {
                script {
                    withCredentials([[
                        $class: 'AmazonWebServicesCredentialsBinding',
                        credentialsId: 'dev-user-aws-credentials',
                        accessKeyVariable: 'AWS_ACCESS_KEY_ID',
                        secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'
                    ]]) {
                        sh """
                            echo "=== 推送镜像到 ECR ==="
                            
                            # 检查 ECR 仓库是否存在,不存在则创建
                            if ! aws ecr describe-repositories --repository-names user-management --region ${AWS_REGION} > /dev/null 2>&1; then
                                echo "创建 ECR 仓库..."
                                aws ecr create-repository --repository-name user-management --region ${AWS_REGION}
                            fi
                            
                            # 登录 ECR
                            aws ecr get-login-password --region ${AWS_REGION} | \
                            docker login --username AWS --password-stdin ${ECR_REPO}
                            
                            # 推送构建版本
                            echo "推送镜像: ${DOCKER_IMAGE}"
                            docker push ${DOCKER_IMAGE}
                            
                            # 推送 latest 标签
                            echo "推送 latest 标签"
                            docker tag ${DOCKER_IMAGE} ${ECR_REPO}:latest
                            docker push ${ECR_REPO}:latest
                            
                            echo "镜像推送完成!"
                        """
                    }
                }
            }
        }
        
        stage('Deploy to EKS') {
            steps {
                script {
                    withCredentials([[
                        $class: 'AmazonWebServicesCredentialsBinding',
                        credentialsId: 'dev-user-aws-credentials',
                        accessKeyVariable: 'AWS_ACCESS_KEY_ID',
                        secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'
                    ]]) {
                        sh """
                            echo "=== 部署到 EKS ==="
                            
                            # 配置 kubeconfig
                            aws eks update-kubeconfig \
                                --region ${AWS_REGION} \
                                --name ${CLUSTER_NAME}
                            
                            echo "当前集群:"
                            kubectl config current-context
                        """
                        
                        // 部署命名空间
                        sh """
                            kubectl apply -f k8s/namespace.yaml
                        """
                        
                        // 部署数据库
                        sh """
                            echo "部署数据库..."
                            kubectl apply -f k8s/database/ -n ${KUBE_NAMESPACE}
                            
                            # 等待数据库就绪
                            echo "等待数据库启动..."
                            sleep 60  # 增加等待时间
                        """
                        
                        // 部署应用
                        sh """
                            echo "部署应用..."
                            kubectl apply -f k8s/application/ -n ${KUBE_NAMESPACE}
                            kubectl apply -f k8s/networking/ -n ${KUBE_NAMESPACE}
                        """
                        
                        // 更新镜像
                        sh """
                            echo "更新应用镜像..."
                            kubectl set image deployment/user-management-app \
                                user-management-app=${DOCKER_IMAGE} \
                                -n ${KUBE_NAMESPACE}
                        """
                        
                        // 等待部署完成
                        sh """
                            echo "等待部署完成..."
                            kubectl rollout status deployment/user-management-app \
                                -n ${KUBE_NAMESPACE} --timeout=600s
                        """
                        
                        // 显示部署状态
                        sh """
                            echo "=== 部署状态 ==="
                            echo "Pods:"
                            kubectl get pods -n ${KUBE_NAMESPACE} -o wide
                            echo ""
                            echo "Services:"
                            kubectl get service -n ${KUBE_NAMESPACE}
                            echo ""
                            echo "Ingress:"
                            kubectl get ingress -n ${KUBE_NAMESPACE}
                            echo ""
                            echo "Ingress 详细信息:"
                            kubectl describe ingress user-management-ingress -n ${KUBE_NAMESPACE} || true
                        """
                    }
                }
            }
        }
        
        stage('Wait for ALB Provisioning') {
            steps {
                script {
                    echo "=== 等待 ALB 配置 ==="
                    echo "ALB 创建通常需要 5-15 分钟,请耐心等待..."
                    
                    // 第一阶段:等待 ALB DNS 名称分配
                    timeout(time: 10, unit: 'MINUTES') {
                        waitUntil {
                            try {
                                def albUrl = sh(
                                    script: """
                                        kubectl get ingress user-management-ingress \
                                        -n ${KUBE_NAMESPACE} \
                                        -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null || echo ""
                                    """,
                                    returnStdout: true
                                ).trim()
                                
                                if (albUrl && albUrl != "") {
                                    echo "✅ ALB DNS 名称已分配: ${albUrl}"
                                    env.ALB_URL = albUrl
                                    return true
                                } else {
                                    echo "⏳ 等待 ALB DNS 名称分配..."
                                    sleep 30
                                    return false
                                }
                            } catch (Exception e) {
                                echo "⏳ 检查 ALB 状态异常: ${e.message}"
                                sleep 30
                                return false
                            }
                        }
                    }
                    
                    echo "ALB DNS 名称: ${env.ALB_URL}"
                }
            }
        }
        
        stage('Health Check') {
            steps {
                script {
                    echo "=== 应用健康检查 ==="
                    echo "ALB 地址: ${env.ALB_URL}"
                    echo "注意: ALB 完全就绪可能需要额外时间..."
                    
                    // 第二阶段:健康检查,但更宽松
                    timeout(time: 8, unit: 'MINUTES') {
                        waitUntil {
                            try {
                                def responseCode = sh(
                                    script: """
                                        curl -s -o /dev/null -w "%{http_code}" \
                                        --connect-timeout 10 \
                                        --max-time 15 \
                                        http://${env.ALB_URL}/actuator/health || echo "000"
                                    """,
                                    returnStdout: true
                                ).trim()
                                
                                if (responseCode == "200") {
                                    echo "✅ 应用健康检查通过!"
                                    return true
                                } else {
                                    echo "⏳ 应用尚未就绪,状态码: ${responseCode}"
                                    echo "这可能是正常的,ALB 仍在配置中..."
                                    sleep 30
                                    return false
                                }
                            } catch (Exception e) {
                                echo "⏳ 健康检查异常: ${e.message}"
                                echo "这可能是正常的,ALB 仍在配置中..."
                                sleep 30
                                return false
                            }
                        }
                    }
                }
            }
        }
    }
    
    post {
        always {
            script {
                cleanWs()
                currentBuild.description = "Build ${env.BUILD_NUMBER}"
                
                echo "=========================================="
                echo "构建结果: ${currentBuild.result}"
                echo "构建号: ${env.BUILD_NUMBER}"
                
                // 显示 ALB 信息(如果已分配)
                if (env.ALB_URL) {
                    echo "ALB 地址: ${env.ALB_URL}"
                    currentBuild.description = "Build ${env.BUILD_NUMBER} - http://${env.ALB_URL}"
                }
                echo "=========================================="
            }
        }
        success {
            script {
                if (env.ALB_URL) {
                    echo "🎉 部署成功!"
                    echo "🌐 应用地址: http://${env.ALB_URL}"
                    echo "📚 API文档: http://${env.ALB_URL}/swagger-ui.html"
                    echo "❤️  健康检查: http://${env.ALB_URL}/actuator/health"
                    echo ""
                    echo "💡 提示: ALB 完全就绪可能需要几分钟时间"
                    echo "如果暂时无法访问,请等待几分钟后重试"
                } else {
                    echo "⚠️ 部署完成,但 ALB 尚未就绪"
                    echo "请稍后检查 ALB 状态:"
                    echo "kubectl get ingress -n ${KUBE_NAMESPACE}"
                }
            }
        }
        failure {
            script {
                echo "❌ 部署失败!"
                echo "🔍 详细日志: ${env.BUILD_URL}console"
                
                // 显示错误诊断信息
                sh """
                    echo "=== 错误诊断 ==="
                    echo "最近的 Pod 事件:"
                    kubectl get events -n ${KUBE_NAMESPACE} --sort-by='.lastTimestamp' | tail -10 || true
                    echo ""
                    echo "Pod 状态:"
                    kubectl get pods -n ${KUBE_NAMESPACE} || true
                    echo ""
                    echo "Ingress 状态:"
                    kubectl get ingress -n ${KUBE_NAMESPACE} || true
                    echo ""
                    echo "Service 状态:"
                    kubectl get service -n ${KUBE_NAMESPACE} || true
                """
            }
        }
        aborted {
            script {
                echo "⏹️ 构建被中止"
                echo "这通常是因为 ALB 创建时间过长"
                echo "部署可能仍在进行中,请检查:"
                echo "kubectl get all -n ${KUBE_NAMESPACE}"
                echo "kubectl get ingress -n ${KUBE_NAMESPACE}"
            }
        }
    }
}