Jenkinsfile: Podman 기반 Kubernetes Pod 템플릿으로 전환

주요 변경사항:
- podTemplate 사용하여 Kubernetes Pod에서 실행
- 3개 컨테이너 사용: podman, gradle, git
- mgoltzsche/podman 이미지로 Podman 빌드
- gradle:jdk21 이미지로 Gradle 빌드
- alpine/git으로 manifest 저장소 업데이트

컨테이너별 역할:
- podman: Docker 이미지 빌드 및 ACR 푸시
- gradle: Gradle 빌드 및 JAR 생성
- git: Kustomize로 manifest 저장소 업데이트

리소스 최적화:
- Pod 자동 정리 (idleMinutes: 1, terminationGracePeriodSeconds: 3)
- 컨테이너별 리소스 제한 설정
- emptyDir 볼륨으로 Gradle 캐시 및 Podman 소켓 공유

Fix: Docker 대신 Podman 사용으로 Jenkins 환경 호환성 개선
This commit is contained in:
hjmoons 2025-10-30 18:47:36 +09:00
parent 7e3f7b9471
commit 258bef0891

342
Jenkinsfile vendored
View File

@ -1,216 +1,214 @@
pipeline { def PIPELINE_ID = "${env.BUILD_NUMBER}"
agent any
parameters { def getImageTag() {
choice( def dateFormat = new java.text.SimpleDateFormat('yyyyMMddHHmmss')
name: 'ENVIRONMENT', def currentDate = new Date()
choices: ['dev', 'staging', 'prod'], return dateFormat.format(currentDate)
description: 'Target environment' }
podTemplate(
label: "${PIPELINE_ID}",
serviceAccount: 'jenkins',
slaveConnectTimeout: 300,
idleMinutes: 1,
activeDeadlineSeconds: 3600,
podRetention: never(),
yaml: '''
spec:
terminationGracePeriodSeconds: 3
restartPolicy: Never
tolerations:
- effect: NoSchedule
key: dedicated
operator: Equal
value: cicd
''',
containers: [
containerTemplate(
name: 'podman',
image: "mgoltzsche/podman",
ttyEnabled: true,
command: 'cat',
privileged: true,
resourceRequestCpu: '500m',
resourceRequestMemory: '2Gi',
resourceLimitCpu: '2000m',
resourceLimitMemory: '4Gi'
),
containerTemplate(
name: 'gradle',
image: 'gradle:jdk21',
ttyEnabled: true,
command: 'cat',
resourceRequestCpu: '500m',
resourceRequestMemory: '1Gi',
resourceLimitCpu: '1000m',
resourceLimitMemory: '2Gi',
envVars: [
envVar(key: 'DOCKER_HOST', value: 'unix:///run/podman/podman.sock'),
envVar(key: 'TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE', value: '/run/podman/podman.sock'),
envVar(key: 'TESTCONTAINERS_RYUK_DISABLED', value: 'true')
]
),
containerTemplate(
name: 'git',
image: 'alpine/git:latest',
command: 'cat',
ttyEnabled: true,
resourceRequestCpu: '100m',
resourceRequestMemory: '256Mi',
resourceLimitCpu: '300m',
resourceLimitMemory: '512Mi'
) )
} ],
volumes: [
emptyDirVolume(mountPath: '/home/gradle/.gradle', memory: false),
emptyDirVolume(mountPath: '/run/podman', memory: false)
]
) {
node(PIPELINE_ID) {
def imageTag = getImageTag()
def environment = params.ENVIRONMENT ?: 'dev'
def services = ['user', 'meeting', 'stt', 'ai', 'notification']
def registry = 'acrdigitalgarage02.azurecr.io'
def imageOrg = 'hgzero'
environment { try {
// Container Registry stage("Get Source") {
REGISTRY = 'acrdigitalgarage02.azurecr.io' checkout scm
IMAGE_ORG = 'hgzero'
// Azure // 환경 변수 로드
RESOURCE_GROUP = 'rg-digitalgarage-02' def configFile = ".github/config/deploy_env_vars_${environment}"
AKS_CLUSTER = 'aks-digitalgarage-02' if (fileExists(configFile)) {
NAMESPACE = 'hgzero' echo "📋 Loading environment variables for ${environment}..."
def props = readProperties file: configFile
// Image Tag echo "Config loaded: ${props}"
IMAGE_TAG = "${new Date().format('yyyyMMddHHmmss')}"
// Services
SERVICES = 'user meeting stt ai notification'
// Credentials
ACR_CREDENTIALS = credentials('acr-credentials')
DOCKERHUB_CREDENTIALS = credentials('dockerhub-credentials')
GIT_CREDENTIALS = credentials('github-credentials-dg0506')
}
stages {
stage('Checkout') {
steps {
script {
echo "🔄 Checking out code..."
checkout scm
} }
} }
}
stage('Setup Java') { stage('Build') {
steps { container('gradle') {
script {
echo "☕ Setting up Java 21..."
// JDK 21 설치 및 대기
def jdkHome = tool name: 'JDK21', type: 'jdk'
def jdkPath = "${jdkHome}/jdk-21"
env.JAVA_HOME = jdkPath
env.PATH = "${jdkPath}/bin:${env.PATH}"
// JDK 설치 완료 대기 및 확인
sh """
echo "Waiting for JDK installation..."
while [ ! -f ${jdkPath}/bin/java ]; do
echo "Waiting for JDK to be extracted..."
sleep 2
done
echo "JDK installation completed"
${jdkPath}/bin/java -version
"""
}
}
}
stage('Load Environment Variables') {
steps {
script {
echo "📋 Loading environment variables for ${params.ENVIRONMENT}..."
def configFile = ".github/config/deploy_env_vars_${params.ENVIRONMENT}"
if (fileExists(configFile)) {
def config = readFile(configFile)
config.split('\n').each { line ->
if (line && !line.startsWith('#')) {
def parts = line.split('=', 2)
if (parts.size() == 2) {
def key = parts[0].trim()
def value = parts[1].trim()
if (key == 'resource_group') {
env.RESOURCE_GROUP = value
} else if (key == 'cluster_name') {
env.AKS_CLUSTER = value
}
}
}
}
}
echo "Registry: ${env.REGISTRY}"
echo "Image Org: ${env.IMAGE_ORG}"
echo "Resource Group: ${env.RESOURCE_GROUP}"
echo "AKS Cluster: ${env.AKS_CLUSTER}"
}
}
}
stage('Build with Gradle') {
steps {
script {
echo "🔨 Building with Gradle..." echo "🔨 Building with Gradle..."
sh 'chmod +x gradlew'
sh """ sh """
export JAVA_HOME=${env.JAVA_HOME} chmod +x gradlew
export PATH=\${JAVA_HOME}/bin:\${PATH}
java -version
./gradlew build -x test ./gradlew build -x test
""" """
} }
} }
}
stage('Archive Artifacts') { stage('Archive Artifacts') {
steps { echo "📦 Archiving build artifacts..."
script { archiveArtifacts artifacts: '**/build/libs/*.jar', fingerprint: true
echo "📦 Archiving build artifacts..."
archiveArtifacts artifacts: '**/build/libs/*.jar', fingerprint: true
}
} }
}
stage('Docker Build & Push') { stage('Build & Push Images') {
steps { timeout(time: 30, unit: 'MINUTES') {
script { container('podman') {
echo "🐳 Building and pushing Docker images..." withCredentials([
usernamePassword(
credentialsId: 'acr-credentials',
usernameVariable: 'ACR_USERNAME',
passwordVariable: 'ACR_PASSWORD'
),
usernamePassword(
credentialsId: 'dockerhub-credentials',
usernameVariable: 'DOCKERHUB_USERNAME',
passwordVariable: 'DOCKERHUB_PASSWORD'
)
]) {
echo "🐳 Building and pushing Docker images..."
// Login to Docker Hub (prevent rate limit) // Login to Docker Hub (prevent rate limit)
sh """ sh "podman login docker.io --username \$DOCKERHUB_USERNAME --password \$DOCKERHUB_PASSWORD"
echo ${DOCKERHUB_CREDENTIALS_PSW} | docker login -u ${DOCKERHUB_CREDENTIALS_USR} --password-stdin
"""
// Login to Azure Container Registry // Login to Azure Container Registry
sh """ sh "podman login ${registry} --username \$ACR_USERNAME --password \$ACR_PASSWORD"
echo ${ACR_CREDENTIALS_PSW} | docker login ${REGISTRY} -u ${ACR_CREDENTIALS_USR} --password-stdin
"""
// Build and push each service // Build and push each service
env.SERVICES.split().each { service -> services.each { service ->
echo "Building ${service}..." echo "Building ${service}..."
def imageTag = "${env.REGISTRY}/${env.IMAGE_ORG}/${service}:${params.ENVIRONMENT}-${env.IMAGE_TAG}" def imageTagFull = "${registry}/${imageOrg}/${service}:${environment}-${imageTag}"
sh """ sh """
docker build \ podman build \\
--build-arg BUILD_LIB_DIR="${service}/build/libs" \ --build-arg BUILD_LIB_DIR="${service}/build/libs" \\
--build-arg ARTIFACTORY_FILE="${service}.jar" \ --build-arg ARTIFACTORY_FILE="${service}.jar" \\
-f deployment/container/Dockerfile-backend \ -f deployment/container/Dockerfile-backend \\
-t ${imageTag} . -t ${imageTagFull} .
""" """
echo "Pushing ${service}..." echo "Pushing ${service}..."
sh "docker push ${imageTag}" sh "podman push ${imageTagFull}"
echo "✅ ${service} image pushed: ${imageTag}" echo "✅ ${service} image pushed: ${imageTagFull}"
}
}
} }
} }
} }
}
stage('Update Manifest Repository') { stage('Update Manifest Repository') {
steps { container('git') {
script { withCredentials([usernamePassword(
echo "📝 Updating manifest repository..." credentialsId: 'github-credentials-dg0506',
usernameVariable: 'GIT_USERNAME',
passwordVariable: 'GIT_TOKEN'
)]) {
echo "📝 Updating manifest repository..."
// Clone manifest repository
sh """
rm -rf manifest-repo
git clone https://${GIT_CREDENTIALS_USR}:${GIT_CREDENTIALS_PSW}@github.com/hjmoons/hgzero-manifest.git manifest-repo
"""
dir('manifest-repo') {
// Install Kustomize
sh """ sh """
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash # 매니페스트 레포지토리 클론
REPO_URL=\$(echo "https://github.com/hjmoons/hgzero-manifest.git" | sed 's|https://||')
git clone https://\${GIT_USERNAME}:\${GIT_TOKEN}@\${REPO_URL} manifest-repo
cd manifest-repo
# Kustomize 설치
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | sh
chmod +x kustomize chmod +x kustomize
"""
// Update manifest # 각 서비스별 이미지 태그 업데이트
dir("hgzero-back/kustomize/overlays/${params.ENVIRONMENT}") { cd hgzero-back/kustomize/overlays/${environment}
env.SERVICES.split().each { service ->
sh """
../../../kustomize edit set image ${env.REGISTRY}/${env.IMAGE_ORG}/${service}:${params.ENVIRONMENT}-${env.IMAGE_TAG}
"""
}
}
// Git commit and push services="user meeting stt ai notification"
sh """ for service in \$services; do
echo "Updating \$service image tag..."
../../../kustomize edit set image ${registry}/${imageOrg}/\$service:${environment}-${imageTag}
done
# Git 설정 및 푸시
cd ../../../..
git config user.name "Jenkins" git config user.name "Jenkins"
git config user.email "jenkins@hgzero.com" git config user.email "jenkins@hgzero.com"
git add . git add .
git commit -m "🚀 Update hgzero ${params.ENVIRONMENT} images to ${params.ENVIRONMENT}-${env.IMAGE_TAG}" git commit -m "🚀 Update hgzero ${environment} images to ${environment}-${imageTag}"
git push origin main git push origin main
echo "✅ 매니페스트 업데이트 완료. ArgoCD가 자동으로 배포합니다."
""" """
} }
echo "✅ Manifest repository updated. ArgoCD will auto-deploy."
} }
} }
}
}
post { stage('Pipeline Complete') {
success { echo "🧹 Pipeline completed. Pod cleanup handled by Jenkins Kubernetes Plugin."
echo "✅ Pipeline completed successfully!"
echo "Environment: ${params.ENVIRONMENT}" if (currentBuild.result == null || currentBuild.result == 'SUCCESS') {
echo "Image Tag: ${env.IMAGE_TAG}" echo "✅ Pipeline completed successfully!"
} echo "Environment: ${environment}"
failure { echo "Image Tag: ${imageTag}"
echo "❌ Pipeline failed!" } else {
echo "❌ Pipeline failed with result: ${currentBuild.result}"
}
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
echo "❌ Pipeline failed with exception: ${e.getMessage()}"
throw e
} finally {
echo "🧹 Cleaning up resources and preparing for pod termination..."
echo "Pod will be terminated in 3 seconds due to terminationGracePeriodSeconds: 3"
} }
} }
} }