diff --git a/.github/actions-pipeline-guide.md b/.github/actions-pipeline-guide.md new file mode 100644 index 0000000..8c29dc8 --- /dev/null +++ b/.github/actions-pipeline-guide.md @@ -0,0 +1,500 @@ +# GitHub Actions CI/CD 파이프라인 구축 가이드 + +## 📋 목차 +1. [개요](#개요) +2. [사전 준비사항](#사전-준비사항) +3. [GitHub 저장소 환경 구성](#github-저장소-환경-구성) +4. [디렉토리 구조](#디렉토리-구조) +5. [Kustomize 구조 설명](#kustomize-구조-설명) +6. [GitHub Actions 워크플로우](#github-actions-워크플로우) +7. [배포 방법](#배포-방법) +8. [롤백 방법](#롤백-방법) +9. [SonarQube 설정](#sonarqube-설정) +10. [트러블슈팅](#트러블슈팅) + +--- + +## 개요 + +HGZero 프로젝트의 백엔드 서비스를 위한 GitHub Actions 기반 CI/CD 파이프라인입니다. + +### 주요 기능 +- ✅ Gradle 기반 빌드 및 테스트 +- ✅ SonarQube 코드 품질 분석 (선택적) +- ✅ Azure Container Registry에 Docker 이미지 빌드 및 푸시 +- ✅ Kustomize를 사용한 환경별(dev/staging/prod) 배포 +- ✅ AKS 클러스터 자동 배포 + +### 지원 서비스 +- **user**: 사용자 관리 서비스 +- **meeting**: 회의 관리 서비스 +- **stt**: 음성 인식 서비스 +- **ai**: AI 처리 서비스 +- **notification**: 알림 서비스 + +--- + +## 사전 준비사항 + +### 1. 프로젝트 정보 +- **시스템명**: hgzero +- **ACR 이름**: acrdigitalgarage02 +- **리소스 그룹**: rg-digitalgarage-02 +- **AKS 클러스터**: aks-digitalgarage-02 +- **네임스페이스**: hgzero +- **JDK 버전**: 21 + +### 2. 필수 도구 +- Git +- kubectl +- Azure CLI +- Kustomize (자동 설치됨) + +--- + +## GitHub 저장소 환경 구성 + +### 1. Repository Secrets 설정 + +`Repository Settings > Secrets and variables > Actions > Repository secrets`에 다음 항목을 등록하세요: + +#### Azure 인증 정보 +```json +AZURE_CREDENTIALS: +{ + "clientId": "{클라이언트ID}", + "clientSecret": "{클라이언트시크릿}", + "subscriptionId": "{구독ID}", + "tenantId": "{테넌트ID}" +} +``` + +**예시:** +```json +{ + "clientId": "5e4b5b41-7208-48b7-b821-d6d5acf50ecf", + "clientSecret": "ldu8Q~GQEzFYU.dJX7_QsahR7n7C2xqkIM6hqbV8", + "subscriptionId": "2513dd36-7978-48e3-9a7c-b221d4874f66", + "tenantId": "4f0a3bfd-1156-4cce-8dc2-a049a13dba23" +} +``` + +#### ACR Credentials + +ACR Credential을 확인하려면: +```bash +az acr credential show --name acrdigitalgarage02 +``` + +등록할 Secrets: +``` +ACR_USERNAME: acrdigitalgarage02 +ACR_PASSWORD: {ACR 패스워드} +``` + +#### SonarQube 설정 + +**SONAR_HOST_URL 확인:** +```bash +kubectl get svc -n sonarqube +``` +출력된 External IP를 사용하여 `http://{External IP}` 형식으로 설정 + +**SONAR_TOKEN 생성:** +1. SonarQube에 로그인 (기본: admin/admin) +2. 우측 상단 'Administrator' > My Account 클릭 +3. Security 탭 선택 후 토큰 생성 + +등록할 Secrets: +``` +SONAR_TOKEN: {SonarQube 토큰} +SONAR_HOST_URL: http://{External IP} +``` + +#### Docker Hub (Rate Limit 해결용) + +**패스워드 생성:** +1. [Docker Hub](https://hub.docker.com) 로그인 +2. 우측 상단 프로필 아이콘 > Account Settings +3. 좌측 메뉴 'Personal Access Tokens' 클릭하여 생성 + +등록할 Secrets: +``` +DOCKERHUB_USERNAME: {Docker Hub 사용자명} +DOCKERHUB_PASSWORD: {Docker Hub 패스워드 또는 토큰} +``` + +### 2. Repository Variables 설정 + +`Repository Settings > Secrets and variables > Actions > Variables > Repository variables`에 등록: + +``` +ENVIRONMENT: dev +SKIP_SONARQUBE: true +``` + +--- + +## 디렉토리 구조 + +``` +.github/ +├── workflows/ +│ └── backend-cicd.yaml # GitHub Actions 워크플로우 +├── kustomize/ +│ ├── base/ # 기본 Kubernetes 매니페스트 +│ │ ├── common/ # 공통 리소스 +│ │ │ ├── cm-common.yaml +│ │ │ ├── secret-common.yaml +│ │ │ ├── secret-imagepull.yaml +│ │ │ └── ingress.yaml +│ │ ├── user/ # User 서비스 +│ │ │ ├── deployment.yaml +│ │ │ ├── service.yaml +│ │ │ └── secret-user.yaml +│ │ ├── meeting/ # Meeting 서비스 +│ │ ├── stt/ # STT 서비스 +│ │ ├── ai/ # AI 서비스 +│ │ ├── notification/ # Notification 서비스 +│ │ └── kustomization.yaml +│ └── overlays/ # 환경별 오버레이 +│ ├── dev/ # 개발 환경 +│ │ ├── kustomization.yaml +│ │ ├── cm-common-patch.yaml +│ │ ├── secret-common-patch.yaml +│ │ ├── ingress-patch.yaml +│ │ ├── deployment-{service}-patch.yaml +│ │ └── secret-{service}-patch.yaml +│ ├── staging/ # 스테이징 환경 +│ └── prod/ # 프로덕션 환경 +├── config/ # 환경별 설정 +│ ├── deploy_env_vars_dev +│ ├── deploy_env_vars_staging +│ └── deploy_env_vars_prod +└── scripts/ + └── deploy-actions.sh # 수동 배포 스크립트 +``` + +--- + +## Kustomize 구조 설명 + +### Base 구조 + +Base는 모든 환경에서 공통으로 사용되는 기본 매니페스트입니다. + +**주요 리소스:** +- **ConfigMap (cm-common)**: 환경 변수, 프로파일 설정 +- **Secret (secret-common)**: JWT 시크릿, Redis 패스워드 +- **Ingress**: API 라우팅 규칙 +- **Deployment**: 각 서비스별 배포 설정 +- **Service**: 각 서비스별 ClusterIP 서비스 +- **Secret**: 각 서비스별 데이터베이스 연결 정보 + +### Overlay 구조 + +각 환경(dev/staging/prod)별로 Base를 오버라이드합니다. + +#### DEV 환경 +- **Replicas**: 1 +- **Resources**: CPU 256m-1024m, Memory 256Mi-1024Mi +- **Profile**: dev +- **DDL**: update +- **Log Level**: DEBUG +- **Image Tag**: dev-{timestamp} + +#### STAGING 환경 +- **Replicas**: 2 +- **Resources**: CPU 512m-2048m, Memory 512Mi-2048Mi +- **Profile**: staging +- **DDL**: validate +- **Log Level**: INFO +- **Image Tag**: staging-{timestamp} +- **SSL**: Enabled + +#### PROD 환경 +- **Replicas**: 3 +- **Resources**: CPU 1024m-4096m, Memory 1024Mi-4096Mi +- **Profile**: prod +- **DDL**: validate +- **Log Level**: WARN +- **JWT Expiration**: 짧게 설정 (보안 강화) +- **Image Tag**: prod-{timestamp} +- **SSL**: Enabled + +--- + +## GitHub Actions 워크플로우 + +### 트리거 조건 + +1. **Push 이벤트**: + - 브랜치: `main`, `develop` + - 경로: 서비스 코드 변경 시 (`user/**`, `meeting/**` 등) + +2. **Pull Request**: + - 대상 브랜치: `main` + +3. **수동 실행 (workflow_dispatch)**: + - Environment 선택: dev/staging/prod + - SonarQube 분석 스킵 선택: true/false + +### 워크플로우 단계 + +#### 1. Build Job +1. 소스코드 체크아웃 +2. JDK 21 설정 +3. 환경 결정 (input 또는 기본값 dev) +4. Gradle 빌드 (테스트 제외) +5. SonarQube 분석 (선택적) +6. 빌드 아티팩트 업로드 +7. 이미지 태그 생성 (타임스탬프 기반) + +#### 2. Release Job +1. 빌드 아티팩트 다운로드 +2. Docker Buildx 설정 +3. Docker Hub 로그인 (Rate Limit 방지) +4. ACR 로그인 +5. 각 서비스별 Docker 이미지 빌드 및 푸시 + +#### 3. Deploy Job +1. Azure CLI 설치 및 로그인 +2. kubectl 설정 +3. AKS Credentials 가져오기 +4. 네임스페이스 생성 +5. Kustomize 설치 +6. 이미지 태그 업데이트 +7. 매니페스트 적용 +8. Deployment Ready 대기 + +--- + +## 배포 방법 + +### 1. 자동 배포 (Push/PR) + +코드를 `main` 또는 `develop` 브랜치에 push하면 자동으로 dev 환경에 배포됩니다. + +```bash +git add . +git commit -m "feat: 새로운 기능 추가" +git push origin develop +``` + +### 2. 수동 배포 (GitHub Actions UI) + +1. GitHub Repository > Actions 탭 이동 +2. "Backend Services CI/CD" 워크플로우 선택 +3. "Run workflow" 버튼 클릭 +4. 환경 선택 (dev/staging/prod) +5. SonarQube 분석 스킵 여부 선택 +6. "Run workflow" 실행 + +### 3. 로컬에서 수동 배포 + +```bash +# 스크립트 실행 권한 확인 +chmod +x .github/scripts/deploy-actions.sh + +# DEV 환경에 배포 (기본) +./.github/scripts/deploy-actions.sh dev latest + +# STAGING 환경에 특정 이미지 태그로 배포 +./.github/scripts/deploy-actions.sh staging 20250127120000 + +# PROD 환경에 배포 +./.github/scripts/deploy-actions.sh prod 20250127120000 +``` + +--- + +## 롤백 방법 + +### 1. GitHub Actions를 통한 롤백 + +1. GitHub > Actions > 성공한 이전 워크플로우 선택 +2. "Re-run all jobs" 클릭 +3. 이전 버전으로 재배포됨 + +### 2. kubectl을 이용한 롤백 + +```bash +# 특정 Revision으로 롤백 +kubectl rollout undo deployment/user -n hgzero --to-revision=2 + +# 이전 버전으로 롤백 +kubectl rollout undo deployment/user -n hgzero + +# 롤백 상태 확인 +kubectl rollout status deployment/user -n hgzero + +# Rollout 히스토리 확인 +kubectl rollout history deployment/user -n hgzero +``` + +### 3. 수동 스크립트를 이용한 롤백 + +```bash +# 이전 안정 버전의 이미지 태그로 배포 +./.github/scripts/deploy-actions.sh dev 20250126110000 +``` + +--- + +## SonarQube 설정 + +### 프로젝트 생성 + +각 서비스별로 SonarQube 프로젝트를 생성하세요: +- hgzero-user-dev +- hgzero-meeting-dev +- hgzero-stt-dev +- hgzero-ai-dev +- hgzero-notification-dev + +### Quality Gate 설정 + +기본 Quality Gate 설정: +- **Coverage**: >= 80% +- **Duplicated Lines**: <= 3% +- **Maintainability Rating**: <= A +- **Reliability Rating**: <= A +- **Security Rating**: <= A + +### Gradle 설정 (이미 구성됨) + +```gradle +// build.gradle +plugins { + id 'org.sonarqube' version '4.0.0.2929' + id 'jacoco' +} + +sonarqube { + properties { + property "sonar.projectKey", "hgzero-${project.name}" + property "sonar.projectName", "hgzero-${project.name}" + } +} + +jacocoTestReport { + reports { + xml.enabled true + } +} +``` + +--- + +## 트러블슈팅 + +### 1. 이미지 Pull 실패 + +**증상**: `ImagePullBackOff` 또는 `ErrImagePull` + +**해결 방법**: +```bash +# ACR 로그인 테스트 +az acr login --name acrdigitalgarage02 + +# Image Pull Secret 재생성 +kubectl delete secret acr-secret -n hgzero +kubectl create secret docker-registry acr-secret \ + --docker-server=acrdigitalgarage02.azurecr.io \ + --docker-username=acrdigitalgarage02 \ + --docker-password={ACR_PASSWORD} \ + -n hgzero +``` + +### 2. Deployment 타임아웃 + +**증상**: `deployment "user" exceeded its progress deadline` + +**해결 방법**: +```bash +# Pod 상태 확인 +kubectl get pods -n hgzero + +# Pod 로그 확인 +kubectl logs -n hgzero {pod-name} + +# Events 확인 +kubectl describe pod -n hgzero {pod-name} +``` + +### 3. Health Check 실패 + +**증상**: Deployment가 Ready 상태로 전환되지 않음 + +**해결 방법**: +```bash +# Actuator health 엔드포인트 확인 +kubectl exec -n hgzero {pod-name} -- curl http://localhost:8080/actuator/health + +# 애플리케이션 로그 확인 +kubectl logs -n hgzero {pod-name} -f +``` + +### 4. Kustomize 빌드 오류 + +**증상**: `kustomize build` 실패 + +**해결 방법**: +```bash +# 로컬에서 Kustomize 검증 +kubectl kustomize .github/kustomize/overlays/dev + +# YAML 문법 검증 +yamllint .github/kustomize/overlays/dev/*.yaml +``` + +### 5. SonarQube 연결 실패 + +**증상**: SonarQube Analysis 단계에서 연결 오류 + +**해결 방법**: +1. SONAR_HOST_URL이 올바른지 확인 +2. SONAR_TOKEN이 유효한지 확인 +3. SonarQube 서비스 상태 확인: + ```bash + kubectl get pods -n sonarqube + kubectl logs -n sonarqube {sonarqube-pod} + ``` + +### 6. 환경 변수 로드 실패 + +**증상**: 환경 설정 파일을 찾을 수 없음 + +**해결 방법**: +```bash +# 파일 존재 확인 +ls -la .github/config/ + +# 파일 내용 확인 +cat .github/config/deploy_env_vars_dev +``` + +--- + +## 참고 자료 + +- [Kustomize 공식 문서](https://kustomize.io/) +- [GitHub Actions 문서](https://docs.github.com/en/actions) +- [Azure Container Registry 문서](https://docs.microsoft.com/en-us/azure/container-registry/) +- [AKS 문서](https://docs.microsoft.com/en-us/azure/aks/) +- [SonarQube 문서](https://docs.sonarqube.org/) + +--- + +## 문의 및 지원 + +문제가 발생하거나 질문이 있으시면: +1. GitHub Issues에 등록 +2. DevOps 팀에 문의 (송주영) +3. Slack #devops 채널 + +--- + +**작성일**: 2025-01-27 +**작성자**: DevOps Team (주영) +**버전**: 1.0.0 diff --git a/.github/config/deploy_env_vars_dev b/.github/config/deploy_env_vars_dev new file mode 100644 index 0000000..e45920c --- /dev/null +++ b/.github/config/deploy_env_vars_dev @@ -0,0 +1,3 @@ +# dev Environment Configuration +resource_group=rg-digitalgarage-02 +cluster_name=aks-digitalgarage-02 diff --git a/.github/config/deploy_env_vars_prod b/.github/config/deploy_env_vars_prod new file mode 100644 index 0000000..c2bdc92 --- /dev/null +++ b/.github/config/deploy_env_vars_prod @@ -0,0 +1,3 @@ +# prod Environment Configuration +resource_group=rg-digitalgarage-02 +cluster_name=aks-digitalgarage-02 diff --git a/.github/config/deploy_env_vars_staging b/.github/config/deploy_env_vars_staging new file mode 100644 index 0000000..5fa855c --- /dev/null +++ b/.github/config/deploy_env_vars_staging @@ -0,0 +1,3 @@ +# staging Environment Configuration +resource_group=rg-digitalgarage-02 +cluster_name=aks-digitalgarage-02 diff --git a/.github/kustomize/base/ai/deployment.yaml b/.github/kustomize/base/ai/deployment.yaml new file mode 100644 index 0000000..2e41bc5 --- /dev/null +++ b/.github/kustomize/base/ai/deployment.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ai + labels: + app: ai +spec: + replicas: 1 + selector: + matchLabels: + app: ai + template: + metadata: + labels: + app: ai + spec: + containers: + - name: ai + image: acrdigitalgarage02.azurecr.io/hgzero/ai:latest + ports: + - containerPort: 8080 + protocol: TCP + envFrom: + - configMapRef: + name: cm-common + - secretRef: + name: secret-common + env: + - name: DB_URL + valueFrom: + secretKeyRef: + name: secret-ai + key: DB_URL + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: secret-ai + key: DB_USERNAME + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: secret-ai + key: DB_PASSWORD + resources: + requests: + cpu: 256m + memory: 256Mi + limits: + cpu: 1024m + memory: 1024Mi + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/.github/kustomize/base/ai/secret-ai.yaml b/.github/kustomize/base/ai/secret-ai.yaml new file mode 100644 index 0000000..7df0040 --- /dev/null +++ b/.github/kustomize/base/ai/secret-ai.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-ai + labels: + app: ai +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-service:5432/aidb" + DB_USERNAME: "aiuser" + DB_PASSWORD: "aipass123" diff --git a/.github/kustomize/base/ai/service.yaml b/.github/kustomize/base/ai/service.yaml new file mode 100644 index 0000000..a287430 --- /dev/null +++ b/.github/kustomize/base/ai/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: ai + labels: + app: ai +spec: + type: ClusterIP + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: ai diff --git a/.github/kustomize/base/common/cm-common.yaml b/.github/kustomize/base/common/cm-common.yaml new file mode 100644 index 0000000..1525bfc --- /dev/null +++ b/.github/kustomize/base/common/cm-common.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm-common +data: + # Spring Profiles + SPRING_PROFILES_ACTIVE: "dev" + + # Database Configuration + DDL_AUTO: "update" + SHOW_SQL: "true" + + # JWT Configuration + JWT_ACCESS_TOKEN_EXPIRATION: "3600000" # 1 hour + JWT_REFRESH_TOKEN_EXPIRATION: "86400000" # 24 hours + + # Logging Configuration + LOG_LEVEL: "INFO" + + # Application Configuration + SERVER_PORT: "8080" + + # Redis Configuration + REDIS_HOST: "redis-service" + REDIS_PORT: "6379" diff --git a/.github/kustomize/base/common/ingress.yaml b/.github/kustomize/base/common/ingress.yaml new file mode 100644 index 0000000..6ac515e --- /dev/null +++ b/.github/kustomize/base/common/ingress.yaml @@ -0,0 +1,48 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hgzero + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/ssl-redirect: "false" +spec: + ingressClassName: nginx + rules: + - host: hgzero-api.20.214.196.128.nip.io + http: + paths: + - path: /api/users + pathType: Prefix + backend: + service: + name: user + port: + number: 8080 + - path: /api/meetings + pathType: Prefix + backend: + service: + name: meeting + port: + number: 8080 + - path: /api/stt + pathType: Prefix + backend: + service: + name: stt + port: + number: 8080 + - path: /api/ai + pathType: Prefix + backend: + service: + name: ai + port: + number: 8080 + - path: /api/notifications + pathType: Prefix + backend: + service: + name: notification + port: + number: 8080 diff --git a/.github/kustomize/base/common/secret-common.yaml b/.github/kustomize/base/common/secret-common.yaml new file mode 100644 index 0000000..1c231d0 --- /dev/null +++ b/.github/kustomize/base/common/secret-common.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-common +type: Opaque +stringData: + # JWT Secret Key (Base64 encoded in production) + JWT_SECRET_KEY: "hgzero-jwt-secret-key-change-in-production" + + # Redis Password + REDIS_PASSWORD: "redis-password-change-in-production" diff --git a/.github/kustomize/base/common/secret-imagepull.yaml b/.github/kustomize/base/common/secret-imagepull.yaml new file mode 100644 index 0000000..0a4ff55 --- /dev/null +++ b/.github/kustomize/base/common/secret-imagepull.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: acr-secret +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: e30K diff --git a/.github/kustomize/base/kustomization.yaml b/.github/kustomize/base/kustomization.yaml new file mode 100644 index 0000000..076e551 --- /dev/null +++ b/.github/kustomize/base/kustomization.yaml @@ -0,0 +1,49 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +metadata: + name: hgzero-base + +resources: + # Common resources + - common/cm-common.yaml + - common/secret-common.yaml + - common/secret-imagepull.yaml + - common/ingress.yaml + + # User service + - user/deployment.yaml + - user/service.yaml + - user/secret-user.yaml + + # Meeting service + - meeting/deployment.yaml + - meeting/service.yaml + - meeting/secret-meeting.yaml + + # STT service + - stt/deployment.yaml + - stt/service.yaml + - stt/secret-stt.yaml + + # AI service + - ai/deployment.yaml + - ai/service.yaml + - ai/secret-ai.yaml + + # Notification service + - notification/deployment.yaml + - notification/service.yaml + - notification/secret-notification.yaml + +images: + - name: acrdigitalgarage02.azurecr.io/hgzero/user + newTag: latest + - name: acrdigitalgarage02.azurecr.io/hgzero/meeting + newTag: latest + - name: acrdigitalgarage02.azurecr.io/hgzero/stt + newTag: latest + - name: acrdigitalgarage02.azurecr.io/hgzero/ai + newTag: latest + - name: acrdigitalgarage02.azurecr.io/hgzero/notification + newTag: latest diff --git a/.github/kustomize/base/meeting/deployment.yaml b/.github/kustomize/base/meeting/deployment.yaml new file mode 100644 index 0000000..cdafa45 --- /dev/null +++ b/.github/kustomize/base/meeting/deployment.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meeting + labels: + app: meeting +spec: + replicas: 1 + selector: + matchLabels: + app: meeting + template: + metadata: + labels: + app: meeting + spec: + containers: + - name: meeting + image: acrdigitalgarage02.azurecr.io/hgzero/meeting:latest + ports: + - containerPort: 8080 + protocol: TCP + envFrom: + - configMapRef: + name: cm-common + - secretRef: + name: secret-common + env: + - name: DB_URL + valueFrom: + secretKeyRef: + name: secret-meeting + key: DB_URL + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: secret-meeting + key: DB_USERNAME + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: secret-meeting + key: DB_PASSWORD + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/.github/kustomize/base/meeting/secret-meeting.yaml b/.github/kustomize/base/meeting/secret-meeting.yaml new file mode 100644 index 0000000..c2a4084 --- /dev/null +++ b/.github/kustomize/base/meeting/secret-meeting.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-meeting +type: Opaque +stringData: + # Meeting Service Database Configuration (Development) + DB_URL: "jdbc:postgresql://postgres-meeting:5432/meeting" + DB_USERNAME: "meeting_user" + DB_PASSWORD: "meeting_password" diff --git a/.github/kustomize/base/meeting/service.yaml b/.github/kustomize/base/meeting/service.yaml new file mode 100644 index 0000000..1200fb7 --- /dev/null +++ b/.github/kustomize/base/meeting/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: meeting + labels: + app: meeting +spec: + type: ClusterIP + selector: + app: meeting + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http diff --git a/.github/kustomize/base/notification/deployment.yaml b/.github/kustomize/base/notification/deployment.yaml new file mode 100644 index 0000000..080e70c --- /dev/null +++ b/.github/kustomize/base/notification/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: notification + labels: + app: notification + system: hgzero +spec: + replicas: 1 + selector: + matchLabels: + app: notification + template: + metadata: + labels: + app: notification + system: hgzero + spec: + containers: + - name: notification + image: acrdigitalgarage02.azurecr.io/hgzero/notification:latest + ports: + - containerPort: 8080 + protocol: TCP + envFrom: + - configMapRef: + name: cm-common + - secretRef: + name: secret-common + env: + - name: DB_URL + valueFrom: + secretKeyRef: + name: secret-notification + key: DB_URL + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: secret-notification + key: DB_USERNAME + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: secret-notification + key: DB_PASSWORD + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/.github/kustomize/base/notification/secret-notification.yaml b/.github/kustomize/base/notification/secret-notification.yaml new file mode 100644 index 0000000..6c5cfa2 --- /dev/null +++ b/.github/kustomize/base/notification/secret-notification.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-notification + labels: + app: notification + system: hgzero +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-service:5432/notification_db" + DB_USERNAME: "notification_user" + DB_PASSWORD: "notification_pass" diff --git a/.github/kustomize/base/notification/service.yaml b/.github/kustomize/base/notification/service.yaml new file mode 100644 index 0000000..d123457 --- /dev/null +++ b/.github/kustomize/base/notification/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: notification + labels: + app: notification + system: hgzero +spec: + type: ClusterIP + selector: + app: notification + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http diff --git a/.github/kustomize/base/stt/deployment.yaml b/.github/kustomize/base/stt/deployment.yaml new file mode 100644 index 0000000..b14c27c --- /dev/null +++ b/.github/kustomize/base/stt/deployment.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: stt + labels: + app: stt +spec: + replicas: 1 + selector: + matchLabels: + app: stt + template: + metadata: + labels: + app: stt + spec: + containers: + - name: stt + image: acrdigitalgarage02.azurecr.io/hgzero/stt:latest + ports: + - containerPort: 8080 + protocol: TCP + envFrom: + - configMapRef: + name: cm-common + - secretRef: + name: secret-common + env: + - name: DB_URL + valueFrom: + secretKeyRef: + name: secret-stt + key: DB_URL + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: secret-stt + key: DB_USERNAME + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: secret-stt + key: DB_PASSWORD + resources: + requests: + cpu: 256m + memory: 256Mi + limits: + cpu: 1024m + memory: 1024Mi + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 diff --git a/.github/kustomize/base/stt/secret-stt.yaml b/.github/kustomize/base/stt/secret-stt.yaml new file mode 100644 index 0000000..0d8825a --- /dev/null +++ b/.github/kustomize/base/stt/secret-stt.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-stt +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-service:5432/sttdb" + DB_USERNAME: "sttuser" + DB_PASSWORD: "sttpass" diff --git a/.github/kustomize/base/stt/service.yaml b/.github/kustomize/base/stt/service.yaml new file mode 100644 index 0000000..29c99d0 --- /dev/null +++ b/.github/kustomize/base/stt/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: stt + labels: + app: stt +spec: + type: ClusterIP + selector: + app: stt + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http diff --git a/.github/kustomize/base/user/deployment.yaml b/.github/kustomize/base/user/deployment.yaml new file mode 100644 index 0000000..21d1469 --- /dev/null +++ b/.github/kustomize/base/user/deployment.yaml @@ -0,0 +1,66 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user + labels: + app: user +spec: + replicas: 1 + selector: + matchLabels: + app: user + template: + metadata: + labels: + app: user + spec: + containers: + - name: user + image: acrdigitalgarage02.azurecr.io/hgzero/user:latest + ports: + - containerPort: 8080 + protocol: TCP + envFrom: + - configMapRef: + name: cm-common + - secretRef: + name: secret-common + env: + - name: DB_URL + valueFrom: + secretKeyRef: + name: secret-user + key: DB_URL + - name: DB_USERNAME + valueFrom: + secretKeyRef: + name: secret-user + key: DB_USERNAME + - name: DB_PASSWORD + valueFrom: + secretKeyRef: + name: secret-user + key: DB_PASSWORD + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" + livenessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 diff --git a/.github/kustomize/base/user/secret-user.yaml b/.github/kustomize/base/user/secret-user.yaml new file mode 100644 index 0000000..8dcd240 --- /dev/null +++ b/.github/kustomize/base/user/secret-user.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-user + labels: + app: user +type: Opaque +stringData: + DB_URL: "jdbc:mysql://mysql-user:3306/userdb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" + DB_USERNAME: "user" + DB_PASSWORD: "user1234" diff --git a/.github/kustomize/base/user/service.yaml b/.github/kustomize/base/user/service.yaml new file mode 100644 index 0000000..4e32d39 --- /dev/null +++ b/.github/kustomize/base/user/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: user + labels: + app: user +spec: + type: ClusterIP + selector: + app: user + ports: + - port: 8080 + targetPort: 8080 + protocol: TCP + name: http diff --git a/.github/kustomize/overlays/dev/cm-common-patch.yaml b/.github/kustomize/overlays/dev/cm-common-patch.yaml new file mode 100644 index 0000000..4047dd8 --- /dev/null +++ b/.github/kustomize/overlays/dev/cm-common-patch.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm-common +data: + # Spring Profiles + SPRING_PROFILES_ACTIVE: "dev" + + # Database Configuration + DDL_AUTO: "update" + SHOW_SQL: "true" + + # JWT Configuration + JWT_ACCESS_TOKEN_EXPIRATION: "3600000" # 1 hour + JWT_REFRESH_TOKEN_EXPIRATION: "86400000" # 24 hours + + # Logging Configuration + LOG_LEVEL: "DEBUG" diff --git a/.github/kustomize/overlays/dev/deployment-ai-patch.yaml b/.github/kustomize/overlays/dev/deployment-ai-patch.yaml new file mode 100644 index 0000000..5c37383 --- /dev/null +++ b/.github/kustomize/overlays/dev/deployment-ai-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ai +spec: + replicas: 1 + template: + spec: + containers: + - name: ai + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" diff --git a/.github/kustomize/overlays/dev/deployment-meeting-patch.yaml b/.github/kustomize/overlays/dev/deployment-meeting-patch.yaml new file mode 100644 index 0000000..797ff08 --- /dev/null +++ b/.github/kustomize/overlays/dev/deployment-meeting-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meeting +spec: + replicas: 1 + template: + spec: + containers: + - name: meeting + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" diff --git a/.github/kustomize/overlays/dev/deployment-notification-patch.yaml b/.github/kustomize/overlays/dev/deployment-notification-patch.yaml new file mode 100644 index 0000000..ed4cea8 --- /dev/null +++ b/.github/kustomize/overlays/dev/deployment-notification-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: notification +spec: + replicas: 1 + template: + spec: + containers: + - name: notification + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" diff --git a/.github/kustomize/overlays/dev/deployment-stt-patch.yaml b/.github/kustomize/overlays/dev/deployment-stt-patch.yaml new file mode 100644 index 0000000..f9ad444 --- /dev/null +++ b/.github/kustomize/overlays/dev/deployment-stt-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: stt +spec: + replicas: 1 + template: + spec: + containers: + - name: stt + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" diff --git a/.github/kustomize/overlays/dev/deployment-user-patch.yaml b/.github/kustomize/overlays/dev/deployment-user-patch.yaml new file mode 100644 index 0000000..1db2284 --- /dev/null +++ b/.github/kustomize/overlays/dev/deployment-user-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user +spec: + replicas: 1 + template: + spec: + containers: + - name: user + resources: + requests: + cpu: "256m" + memory: "256Mi" + limits: + cpu: "1024m" + memory: "1024Mi" diff --git a/.github/kustomize/overlays/dev/ingress-patch.yaml b/.github/kustomize/overlays/dev/ingress-patch.yaml new file mode 100644 index 0000000..6ac515e --- /dev/null +++ b/.github/kustomize/overlays/dev/ingress-patch.yaml @@ -0,0 +1,48 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hgzero + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/ssl-redirect: "false" +spec: + ingressClassName: nginx + rules: + - host: hgzero-api.20.214.196.128.nip.io + http: + paths: + - path: /api/users + pathType: Prefix + backend: + service: + name: user + port: + number: 8080 + - path: /api/meetings + pathType: Prefix + backend: + service: + name: meeting + port: + number: 8080 + - path: /api/stt + pathType: Prefix + backend: + service: + name: stt + port: + number: 8080 + - path: /api/ai + pathType: Prefix + backend: + service: + name: ai + port: + number: 8080 + - path: /api/notifications + pathType: Prefix + backend: + service: + name: notification + port: + number: 8080 diff --git a/.github/kustomize/overlays/dev/kustomization.yaml b/.github/kustomize/overlays/dev/kustomization.yaml new file mode 100644 index 0000000..edb6bc5 --- /dev/null +++ b/.github/kustomize/overlays/dev/kustomization.yaml @@ -0,0 +1,84 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: hgzero + +resources: + - ../../base + +patches: + # Common patches + - path: cm-common-patch.yaml + target: + kind: ConfigMap + name: cm-common + - path: secret-common-patch.yaml + target: + kind: Secret + name: secret-common + - path: ingress-patch.yaml + target: + kind: Ingress + name: hgzero + + # User service patches + - path: deployment-user-patch.yaml + target: + kind: Deployment + name: user + - path: secret-user-patch.yaml + target: + kind: Secret + name: secret-user + + # Meeting service patches + - path: deployment-meeting-patch.yaml + target: + kind: Deployment + name: meeting + - path: secret-meeting-patch.yaml + target: + kind: Secret + name: secret-meeting + + # STT service patches + - path: deployment-stt-patch.yaml + target: + kind: Deployment + name: stt + - path: secret-stt-patch.yaml + target: + kind: Secret + name: secret-stt + + # AI service patches + - path: deployment-ai-patch.yaml + target: + kind: Deployment + name: ai + - path: secret-ai-patch.yaml + target: + kind: Secret + name: secret-ai + + # Notification service patches + - path: deployment-notification-patch.yaml + target: + kind: Deployment + name: notification + - path: secret-notification-patch.yaml + target: + kind: Secret + name: secret-notification + +images: + - name: acrdigitalgarage02.azurecr.io/hgzero/user + newTag: dev-latest + - name: acrdigitalgarage02.azurecr.io/hgzero/meeting + newTag: dev-latest + - name: acrdigitalgarage02.azurecr.io/hgzero/stt + newTag: dev-latest + - name: acrdigitalgarage02.azurecr.io/hgzero/ai + newTag: dev-latest + - name: acrdigitalgarage02.azurecr.io/hgzero/notification + newTag: dev-latest diff --git a/.github/kustomize/overlays/dev/secret-ai-patch.yaml b/.github/kustomize/overlays/dev/secret-ai-patch.yaml new file mode 100644 index 0000000..e99705c --- /dev/null +++ b/.github/kustomize/overlays/dev/secret-ai-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-ai + labels: + app: ai +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-service-dev:5432/aidb_dev" + DB_USERNAME: "aiuser_dev" + DB_PASSWORD: "aipass_dev123" diff --git a/.github/kustomize/overlays/dev/secret-common-patch.yaml b/.github/kustomize/overlays/dev/secret-common-patch.yaml new file mode 100644 index 0000000..7ec3e2f --- /dev/null +++ b/.github/kustomize/overlays/dev/secret-common-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-common +type: Opaque +stringData: + # JWT Secret Key (개발용) + JWT_SECRET_KEY: "hgzero-jwt-secret-key-change-in-production" + + # Redis Password (개발용) + REDIS_PASSWORD: "redis-password-change-in-production" diff --git a/.github/kustomize/overlays/dev/secret-meeting-patch.yaml b/.github/kustomize/overlays/dev/secret-meeting-patch.yaml new file mode 100644 index 0000000..aa64ac7 --- /dev/null +++ b/.github/kustomize/overlays/dev/secret-meeting-patch.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-meeting +type: Opaque +stringData: + # Meeting Service Database Configuration (Development) + DB_URL: "jdbc:postgresql://postgres-meeting-dev:5432/meeting_dev" + DB_USERNAME: "meeting_dev_user" + DB_PASSWORD: "meeting_dev_password" diff --git a/.github/kustomize/overlays/dev/secret-notification-patch.yaml b/.github/kustomize/overlays/dev/secret-notification-patch.yaml new file mode 100644 index 0000000..36a7b59 --- /dev/null +++ b/.github/kustomize/overlays/dev/secret-notification-patch.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-notification + labels: + app: notification + system: hgzero +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-service-dev:5432/notification_db_dev" + DB_USERNAME: "notification_dev_user" + DB_PASSWORD: "notification_dev_pass" diff --git a/.github/kustomize/overlays/dev/secret-stt-patch.yaml b/.github/kustomize/overlays/dev/secret-stt-patch.yaml new file mode 100644 index 0000000..1d0c9e6 --- /dev/null +++ b/.github/kustomize/overlays/dev/secret-stt-patch.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-stt +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-service-dev:5432/sttdb_dev" + DB_USERNAME: "sttuser_dev" + DB_PASSWORD: "sttpass_dev" diff --git a/.github/kustomize/overlays/dev/secret-user-patch.yaml b/.github/kustomize/overlays/dev/secret-user-patch.yaml new file mode 100644 index 0000000..be99004 --- /dev/null +++ b/.github/kustomize/overlays/dev/secret-user-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-user + labels: + app: user +type: Opaque +stringData: + DB_URL: "jdbc:mysql://mysql-user-dev:3306/userdb_dev?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true" + DB_USERNAME: "user_dev" + DB_PASSWORD: "user_dev1234" diff --git a/.github/kustomize/overlays/prod/cm-common-patch.yaml b/.github/kustomize/overlays/prod/cm-common-patch.yaml new file mode 100644 index 0000000..5874c91 --- /dev/null +++ b/.github/kustomize/overlays/prod/cm-common-patch.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: common-config + namespace: hgzero +data: + SPRING_PROFILES_ACTIVE: "prod" + DDL_AUTO: "validate" + LOG_LEVEL: "WARN" + JWT_ACCESS_TOKEN_EXPIRATION: "1800000" + JWT_REFRESH_TOKEN_EXPIRATION: "43200000" + REDIS_HOST: "redis-svc.hgzero.svc.cluster.local" + REDIS_PORT: "6379" + KAFKA_BOOTSTRAP_SERVERS: "kafka-svc.hgzero.svc.cluster.local:9092" diff --git a/.github/kustomize/overlays/prod/deployment-ai-patch.yaml b/.github/kustomize/overlays/prod/deployment-ai-patch.yaml new file mode 100644 index 0000000..7fa27c9 --- /dev/null +++ b/.github/kustomize/overlays/prod/deployment-ai-patch.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ai-deploy + namespace: hgzero +spec: + replicas: 3 + template: + spec: + containers: + - name: ai-container + resources: + requests: + cpu: "1024m" + memory: "1024Mi" + limits: + cpu: "4096m" + memory: "4096Mi" diff --git a/.github/kustomize/overlays/prod/deployment-meeting-patch.yaml b/.github/kustomize/overlays/prod/deployment-meeting-patch.yaml new file mode 100644 index 0000000..11fcfeb --- /dev/null +++ b/.github/kustomize/overlays/prod/deployment-meeting-patch.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meeting-deploy + namespace: hgzero +spec: + replicas: 3 + template: + spec: + containers: + - name: meeting-container + resources: + requests: + cpu: "1024m" + memory: "1024Mi" + limits: + cpu: "4096m" + memory: "4096Mi" diff --git a/.github/kustomize/overlays/prod/deployment-notification-patch.yaml b/.github/kustomize/overlays/prod/deployment-notification-patch.yaml new file mode 100644 index 0000000..b832ef2 --- /dev/null +++ b/.github/kustomize/overlays/prod/deployment-notification-patch.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: notification-deploy + namespace: hgzero +spec: + replicas: 3 + template: + spec: + containers: + - name: notification-container + resources: + requests: + cpu: "1024m" + memory: "1024Mi" + limits: + cpu: "4096m" + memory: "4096Mi" diff --git a/.github/kustomize/overlays/prod/deployment-stt-patch.yaml b/.github/kustomize/overlays/prod/deployment-stt-patch.yaml new file mode 100644 index 0000000..1dc8fb7 --- /dev/null +++ b/.github/kustomize/overlays/prod/deployment-stt-patch.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: stt-deploy + namespace: hgzero +spec: + replicas: 3 + template: + spec: + containers: + - name: stt-container + resources: + requests: + cpu: "1024m" + memory: "1024Mi" + limits: + cpu: "4096m" + memory: "4096Mi" diff --git a/.github/kustomize/overlays/prod/deployment-user-patch.yaml b/.github/kustomize/overlays/prod/deployment-user-patch.yaml new file mode 100644 index 0000000..5eddde0 --- /dev/null +++ b/.github/kustomize/overlays/prod/deployment-user-patch.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-deploy + namespace: hgzero +spec: + replicas: 3 + template: + spec: + containers: + - name: user-container + resources: + requests: + cpu: "1024m" + memory: "1024Mi" + limits: + cpu: "4096m" + memory: "4096Mi" diff --git a/.github/kustomize/overlays/prod/ingress-patch.yaml b/.github/kustomize/overlays/prod/ingress-patch.yaml new file mode 100644 index 0000000..96b2659 --- /dev/null +++ b/.github/kustomize/overlays/prod/ingress-patch.yaml @@ -0,0 +1,49 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hgzero-ingress + namespace: hgzero + annotations: + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + ingressClassName: nginx + rules: + - host: hgzero-api.example.com + http: + paths: + - path: /user(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: user-svc + port: + number: 8080 + - path: /meeting(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: meeting-svc + port: + number: 8081 + - path: /stt(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: stt-svc + port: + number: 8082 + - path: /ai(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: ai-svc + port: + number: 8083 + - path: /notification(/|$)(.*) + pathType: ImplementationSpecific + backend: + service: + name: notification-svc + port: + number: 8084 diff --git a/.github/kustomize/overlays/prod/kustomization.yaml b/.github/kustomize/overlays/prod/kustomization.yaml new file mode 100644 index 0000000..134c306 --- /dev/null +++ b/.github/kustomize/overlays/prod/kustomization.yaml @@ -0,0 +1,84 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: hgzero + +bases: + - ../../base + +patches: + # Common patches + - path: cm-common-patch.yaml + target: + kind: ConfigMap + name: common-config + - path: secret-common-patch.yaml + target: + kind: Secret + name: common-secret + - path: ingress-patch.yaml + target: + kind: Ingress + name: hgzero-ingress + + # User service patches + - path: deployment-user-patch.yaml + target: + kind: Deployment + name: user-deploy + - path: secret-user-patch.yaml + target: + kind: Secret + name: user-secret + + # Meeting service patches + - path: deployment-meeting-patch.yaml + target: + kind: Deployment + name: meeting-deploy + - path: secret-meeting-patch.yaml + target: + kind: Secret + name: meeting-secret + + # STT service patches + - path: deployment-stt-patch.yaml + target: + kind: Deployment + name: stt-deploy + - path: secret-stt-patch.yaml + target: + kind: Secret + name: stt-secret + + # AI service patches + - path: deployment-ai-patch.yaml + target: + kind: Deployment + name: ai-deploy + - path: secret-ai-patch.yaml + target: + kind: Secret + name: ai-secret + + # Notification service patches + - path: deployment-notification-patch.yaml + target: + kind: Deployment + name: notification-deploy + - path: secret-notification-patch.yaml + target: + kind: Secret + name: notification-secret + +images: + - name: user-service + newTag: prod-latest + - name: meeting-service + newTag: prod-latest + - name: stt-service + newTag: prod-latest + - name: ai-service + newTag: prod-latest + - name: notification-service + newTag: prod-latest diff --git a/.github/kustomize/overlays/prod/secret-ai-patch.yaml b/.github/kustomize/overlays/prod/secret-ai-patch.yaml new file mode 100644 index 0000000..bf17d50 --- /dev/null +++ b/.github/kustomize/overlays/prod/secret-ai-patch.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ai-secret + namespace: hgzero +type: Opaque +stringData: + OPENAI_API_KEY: "your-openai-api-key" + LANGCHAIN_API_KEY: "your-langchain-api-key" diff --git a/.github/kustomize/overlays/prod/secret-common-patch.yaml b/.github/kustomize/overlays/prod/secret-common-patch.yaml new file mode 100644 index 0000000..5d14280 --- /dev/null +++ b/.github/kustomize/overlays/prod/secret-common-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: common-secret + namespace: hgzero +type: Opaque +stringData: + JWT_SECRET_KEY: "your-prod-secret-key-change-this-in-production" + REDIS_PASSWORD: "your-prod-redis-password" + KAFKA_USERNAME: "admin" + KAFKA_PASSWORD: "admin-secret" diff --git a/.github/kustomize/overlays/prod/secret-meeting-patch.yaml b/.github/kustomize/overlays/prod/secret-meeting-patch.yaml new file mode 100644 index 0000000..5ad2f33 --- /dev/null +++ b/.github/kustomize/overlays/prod/secret-meeting-patch.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Secret +metadata: + name: meeting-secret + namespace: hgzero +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-prod:5432/meeting_prod" + DB_USERNAME: "meeting_admin" + DB_PASSWORD: "meeting_prod_password" + OPENAI_API_KEY: "your-openai-api-key" + S3_ACCESS_KEY: "your-s3-access-key" + S3_SECRET_KEY: "your-s3-secret-key" diff --git a/.github/kustomize/overlays/prod/secret-notification-patch.yaml b/.github/kustomize/overlays/prod/secret-notification-patch.yaml new file mode 100644 index 0000000..22f448b --- /dev/null +++ b/.github/kustomize/overlays/prod/secret-notification-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: notification-secret + namespace: hgzero +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-prod:5432/notification_prod" + DB_USERNAME: "notification_admin" + DB_PASSWORD: "notification_prod_password" + OPENAI_API_KEY: "your-openai-api-key" diff --git a/.github/kustomize/overlays/prod/secret-stt-patch.yaml b/.github/kustomize/overlays/prod/secret-stt-patch.yaml new file mode 100644 index 0000000..acc37b7 --- /dev/null +++ b/.github/kustomize/overlays/prod/secret-stt-patch.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: stt-secret + namespace: hgzero +type: Opaque +stringData: + OPENAI_API_KEY: "your-openai-api-key" + S3_ACCESS_KEY: "your-s3-access-key" + S3_SECRET_KEY: "your-s3-secret-key" diff --git a/.github/kustomize/overlays/prod/secret-user-patch.yaml b/.github/kustomize/overlays/prod/secret-user-patch.yaml new file mode 100644 index 0000000..2f14bb1 --- /dev/null +++ b/.github/kustomize/overlays/prod/secret-user-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: user-secret + namespace: hgzero +type: Opaque +stringData: + DB_URL: "jdbc:postgresql://postgres-prod:5432/user_prod" + DB_USERNAME: "user_admin" + DB_PASSWORD: "user_prod_password" + OPENAI_API_KEY: "your-openai-api-key" diff --git a/.github/kustomize/overlays/staging/README.md b/.github/kustomize/overlays/staging/README.md new file mode 100644 index 0000000..5c034eb --- /dev/null +++ b/.github/kustomize/overlays/staging/README.md @@ -0,0 +1,99 @@ +# STAGING Environment Kustomize Overlay + +## 개요 +STAGING 환경을 위한 Kustomize overlay 설정입니다. + +## 환경 설정 + +### 네임스페이스 +- `hgzero` + +### 공통 설정 +- **프로파일**: staging +- **DDL 모드**: validate +- **로그 레벨**: INFO +- **Ingress 호스트**: hgzero-staging-api.example.com +- **SSL 리다이렉트**: 활성화 + +### 리소스 설정 +- **Replicas**: 2 +- **Resource Requests**: + - CPU: 512m + - Memory: 512Mi +- **Resource Limits**: + - CPU: 2048m + - Memory: 2048Mi + +## 생성된 파일 목록 (총 14개) + +### Common Patches (3개) +1. `cm-common-patch.yaml` - 공통 ConfigMap 패치 +2. `secret-common-patch.yaml` - 공통 Secret 패치 (JWT, Redis) +3. `ingress-patch.yaml` - Ingress 패치 (호스트, SSL) + +### Service-specific Patches (10개) +각 서비스(user, meeting, stt, ai, notification)별 2개 파일: +- `deployment-{서비스명}-patch.yaml` - Deployment 리소스 패치 +- `secret-{서비스명}-patch.yaml` - DB 연결 정보 패치 + +4. `deployment-user-patch.yaml` +5. `secret-user-patch.yaml` +6. `deployment-meeting-patch.yaml` +7. `secret-meeting-patch.yaml` +8. `deployment-stt-patch.yaml` +9. `secret-stt-patch.yaml` +10. `deployment-ai-patch.yaml` +11. `secret-ai-patch.yaml` +12. `deployment-notification-patch.yaml` +13. `secret-notification-patch.yaml` + +### Kustomization (1개) +14. `kustomization.yaml` - Kustomize 설정 파일 + +## 데이터베이스 설정 + +각 서비스별 STAGING 환경 DB 정보: +- **호스트**: {서비스명}-db-staging +- **포트**: 5432 +- **데이터베이스명**: {서비스명}_db_staging +- **사용자명**: {서비스명}_service +- **비밀번호**: stringData로 정의 (실제 환경에서 변경 필요) + +## 이미지 태그 +모든 서비스: `staging-latest` + +## 사용 방법 + +### 1. Kustomize 빌드 확인 +```bash +kubectl kustomize .github/kustomize/overlays/staging +``` + +### 2. STAGING 환경 배포 +```bash +kubectl apply -k .github/kustomize/overlays/staging +``` + +### 3. 배포 상태 확인 +```bash +kubectl get all -n hgzero +``` + +### 4. Secret 업데이트 (실제 배포 시) +```bash +# Secret 파일들의 stringData를 실제 STAGING 환경 값으로 변경 +vi .github/kustomize/overlays/staging/secret-common-patch.yaml +vi .github/kustomize/overlays/staging/secret-user-patch.yaml +# ... (각 서비스별 secret 파일 수정) +``` + +## 주의사항 +1. Secret 파일들의 비밀번호는 반드시 실제 환경에 맞게 변경해야 합니다 +2. Ingress 호스트명을 실제 STAGING 도메인으로 변경해야 합니다 +3. DB 호스트명이 실제 STAGING 환경과 일치하는지 확인해야 합니다 +4. 리소스 제한은 실제 부하 테스트 결과에 따라 조정이 필요할 수 있습니다 + +## 다음 단계 +- PROD 환경 overlay 생성 +- CI/CD 파이프라인과 통합 +- Monitoring 및 Logging 설정 추가 diff --git a/.github/kustomize/overlays/staging/cm-common-patch.yaml b/.github/kustomize/overlays/staging/cm-common-patch.yaml new file mode 100644 index 0000000..c859d70 --- /dev/null +++ b/.github/kustomize/overlays/staging/cm-common-patch.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: hgzero-common-config +data: + SPRING_PROFILES_ACTIVE: "staging" + DDL_AUTO: "validate" + LOG_LEVEL: "INFO" diff --git a/.github/kustomize/overlays/staging/deployment-ai-patch.yaml b/.github/kustomize/overlays/staging/deployment-ai-patch.yaml new file mode 100644 index 0000000..f63445c --- /dev/null +++ b/.github/kustomize/overlays/staging/deployment-ai-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ai-service +spec: + replicas: 2 + template: + spec: + containers: + - name: ai-service + resources: + requests: + memory: "512Mi" + cpu: "512m" + limits: + memory: "2048Mi" + cpu: "2048m" diff --git a/.github/kustomize/overlays/staging/deployment-meeting-patch.yaml b/.github/kustomize/overlays/staging/deployment-meeting-patch.yaml new file mode 100644 index 0000000..0976b94 --- /dev/null +++ b/.github/kustomize/overlays/staging/deployment-meeting-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: meeting-service +spec: + replicas: 2 + template: + spec: + containers: + - name: meeting-service + resources: + requests: + memory: "512Mi" + cpu: "512m" + limits: + memory: "2048Mi" + cpu: "2048m" diff --git a/.github/kustomize/overlays/staging/deployment-notification-patch.yaml b/.github/kustomize/overlays/staging/deployment-notification-patch.yaml new file mode 100644 index 0000000..e4b04c1 --- /dev/null +++ b/.github/kustomize/overlays/staging/deployment-notification-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: notification-service +spec: + replicas: 2 + template: + spec: + containers: + - name: notification-service + resources: + requests: + memory: "512Mi" + cpu: "512m" + limits: + memory: "2048Mi" + cpu: "2048m" diff --git a/.github/kustomize/overlays/staging/deployment-stt-patch.yaml b/.github/kustomize/overlays/staging/deployment-stt-patch.yaml new file mode 100644 index 0000000..48b2409 --- /dev/null +++ b/.github/kustomize/overlays/staging/deployment-stt-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: stt-service +spec: + replicas: 2 + template: + spec: + containers: + - name: stt-service + resources: + requests: + memory: "512Mi" + cpu: "512m" + limits: + memory: "2048Mi" + cpu: "2048m" diff --git a/.github/kustomize/overlays/staging/deployment-user-patch.yaml b/.github/kustomize/overlays/staging/deployment-user-patch.yaml new file mode 100644 index 0000000..2a75059 --- /dev/null +++ b/.github/kustomize/overlays/staging/deployment-user-patch.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: user-service +spec: + replicas: 2 + template: + spec: + containers: + - name: user-service + resources: + requests: + memory: "512Mi" + cpu: "512m" + limits: + memory: "2048Mi" + cpu: "2048m" diff --git a/.github/kustomize/overlays/staging/ingress-patch.yaml b/.github/kustomize/overlays/staging/ingress-patch.yaml new file mode 100644 index 0000000..9de7857 --- /dev/null +++ b/.github/kustomize/overlays/staging/ingress-patch.yaml @@ -0,0 +1,46 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: hgzero-ingress + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" +spec: + rules: + - host: hgzero-staging-api.example.com + http: + paths: + - path: /api/users + pathType: Prefix + backend: + service: + name: user-service + port: + number: 8080 + - path: /api/meetings + pathType: Prefix + backend: + service: + name: meeting-service + port: + number: 8080 + - path: /api/stt + pathType: Prefix + backend: + service: + name: stt-service + port: + number: 8080 + - path: /api/ai + pathType: Prefix + backend: + service: + name: ai-service + port: + number: 8080 + - path: /api/notifications + pathType: Prefix + backend: + service: + name: notification-service + port: + number: 8080 diff --git a/.github/kustomize/overlays/staging/kustomization.yaml b/.github/kustomize/overlays/staging/kustomization.yaml new file mode 100644 index 0000000..4210839 --- /dev/null +++ b/.github/kustomize/overlays/staging/kustomization.yaml @@ -0,0 +1,91 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: hgzero + +resources: +- ../../base + +patches: +# Common patches +- path: cm-common-patch.yaml + target: + kind: ConfigMap + name: hgzero-common-config + +- path: secret-common-patch.yaml + target: + kind: Secret + name: hgzero-common-secret + +- path: ingress-patch.yaml + target: + kind: Ingress + name: hgzero-ingress + +# User service patches +- path: deployment-user-patch.yaml + target: + kind: Deployment + name: user-service + +- path: secret-user-patch.yaml + target: + kind: Secret + name: user-service-secret + +# Meeting service patches +- path: deployment-meeting-patch.yaml + target: + kind: Deployment + name: meeting-service + +- path: secret-meeting-patch.yaml + target: + kind: Secret + name: meeting-service-secret + +# STT service patches +- path: deployment-stt-patch.yaml + target: + kind: Deployment + name: stt-service + +- path: secret-stt-patch.yaml + target: + kind: Secret + name: stt-service-secret + +# AI service patches +- path: deployment-ai-patch.yaml + target: + kind: Deployment + name: ai-service + +- path: secret-ai-patch.yaml + target: + kind: Secret + name: ai-service-secret + +# Notification service patches +- path: deployment-notification-patch.yaml + target: + kind: Deployment + name: notification-service + +- path: secret-notification-patch.yaml + target: + kind: Secret + name: notification-service-secret + +images: +- name: user-service + newTag: staging-latest +- name: meeting-service + newTag: staging-latest +- name: stt-service + newTag: staging-latest +- name: ai-service + newTag: staging-latest +- name: notification-service + newTag: staging-latest diff --git a/.github/kustomize/overlays/staging/secret-ai-patch.yaml b/.github/kustomize/overlays/staging/secret-ai-patch.yaml new file mode 100644 index 0000000..16fe297 --- /dev/null +++ b/.github/kustomize/overlays/staging/secret-ai-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: ai-service-secret +type: Opaque +stringData: + DB_HOST: "ai-db-staging" + DB_PORT: "5432" + DB_NAME: "ai_db_staging" + DB_USERNAME: "ai_service" + DB_PASSWORD: "your-staging-ai-db-password" diff --git a/.github/kustomize/overlays/staging/secret-common-patch.yaml b/.github/kustomize/overlays/staging/secret-common-patch.yaml new file mode 100644 index 0000000..92c4565 --- /dev/null +++ b/.github/kustomize/overlays/staging/secret-common-patch.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: hgzero-common-secret +type: Opaque +stringData: + JWT_SECRET: "your-staging-jwt-secret-key-here-change-in-production" + JWT_EXPIRATION: "3600000" + REDIS_PASSWORD: "your-staging-redis-password" diff --git a/.github/kustomize/overlays/staging/secret-meeting-patch.yaml b/.github/kustomize/overlays/staging/secret-meeting-patch.yaml new file mode 100644 index 0000000..4408298 --- /dev/null +++ b/.github/kustomize/overlays/staging/secret-meeting-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: meeting-service-secret +type: Opaque +stringData: + DB_HOST: "meeting-db-staging" + DB_PORT: "5432" + DB_NAME: "meeting_db_staging" + DB_USERNAME: "meeting_service" + DB_PASSWORD: "your-staging-meeting-db-password" diff --git a/.github/kustomize/overlays/staging/secret-notification-patch.yaml b/.github/kustomize/overlays/staging/secret-notification-patch.yaml new file mode 100644 index 0000000..7314016 --- /dev/null +++ b/.github/kustomize/overlays/staging/secret-notification-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: notification-service-secret +type: Opaque +stringData: + DB_HOST: "notification-db-staging" + DB_PORT: "5432" + DB_NAME: "notification_db_staging" + DB_USERNAME: "notification_service" + DB_PASSWORD: "your-staging-notification-db-password" diff --git a/.github/kustomize/overlays/staging/secret-stt-patch.yaml b/.github/kustomize/overlays/staging/secret-stt-patch.yaml new file mode 100644 index 0000000..726a148 --- /dev/null +++ b/.github/kustomize/overlays/staging/secret-stt-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: stt-service-secret +type: Opaque +stringData: + DB_HOST: "stt-db-staging" + DB_PORT: "5432" + DB_NAME: "stt_db_staging" + DB_USERNAME: "stt_service" + DB_PASSWORD: "your-staging-stt-db-password" diff --git a/.github/kustomize/overlays/staging/secret-user-patch.yaml b/.github/kustomize/overlays/staging/secret-user-patch.yaml new file mode 100644 index 0000000..a956838 --- /dev/null +++ b/.github/kustomize/overlays/staging/secret-user-patch.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + name: user-service-secret +type: Opaque +stringData: + DB_HOST: "user-db-staging" + DB_PORT: "5432" + DB_NAME: "user_db_staging" + DB_USERNAME: "user_service" + DB_PASSWORD: "your-staging-user-db-password" diff --git a/.github/scripts/deploy-actions.sh b/.github/scripts/deploy-actions.sh new file mode 100755 index 0000000..37d311d --- /dev/null +++ b/.github/scripts/deploy-actions.sh @@ -0,0 +1,69 @@ +#!/bin/bash +set -e + +ENVIRONMENT=${1:-dev} +IMAGE_TAG=${2:-latest} + +echo "🚀 Manual deployment starting..." +echo "Environment: $ENVIRONMENT" +echo "Image Tag: $IMAGE_TAG" + +# Check if kustomize is installed +if ! command -v kustomize &> /dev/null; then + echo "Installing Kustomize..." + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + sudo mv kustomize /usr/local/bin/ +fi + +# Load environment variables from .github/config +if [[ -f ".github/config/deploy_env_vars_${ENVIRONMENT}" ]]; then + source ".github/config/deploy_env_vars_${ENVIRONMENT}" + echo "✅ Environment variables loaded for $ENVIRONMENT" +else + echo "❌ Environment configuration file not found: .github/config/deploy_env_vars_${ENVIRONMENT}" + exit 1 +fi + +# Create namespace +echo "📝 Creating namespace hgzero..." +kubectl create namespace hgzero --dry-run=client -o yaml | kubectl apply -f - + +# 환경별 이미지 태그 업데이트 (.github/kustomize 사용) +cd .github/kustomize/overlays/${ENVIRONMENT} + +echo "🔄 Updating image tags..." +# 서비스 배열 정의 +services=(user meeting stt ai notification) + +# 각 서비스별 이미지 태그 업데이트 +for service in "${services[@]}"; do + kustomize edit set image acrdigitalgarage02.azurecr.io/hgzero/$service:${ENVIRONMENT}-${IMAGE_TAG} +done + +echo "🚀 Deploying to Kubernetes..." +# 배포 실행 +kubectl apply -k . + +echo "⏳ Waiting for deployments to be ready..." +# 서비스별 배포 상태 확인 +for service in "${services[@]}"; do + kubectl rollout status deployment/$service -n hgzero --timeout=300s || echo "⚠️ $service deployment timeout" +done + +echo "🔍 Health check..." +# 각 서비스의 Health Check +for service in "${services[@]}"; do + POD=$(kubectl get pod -n hgzero -l app.kubernetes.io/name=$service -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "") + if [[ -n "$POD" ]]; then + kubectl -n hgzero exec $POD -- curl -f http://localhost:8080/actuator/health 2>/dev/null || echo "⚠️ $service health check failed" + else + echo "⚠️ $service pod not found" + fi +done + +echo "📋 Service Information:" +kubectl get pods -n hgzero +kubectl get services -n hgzero +kubectl get ingress -n hgzero + +echo "✅ GitHub Actions deployment completed successfully!" diff --git a/.github/workflows/backend-cicd.yaml b/.github/workflows/backend-cicd.yaml new file mode 100644 index 0000000..3d66175 --- /dev/null +++ b/.github/workflows/backend-cicd.yaml @@ -0,0 +1,276 @@ +name: Backend Services CI/CD + +on: + push: + branches: [ main, develop ] + paths: + - 'user/**' + - 'meeting/**' + - 'stt/**' + - 'ai/**' + - 'notification/**' + - 'common/**' + - '.github/**' + pull_request: + branches: [ main ] + workflow_dispatch: + inputs: + ENVIRONMENT: + description: 'Target environment' + required: true + default: 'dev' + type: choice + options: + - dev + - staging + - prod + SKIP_SONARQUBE: + description: 'Skip SonarQube Analysis' + required: false + default: 'true' + type: choice + options: + - 'true' + - 'false' + +env: + REGISTRY: acrdigitalgarage02.azurecr.io + IMAGE_ORG: hgzero + RESOURCE_GROUP: rg-digitalgarage-02 + AKS_CLUSTER: aks-digitalgarage-02 + NAMESPACE: hgzero + +jobs: + build: + name: Build and Test + runs-on: ubuntu-latest + outputs: + image_tag: ${{ steps.set_outputs.outputs.image_tag }} + environment: ${{ steps.set_outputs.outputs.environment }} + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'temurin' + cache: 'gradle' + + - name: Determine environment + id: determine_env + run: | + # Use input parameter or default to 'dev' + ENVIRONMENT="${{ github.event.inputs.ENVIRONMENT || 'dev' }}" + echo "environment=$ENVIRONMENT" >> $GITHUB_OUTPUT + + - name: Load environment variables + id: env_vars + run: | + ENV=${{ steps.determine_env.outputs.environment }} + + # Initialize variables with defaults + REGISTRY="acrdigitalgarage02.azurecr.io" + IMAGE_ORG="hgzero" + RESOURCE_GROUP="rg-digitalgarage-02" + AKS_CLUSTER="aks-digitalgarage-02" + NAMESPACE="hgzero" + + # Read environment variables from .github/config file + if [[ -f ".github/config/deploy_env_vars_${ENV}" ]]; then + while IFS= read -r line || [[ -n "$line" ]]; do + # Skip comments and empty lines + [[ "$line" =~ ^#.*$ ]] && continue + [[ -z "$line" ]] && continue + + # Extract key-value pairs + key=$(echo "$line" | cut -d '=' -f1) + value=$(echo "$line" | cut -d '=' -f2-) + + # Override defaults if found in config + case "$key" in + "resource_group") RESOURCE_GROUP="$value" ;; + "cluster_name") AKS_CLUSTER="$value" ;; + esac + done < ".github/config/deploy_env_vars_${ENV}" + fi + + # Export for other jobs + echo "REGISTRY=$REGISTRY" >> $GITHUB_ENV + echo "IMAGE_ORG=$IMAGE_ORG" >> $GITHUB_ENV + echo "RESOURCE_GROUP=$RESOURCE_GROUP" >> $GITHUB_ENV + echo "AKS_CLUSTER=$AKS_CLUSTER" >> $GITHUB_ENV + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: | + ./gradlew build -x test + + - name: SonarQube Analysis & Quality Gate + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: | + # Check if SonarQube should be skipped + SKIP_SONARQUBE="${{ github.event.inputs.SKIP_SONARQUBE || 'true' }}" + + if [[ "$SKIP_SONARQUBE" == "true" ]]; then + echo "⏭️ Skipping SonarQube Analysis (SKIP_SONARQUBE=$SKIP_SONARQUBE)" + exit 0 + fi + + # Define services array + services=(user meeting stt ai notification) + + # Run tests, coverage reports, and SonarQube analysis for each service + for service in "${services[@]}"; do + ./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \ + -Dsonar.projectKey=hgzero-$service-${{ steps.determine_env.outputs.environment }} \ + -Dsonar.projectName=hgzero-$service-${{ steps.determine_env.outputs.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/** + done + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: app-builds + path: | + user/build/libs/*.jar + meeting/build/libs/*.jar + stt/build/libs/*.jar + ai/build/libs/*.jar + notification/build/libs/*.jar + + - name: Set outputs + id: set_outputs + run: | + # Generate timestamp for image tag + IMAGE_TAG=$(date +%Y%m%d%H%M%S) + echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT + echo "environment=${{ steps.determine_env.outputs.environment }}" >> $GITHUB_OUTPUT + + release: + name: Build and Push Docker Images + needs: build + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: app-builds + + - name: Set environment variables from build job + run: | + echo "REGISTRY=${{ env.REGISTRY }}" >> $GITHUB_ENV + echo "IMAGE_ORG=${{ env.IMAGE_ORG }}" >> $GITHUB_ENV + echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV + echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Docker Hub (prevent rate limit) + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Login to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} + + - name: Build and push Docker images for all services + run: | + # Define services array + services=(user meeting stt ai notification) + + # Build and push each service image + for service in "${services[@]}"; do + echo "Building and pushing $service..." + docker build \ + --build-arg BUILD_LIB_DIR="$service/build/libs" \ + --build-arg ARTIFACTORY_FILE="$service.jar" \ + -f deployment/container/Dockerfile-backend \ + -t ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} . + + docker push ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/$service:${{ needs.build.outputs.environment }}-${{ needs.build.outputs.image_tag }} + done + + deploy: + name: Deploy to Kubernetes + needs: [build, release] + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set image tag environment variable + run: | + echo "IMAGE_TAG=${{ needs.build.outputs.image_tag }}" >> $GITHUB_ENV + echo "ENVIRONMENT=${{ needs.build.outputs.environment }}" >> $GITHUB_ENV + + - name: Install Azure CLI + run: | + curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash + + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + + - name: Get AKS Credentials + run: | + az aks get-credentials --resource-group ${{ env.RESOURCE_GROUP }} --name ${{ env.AKS_CLUSTER }} --overwrite-existing + + - name: Create namespace + run: | + kubectl create namespace ${{ env.NAMESPACE }} --dry-run=client -o yaml | kubectl apply -f - + + - name: Install Kustomize + run: | + curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash + sudo mv kustomize /usr/local/bin/ + + - name: Update Kustomize images and deploy + run: | + # 환경별 디렉토리로 이동 + cd .github/kustomize/overlays/${{ env.ENVIRONMENT }} + + # 각 서비스별 이미지 태그 업데이트 + kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/user:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/meeting:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/stt:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/ai:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/notification:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }} + + # 매니페스트 적용 + kubectl apply -k . + + - name: Wait for deployments to be ready + run: | + echo "Waiting for deployments to be ready..." + kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/user --timeout=300s || true + kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/meeting --timeout=300s || true + kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/stt --timeout=300s || true + kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/ai --timeout=300s || true + kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/notification --timeout=300s || true