From a87032e7a6b6861c5439421064d4f6a8532aff43 Mon Sep 17 00:00:00 2001 From: John Hanzu Kim Date: Tue, 17 Jun 2025 16:52:51 +0900 Subject: [PATCH 1/5] Update Jenkinsfile --- smarketing-java/deployment/Jenkinsfile | 82 ++++++++++++++------------ 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/smarketing-java/deployment/Jenkinsfile b/smarketing-java/deployment/Jenkinsfile index 685d9b3..675d3fa 100644 --- a/smarketing-java/deployment/Jenkinsfile +++ b/smarketing-java/deployment/Jenkinsfile @@ -173,48 +173,52 @@ podTemplate( stage('Generate & Apply Manifest') { container('envsubst') { - sh """ - echo "=== 환경변수 설정 ===" - export namespace=${namespace} - export allowed_origins=${props.allowed_origins} - export jwt_secret_key=${props.jwt_secret_key} - export postgres_user=${props.postgres_user} - export postgres_password=${props.postgres_password} - export replicas=${props.replicas} - - # PostgreSQL 환경변수 추가 - export postgres_host=${props.POSTGRES_HOST} - export postgres_port=${props.POSTGRES_PORT:-5432} - export postgres_db_member=member - export postgres_db_store=store - export postgres_db_marketing_content=marketing_content - export postgres_db_ai_recommend=AiRecommendationDB - - # Redis 환경변수 추가 - export redis_host=${props.REDIS_HOST} - export redis_port=${props.REDIS_PORT} - export redis_password=${props.REDIS_PASSWORD} - + script { + def postgresPort = props.POSTGRES_PORT ?: '5432' + def redisPort = props.REDIS_PORT ?: '6379' - # 리소스 요구사항 조정 (작게) - export resources_requests_cpu=100m - export resources_requests_memory=128Mi - export resources_limits_cpu=500m - export resources_limits_memory=512Mi + sh """ + echo "=== 환경변수 설정 ===" + export namespace=${namespace} + export allowed_origins=${props.allowed_origins} + export jwt_secret_key=${props.jwt_secret_key} + export postgres_user=${props.postgres_user} + export postgres_password=${props.postgres_password} + export replicas=${props.replicas} + + # PostgreSQL 환경변수 추가 + export postgres_host=${props.POSTGRES_HOST} + export postgres_port=${postgresPort} + export postgres_db_member=member + export postgres_db_store=store + export postgres_db_marketing_content=marketing_content + export postgres_db_ai_recommend=AiRecommendationDB + + # Redis 환경변수 추가 + export redis_host=${props.REDIS_HOST} + export redis_port=${redisPort} + export redis_password=${props.REDIS_PASSWORD} + + # 리소스 요구사항 + 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 member_image_path=${props.registry}/${props.image_org}/member:${imageTag} - export store_image_path=${props.registry}/${props.image_org}/store:${imageTag} - export marketing_content_image_path=${props.registry}/${props.image_org}/marketing-content:${imageTag} - export ai_recommend_image_path=${props.registry}/${props.image_org}/ai-recommend:${imageTag} + # 이미지 경로 환경변수 설정 + export member_image_path=${props.registry}/${props.image_org}/member:${imageTag} + export store_image_path=${props.registry}/${props.image_org}/store:${imageTag} + export marketing_content_image_path=${props.registry}/${props.image_org}/marketing-content:${imageTag} + export ai_recommend_image_path=${props.registry}/${props.image_org}/ai-recommend:${imageTag} - echo "=== Manifest 생성 ===" - envsubst < smarketing-java/deployment/${manifest}.template > smarketing-java/deployment/${manifest} - - echo "=== Generated Manifest File ===" - cat smarketing-java/deployment/${manifest} - echo "===============================" - """ + echo "=== Manifest 생성 ===" + envsubst < smarketing-java/deployment/${manifest}.template > smarketing-java/deployment/${manifest} + + echo "=== Generated Manifest File ===" + cat smarketing-java/deployment/${manifest} + echo "===============================" + """ + } } container('azure-cli') { From 135264dd952bc1542a1eb74cb47985de5a9b1fcf Mon Sep 17 00:00:00 2001 From: John Hanzu Kim Date: Tue, 17 Jun 2025 17:03:00 +0900 Subject: [PATCH 2/5] Update Jenkinsfile --- smarketing-java/deployment/Jenkinsfile | 336 ++++++------------------- 1 file changed, 82 insertions(+), 254 deletions(-) diff --git a/smarketing-java/deployment/Jenkinsfile b/smarketing-java/deployment/Jenkinsfile index 675d3fa..14bd4c3 100644 --- a/smarketing-java/deployment/Jenkinsfile +++ b/smarketing-java/deployment/Jenkinsfile @@ -1,260 +1,88 @@ -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: 'gradle', image: 'gradle:jdk17', ttyEnabled: true, command: 'cat'), - containerTemplate(name: 'docker', image: 'docker:20.10.16-dind', ttyEnabled: true, 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: '/home/gradle/.gradle', memory: false), - emptyDirVolume(mountPath: '/root/.azure', memory: false), - emptyDirVolume(mountPath: '/var/run', memory: false) - ] -) { - node(PIPELINE_ID) { - def props - def imageTag = getImageTag() - def manifest = "deploy.yaml" - def namespace - def services = ['member', 'store', 'marketing-content', 'ai-recommend'] - - stage("Get Source") { - checkout scm +stage('Generate & Apply Manifest') { + container('envsubst') { + script { + def postgresPort = "'" + (props.POSTGRES_PORT ?: '5432') + "'" + def redisPort = "'" + (props.REDIS_PORT ?: '6379') + "'" - // smarketing-java 하위에 있는 설정 파일 읽기 - props = readProperties file: "smarketing-java/deployment/deploy_env_vars" - namespace = "${props.namespace}" + sh """ + echo "=== 환경변수 설정 ===" + export namespace=${namespace} + export allowed_origins=${props.allowed_origins} + export jwt_secret_key=${props.jwt_secret_key} + export postgres_user=${props.postgres_user} + export postgres_password=${props.postgres_password} + export replicas=${props.replicas} + + # PostgreSQL 환경변수 추가 (문자열로) + export postgres_host=${props.POSTGRES_HOST} + export postgres_port=${postgresPort} + export postgres_db_member=member + export postgres_db_store=store + export postgres_db_marketing_content=marketing_content + export postgres_db_ai_recommend=AiRecommendationDB + + # Redis 환경변수 추가 (문자열로) + export redis_host=${props.REDIS_HOST} + export redis_port=${redisPort} + export redis_password=${props.REDIS_PASSWORD} + + # 리소스 요구사항 + 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} - echo "=== Build Information ===" - echo "Services: ${services}" - echo "Namespace: ${namespace}" - echo "Image Tag: ${imageTag}" - } + # 이미지 경로 환경변수 설정 + export member_image_path=${props.registry}/${props.image_org}/member:${imageTag} + export store_image_path=${props.registry}/${props.image_org}/store:${imageTag} + export marketing_content_image_path=${props.registry}/${props.image_org}/marketing-content:${imageTag} + export ai_recommend_image_path=${props.registry}/${props.image_org}/ai-recommend:${imageTag} - stage("Check Changes") { - script { - def changes = sh( - script: "git diff --name-only HEAD~1 HEAD", - returnStdout: true - ).trim() - - if (!changes.contains("smarketing-java/")) { - echo "No changes in smarketing-java, skipping build" - currentBuild.result = 'SUCCESS' - error("Stopping pipeline - no changes detected") - } - - echo "Changes detected in smarketing-java, proceeding with build" - } - } - - stage("Setup AKS") { - container('azure-cli') { - withCredentials([azureServicePrincipal('azure-credentials')]) { - sh """ - echo "=== Azure 로그인 ===" - az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID - az account set --subscription 2513dd36-7978-48e3-9a7c-b221d4874f66 - - echo "=== AKS 인증정보 가져오기 (rg-digitalgarage-02) ===" - az aks get-credentials --resource-group rg-digitalgarage-02 --name aks-digitalgarage-02 --overwrite-existing - - echo "=== 네임스페이스 생성 ===" - kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f - - - echo "=== Image Pull Secret 생성 ===" - kubectl create secret docker-registry acr-secret \\ - --docker-server=${props.registry} \\ - --docker-username=acrdigitalgarage02 \\ - --docker-password=\$(az acr credential show --name acrdigitalgarage02 --query passwords[0].value -o tsv) \\ - --namespace=${namespace} \\ - --dry-run=client -o yaml | kubectl apply -f - - - echo "=== 클러스터 상태 확인 ===" - kubectl get nodes - kubectl get ns ${namespace} - - echo "=== 현재 연결된 클러스터 확인 ===" - kubectl config current-context - """ - } - } - } - - stage('Build Applications') { - container('gradle') { - sh """ - echo "=== smarketing-java 디렉토리로 이동 ===" - cd smarketing-java - - echo "=== gradlew 권한 설정 ===" - chmod +x gradlew - - echo "=== 전체 서비스 빌드 ===" - ./gradlew :member:clean :member:build -x test - ./gradlew :store:clean :store:build -x test - ./gradlew :marketing-content:clean :marketing-content:build -x test - ./gradlew :ai-recommend:clean :ai-recommend:build -x test - - echo "=== 빌드 결과 확인 ===" - find . -name "*.jar" -path "*/build/libs/*" | grep -v 'plain.jar' - """ - } - } - - stage('Build & Push Images') { - container('docker') { - sh """ - echo "=== Docker 데몬 시작 대기 ===" - timeout 30 sh -c 'until docker info; do sleep 1; done' - """ - - // ACR Credential을 Jenkins에서 직접 사용 - withCredentials([usernamePassword( - credentialsId: 'acr-credentials', - usernameVariable: 'ACR_USERNAME', - passwordVariable: 'ACR_PASSWORD' - )]) { - sh """ - echo "=== Docker로 ACR 로그인 ===" - echo "\$ACR_PASSWORD" | docker login ${props.registry} --username \$ACR_USERNAME --password-stdin - """ - - services.each { service -> - script { - def buildDir = "smarketing-java/${service}" - def fullImageName = "${props.registry}/${props.image_org}/${service}:${imageTag}" - - echo "Building image for ${service}: ${fullImageName}" - - // 실제 JAR 파일명 동적 탐지 - def actualJarFile = sh( - script: """ - cd ${buildDir}/build/libs - ls *.jar | grep -v 'plain.jar' | head -1 - """, - returnStdout: true - ).trim() - - if (!actualJarFile) { - error "${service} JAR 파일을 찾을 수 없습니다" - } - - echo "발견된 JAR 파일: ${actualJarFile}" - - sh """ - echo "=== ${service} 이미지 빌드 ===" - docker build \\ - --build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \\ - --build-arg ARTIFACTORY_FILE="${actualJarFile}" \\ - -f smarketing-java/deployment/container/Dockerfile \\ - -t ${fullImageName} . - - echo "=== ${service} 이미지 푸시 ===" - docker push ${fullImageName} - - echo "Successfully built and pushed: ${fullImageName}" - """ - } - } - } - } - } - - stage('Generate & Apply Manifest') { - container('envsubst') { - script { - def postgresPort = props.POSTGRES_PORT ?: '5432' - def redisPort = props.REDIS_PORT ?: '6379' - - sh """ - echo "=== 환경변수 설정 ===" - export namespace=${namespace} - export allowed_origins=${props.allowed_origins} - export jwt_secret_key=${props.jwt_secret_key} - export postgres_user=${props.postgres_user} - export postgres_password=${props.postgres_password} - export replicas=${props.replicas} - - # PostgreSQL 환경변수 추가 - export postgres_host=${props.POSTGRES_HOST} - export postgres_port=${postgresPort} - export postgres_db_member=member - export postgres_db_store=store - export postgres_db_marketing_content=marketing_content - export postgres_db_ai_recommend=AiRecommendationDB - - # Redis 환경변수 추가 - export redis_host=${props.REDIS_HOST} - export redis_port=${redisPort} - export redis_password=${props.REDIS_PASSWORD} - - # 리소스 요구사항 - 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 member_image_path=${props.registry}/${props.image_org}/member:${imageTag} - export store_image_path=${props.registry}/${props.image_org}/store:${imageTag} - export marketing_content_image_path=${props.registry}/${props.image_org}/marketing-content:${imageTag} - export ai_recommend_image_path=${props.registry}/${props.image_org}/ai-recommend:${imageTag} - - echo "=== Manifest 생성 ===" - envsubst < smarketing-java/deployment/${manifest}.template > smarketing-java/deployment/${manifest} - - echo "=== Generated Manifest File ===" - cat smarketing-java/deployment/${manifest} - echo "===============================" - """ - } - } - - container('azure-cli') { - sh """ - echo "=== 현재 연결된 클러스터 재확인 ===" - kubectl config current-context - kubectl cluster-info | head -3 - - echo "=== PostgreSQL 서비스 확인 ===" - kubectl get svc -n ${namespace} | grep postgresql || echo "PostgreSQL 서비스가 없습니다. 먼저 설치해주세요." - - echo "=== Manifest 적용 ===" - kubectl apply -f smarketing-java/deployment/${manifest} - - echo "=== 배포 상태 확인 (60초 대기) ===" - kubectl -n ${namespace} get deployments - kubectl -n ${namespace} get pods - - echo "=== 각 서비스 배포 대기 (60초 timeout) ===" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/member --timeout=60s || echo "member deployment 대기 타임아웃" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/store --timeout=60s || echo "store deployment 대기 타임아웃" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/marketing-content --timeout=60s || echo "marketing-content deployment 대기 타임아웃" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/ai-recommend --timeout=60s || echo "ai-recommend deployment 대기 타임아웃" - - echo "=== 최종 상태 ===" - kubectl -n ${namespace} get all - - echo "=== 실패한 Pod 상세 정보 ===" - for pod in \$(kubectl -n ${namespace} get pods --field-selector=status.phase!=Running -o name 2>/dev/null || true); do - if [ ! -z "\$pod" ]; then - echo "=== 실패한 Pod: \$pod ===" - kubectl -n ${namespace} describe \$pod | tail -20 - fi - done - """ - } + echo "=== Manifest 생성 ===" + envsubst < smarketing-java/deployment/${manifest}.template > smarketing-java/deployment/${manifest} + + echo "=== Generated Manifest File ===" + cat smarketing-java/deployment/${manifest} + echo "===============================" + """ } } + + container('azure-cli') { + sh """ + echo "=== 기존 ConfigMap 삭제 (타입 충돌 해결) ===" + kubectl delete configmap member-config store-config marketing-content-config ai-recommend-config -n ${namespace} --ignore-not-found=true + + echo "=== 현재 연결된 클러스터 재확인 ===" + kubectl config current-context + kubectl cluster-info | head -3 + + echo "=== PostgreSQL 서비스 확인 ===" + kubectl get svc -n ${namespace} | grep postgresql || echo "PostgreSQL 서비스가 없습니다. 먼저 설치해주세요." + + echo "=== Manifest 적용 ===" + kubectl apply -f smarketing-java/deployment/${manifest} + + echo "=== 배포 상태 확인 (60초 대기) ===" + kubectl -n ${namespace} get deployments + kubectl -n ${namespace} get pods + + echo "=== 각 서비스 배포 대기 (60초 timeout) ===" + timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/member --timeout=60s || echo "member deployment 대기 타임아웃" + timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/store --timeout=60s || echo "store deployment 대기 타임아웃" + timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/marketing-content --timeout=60s || echo "marketing-content deployment 대기 타임아웃" + timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/ai-recommend --timeout=60s || echo "ai-recommend deployment 대기 타임아웃" + + echo "=== 최종 상태 ===" + kubectl -n ${namespace} get all + + echo "=== 실패한 Pod 상세 정보 ===" + for pod in \$(kubectl -n ${namespace} get pods --field-selector=status.phase!=Running -o name 2>/dev/null || true); do + if [ ! -z "\$pod" ]; then + echo "=== 실패한 Pod: \$pod ===" + kubectl -n ${namespace} describe \$pod | tail -20 + fi + done + """ + } } From e70821edfe37e2dfc1d178d8c457c66f7892b549 Mon Sep 17 00:00:00 2001 From: John Hanzu Kim Date: Tue, 17 Jun 2025 17:04:38 +0900 Subject: [PATCH 3/5] Update Jenkinsfile --- smarketing-java/deployment/Jenkinsfile | 350 +++++++++++++++++++------ 1 file changed, 268 insertions(+), 82 deletions(-) diff --git a/smarketing-java/deployment/Jenkinsfile b/smarketing-java/deployment/Jenkinsfile index 14bd4c3..b2934e3 100644 --- a/smarketing-java/deployment/Jenkinsfile +++ b/smarketing-java/deployment/Jenkinsfile @@ -1,88 +1,274 @@ -stage('Generate & Apply Manifest') { - container('envsubst') { - script { - def postgresPort = "'" + (props.POSTGRES_PORT ?: '5432') + "'" - def redisPort = "'" + (props.REDIS_PORT ?: '6379') + "'" +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: 'gradle', image: 'gradle:jdk17', ttyEnabled: true, command: 'cat'), + containerTemplate(name: 'docker', image: 'docker:20.10.16-dind', ttyEnabled: true, 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: '/home/gradle/.gradle', memory: false), + emptyDirVolume(mountPath: '/root/.azure', memory: false), + emptyDirVolume(mountPath: '/var/run', memory: false) + ] +) { + node(PIPELINE_ID) { + def props + def imageTag = getImageTag() + def manifest = "deploy.yaml" + def namespace + def services = ['member', 'store', 'marketing-content', 'ai-recommend'] + + stage("Get Source") { + checkout scm - sh """ - echo "=== 환경변수 설정 ===" - export namespace=${namespace} - export allowed_origins=${props.allowed_origins} - export jwt_secret_key=${props.jwt_secret_key} - export postgres_user=${props.postgres_user} - export postgres_password=${props.postgres_password} - export replicas=${props.replicas} - - # PostgreSQL 환경변수 추가 (문자열로) - export postgres_host=${props.POSTGRES_HOST} - export postgres_port=${postgresPort} - export postgres_db_member=member - export postgres_db_store=store - export postgres_db_marketing_content=marketing_content - export postgres_db_ai_recommend=AiRecommendationDB - - # Redis 환경변수 추가 (문자열로) - export redis_host=${props.REDIS_HOST} - export redis_port=${redisPort} - export redis_password=${props.REDIS_PASSWORD} - - # 리소스 요구사항 - 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} + // smarketing-java 하위에 있는 설정 파일 읽기 + props = readProperties file: "smarketing-java/deployment/deploy_env_vars" + namespace = "${props.namespace}" - # 이미지 경로 환경변수 설정 - export member_image_path=${props.registry}/${props.image_org}/member:${imageTag} - export store_image_path=${props.registry}/${props.image_org}/store:${imageTag} - export marketing_content_image_path=${props.registry}/${props.image_org}/marketing-content:${imageTag} - export ai_recommend_image_path=${props.registry}/${props.image_org}/ai-recommend:${imageTag} + echo "=== Build Information ===" + echo "Services: ${services}" + echo "Namespace: ${namespace}" + echo "Image Tag: ${imageTag}" + } - echo "=== Manifest 생성 ===" - envsubst < smarketing-java/deployment/${manifest}.template > smarketing-java/deployment/${manifest} - - echo "=== Generated Manifest File ===" - cat smarketing-java/deployment/${manifest} - echo "===============================" - """ + stage("Check Changes") { + script { + def changes = sh( + script: "git diff --name-only HEAD~1 HEAD", + returnStdout: true + ).trim() + + if (!changes.contains("smarketing-java/")) { + echo "No changes in smarketing-java, skipping build" + currentBuild.result = 'SUCCESS' + error("Stopping pipeline - no changes detected") + } + + echo "Changes detected in smarketing-java, proceeding with build" + } + } + + stage("Setup AKS") { + container('azure-cli') { + withCredentials([azureServicePrincipal('azure-credentials')]) { + sh """ + echo "=== Azure 로그인 ===" + az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET -t \$AZURE_TENANT_ID + az account set --subscription 2513dd36-7978-48e3-9a7c-b221d4874f66 + + echo "=== AKS 인증정보 가져오기 (rg-digitalgarage-02) ===" + az aks get-credentials --resource-group rg-digitalgarage-02 --name aks-digitalgarage-02 --overwrite-existing + + echo "=== 네임스페이스 생성 ===" + kubectl create namespace ${namespace} --dry-run=client -o yaml | kubectl apply -f - + + echo "=== Image Pull Secret 생성 ===" + kubectl create secret docker-registry acr-secret \\ + --docker-server=${props.registry} \\ + --docker-username=acrdigitalgarage02 \\ + --docker-password=\$(az acr credential show --name acrdigitalgarage02 --query passwords[0].value -o tsv) \\ + --namespace=${namespace} \\ + --dry-run=client -o yaml | kubectl apply -f - + + echo "=== 클러스터 상태 확인 ===" + kubectl get nodes + kubectl get ns ${namespace} + + echo "=== 현재 연결된 클러스터 확인 ===" + kubectl config current-context + """ + } + } + } + + stage('Build Applications') { + container('gradle') { + sh """ + echo "=== smarketing-java 디렉토리로 이동 ===" + cd smarketing-java + + echo "=== gradlew 권한 설정 ===" + chmod +x gradlew + + echo "=== 전체 서비스 빌드 ===" + ./gradlew :member:clean :member:build -x test + ./gradlew :store:clean :store:build -x test + ./gradlew :marketing-content:clean :marketing-content:build -x test + ./gradlew :ai-recommend:clean :ai-recommend:build -x test + + echo "=== 빌드 결과 확인 ===" + find . -name "*.jar" -path "*/build/libs/*" | grep -v 'plain.jar' + """ + } + } + + stage('Build & Push Images') { + container('docker') { + sh """ + echo "=== Docker 데몬 시작 대기 ===" + timeout 30 sh -c 'until docker info; do sleep 1; done' + """ + + // ACR Credential을 Jenkins에서 직접 사용 + withCredentials([usernamePassword( + credentialsId: 'acr-credentials', + usernameVariable: 'ACR_USERNAME', + passwordVariable: 'ACR_PASSWORD' + )]) { + sh """ + echo "=== Docker로 ACR 로그인 ===" + echo "\$ACR_PASSWORD" | docker login ${props.registry} --username \$ACR_USERNAME --password-stdin + """ + + services.each { service -> + script { + def buildDir = "smarketing-java/${service}" + def fullImageName = "${props.registry}/${props.image_org}/${service}:${imageTag}" + + echo "Building image for ${service}: ${fullImageName}" + + // 실제 JAR 파일명 동적 탐지 + def actualJarFile = sh( + script: """ + cd ${buildDir}/build/libs + ls *.jar | grep -v 'plain.jar' | head -1 + """, + returnStdout: true + ).trim() + + if (!actualJarFile) { + error "${service} JAR 파일을 찾을 수 없습니다" + } + + echo "발견된 JAR 파일: ${actualJarFile}" + + sh """ + echo "=== ${service} 이미지 빌드 ===" + docker build \\ + --build-arg BUILD_LIB_DIR="${buildDir}/build/libs" \\ + --build-arg ARTIFACTORY_FILE="${actualJarFile}" \\ + -f smarketing-java/deployment/container/Dockerfile \\ + -t ${fullImageName} . + + echo "=== ${service} 이미지 푸시 ===" + docker push ${fullImageName} + + echo "Successfully built and pushed: ${fullImageName}" + """ + } + } + } + } + } + + stage('Generate & Apply Manifest') { + container('envsubst') { + sh """ + echo "=== 환경변수 설정 ===" + export namespace=${namespace} + export allowed_origins='${props.allowed_origins}' + export jwt_secret_key='${props.jwt_secret_key}' + export postgres_user='${props.POSTGRES_USER}' + export postgres_password='${props.POSTGRES_PASSWORD}' + export replicas=${props.replicas} + + # PostgreSQL 환경변수 추가 (문자열로) + export postgres_host='${props.POSTGRES_HOST}' + export postgres_port='5432' + export postgres_db_member='member' + export postgres_db_store='store' + export postgres_db_marketing_content='marketing_content' + export postgres_db_ai_recommend='AiRecommendationDB' + + # Redis 환경변수 추가 (문자열로) + export redis_host='${props.REDIS_HOST}' + export redis_port='6380' + export redis_password='${props.REDIS_PASSWORD}' + + # 리소스 요구사항 + 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 member_image_path='${props.registry}/${props.image_org}/member:${imageTag}' + export store_image_path='${props.registry}/${props.image_org}/store:${imageTag}' + export marketing_content_image_path='${props.registry}/${props.image_org}/marketing-content:${imageTag}' + export ai_recommend_image_path='${props.registry}/${props.image_org}/ai-recommend:${imageTag}' + + echo "=== 환경변수 확인 ===" + echo "postgres_host: \$postgres_host" + echo "postgres_port: \$postgres_port" + echo "redis_host: \$redis_host" + echo "redis_port: \$redis_port" + echo "postgres_user: \$postgres_user" + + echo "=== Manifest 생성 ===" + envsubst < smarketing-java/deployment/${manifest}.template > smarketing-java/deployment/${manifest} + + echo "=== Generated Manifest File ===" + cat smarketing-java/deployment/${manifest} + echo "===============================" + """ + } + + container('azure-cli') { + sh """ + echo "=== 현재 연결된 클러스터 재확인 ===" + kubectl config current-context + kubectl cluster-info | head -3 + + echo "=== 기존 ConfigMap 삭제 (타입 충돌 해결) ===" + kubectl delete configmap member-config store-config marketing-content-config ai-recommend-config -n ${namespace} --ignore-not-found=true + + echo "=== PostgreSQL 서비스 확인 ===" + kubectl get svc -n ${namespace} | grep postgresql || echo "PostgreSQL 서비스가 없습니다." + + echo "=== Manifest 적용 ===" + kubectl apply -f smarketing-java/deployment/${manifest} + + echo "=== 배포 상태 확인 (30초 대기) ===" + sleep 30 + kubectl -n ${namespace} get deployments + kubectl -n ${namespace} get pods + + echo "=== ConfigMap 확인 ===" + kubectl -n ${namespace} get configmap ai-recommend-config -o yaml + + echo "=== 각 서비스 배포 대기 (90초 timeout) ===" + timeout 90 kubectl -n ${namespace} wait --for=condition=available deployment/member --timeout=90s || echo "member deployment 대기 타임아웃" + timeout 90 kubectl -n ${namespace} wait --for=condition=available deployment/store --timeout=90s || echo "store deployment 대기 타임아웃" + timeout 90 kubectl -n ${namespace} wait --for=condition=available deployment/marketing-content --timeout=90s || echo "marketing-content deployment 대기 타임아웃" + timeout 90 kubectl -n ${namespace} wait --for=condition=available deployment/ai-recommend --timeout=90s || echo "ai-recommend deployment 대기 타임아웃" + + echo "=== 최종 상태 ===" + kubectl -n ${namespace} get all + + echo "=== Pod 로그 확인 (ai-recommend) ===" + kubectl -n ${namespace} logs deployment/ai-recommend --tail=50 || echo "ai-recommend 로그를 가져올 수 없습니다" + + echo "=== 실패한 Pod 상세 정보 ===" + for pod in \$(kubectl -n ${namespace} get pods --field-selector=status.phase!=Running -o name 2>/dev/null || true); do + if [ ! -z "\$pod" ]; then + echo "=== 실패한 Pod: \$pod ===" + kubectl -n ${namespace} describe \$pod | tail -30 + echo "=== Pod 로그: \$pod ===" + kubectl -n ${namespace} logs \$pod --tail=20 || echo "로그를 가져올 수 없습니다" + fi + done + """ + } } } - - container('azure-cli') { - sh """ - echo "=== 기존 ConfigMap 삭제 (타입 충돌 해결) ===" - kubectl delete configmap member-config store-config marketing-content-config ai-recommend-config -n ${namespace} --ignore-not-found=true - - echo "=== 현재 연결된 클러스터 재확인 ===" - kubectl config current-context - kubectl cluster-info | head -3 - - echo "=== PostgreSQL 서비스 확인 ===" - kubectl get svc -n ${namespace} | grep postgresql || echo "PostgreSQL 서비스가 없습니다. 먼저 설치해주세요." - - echo "=== Manifest 적용 ===" - kubectl apply -f smarketing-java/deployment/${manifest} - - echo "=== 배포 상태 확인 (60초 대기) ===" - kubectl -n ${namespace} get deployments - kubectl -n ${namespace} get pods - - echo "=== 각 서비스 배포 대기 (60초 timeout) ===" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/member --timeout=60s || echo "member deployment 대기 타임아웃" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/store --timeout=60s || echo "store deployment 대기 타임아웃" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/marketing-content --timeout=60s || echo "marketing-content deployment 대기 타임아웃" - timeout 60 kubectl -n ${namespace} wait --for=condition=available deployment/ai-recommend --timeout=60s || echo "ai-recommend deployment 대기 타임아웃" - - echo "=== 최종 상태 ===" - kubectl -n ${namespace} get all - - echo "=== 실패한 Pod 상세 정보 ===" - for pod in \$(kubectl -n ${namespace} get pods --field-selector=status.phase!=Running -o name 2>/dev/null || true); do - if [ ! -z "\$pod" ]; then - echo "=== 실패한 Pod: \$pod ===" - kubectl -n ${namespace} describe \$pod | tail -20 - fi - done - """ - } } From 37a69d88340766ec5ef896f5252633ff9fb57371 Mon Sep 17 00:00:00 2001 From: John Hanzu Kim Date: Tue, 17 Jun 2025 17:07:16 +0900 Subject: [PATCH 4/5] Update deploy.yaml.template --- .../deployment/deploy.yaml.template | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/smarketing-java/deployment/deploy.yaml.template b/smarketing-java/deployment/deploy.yaml.template index 0a45408..7920afb 100644 --- a/smarketing-java/deployment/deploy.yaml.template +++ b/smarketing-java/deployment/deploy.yaml.template @@ -19,6 +19,23 @@ data: # 또는 Management port를 main port와 동일하게 MANAGEMENT_SERVER_PORT: '' +--- +# ConfigMap +apiVersion: v1 +kind: ConfigMap +metadata: + name: common-config + namespace: ${namespace} +data: + ALLOWED_ORIGINS: ${allowed_origins} + JPA_DDL_AUTO: update + JPA_SHOW_SQL: 'true' + # Actuator 설정 + MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: '*' + MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: always + MANAGEMENT_ENDPOINT_HEALTH_ENABLED: 'true' + MANAGEMENT_ENDPOINTS_WEB_BASE_PATH: /actuator + --- apiVersion: v1 kind: ConfigMap @@ -29,7 +46,7 @@ data: SERVER_PORT: '8081' POSTGRES_HOST: ${postgres_host} POSTGRES_PORT: ${postgres_port} - POSTGRES_DB: ${postgres_db_ai_recommend} + POSTGRES_DB: ${postgres_db_member} REDIS_HOST: ${redis_host} REDIS_PORT: ${redis_port} JPA_DDL_AUTO: 'create-drop' @@ -45,7 +62,7 @@ data: SERVER_PORT: '8082' POSTGRES_HOST: ${postgres_host} POSTGRES_PORT: ${postgres_port} - POSTGRES_DB: ${postgres_db_ai_recommend} + POSTGRES_DB: ${postgres_db_store} REDIS_HOST: ${redis_host} REDIS_PORT: ${redis_port} JPA_DDL_AUTO: 'create-drop' @@ -61,13 +78,12 @@ data: SERVER_PORT: '8083' POSTGRES_HOST: ${postgres_host} POSTGRES_PORT: ${postgres_port} - POSTGRES_DB: ${postgres_db_ai_recommend} + POSTGRES_DB: ${postgres_db_marketing_content} REDIS_HOST: ${redis_host} REDIS_PORT: ${redis_port} JPA_DDL_AUTO: 'create-drop' JPA_SHOW_SQL: 'true' - --- apiVersion: v1 kind: ConfigMap @@ -84,6 +100,7 @@ data: JPA_DDL_AUTO: 'create-drop' JPA_SHOW_SQL: 'true' + --- # Secrets apiVersion: v1 From c2244293ff4cf5a418fbdbaf690ef089c6972bbd Mon Sep 17 00:00:00 2001 From: John Hanzu Kim Date: Tue, 17 Jun 2025 17:15:01 +0900 Subject: [PATCH 5/5] Update deploy.yaml.template --- .../deployment/deploy.yaml.template | 104 ++++-------------- 1 file changed, 22 insertions(+), 82 deletions(-) diff --git a/smarketing-java/deployment/deploy.yaml.template b/smarketing-java/deployment/deploy.yaml.template index 7920afb..0416d61 100644 --- a/smarketing-java/deployment/deploy.yaml.template +++ b/smarketing-java/deployment/deploy.yaml.template @@ -1,28 +1,6 @@ # ConfigMap apiVersion: v1 kind: ConfigMap -metadata: - name: common-config - namespace: ${namespace} -data: - ALLOWED_ORIGINS: ${allowed_origins} - JPA_DDL_AUTO: update - JPA_SHOW_SQL: 'true' - # 🔧 강화된 Actuator 설정 - MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: '*' - MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: always - MANAGEMENT_ENDPOINT_HEALTH_ENABLED: 'true' - MANAGEMENT_ENDPOINTS_WEB_BASE_PATH: /actuator - MANAGEMENT_SERVER_PORT: '8080' - # Spring Security 비활성화 (Actuator용) - SPRING_AUTOCONFIGURE_EXCLUDE: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration - # 또는 Management port를 main port와 동일하게 - MANAGEMENT_SERVER_PORT: '' - ---- -# ConfigMap -apiVersion: v1 -kind: ConfigMap metadata: name: common-config namespace: ${namespace} @@ -45,10 +23,10 @@ metadata: data: SERVER_PORT: '8081' POSTGRES_HOST: ${postgres_host} - POSTGRES_PORT: ${postgres_port} + POSTGRES_PORT: '5432' POSTGRES_DB: ${postgres_db_member} REDIS_HOST: ${redis_host} - REDIS_PORT: ${redis_port} + REDIS_PORT: '6380' JPA_DDL_AUTO: 'create-drop' JPA_SHOW_SQL: 'true' @@ -61,10 +39,10 @@ metadata: data: SERVER_PORT: '8082' POSTGRES_HOST: ${postgres_host} - POSTGRES_PORT: ${postgres_port} + POSTGRES_PORT: '5432' POSTGRES_DB: ${postgres_db_store} REDIS_HOST: ${redis_host} - REDIS_PORT: ${redis_port} + REDIS_PORT: '6380' JPA_DDL_AUTO: 'create-drop' JPA_SHOW_SQL: 'true' @@ -77,10 +55,10 @@ metadata: data: SERVER_PORT: '8083' POSTGRES_HOST: ${postgres_host} - POSTGRES_PORT: ${postgres_port} + POSTGRES_PORT: '5432' POSTGRES_DB: ${postgres_db_marketing_content} REDIS_HOST: ${redis_host} - REDIS_PORT: ${redis_port} + REDIS_PORT: '6380' JPA_DDL_AUTO: 'create-drop' JPA_SHOW_SQL: 'true' @@ -93,14 +71,13 @@ metadata: data: SERVER_PORT: '8084' POSTGRES_HOST: ${postgres_host} - POSTGRES_PORT: ${postgres_port} + POSTGRES_PORT: '5432' POSTGRES_DB: ${postgres_db_ai_recommend} REDIS_HOST: ${redis_host} - REDIS_PORT: ${redis_port} + REDIS_PORT: '6380' JPA_DDL_AUTO: 'create-drop' JPA_SHOW_SQL: 'true' - --- # Secrets apiVersion: v1 @@ -206,35 +183,25 @@ spec: - secretRef: name: member-secret startupProbe: - exec: - command: - - /bin/sh - - -c - - "nc -z member-postgresql 5432" + tcpSocket: + port: 8081 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 10 - # 🔧 개선된 Health Check 설정 livenessProbe: httpGet: path: /actuator/health port: 8081 - httpHeaders: - - name: Accept - value: application/json - initialDelaySeconds: 120 # 2분으로 증가 + initialDelaySeconds: 120 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: - path: /actuator/health/readiness + path: /actuator/health port: 8081 - httpHeaders: - - name: Accept - value: application/json - initialDelaySeconds: 60 # 1분으로 증가 + initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 @@ -282,11 +249,8 @@ spec: - secretRef: name: store-secret startupProbe: - exec: - command: - - /bin/sh - - -c - - "nc -z store-postgresql 5432" + tcpSocket: + port: 8082 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 @@ -295,20 +259,14 @@ spec: httpGet: path: /actuator/health port: 8082 - httpHeaders: - - name: Accept - value: application/json initialDelaySeconds: 120 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: - path: /actuator/health/readiness + path: /actuator/health port: 8082 - httpHeaders: - - name: Accept - value: application/json initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 5 @@ -357,11 +315,8 @@ spec: - secretRef: name: marketing-content-secret startupProbe: - exec: - command: - - /bin/sh - - -c - - "nc -z marketing-content-postgresql 5432" + tcpSocket: + port: 8083 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 @@ -370,20 +325,14 @@ spec: httpGet: path: /actuator/health port: 8083 - httpHeaders: - - name: Accept - value: application/json initialDelaySeconds: 120 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: - path: /actuator/health/readiness + path: /actuator/health port: 8083 - httpHeaders: - - name: Accept - value: application/json initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 5 @@ -432,11 +381,8 @@ spec: - secretRef: name: ai-recommend-secret startupProbe: - exec: - command: - - /bin/sh - - -c - - "nc -z ai-recommend-postgresql 5432" + tcpSocket: + port: 8084 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 @@ -445,20 +391,14 @@ spec: httpGet: path: /actuator/health port: 8084 - httpHeaders: - - name: Accept - value: application/json initialDelaySeconds: 120 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: - path: /actuator/health/readiness + path: /actuator/health port: 8084 - httpHeaders: - - name: Accept - value: application/json initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 5