Jenkinsfile 16.6 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"
        VPC_NAME = "comic-website-prod-vpc"  
    }
    
    options {
        buildDiscarder(logRotator(numToKeepStr: '10'))
        timeout(time: 45, unit: 'MINUTES')
        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
                        """
                        
                        // 部署应用(不再部署 networking/ingress.yaml)
                        sh """
                            echo "部署应用..."
                            kubectl apply -f k8s/application/ -n ${KUBE_NAMESPACE}
                            # 注意:不再部署 k8s/networking/ 因为不再需要 ALB Ingress
                        """
                        
                        // 更新镜像
                        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,因为使用 API Gateway + NLB 架构"
                        """
                    }
                }
            }
        }
        
        stage('Deploy API Gateway with Terraform') {
            steps {
                script {
                    withCredentials([[
                        $class: 'AmazonWebServicesCredentialsBinding',
                        credentialsId: 'dev-user-aws-credentials',
                        accessKeyVariable: 'AWS_ACCESS_KEY_ID',
                        secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'
                    ]]) {
                        dir('terraform') {
                            sh """
                                echo "=== 部署 API Gateway + NLB ==="
                                echo "初始化 Terraform..."
                                terraform init -upgrade -input=false
                                
                                echo "规划部署..."
                                terraform plan \
                                    -var="aws_region=${AWS_REGION}" \
                                    -var="cluster_name=${CLUSTER_NAME}" \
                                    -var="vpc_name=${VPC_NAME}" \
                                    -var="namespace=${KUBE_NAMESPACE}" \
                                    -var="environment=prod" \
                                    -out=tfplan
                                
                                echo "应用配置..."
                                terraform apply -auto-approve tfplan
                                
                                echo "获取输出信息..."
                                terraform output -json > tf_output.json
                            """
                            
                            // 读取 Terraform 输出
                            def tfOutput = readJSON file: 'terraform/tf_output.json'
                            env.API_GATEWAY_URL = tfOutput.api_gateway_url.value
                            env.NLB_DNS_NAME = tfOutput.nlb_dns_name.value
                        }
                    }
                }
            }
        }
        
        stage('Health Check via API Gateway') {
            steps {
                script {
                    echo "=== 通过 API Gateway 进行健康检查 ==="
                    
                    if (env.API_GATEWAY_URL) {
                        echo "API Gateway URL: ${env.API_GATEWAY_URL}"
                        echo "注意: API Gateway 完全就绪可能需要几分钟时间..."
                        
                        // 健康检查,通过 API Gateway
                        def healthChecked = false
                        def maxHealthAttempts = 12  // 6分钟
                        def healthAttempt = 0
                        
                        while (!healthChecked && healthAttempt < maxHealthAttempts) {
                            healthAttempt++
                            echo "健康检查尝试 ${healthAttempt}/${maxHealthAttempts}..."
                            
                            try {
                                def responseCode = sh(
                                    script: """
                                        curl -s -o /dev/null -w "%{http_code}" \
                                        --connect-timeout 10 \
                                        --max-time 15 \
                                        ${env.API_GATEWAY_URL}/api/health || echo "000"
                                    """,
                                    returnStdout: true
                                ).trim()
                                
                                if (responseCode == "200") {
                                    echo "✅ 应用健康检查通过!"
                                    healthChecked = true
                                } else {
                                    echo "⏳ 应用尚未就绪,状态码: ${responseCode}"
                                    if (healthAttempt < maxHealthAttempts) {
                                        echo "等待30秒后重试..."
                                        sleep 30
                                    }
                                }
                            } catch (Exception e) {
                                echo "⏳ 健康检查异常: ${e.message}"
                                if (healthAttempt < maxHealthAttempts) {
                                    echo "等待30秒后重试..."
                                    sleep 30
                                }
                            }
                        }
                        
                        if (!healthChecked) {
                            echo "⚠️ 健康检查未通过,但部署可能仍在进行中"
                            echo "API Gateway 完全就绪可能需要更多时间"
                        } else {
                            echo "🎉 API Gateway 健康检查成功!"
                        }
                    } else {
                        echo "⚠️ 无 API Gateway URL,跳过健康检查"
                        echo "请检查 Terraform 部署日志"
                    }
                }
            }
        }
        
        stage('Integration Test') {
            steps {
                script {
                    echo "=== 集成测试 ==="
                    
                    if (env.API_GATEWAY_URL) {
                        // 测试基本功能
                        sh """
                            echo "测试根路径..."
                            curl -s ${env.API_GATEWAY_URL}/ | head -c 100
                            echo ""
                            
                            echo "测试健康检查..."
                            curl -s ${env.API_GATEWAY_URL}/api/health
                            echo ""
                            
                            echo "测试用户列表..."
                            curl -s ${env.API_GATEWAY_URL}/api/users
                            echo ""
                        """
                        
                        echo "✅ 集成测试完成"
                    } else {
                        echo "⚠️ 无 API Gateway URL,跳过集成测试"
                    }
                }
            }
        }
    }
    
    post {
        always {
            script {
                cleanWs()
                currentBuild.description = "Build ${env.BUILD_NUMBER}"
                
                echo "=========================================="
                echo "构建结果: ${currentBuild.result}"
                echo "构建号: ${env.BUILD_NUMBER}"
                
                // 显示 API Gateway 信息
                if (env.API_GATEWAY_URL) {
                    echo "API Gateway URL: ${env.API_GATEWAY_URL}"
                    currentBuild.description = "Build ${env.BUILD_NUMBER} - ${env.API_GATEWAY_URL}"
                }
                echo "=========================================="
            }
        }
        success {
            script {
                if (env.API_GATEWAY_URL) {
                    echo "🎉 部署成功!"
                    echo "🌐 API Gateway 地址: ${env.API_GATEWAY_URL}"
                    echo "📚 API文档: ${env.API_GATEWAY_URL}/swagger-ui.html"
                    echo "❤️  健康检查: ${env.API_GATEWAY_URL}/api/health"
                    echo "👥 用户管理: ${env.API_GATEWAY_URL}/api/users"
                    echo ""
                    echo "💡 架构信息:"
                    echo "用户 → API Gateway → NLB → EKS Pods"
                    echo ""
                    echo "💡 提示: API Gateway 完全就绪可能需要几分钟时间"
                    echo "如果暂时无法访问,请等待几分钟后重试"
                } else {
                    echo "⚠️ 部署完成,但 API Gateway 信息未获取"
                    echo "请检查 Terraform 部署阶段日志"
                }
                
                // 显示 NLB 信息(用于调试)
                if (env.NLB_DNS_NAME) {
                    echo ""
                    echo "🔧 调试信息:"
                    echo "NLB DNS: ${env.NLB_DNS_NAME}"
                }
            }
        }
        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 "Service 状态:"
                    kubectl get service -n ${KUBE_NAMESPACE} || true
                    echo ""
                    echo "NLB Service 状态:"
                    kubectl get svc -n ${KUBE_NAMESPACE} | grep nlb || true
                """
                
                // 如果 Terraform 目录存在,显示状态
                dir('terraform') {
                    sh """
                        echo "=== Terraform 状态 ==="
                        terraform show -json || echo "Terraform 状态不可用"
                    """
                }
            }
        }
        aborted {
            script {
                echo "⏹️ 构建被中止"
                echo "部署可能仍在进行中,请检查:"
                echo "kubectl get all -n ${KUBE_NAMESPACE}"
                echo "kubectl get svc -n ${KUBE_NAMESPACE}"
                
                dir('terraform') {
                    sh """
                        echo "Terraform 状态:"
                        terraform state list || echo "Terraform 状态不可用"
                    """
                }
            }
        }
    }
}