def PIPELINE_ID = "${env.BUILD_NUMBER}" def getImageTag() { def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss') def currentDate = new Date() return dateFormat.format(currentDate) } podTemplate( label: "${PIPELINE_ID}", serviceAccount: 'jenkins', containers: [ containerTemplate(name: 'podman', image: "mgoltzsche/podman", ttyEnabled: true, command: 'cat', privileged: true), containerTemplate(name: 'azure-cli', image: 'hiondal/azure-kubectl:latest', command: 'cat', ttyEnabled: true), containerTemplate(name: 'envsubst', image: "hiondal/envsubst", command: 'sleep', args: '1h') ], volumes: [ emptyDirVolume(mountPath: '/run/podman', memory: false), emptyDirVolume(mountPath: '/root/.azure', memory: false) ] ) { node(PIPELINE_ID) { def props def imageTag = getImageTag() def manifest = "deploy.yaml" def namespace stage("Get Source") { checkout scm props = readProperties file: "smarketing-ai/deployment/deploy_env_vars" namespace = "${props.namespace}" echo "Registry: ${props.registry}" echo "Image Org: ${props.image_org}" echo "Team ID: ${props.teamid}" } // stage("Check Changes") { // script { // def changes = sh( // script: "git diff --name-only HEAD~1 HEAD", // returnStdout: true // ).trim() // // echo "Changed files: ${changes}" // // if (!changes.contains("smarketing-ai/")) { // echo "No changes in smarketing-ai, skipping build" // currentBuild.result = 'SUCCESS' // error("Stopping pipeline - no changes detected") // } // // echo "Changes detected in smarketing-ai, proceeding with build" // } // } stage("Setup AKS") { container('azure-cli') { withCredentials([azureServicePrincipal('azure-credentials')]) { sh """ az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID az aks get-credentials --resource-group rg-digitalgarage-02 --name aks-digitalgarage-02 --overwrite-existing kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f - """ } } } stage('Build & Push Docker Image') { container('podman') { sh 'podman system service -t 0 unix:///run/podman/podman.sock & sleep 2' withCredentials([usernamePassword( credentialsId: 'acr-credentials', usernameVariable: 'ACR_USERNAME', passwordVariable: 'ACR_PASSWORD' )]) { sh """ echo "==========================================" echo "Building smarketing-ai Python Flask application" echo "Image Tag: ${imageTag}" echo "==========================================" # ACR 로그인 echo \$ACR_PASSWORD | podman login ${props.registry} --username \$ACR_USERNAME --password-stdin # Docker 이미지 빌드 podman build \ -f smarketing-ai/deployment/Dockerfile \ -t ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} . # 이미지 푸시 podman push ${props.registry}/${props.image_org}/smarketing-ai:${imageTag} echo "Successfully built and pushed: ${props.registry}/${props.image_org}/smarketing-ai:${imageTag}" """ } } } stage('Generate & Apply Manifest') { container('envsubst') { withCredentials([ string(credentialsId: 'SECRET_KEY', variable: 'SECRET_KEY'), string(credentialsId: 'CLAUDE_API_KEY', variable: 'CLAUDE_API_KEY'), string(credentialsId: 'OPENAI_API_KEY', variable: 'OPENAI_API_KEY'), string(credentialsId: 'AZURE_STORAGE_ACCOUNT_NAME', variable: 'AZURE_STORAGE_ACCOUNT_NAME'), string(credentialsId: 'AZURE_STORAGE_ACCOUNT_KEY', variable: 'AZURE_STORAGE_ACCOUNT_KEY') ]) { sh """ export namespace=${namespace} export replicas=${props.replicas} export resources_requests_cpu=${props.resources_requests_cpu} export resources_requests_memory=${props.resources_requests_memory} export resources_limits_cpu=${props.resources_limits_cpu} export resources_limits_memory=${props.resources_limits_memory} export upload_folder=${props.upload_folder} export max_content_length=${props.max_content_length} export allowed_extensions=${props.allowed_extensions} export server_host=${props.server_host} export server_port=${props.server_port} export azure_storage_container_name=${props.azure_storage_container_name} # 이미지 경로 환경변수 설정 export smarketing_image_path=${props.registry}/${props.image_org}/smarketing-ai:${imageTag} # Sensitive 환경변수 설정 (Jenkins Credentials에서) export secret_key=\$SECRET_KEY export claude_api_key=\$CLAUDE_API_KEY export openai_api_key=\$OPENAI_API_KEY export azure_storage_account_name=\$AZURE_STORAGE_ACCOUNT_NAME export azure_storage_account_key=\$AZURE_STORAGE_ACCOUNT_KEY # manifest 생성 envsubst < smarketing-ai/deployment/${manifest}.template > smarketing-ai/deployment/${manifest} echo "Generated manifest file:" cat smarketing-ai/deployment/${manifest} """ } } container('azure-cli') { sh """ kubectl apply -f smarketing-ai/deployment/${manifest} echo "Waiting for smarketing deployment to be ready..." kubectl -n ${namespace} wait --for=condition=available deployment/smarketing --timeout=300s echo "==========================================" echo "Getting LoadBalancer External IP..." # External IP 확인 (최대 5분 대기) for i in {1..30}; do EXTERNAL_IP=\$(kubectl -n ${namespace} get service smarketing-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}') if [ "\$EXTERNAL_IP" != "" ] && [ "\$EXTERNAL_IP" != "null" ]; then echo "External IP assigned: \$EXTERNAL_IP" break fi echo "Waiting for External IP... (attempt \$i/30)" sleep 10 done # 서비스 상태 확인 kubectl -n ${namespace} get pods -l app=smarketing kubectl -n ${namespace} get service smarketing-service echo "==========================================" echo "Deployment Complete!" echo "Service URL: http://\$EXTERNAL_IP:${props.server_port}" echo "Health Check: http://\$EXTERNAL_IP:${props.server_port}/health" echo "==========================================" """ } } } }