Jenkinsfile 18.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
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"  
        TERRAFORM_DIR = "terraform-api-gateway"
    }
    
    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 Application') {
            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}
                        """
                        
                        // 部署 NLB Service
                        sh """
                            echo "部署 NLB Service..."
                            kubectl apply -f k8s/networking/nlb-service.yaml -n ${KUBE_NAMESPACE}
                            
                            # 等待 NLB 创建
                            echo "等待 NLB 创建..."
                            sleep 30
                        """
                        
                        // 更新镜像
                        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
                        """
                        
                        // 获取 NLB DNS 名称
                        sh """
                            echo "获取 NLB DNS 名称..."
                            NLB_DNS=\$(kubectl get service user-management-nlb -n ${KUBE_NAMESPACE} -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
                            echo "🎯 NLB DNS: \$NLB_DNS"
                        """
                        
                        // 显示部署状态
                        sh """
                            echo "=== 部署状态 ==="
                            echo "Pods:"
                            kubectl get pods -n ${KUBE_NAMESPACE} -o wide
                            echo ""
                            echo "Services:"
                            kubectl get service -n ${KUBE_NAMESPACE}
                            echo ""
                            echo "NLB Service 状态:"
                            kubectl get svc user-management-nlb -n ${KUBE_NAMESPACE} -o wide
                            echo ""
                            echo "💡 架构: 用户 → NLB → EKS Pods"
                        """
                    }
                }
            }
        }
        
        stage('Health Check') {
            steps {
                script {
                    echo "=== 健康检查 ==="
                    
                    // 通过 NLB 进行健康检查
                    sh """
                        echo "通过 NLB 进行健康检查..."
                        NLB_DNS=\$(kubectl get service user-management-nlb -n ${KUBE_NAMESPACE} -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
                        echo "NLB DNS: \$NLB_DNS"
                        
                        if [ ! -z "\$NLB_DNS" ]; then
                            echo "等待 NLB 完全就绪..."
                            sleep 30
                            
                            echo "测试健康检查端点..."
                            curl -s http://\$NLB_DNS/api/health || echo "健康检查请求失败"
                        else
                            echo "未获取到 NLB DNS,跳过健康检查"
                        fi
                    """
                }
            }
        }

        // ========== 新增的 API Gateway 部署阶段 ==========
        stage('Deploy API Gateway') {
            steps {
                script {
                    withCredentials([[
                        $class: 'AmazonWebServicesCredentialsBinding',
                        credentialsId: 'dev-user-aws-credentials',
                        accessKeyVariable: 'AWS_ACCESS_KEY_ID',
                        secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'
                    ]]) {
                        sh """
                            echo "=== 部署 API Gateway ==="
                            
                            # 获取 NLB DNS 名称
                            NLB_DNS=\$(kubectl get service user-management-nlb -n ${KUBE_NAMESPACE} -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
                            echo "NLB DNS: \$NLB_DNS"
                            
                            if [ -z "\$NLB_DNS" ]; then
                                echo "❌ 无法获取 NLB DNS,跳过 API Gateway 部署"
                                exit 0
                            fi
                            
                            # 创建 Terraform 目录
                            mkdir -p ${TERRAFORM_DIR}
                            cd ${TERRAFORM_DIR}
                            
                            # 创建 backend.tf
                            cat > backend.tf << EOF
terraform {
  backend "s3" {
    bucket = "terraform-state-${AWS_ACCOUNT_ID}"
    key    = "user-management-api-gateway/terraform.tfstate"
    region = "${AWS_REGION}"
  }
}
EOF

                            # 创建 providers.tf
                            cat > providers.tf << EOF
terraform {
  required_version = ">= 1.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}
EOF

                            # 创建 variables.tf
                            cat > variables.tf << EOF
variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-east-1"
}

variable "api_name" {
  description = "API Gateway name"
  type        = string
  default     = "user-management-gateway"
}

variable "stage_name" {
  description = "API stage name"
  type        = string
  default     = "prod"
}

variable "k8s_service_url" {
  description = "K8s NLB service endpoint"
  type        = string
}
EOF

                            # 创建 main.tf
                            cat > main.tf << EOF
# 创建 REST API Gateway
resource "aws_api_gateway_rest_api" "user_management" {
  name        = var.api_name
  description = "API Gateway for Spring Boot User Management Application"
  
  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

# 创建代理资源,捕获所有路径
resource "aws_api_gateway_resource" "proxy" {
  rest_api_id = aws_api_gateway_rest_api.user_management.id
  parent_id   = aws_api_gateway_rest_api.user_management.root_resource_id
  path_part   = "{proxy+}"
}

# 为代理资源创建 ANY 方法(捕获所有HTTP方法)
resource "aws_api_gateway_method" "proxy" {
  rest_api_id   = aws_api_gateway_rest_api.user_management.id
  resource_id   = aws_api_gateway_resource.proxy.id
  http_method   = "ANY"
  authorization = "NONE"
  
  request_parameters = {
    "method.request.path.proxy" = true
  }
}

# 为根路径创建方法
resource "aws_api_gateway_method" "root" {
  rest_api_id   = aws_api_gateway_rest_api.user_management.id
  resource_id   = aws_api_gateway_rest_api.user_management.root_resource_id
  http_method   = "ANY"
  authorization = "NONE"
}

# 创建与 K8s NLB 的集成 - 代理路径
resource "aws_api_gateway_integration" "k8s_integration" {
  rest_api_id = aws_api_gateway_rest_api.user_management.id
  resource_id = aws_api_gateway_resource.proxy.id
  http_method = aws_api_gateway_method.proxy.http_method
  
  integration_http_method = "ANY"
  type                    = "HTTP_PROXY"
  uri                     = "http://\${var.k8s_service_url}/{proxy}"
  
  request_parameters = {
    "integration.request.path.proxy" = "method.request.path.proxy"
  }
}

# 创建与 K8s NLB 的集成 - 根路径
resource "aws_api_gateway_integration" "root_integration" {
  rest_api_id = aws_api_gateway_rest_api.user_management.id
  resource_id = aws_api_gateway_rest_api.user_management.root_resource_id
  http_method = aws_api_gateway_method.root.http_method
  
  integration_http_method = "ANY"
  type                    = "HTTP_PROXY"
  uri                     = "http://\${var.k8s_service_url}/"
}

# 创建部署
resource "aws_api_gateway_deployment" "deployment" {
  depends_on = [
    aws_api_gateway_integration.k8s_integration,
    aws_api_gateway_integration.root_integration
  ]
  
  rest_api_id = aws_api_gateway_rest_api.user_management.id

  triggers = {
    redeployment = sha1(jsonencode([
      aws_api_gateway_resource.proxy.id,
      aws_api_gateway_method.proxy.id,
      aws_api_gateway_method.root.id,
      aws_api_gateway_integration.k8s_integration.id,
      aws_api_gateway_integration.root_integration.id
    ]))
  }

  lifecycle {
    create_before_destroy = true
  }
}

# 创建阶段
resource "aws_api_gateway_stage" "prod" {
  deployment_id = aws_api_gateway_deployment.deployment.id
  rest_api_id   = aws_api_gateway_rest_api.user_management.id
  stage_name    = var.stage_name
}
EOF

                            # 创建 outputs.tf
                            cat > outputs.tf << EOF
output "api_url" {
  description = "API Gateway 完整访问 URL"
  value       = aws_api_gateway_stage.prod.invoke_url
}

output "api_id" {
  description = "API Gateway ID"
  value       = aws_api_gateway_rest_api.user_management.id
}

output "stage_name" {
  description = "API Stage 名称"
  value       = aws_api_gateway_stage.prod.stage_name
}

output "rest_api_id" {
  description = "REST API ID"
  value       = aws_api_gateway_rest_api.user_management.id
}
EOF

                            # 创建 terraform.tfvars
                            cat > terraform.tfvars << EOF
aws_region      = "${AWS_REGION}"
api_name        = "user-management-gateway-\${BUILD_NUMBER}"
stage_name      = "prod"
k8s_service_url = "\$NLB_DNS"
EOF

                            # 初始化并部署 Terraform
                            echo "初始化 Terraform..."
                            terraform init

                            echo "部署 API Gateway..."
                            terraform apply -auto-approve

                            # 获取 API Gateway URL
                            API_URL=\$(terraform output -raw api_url)
                            echo "🎯 API Gateway URL: \$API_URL"
                            
                            cd ..
                        """
                    }
                }
            }
        }
    }
    
    post {
        always {
            script {
                cleanWs()
                currentBuild.description = "Build ${env.BUILD_NUMBER}"
                
                echo "=========================================="
                echo "构建结果: ${currentBuild.result}"
                echo "构建号: ${env.BUILD_NUMBER}"
                echo "=========================================="
            }
        }
        success {
            script {
                echo "🎉 部署成功!"
                echo "📦 应用已部署到 EKS"
                echo "🌐 通过 NLB 访问应用"
                echo "🚀 API Gateway 已创建"
                
                sh """
                    NLB_DNS=\$(kubectl get service user-management-nlb -n ${KUBE_NAMESPACE} -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null || echo "")
                    if [ ! -z "\$NLB_DNS" ]; then
                        echo "🔗 NLB 地址: http://\$NLB_DNS"
                        echo "❤️  健康检查: http://\$NLB_DNS/api/health"
                        echo "👥 用户管理: http://\$NLB_DNS/api/users"
                    fi
                    
                    # 获取 API Gateway URL
                    if [ -d "${TERRAFORM_DIR}" ]; then
                        cd ${TERRAFORM_DIR}
                        API_URL=\$(terraform output -raw api_url 2>/dev/null || echo "")
                        if [ ! -z "\$API_URL" ]; then
                            echo "🎯 API Gateway 地址: \$API_URL"
                            echo "🔗 通过 API Gateway 访问:"
                            echo "   ❤️  健康检查: \$API_URL/api/health"
                            echo "   👥 用户管理: \$API_URL/api/users"
                            echo "   📚 API文档: \$API_URL/swagger-ui.html"
                        fi
                        cd ..
                    fi
                """
            }
        }
        failure {
            script {
                echo "❌ 部署失败!"
                echo "🔍 详细日志: ${env.BUILD_URL}console"
                
                // 显示错误诊断信息
                sh """
                    echo "=== 错误诊断 ==="
                    echo "Pod 状态:"
                    kubectl get pods -n ${KUBE_NAMESPACE} || true
                    echo ""
                    echo "Service 状态:"
                    kubectl get service -n ${KUBE_NAMESPACE} || true
                    echo ""
                    echo "最近的事件:"
                    kubectl get events -n ${KUBE_NAMESPACE} --sort-by='.lastTimestamp' | tail -10 || true
                """
            }
        }
    }
}