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: "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' return } 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 deployment/container/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 < deployment/${manifest}.template > deployment/${manifest} echo "Generated manifest file:" cat deployment/${manifest} """ } } container('azure-cli') { sh """ kubectl apply -f 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 "==========================================" """ } } } }