From 3483c9c1b2ef5a8e4e17bb355e2f2b37b69d08cc Mon Sep 17 00:00:00 2001 From: hjmoons Date: Thu, 30 Oct 2025 18:16:56 +0900 Subject: [PATCH] =?UTF-8?q?Jenkins=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Jenkinsfile: GitHub Actions 대체 Jenkins Pipeline 구축 - 5개 백엔드 서비스 빌드 (user, meeting, stt, ai, notification) - Gradle 빌드 및 SonarQube 분석 (선택사항) - Docker 이미지 빌드 및 ACR 푸시 - Manifest 저장소 업데이트 (ArgoCD 연동) - 환경별 배포 지원 (dev/staging/prod) - deployment/jenkins/JENKINS_SETUP.md: Jenkins 설정 가이드 - Credentials 설정 방법 - JDK 21 및 SonarQube 설정 - Pipeline Job 생성 및 실행 가이드 - 트러블슈팅 가이드 - 사용 이유: GitHub Actions 차단으로 인한 대체 CI/CD 구축 --- Jenkinsfile | 234 ++++++++++++++++++++++++ ai-java-back/.run/ai-service.run.xml | 113 ------------ deployment/jenkins/JENKINS_SETUP.md | 256 +++++++++++++++++++++++++++ stt/.run/stt.run.xml | 90 ---------- 4 files changed, 490 insertions(+), 203 deletions(-) create mode 100644 Jenkinsfile delete mode 100644 ai-java-back/.run/ai-service.run.xml create mode 100644 deployment/jenkins/JENKINS_SETUP.md delete mode 100644 stt/.run/stt.run.xml diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..7f4e4fd --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,234 @@ +pipeline { + agent any + + parameters { + choice( + name: 'ENVIRONMENT', + choices: ['dev', 'staging', 'prod'], + description: 'Target environment' + ) + choice( + name: 'SKIP_SONARQUBE', + choices: ['true', 'false'], + description: 'Skip SonarQube Analysis' + ) + } + + environment { + // Container Registry + REGISTRY = 'acrdigitalgarage02.azurecr.io' + IMAGE_ORG = 'hgzero' + + // Azure + RESOURCE_GROUP = 'rg-digitalgarage-02' + AKS_CLUSTER = 'aks-digitalgarage-02' + NAMESPACE = 'hgzero' + + // Image Tag + IMAGE_TAG = "${new Date().format('yyyyMMddHHmmss')}" + + // Services + SERVICES = 'user meeting stt ai notification' + + // Credentials + ACR_CREDENTIALS = credentials('acr-credentials') + DOCKERHUB_CREDENTIALS = credentials('dockerhub-credentials') + SONAR_TOKEN = credentials('sonar-token') + GIT_CREDENTIALS = credentials('git-credentials') + } + + stages { + stage('Checkout') { + steps { + script { + echo "🔄 Checking out code..." + checkout scm + } + } + } + + stage('Setup Java') { + steps { + script { + echo "☕ Setting up Java 21..." + // Jenkins에 JDK 21이 설정되어 있어야 함 + // Global Tool Configuration에서 'JDK21' 이름으로 설정 + } + } + } + + 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..." + sh 'chmod +x gradlew' + sh './gradlew build -x test' + } + } + } + + stage('SonarQube Analysis') { + when { + expression { params.SKIP_SONARQUBE == 'false' } + } + steps { + script { + echo "🔍 Running SonarQube Analysis..." + + withSonarQubeEnv('SonarQube') { + env.SERVICES.split().each { service -> + echo "Analyzing ${service}..." + sh """ + ./gradlew :${service}:test :${service}:jacocoTestReport :${service}:sonar \ + -Dsonar.projectKey=hgzero-${service}-${params.ENVIRONMENT} \ + -Dsonar.projectName=hgzero-${service}-${params.ENVIRONMENT} \ + -Dsonar.host.url=\${SONAR_HOST_URL} \ + -Dsonar.token=\${SONAR_TOKEN} \ + -Dsonar.java.binaries=build/classes/java/main \ + -Dsonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml \ + -Dsonar.exclusions=**/config/**,**/entity/**,**/dto/**,**/*Application.class,**/exception/** + """ + } + } + } + } + } + + stage('Archive Artifacts') { + steps { + script { + echo "📦 Archiving build artifacts..." + archiveArtifacts artifacts: '**/build/libs/*.jar', fingerprint: true + } + } + } + + stage('Docker Build & Push') { + steps { + script { + echo "🐳 Building and pushing Docker images..." + + // Login to Docker Hub (prevent rate limit) + sh """ + echo ${DOCKERHUB_CREDENTIALS_PSW} | docker login -u ${DOCKERHUB_CREDENTIALS_USR} --password-stdin + """ + + // Login to Azure Container Registry + sh """ + echo ${ACR_CREDENTIALS_PSW} | docker login ${REGISTRY} -u ${ACR_CREDENTIALS_USR} --password-stdin + """ + + // Build and push each service + env.SERVICES.split().each { service -> + echo "Building ${service}..." + + def imageTag = "${env.REGISTRY}/${env.IMAGE_ORG}/${service}:${params.ENVIRONMENT}-${env.IMAGE_TAG}" + + sh """ + docker build \ + --build-arg BUILD_LIB_DIR="${service}/build/libs" \ + --build-arg ARTIFACTORY_FILE="${service}.jar" \ + -f deployment/container/Dockerfile-backend \ + -t ${imageTag} . + """ + + echo "Pushing ${service}..." + sh "docker push ${imageTag}" + + echo "✅ ${service} image pushed: ${imageTag}" + } + } + } + } + + stage('Update Manifest Repository') { + steps { + script { + 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 """ + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + chmod +x kustomize + """ + + // Update manifest + dir("hgzero-back/kustomize/overlays/${params.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 + sh """ + git config user.name "Jenkins" + git config user.email "jenkins@hgzero.com" + git add . + git commit -m "🚀 Update hgzero ${params.ENVIRONMENT} images to ${params.ENVIRONMENT}-${env.IMAGE_TAG}" + git push origin main + """ + } + + echo "✅ Manifest repository updated. ArgoCD will auto-deploy." + } + } + } + } + + post { + success { + echo "✅ Pipeline completed successfully!" + echo "Environment: ${params.ENVIRONMENT}" + echo "Image Tag: ${env.IMAGE_TAG}" + } + failure { + echo "❌ Pipeline failed!" + } + always { + // Clean workspace + cleanWs() + } + } +} diff --git a/ai-java-back/.run/ai-service.run.xml b/ai-java-back/.run/ai-service.run.xml deleted file mode 100644 index 854373b..0000000 --- a/ai-java-back/.run/ai-service.run.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - true - true - - - - - false - false - - - \ No newline at end of file diff --git a/deployment/jenkins/JENKINS_SETUP.md b/deployment/jenkins/JENKINS_SETUP.md new file mode 100644 index 0000000..da375fc --- /dev/null +++ b/deployment/jenkins/JENKINS_SETUP.md @@ -0,0 +1,256 @@ +# Jenkins Pipeline 설정 가이드 + +## 개요 +GitHub Actions가 막혀 있을 때 Jenkins를 사용하여 HGZero 백엔드 서비스를 빌드하고 배포하는 가이드입니다. + +## 전제 조건 + +### 1. Jenkins 서버 요구사항 +- Jenkins 2.x 이상 +- Docker가 설치되어 있어야 함 +- Kustomize CLI 다운로드 가능한 환경 +- 인터넷 연결 (Docker Hub, Azure Container Registry, GitHub 접근) + +### 2. 필수 Jenkins 플러그인 +다음 플러그인들이 설치되어 있어야 합니다: +- Pipeline (기본 포함) +- Git Plugin +- Docker Pipeline Plugin +- SonarQube Scanner Plugin (선택사항) +- Credentials Plugin (기본 포함) + +## Jenkins 설정 단계 + +### 1단계: Credentials 설정 + +Jenkins 관리 → Manage Credentials → Global credentials에서 다음 credential들을 추가합니다: + +#### 1.1 ACR Credentials +- **ID**: `acr-credentials` +- **Type**: Username with password +- **Username**: Azure Container Registry 사용자명 +- **Password**: Azure Container Registry 비밀번호 +- **Description**: Azure Container Registry Credentials + +#### 1.2 Docker Hub Credentials +- **ID**: `dockerhub-credentials` +- **Type**: Username with password +- **Username**: Docker Hub 사용자명 +- **Password**: Docker Hub 비밀번호 또는 Access Token +- **Description**: Docker Hub Credentials (rate limit 방지용) + +#### 1.3 SonarQube Token (선택사항) +- **ID**: `sonar-token` +- **Type**: Secret text +- **Secret**: SonarQube authentication token +- **Description**: SonarQube Token + +#### 1.4 Git Credentials +- **ID**: `git-credentials` +- **Type**: Username with password +- **Username**: GitHub 사용자명 +- **Password**: GitHub Personal Access Token (repo 권한 필요) +- **Description**: GitHub Credentials for Manifest Repository + +> **중요**: GitHub Personal Access Token 생성 시 다음 권한이 필요합니다: +> - `repo` (Full control of private repositories) + +### 2단계: JDK 21 설정 + +Jenkins 관리 → Global Tool Configuration → JDK에서: +1. "Add JDK" 클릭 +2. **Name**: `JDK21` +3. "Install automatically" 체크 +4. Install from adoptium.net 선택 +5. Version: jdk-21.x.x 선택 + +> **참고**: 또는 Jenkins 서버에 JDK 21이 이미 설치되어 있다면, 설치 경로를 지정할 수 있습니다. + +### 3단계: SonarQube 서버 설정 (선택사항) + +SonarQube 분석을 사용하려면: + +Jenkins 관리 → Configure System → SonarQube servers에서: +1. "Add SonarQube" 클릭 +2. **Name**: `SonarQube` +3. **Server URL**: SonarQube 서버 URL +4. **Server authentication token**: 앞서 생성한 `sonar-token` credential 선택 + +### 4단계: Pipeline Job 생성 + +1. Jenkins 메인 화면에서 "New Item" 클릭 +2. Job 이름 입력 (예: `hgzero-backend-pipeline`) +3. "Pipeline" 선택 +4. "OK" 클릭 + +Pipeline 설정: +1. **General** + - "This project is parameterized" 체크 (자동으로 Jenkinsfile의 parameters가 인식됨) + +2. **Pipeline** + - **Definition**: Pipeline script from SCM + - **SCM**: Git + - **Repository URL**: `https://github.com/hjmoons/HGZero.git` (또는 실제 저장소 URL) + - **Credentials**: 필요시 Git credentials 선택 + - **Branch Specifier**: `*/main` (또는 원하는 브랜치) + - **Script Path**: `Jenkinsfile` + +3. "Save" 클릭 + +## Pipeline 실행 + +### 수동 실행 +1. 생성한 Pipeline Job 선택 +2. "Build with Parameters" 클릭 +3. 파라미터 선택: + - **ENVIRONMENT**: `dev`, `staging`, 또는 `prod` 선택 + - **SKIP_SONARQUBE**: `true` (건너뛰기) 또는 `false` (실행) +4. "Build" 클릭 + +### Pipeline 동작 흐름 + +``` +1. Checkout + └─> Git 저장소에서 코드 체크아웃 + +2. Setup Java + └─> JDK 21 환경 설정 + +3. Load Environment Variables + └─> .github/config/deploy_env_vars_{환경} 파일에서 환경변수 로드 + +4. Build with Gradle + └─> ./gradlew build -x test 실행 (JAR 파일 생성) + +5. SonarQube Analysis (선택사항) + └─> 각 서비스별 테스트 및 코드 품질 분석 + +6. Archive Artifacts + └─> 생성된 JAR 파일을 Jenkins에 보관 + +7. Docker Build & Push + └─> 각 서비스별 Docker 이미지 빌드 및 ACR에 푸시 + +8. Update Manifest Repository + └─> hgzero-manifest 저장소의 Kustomize 파일 업데이트 + └─> ArgoCD가 자동으로 배포 감지 +``` + +## 빌드 결과 확인 + +### 성공 시 +- Console Output에 "✅ Pipeline completed successfully!" 메시지 표시 +- Environment와 Image Tag 정보 출력 +- Manifest 저장소가 업데이트됨 +- ArgoCD가 자동으로 새 이미지를 배포 + +### 실패 시 +- Console Output에 "❌ Pipeline failed!" 메시지 표시 +- 실패한 Stage와 에러 메시지 확인 +- Workspace가 자동으로 정리됨 + +## 환경별 설정 + +### Dev 환경 +```bash +ENVIRONMENT: dev +``` +- 개발 환경에 배포 +- 빠른 반복 개발용 + +### Staging 환경 +```bash +ENVIRONMENT: staging +``` +- 스테이징 환경에 배포 +- 프로덕션 배포 전 최종 검증 + +### Production 환경 +```bash +ENVIRONMENT: prod +``` +- 프로덕션 환경에 배포 +- **주의**: 프로덕션 배포는 신중하게 진행 + +## 트러블슈팅 + +### 문제 1: Gradle 빌드 실패 +**증상**: `./gradlew build` 실패 + +**해결방법**: +- Console Output에서 에러 로그 확인 +- Java 버전이 21인지 확인 +- Gradle wrapper 권한 확인: `chmod +x gradlew` + +### 문제 2: Docker 로그인 실패 +**증상**: Docker login 실패 + +**해결방법**: +- Jenkins Credentials에 ACR credentials가 올바르게 설정되었는지 확인 +- Azure Container Registry URL이 `acrdigitalgarage02.azurecr.io`인지 확인 +- Credential ID가 정확히 `acr-credentials`인지 확인 + +### 문제 3: Git clone 실패 (Manifest Repository) +**증상**: hgzero-manifest 저장소 clone 실패 + +**해결방법**: +- GitHub Personal Access Token이 유효한지 확인 +- Token에 `repo` 권한이 있는지 확인 +- Credential ID가 정확히 `git-credentials`인지 확인 + +### 문제 4: Kustomize 설치 실패 +**증상**: Kustomize 다운로드 실패 + +**해결방법**: +- Jenkins 서버에서 인터넷 연결 확인 +- curl 명령어가 사용 가능한지 확인 +- 필요시 Kustomize를 미리 설치하고 PATH에 추가 + +## 보안 고려사항 + +1. **Credentials 관리** + - 모든 비밀번호와 토큰은 Jenkins Credentials로 관리 + - Pipeline 코드에 절대 하드코딩하지 않음 + +2. **Git Credentials** + - Personal Access Token 사용 (비밀번호 사용 금지) + - 최소 권한 원칙 적용 + +3. **Docker Registry** + - ACR 접근은 Service Principal 또는 관리되는 자격 증명 사용 권장 + +## 추가 참고사항 + +### Webhook 설정 (선택사항) +GitHub push 이벤트 시 자동으로 빌드를 트리거하려면: + +1. Jenkins Job → Configure → Build Triggers +2. "GitHub hook trigger for GITScm polling" 체크 +3. GitHub 저장소 → Settings → Webhooks +4. Jenkins URL 추가: `http://{jenkins-url}/github-webhook/` + +### 알림 설정 (선택사항) +빌드 결과를 이메일이나 Slack으로 받으려면: + +- Email: Email Extension Plugin 설치 및 설정 +- Slack: Slack Notification Plugin 설치 및 설정 + +## 지원 서비스 + +현재 Pipeline에서 빌드하는 서비스: +- `user`: 사용자 관리 서비스 +- `meeting`: 회의 관리 서비스 +- `stt`: 음성-텍스트 변환 서비스 +- `ai`: AI 처리 서비스 +- `notification`: 알림 서비스 + +## 관련 파일 + +- `Jenkinsfile`: Pipeline 정의 파일 +- `.github/config/deploy_env_vars_{환경}`: 환경별 변수 파일 +- `deployment/container/Dockerfile-backend`: 백엔드 Dockerfile +- `hgzero-manifest` 저장소: Kubernetes 매니페스트 파일 + +## 문의 + +Pipeline 관련 문제가 발생하면 Jenkins Console Output을 확인하고, 필요시 DevOps 팀에 문의하세요. diff --git a/stt/.run/stt.run.xml b/stt/.run/stt.run.xml deleted file mode 100644 index 598caeb..0000000 --- a/stt/.run/stt.run.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - true - true - - - - - false - false - - - \ No newline at end of file