mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 07:56:24 +00:00
Merge branch 'main' into feature/stt-ai - 로그 파일 충돌 해결
This commit is contained in:
commit
7e30f6b82e
500
.github/actions-pipeline-guide.md
vendored
Normal file
500
.github/actions-pipeline-guide.md
vendored
Normal file
@ -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
|
||||
3
.github/config/deploy_env_vars_dev
vendored
Normal file
3
.github/config/deploy_env_vars_dev
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# dev Environment Configuration
|
||||
resource_group=rg-digitalgarage-02
|
||||
cluster_name=aks-digitalgarage-02
|
||||
3
.github/config/deploy_env_vars_prod
vendored
Normal file
3
.github/config/deploy_env_vars_prod
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# prod Environment Configuration
|
||||
resource_group=rg-digitalgarage-02
|
||||
cluster_name=aks-digitalgarage-02
|
||||
3
.github/config/deploy_env_vars_staging
vendored
Normal file
3
.github/config/deploy_env_vars_staging
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# staging Environment Configuration
|
||||
resource_group=rg-digitalgarage-02
|
||||
cluster_name=aks-digitalgarage-02
|
||||
66
.github/kustomize/base/ai/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/ai/deployment.yaml
vendored
Normal file
@ -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
|
||||
11
.github/kustomize/base/ai/secret-ai.yaml
vendored
Normal file
11
.github/kustomize/base/ai/secret-ai.yaml
vendored
Normal file
@ -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"
|
||||
15
.github/kustomize/base/ai/service.yaml
vendored
Normal file
15
.github/kustomize/base/ai/service.yaml
vendored
Normal file
@ -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
|
||||
25
.github/kustomize/base/common/cm-common.yaml
vendored
Normal file
25
.github/kustomize/base/common/cm-common.yaml
vendored
Normal file
@ -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"
|
||||
48
.github/kustomize/base/common/ingress.yaml
vendored
Normal file
48
.github/kustomize/base/common/ingress.yaml
vendored
Normal file
@ -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
|
||||
11
.github/kustomize/base/common/secret-common.yaml
vendored
Normal file
11
.github/kustomize/base/common/secret-common.yaml
vendored
Normal file
@ -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"
|
||||
7
.github/kustomize/base/common/secret-imagepull.yaml
vendored
Normal file
7
.github/kustomize/base/common/secret-imagepull.yaml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: acr-secret
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
.dockerconfigjson: e30K
|
||||
49
.github/kustomize/base/kustomization.yaml
vendored
Normal file
49
.github/kustomize/base/kustomization.yaml
vendored
Normal file
@ -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
|
||||
66
.github/kustomize/base/meeting/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/meeting/deployment.yaml
vendored
Normal file
@ -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
|
||||
10
.github/kustomize/base/meeting/secret-meeting.yaml
vendored
Normal file
10
.github/kustomize/base/meeting/secret-meeting.yaml
vendored
Normal file
@ -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"
|
||||
15
.github/kustomize/base/meeting/service.yaml
vendored
Normal file
15
.github/kustomize/base/meeting/service.yaml
vendored
Normal file
@ -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
|
||||
68
.github/kustomize/base/notification/deployment.yaml
vendored
Normal file
68
.github/kustomize/base/notification/deployment.yaml
vendored
Normal file
@ -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
|
||||
12
.github/kustomize/base/notification/secret-notification.yaml
vendored
Normal file
12
.github/kustomize/base/notification/secret-notification.yaml
vendored
Normal file
@ -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"
|
||||
16
.github/kustomize/base/notification/service.yaml
vendored
Normal file
16
.github/kustomize/base/notification/service.yaml
vendored
Normal file
@ -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
|
||||
66
.github/kustomize/base/stt/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/stt/deployment.yaml
vendored
Normal file
@ -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
|
||||
9
.github/kustomize/base/stt/secret-stt.yaml
vendored
Normal file
9
.github/kustomize/base/stt/secret-stt.yaml
vendored
Normal file
@ -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"
|
||||
15
.github/kustomize/base/stt/service.yaml
vendored
Normal file
15
.github/kustomize/base/stt/service.yaml
vendored
Normal file
@ -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
|
||||
66
.github/kustomize/base/user/deployment.yaml
vendored
Normal file
66
.github/kustomize/base/user/deployment.yaml
vendored
Normal file
@ -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
|
||||
11
.github/kustomize/base/user/secret-user.yaml
vendored
Normal file
11
.github/kustomize/base/user/secret-user.yaml
vendored
Normal file
@ -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"
|
||||
15
.github/kustomize/base/user/service.yaml
vendored
Normal file
15
.github/kustomize/base/user/service.yaml
vendored
Normal file
@ -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
|
||||
18
.github/kustomize/overlays/dev/cm-common-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/dev/cm-common-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/dev/deployment-ai-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-ai-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/dev/deployment-meeting-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-meeting-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/dev/deployment-notification-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-notification-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/dev/deployment-stt-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-stt-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/dev/deployment-user-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/dev/deployment-user-patch.yaml
vendored
Normal file
@ -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"
|
||||
48
.github/kustomize/overlays/dev/ingress-patch.yaml
vendored
Normal file
48
.github/kustomize/overlays/dev/ingress-patch.yaml
vendored
Normal file
@ -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
|
||||
84
.github/kustomize/overlays/dev/kustomization.yaml
vendored
Normal file
84
.github/kustomize/overlays/dev/kustomization.yaml
vendored
Normal file
@ -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
|
||||
11
.github/kustomize/overlays/dev/secret-ai-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/dev/secret-ai-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/dev/secret-common-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/dev/secret-common-patch.yaml
vendored
Normal file
@ -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"
|
||||
10
.github/kustomize/overlays/dev/secret-meeting-patch.yaml
vendored
Normal file
10
.github/kustomize/overlays/dev/secret-meeting-patch.yaml
vendored
Normal file
@ -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"
|
||||
12
.github/kustomize/overlays/dev/secret-notification-patch.yaml
vendored
Normal file
12
.github/kustomize/overlays/dev/secret-notification-patch.yaml
vendored
Normal file
@ -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"
|
||||
9
.github/kustomize/overlays/dev/secret-stt-patch.yaml
vendored
Normal file
9
.github/kustomize/overlays/dev/secret-stt-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/dev/secret-user-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/dev/secret-user-patch.yaml
vendored
Normal file
@ -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"
|
||||
14
.github/kustomize/overlays/prod/cm-common-patch.yaml
vendored
Normal file
14
.github/kustomize/overlays/prod/cm-common-patch.yaml
vendored
Normal file
@ -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"
|
||||
18
.github/kustomize/overlays/prod/deployment-ai-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-ai-patch.yaml
vendored
Normal file
@ -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"
|
||||
18
.github/kustomize/overlays/prod/deployment-meeting-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-meeting-patch.yaml
vendored
Normal file
@ -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"
|
||||
18
.github/kustomize/overlays/prod/deployment-notification-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-notification-patch.yaml
vendored
Normal file
@ -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"
|
||||
18
.github/kustomize/overlays/prod/deployment-stt-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-stt-patch.yaml
vendored
Normal file
@ -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"
|
||||
18
.github/kustomize/overlays/prod/deployment-user-patch.yaml
vendored
Normal file
18
.github/kustomize/overlays/prod/deployment-user-patch.yaml
vendored
Normal file
@ -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"
|
||||
49
.github/kustomize/overlays/prod/ingress-patch.yaml
vendored
Normal file
49
.github/kustomize/overlays/prod/ingress-patch.yaml
vendored
Normal file
@ -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
|
||||
84
.github/kustomize/overlays/prod/kustomization.yaml
vendored
Normal file
84
.github/kustomize/overlays/prod/kustomization.yaml
vendored
Normal file
@ -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
|
||||
9
.github/kustomize/overlays/prod/secret-ai-patch.yaml
vendored
Normal file
9
.github/kustomize/overlays/prod/secret-ai-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/prod/secret-common-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/prod/secret-common-patch.yaml
vendored
Normal file
@ -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"
|
||||
13
.github/kustomize/overlays/prod/secret-meeting-patch.yaml
vendored
Normal file
13
.github/kustomize/overlays/prod/secret-meeting-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/prod/secret-notification-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/prod/secret-notification-patch.yaml
vendored
Normal file
@ -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"
|
||||
10
.github/kustomize/overlays/prod/secret-stt-patch.yaml
vendored
Normal file
10
.github/kustomize/overlays/prod/secret-stt-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/prod/secret-user-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/prod/secret-user-patch.yaml
vendored
Normal file
@ -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"
|
||||
99
.github/kustomize/overlays/staging/README.md
vendored
Normal file
99
.github/kustomize/overlays/staging/README.md
vendored
Normal file
@ -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 설정 추가
|
||||
8
.github/kustomize/overlays/staging/cm-common-patch.yaml
vendored
Normal file
8
.github/kustomize/overlays/staging/cm-common-patch.yaml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: hgzero-common-config
|
||||
data:
|
||||
SPRING_PROFILES_ACTIVE: "staging"
|
||||
DDL_AUTO: "validate"
|
||||
LOG_LEVEL: "INFO"
|
||||
17
.github/kustomize/overlays/staging/deployment-ai-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-ai-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/staging/deployment-meeting-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-meeting-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/staging/deployment-notification-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-notification-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/staging/deployment-stt-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-stt-patch.yaml
vendored
Normal file
@ -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"
|
||||
17
.github/kustomize/overlays/staging/deployment-user-patch.yaml
vendored
Normal file
17
.github/kustomize/overlays/staging/deployment-user-patch.yaml
vendored
Normal file
@ -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"
|
||||
46
.github/kustomize/overlays/staging/ingress-patch.yaml
vendored
Normal file
46
.github/kustomize/overlays/staging/ingress-patch.yaml
vendored
Normal file
@ -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
|
||||
91
.github/kustomize/overlays/staging/kustomization.yaml
vendored
Normal file
91
.github/kustomize/overlays/staging/kustomization.yaml
vendored
Normal file
@ -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
|
||||
11
.github/kustomize/overlays/staging/secret-ai-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-ai-patch.yaml
vendored
Normal file
@ -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"
|
||||
9
.github/kustomize/overlays/staging/secret-common-patch.yaml
vendored
Normal file
9
.github/kustomize/overlays/staging/secret-common-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/staging/secret-meeting-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-meeting-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/staging/secret-notification-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-notification-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/staging/secret-stt-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-stt-patch.yaml
vendored
Normal file
@ -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"
|
||||
11
.github/kustomize/overlays/staging/secret-user-patch.yaml
vendored
Normal file
11
.github/kustomize/overlays/staging/secret-user-patch.yaml
vendored
Normal file
@ -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"
|
||||
69
.github/scripts/deploy-actions.sh
vendored
Executable file
69
.github/scripts/deploy-actions.sh
vendored
Executable file
@ -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!"
|
||||
276
.github/workflows/backend-cicd.yaml
vendored
Normal file
276
.github/workflows/backend-cicd.yaml
vendored
Normal file
@ -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
|
||||
254
.github/workflows/backend-cicd_ArgoCD.yaml
vendored
Normal file
254
.github/workflows/backend-cicd_ArgoCD.yaml
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
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
|
||||
|
||||
update-manifest:
|
||||
name: Update Manifest Repository
|
||||
needs: [build, release]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- 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: Update Manifest Repository
|
||||
run: |
|
||||
# 매니페스트 레포지토리 클론
|
||||
REPO_URL=$(echo "https://github.com/hjmoons/hgzero-manifest.git" | sed 's|https://||')
|
||||
git clone https://${{ secrets.GIT_USERNAME }}:${{ secrets.GIT_PASSWORD }}@${REPO_URL} manifest-repo
|
||||
cd manifest-repo
|
||||
|
||||
# Kustomize 설치
|
||||
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||
sudo mv kustomize /usr/local/bin/
|
||||
|
||||
# 매니페스트 업데이트
|
||||
cd hgzero-back/kustomize/overlays/${{ env.ENVIRONMENT }}
|
||||
|
||||
# 각 서비스별 이미지 태그 업데이트
|
||||
services="user meeting stt ai notification"
|
||||
for service in $services; do
|
||||
kustomize edit set image acrdigitalgarage02.azurecr.io/hgzero/$service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||
done
|
||||
|
||||
# Git 설정 및 푸시
|
||||
cd ../../../..
|
||||
git config user.name "GitHub Actions"
|
||||
git config user.email "actions@github.com"
|
||||
git add .
|
||||
git commit -m "🚀 Update hgzero ${{ env.ENVIRONMENT }} images to ${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}"
|
||||
git push origin main
|
||||
|
||||
echo "✅ 매니페스트 업데이트 완료. ArgoCD가 자동으로 배포합니다."
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ import java.util.stream.Collectors;
|
||||
* GET /api/terms/{term}/explain
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/terms")
|
||||
@RequestMapping("/api/ai/terms")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Term", description = "전문용어 감지 및 설명 API")
|
||||
|
||||
@ -20,7 +20,7 @@ import java.util.stream.Collectors;
|
||||
* GET /api/transcripts/{meetingId}/related
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/transcripts")
|
||||
@RequestMapping("/api/ai/transcripts")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Relation", description = "관련 회의록 조회 API")
|
||||
|
||||
@ -19,7 +19,7 @@ import java.time.LocalDateTime;
|
||||
* POST /api/sections/{sectionId}/regenerate-summary
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/sections")
|
||||
@RequestMapping("/api/ai/sections")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Section", description = "섹션 AI 요약 재생성 API")
|
||||
|
||||
@ -31,7 +31,7 @@ import java.util.stream.Collectors;
|
||||
* POST /api/suggestions/decision
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/suggestions")
|
||||
@RequestMapping("/api/ai/suggestions")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Suggestion", description = "논의사항/결정사항 제안 API")
|
||||
|
||||
@ -24,7 +24,7 @@ import java.util.stream.Collectors;
|
||||
* POST /api/terms/detect
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/terms")
|
||||
@RequestMapping("/api/ai/terms")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Term", description = "전문용어 감지 및 설명 API")
|
||||
|
||||
@ -23,7 +23,7 @@ import java.util.stream.Collectors;
|
||||
* POST /api/todos/extract
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/todos")
|
||||
@RequestMapping("/api/ai/todos")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Todo", description = "Todo 자동 추출 API")
|
||||
|
||||
@ -21,7 +21,7 @@ import java.util.stream.Collectors;
|
||||
* POST /api/transcripts/process
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/transcripts")
|
||||
@RequestMapping("/api/ai/transcripts")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
@Tag(name = "Transcript", description = "회의록 자동 작성 API")
|
||||
|
||||
File diff suppressed because one or more lines are too long
82
claude/build-image-back.md
Normal file
82
claude/build-image-back.md
Normal file
@ -0,0 +1,82 @@
|
||||
# 백엔드 컨테이너이미지 작성가이드
|
||||
|
||||
[요청사항]
|
||||
- 백엔드 각 서비스를의 컨테이너 이미지 생성
|
||||
- 실제 빌드 수행 및 검증까지 완료
|
||||
- '[결과파일]'에 수행한 명령어를 포함하여 컨테이너 이미지 작성 과정 생성
|
||||
|
||||
[작업순서]
|
||||
- 서비스명 확인
|
||||
서비스명은 settings.gradle에서 확인
|
||||
|
||||
예시) include 'common'하위의 4개가 서비스명임.
|
||||
```
|
||||
rootProject.name = 'tripgen'
|
||||
|
||||
include 'common'
|
||||
include 'user-service'
|
||||
include 'location-service'
|
||||
include 'ai-service'
|
||||
include 'trip-service'
|
||||
```
|
||||
|
||||
- 실행Jar 파일 설정
|
||||
실행Jar 파일명을 서비스명과 일치하도록 build.gradle에 설정 합니다.
|
||||
```
|
||||
bootJar {
|
||||
archiveFileName = '{서비스명}.jar'
|
||||
}
|
||||
```
|
||||
|
||||
- Dockerfile 생성
|
||||
아래 내용으로 deployment/container/Dockerfile-backend 생성
|
||||
```
|
||||
# Build stage
|
||||
FROM openjdk:23-oraclelinux8 AS builder
|
||||
ARG BUILD_LIB_DIR
|
||||
ARG ARTIFACTORY_FILE
|
||||
COPY ${BUILD_LIB_DIR}/${ARTIFACTORY_FILE} app.jar
|
||||
|
||||
# Run stage
|
||||
FROM openjdk:23-slim
|
||||
ENV USERNAME=k8s
|
||||
ENV ARTIFACTORY_HOME=/home/${USERNAME}
|
||||
ENV JAVA_OPTS=""
|
||||
|
||||
# Add a non-root user
|
||||
RUN adduser --system --group ${USERNAME} && \
|
||||
mkdir -p ${ARTIFACTORY_HOME} && \
|
||||
chown ${USERNAME}:${USERNAME} ${ARTIFACTORY_HOME}
|
||||
|
||||
WORKDIR ${ARTIFACTORY_HOME}
|
||||
COPY --from=builder app.jar app.jar
|
||||
RUN chown ${USERNAME}:${USERNAME} app.jar
|
||||
|
||||
USER ${USERNAME}
|
||||
|
||||
ENTRYPOINT [ "sh", "-c" ]
|
||||
CMD ["java ${JAVA_OPTS} -jar app.jar"]
|
||||
```
|
||||
|
||||
- 컨테이너 이미지 생성
|
||||
아래 명령으로 각 서비스 빌드. shell 파일을 생성하지 말고 command로 수행.
|
||||
서브에이젼트를 생성하여 병렬로 수행.
|
||||
```
|
||||
DOCKER_FILE=deployment/container/Dockerfile-backend
|
||||
service={서비스명}
|
||||
|
||||
docker build \
|
||||
--platform linux/amd64 \
|
||||
--build-arg BUILD_LIB_DIR="${서비스명}/build/libs" \
|
||||
--build-arg ARTIFACTORY_FILE="${서비스명}.jar" \
|
||||
-f ${DOCKER_FILE} \
|
||||
-t ${서비스명}:latest .
|
||||
```
|
||||
- 생성된 이미지 확인
|
||||
아래 명령으로 모든 서비스의 이미지가 빌드되었는지 확인
|
||||
```
|
||||
docker images | grep {서비스명}
|
||||
```
|
||||
|
||||
[결과파일]
|
||||
deployment/container/build-image.md
|
||||
770
claude/deploy-actions-cicd-back.md
Normal file
770
claude/deploy-actions-cicd-back.md
Normal file
@ -0,0 +1,770 @@
|
||||
# 백엔드 GitHub Actions 파이프라인 작성 가이드
|
||||
|
||||
[요청사항]
|
||||
- GitHub Actions 기반 CI/CD 파이프라인 구축 가이드 작성
|
||||
- 환경별(dev/staging/prod) Kustomize 매니페스트 관리 및 자동 배포 구현
|
||||
- SonarQube 코드 품질 분석과 Quality Gate 포함
|
||||
- Kustomize 매니페스트 생성부터 배포까지 전체 과정 안내
|
||||
- '[결과파일]'에 구축 방법 및 파이프라인 작성 가이드 생성
|
||||
- 아래 작업은 실제 수행하여 파일 생성
|
||||
- Kustomize 디렉토리 구조 생성
|
||||
- Base Kustomization 작성
|
||||
- 환경별 Overlay 작성
|
||||
- 환경별 Patch 파일 생성
|
||||
- GitHub Actions 워크플로우 파일 작성
|
||||
- 환경별 배포 변수 파일 작성
|
||||
- 수동 배포 스크립트 작성
|
||||
|
||||
[작업순서]
|
||||
- 사전 준비사항 확인
|
||||
프롬프트의 '[실행정보]'섹션에서 아래정보를 확인
|
||||
- {ACR_NAME}: Azure Container Registry 이름
|
||||
- {RESOURCE_GROUP}: Azure 리소스 그룹명
|
||||
- {AKS_CLUSTER}: AKS 클러스터명
|
||||
- {NAMESPACE}: Namespace명
|
||||
예시)
|
||||
```
|
||||
[실행정보]
|
||||
- ACR_NAME: acrdigitalgarage01
|
||||
- RESOURCE_GROUP: rg-digitalgarage-01
|
||||
- AKS_CLUSTER: aks-digitalgarage-01
|
||||
- NAMESPACE: phonebill-dg0500
|
||||
```
|
||||
|
||||
- 시스템명과 서비스명 확인
|
||||
settings.gradle에서 확인.
|
||||
- {SYSTEM_NAME}: rootProject.name
|
||||
- {SERVICE_NAMES}: include 'common'하위의 include문 뒤의 값임
|
||||
|
||||
예시) include 'common'하위의 서비스명들.
|
||||
```
|
||||
rootProject.name = 'phonebill'
|
||||
|
||||
include 'common'
|
||||
include 'api-gateway'
|
||||
include 'user-service'
|
||||
include 'order-service'
|
||||
include 'payment-service'
|
||||
```
|
||||
|
||||
- JDK버전 확인
|
||||
루트 build.gradle에서 JDK 버전 확인.
|
||||
{JDK_VERSION}: 'java' 섹션에서 JDK 버전 확인. 아래 예에서는 21임.
|
||||
```
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- GitHub 저장소 환경 구성 안내
|
||||
- GitHub Repository Secrets 설정
|
||||
- Azure 접근 인증정보 설정
|
||||
```
|
||||
# Azure Service Principal
|
||||
Repository Settings > Secrets and variables > Actions > Repository secrets에 등록
|
||||
|
||||
AZURE_CREDENTIALS:
|
||||
{
|
||||
"clientId": "{클라이언트ID}",
|
||||
"clientSecret": "{클라이언트시크릿}",
|
||||
"subscriptionId": "{구독ID}",
|
||||
"tenantId": "{테넌트ID}"
|
||||
}
|
||||
예시)
|
||||
{
|
||||
"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
|
||||
Credential 구하는 방법 안내
|
||||
az acr credential show --name {acr 이름}
|
||||
예) az acr credential show --name acrdigitalgarage01
|
||||
```
|
||||
ACR_USERNAME: {ACR_NAME}
|
||||
ACR_PASSWORD: {ACR패스워드}
|
||||
```
|
||||
- SonarQube URL과 인증 토큰
|
||||
SONAR_HOST_URL 구하는 방법과 SONAR_TOKEN 작성법 안내
|
||||
SONAR_HOST_URL: 아래 명령 수행 후 http://{External IP}를 지정
|
||||
k get svc -n sonarqube
|
||||
예) http://20.249.187.69
|
||||
|
||||
SONAR_TOKEN 값은 아래와 같이 작성
|
||||
- SonarQube 로그인 후 우측 상단 'Administrator' > My Account 클릭
|
||||
- Security 탭 선택 후 토큰 생성
|
||||
|
||||
```
|
||||
SONAR_TOKEN: {SonarQube토큰}
|
||||
SONAR_HOST_URL: {SonarQube서버URL}
|
||||
```
|
||||
|
||||
- Docker Hub (Rate Limit 해결용)
|
||||
Docker Hub 패스워드 작성 방법 안내
|
||||
- DockerHub(https://hub.docker.com)에 로그인
|
||||
- 우측 상단 프로필 아이콘 클릭 후 Account Settings를 선택
|
||||
- 좌측메뉴에서 'Personal Access Tokens' 클릭하여 생성
|
||||
```
|
||||
DOCKERHUB_USERNAME: {Docker Hub 사용자명}
|
||||
DOCKERHUB_PASSWORD: {Docker Hub 패스워드}
|
||||
```
|
||||
|
||||
- GitHub Repository Variables 설정
|
||||
```
|
||||
# Workflow 제어 변수
|
||||
Repository Settings > Secrets and variables > Actions > Variables > Repository variables에 등록
|
||||
|
||||
ENVIRONMENT: dev (기본값, 수동실행시 선택 가능: dev/staging/prod)
|
||||
SKIP_SONARQUBE: true (기본값, 수동실행시 선택 가능: true/false)
|
||||
```
|
||||
|
||||
**사용 방법:**
|
||||
- **자동 실행**: Push/PR 시 기본값 사용 (ENVIRONMENT=dev, SKIP_SONARQUBE=true)
|
||||
- **수동 실행**: Actions 탭 > "Backend Services CI/CD" > "Run workflow" 버튼 클릭
|
||||
- Environment: dev/staging/prod 선택
|
||||
- Skip SonarQube Analysis: true/false 선택
|
||||
|
||||
- Kustomize 디렉토리 구조 생성
|
||||
- GitHub Actions 전용 Kustomize 디렉토리 생성
|
||||
```bash
|
||||
mkdir -p .github/kustomize/{base,overlays/{dev,staging,prod}}
|
||||
mkdir -p .github/kustomize/base/{common,{서비스명1},{서비스명2},...}
|
||||
mkdir -p .github/{config,scripts}
|
||||
```
|
||||
- 기존 k8s 매니페스트를 base로 복사
|
||||
```bash
|
||||
# 기존 deployment/k8s/* 파일들을 base로 복사
|
||||
cp deployment/k8s/common/* .github/kustomize/base/common/
|
||||
cp deployment/k8s/{서비스명}/* .github/kustomize/base/{서비스명}/
|
||||
|
||||
# 네임스페이스 하드코딩 제거
|
||||
find .github/kustomize/base -name "*.yaml" -exec sed -i 's/namespace: .*//' {} \;
|
||||
```
|
||||
|
||||
- Base Kustomization 작성
|
||||
`.github/kustomize/base/kustomization.yaml` 파일 생성
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
metadata:
|
||||
name: {SYSTEM_NAME}-base
|
||||
|
||||
resources:
|
||||
# Common resources
|
||||
- common/configmap-common.yaml
|
||||
- common/secret-common.yaml
|
||||
- common/secret-imagepull.yaml
|
||||
- common/ingress.yaml
|
||||
|
||||
# 각 서비스별 리소스
|
||||
- {SERVICE_NAME}/deployment.yaml
|
||||
- {SERVICE_NAME}/service.yaml
|
||||
- {SERVICE_NAME}/configmap.yaml
|
||||
- {SERVICE_NAME}/secret.yaml
|
||||
|
||||
images:
|
||||
- name: {ACR_NAME}.azurecr.io/{SYSTEM_NAME}/{SERVICE_NAME}
|
||||
newTag: latest
|
||||
```
|
||||
|
||||
- 환경별 Patch 파일 생성
|
||||
각 환경별로 필요한 patch 파일들을 생성합니다.
|
||||
**중요원칙**:
|
||||
- **base 매니페스트에 없는 항목은 추가 안함**
|
||||
- **base 매니페스트와 항목이 일치해야 함**
|
||||
- Secret 매니페스트에 'data'가 아닌 'stringData'사용
|
||||
|
||||
**1. ConfigMap Common Patch 파일 생성**
|
||||
`.github/kustomize/overlays/{ENVIRONMENT}/cm-common-patch.yaml`
|
||||
|
||||
- base 매니페스트를 환경별로 복사
|
||||
```
|
||||
cp .github/kustomize/base/common/cm-common.yaml .github/kustomize/overlays/{ENVIRONMENT}/cm-common-patch.yaml
|
||||
```
|
||||
|
||||
- SPRING_PROFILES_ACTIVE를 환경에 맞게 설정 (dev/staging/prod)
|
||||
- DDL_AUTO 설정: dev는 "update", staging/prod는 "validate"
|
||||
- JWT 토큰 유효시간은 prod에서 보안을 위해 짧게 설정
|
||||
|
||||
**2. Secret Common Patch 파일 생성**
|
||||
`.github/kustomize/overlays/{ENVIRONMENT}/secret-common-patch.yaml`
|
||||
|
||||
- base 매니페스트를 환경별로 복사
|
||||
```
|
||||
cp .github/kustomize/base/common/secret-common.yaml .github/kustomize/overlays/{ENVIRONMENT}/secret-common-patch.yaml
|
||||
```
|
||||
|
||||
**3. Ingress Patch 파일 생성**
|
||||
`.github/kustomize/overlays/{ENVIRONMENT}/ingress-patch.yaml`
|
||||
- base의 ingress.yaml을 환경별로 오버라이드
|
||||
- **⚠️ 중요**: 개발환경 Ingress Host의 기본값은 base의 ingress.yaml과 **정확히 동일하게** 함
|
||||
- base에서 `host: {SYSTEM_NAME}-api.20.214.196.128.nip.io` 이면
|
||||
- dev에서도 `host: {SYSTEM_NAME}-api.20.214.196.128.nip.io` 로 동일하게 설정
|
||||
- **절대** `{SYSTEM_NAME}-dev-api.xxx` 처럼 변경하지 말 것
|
||||
- Staging/Prod 환경별 도메인 설정: {SYSTEM_NAME}.도메인 형식
|
||||
- service name을 '{서비스명}'으로 함.
|
||||
- Staging/prod 환경은 HTTPS 강제 적용 및 SSL 인증서 설정
|
||||
- staging/prod는 nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
||||
- dev는 nginx.ingress.kubernetes.io/ssl-redirect: "false"
|
||||
|
||||
**4. deployment Patch 파일 생성** ⚠️ **중요**
|
||||
각 서비스별로 별도 파일 생성
|
||||
`.github/kustomize/overlays/{ENVIRONMENT}/deployment-{SERVICE_NAME}-patch.yaml`
|
||||
|
||||
**필수 포함 사항:**
|
||||
- ✅ **replicas 설정**: 각 서비스별 Deployment의 replica 수를 환경별로 설정
|
||||
- dev: 모든 서비스 1 replica (리소스 절약)
|
||||
- staging: 모든 서비스 2 replicas
|
||||
- prod: 모든 서비스 3 replicas
|
||||
- ✅ **resources 설정**: 각 서비스별 Deployment의 resources를 환경별로 설정
|
||||
- dev: requests(256m CPU, 256Mi Memory), limits(1024m CPU, 1024Mi Memory)
|
||||
- staging: requests(512m CPU, 512Mi Memory), limits(2048m CPU, 2048Mi Memory)
|
||||
- prod: requests(1024m CPU, 1024Mi Memory), limits(4096m CPU, 4096Mi Memory)
|
||||
|
||||
**5. Secret Service Patch 파일 생성**
|
||||
각 서비스별로 별도 파일 생성
|
||||
`.github/kustomize/overlays/{ENVIRONMENT}/secret-{SERVICE_NAME}-patch.yaml`
|
||||
|
||||
- base 매니페스트를 환경별로 복사
|
||||
```
|
||||
cp .github/kustomize/base/{SERVICE_NAME}/secret-{SERVICE_NAME}.yaml .github/kustomize/overlays/{ENVIRONMENT}/secret-{SERVICE_NAME}-patch.yaml
|
||||
```
|
||||
- 환경별 데이터베이스 연결 정보로 수정
|
||||
- **⚠️ 중요**: 패스워드 등 민감정보는 실제 환경 구축 시 별도 설정
|
||||
|
||||
- 환경별 Overlay 작성
|
||||
각 환경별로 `overlays/{환경}/kustomization.yaml` 생성
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: {NAMESPACE}
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
patches:
|
||||
- path: cm-common-patch.yaml
|
||||
target:
|
||||
kind: ConfigMap
|
||||
name: cm-common
|
||||
- path: deployment-{SERVICE_NAME}-patch.yaml
|
||||
target:
|
||||
kind: Deployment
|
||||
name: {SERVICE_NAME}
|
||||
- path: ingress-patch.yaml
|
||||
target:
|
||||
kind: Ingress
|
||||
name: {SYSTEM_NAME}
|
||||
- path: secret-common-patch.yaml
|
||||
target:
|
||||
kind: Secret
|
||||
name: secret-common
|
||||
- path: secret-{SERVICE_NAME}-patch.yaml
|
||||
target:
|
||||
kind: Secret
|
||||
name: secret-{SERVICE_NAME}
|
||||
|
||||
images:
|
||||
- name: {ACR_NAME}.azurecr.io/{SYSTEM_NAME}/{SERVICE_NAME}
|
||||
newTag: {ENVIRONMENT}-latest
|
||||
|
||||
```
|
||||
|
||||
- GitHub Actions 워크플로우 작성
|
||||
`.github/workflows/backend-cicd.yaml` 파일 생성 방법을 안내합니다.
|
||||
|
||||
주요 구성 요소:
|
||||
- **Build & Test**: Gradle 기반 빌드 및 단위 테스트
|
||||
- **SonarQube Analysis**: 코드 품질 분석 및 Quality Gate
|
||||
- **Container Build & Push**: 환경별 이미지 태그로 빌드 및 푸시
|
||||
- **Kustomize Deploy**: 환경별 매니페스트 적용
|
||||
|
||||
```yaml
|
||||
name: Backend Services CI/CD
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
paths:
|
||||
- '{서비스명1}/**'
|
||||
- '{서비스명2}/**'
|
||||
- '{서비스명3}/**'
|
||||
- '{서비스명N}/**'
|
||||
- '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: ${{ secrets.REGISTRY }}
|
||||
IMAGE_ORG: ${{ secrets.IMAGE_ORG }}
|
||||
RESOURCE_GROUP: ${{ secrets.RESOURCE_GROUP }}
|
||||
AKS_CLUSTER: ${{ secrets.AKS_CLUSTER }}
|
||||
|
||||
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 {버전}
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '{JDK버전}'
|
||||
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="{ACR_NAME}.azurecr.io"
|
||||
IMAGE_ORG="{SYSTEM_NAME}"
|
||||
RESOURCE_GROUP="{RESOURCE_GROUP}"
|
||||
AKS_CLUSTER="{AKS_CLUSTER}"
|
||||
NAMESPACE="{NAMESPACE}"
|
||||
|
||||
# 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=({SERVICE_NAME1} {SERVICE_NAME2} {SERVICE_NAME3} {SERVICE_NAMEN})
|
||||
|
||||
# Run tests, coverage reports, and SonarQube analysis for each service
|
||||
for service in "${services[@]}"; do
|
||||
./gradlew :$service:test :$service:jacocoTestReport :$service:sonar \
|
||||
-Dsonar.projectKey={SYSTEM_NAME}-$service-${{ steps.determine_env.outputs.environment }} \
|
||||
-Dsonar.projectName={SYSTEM_NAME}-$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: |
|
||||
{SERVICE_NAME1}/build/libs/*.jar
|
||||
{SERVICE_NAME2}/build/libs/*.jar
|
||||
{SERVICE_NAME3}/build/libs/*.jar
|
||||
{SERVICE_NAMEN}/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=${{ needs.build.outputs.registry }}" >> $GITHUB_ENV
|
||||
echo "IMAGE_ORG=${{ needs.build.outputs.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=({SERVICE_NAME1} {SERVICE_NAME2} {SERVICE_NAME3} {SERVICE_NAMEN})
|
||||
|
||||
# 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 deployment/cicd/kustomize/overlays/${{ env.ENVIRONMENT }}
|
||||
|
||||
# 각 서비스별 이미지 태그 업데이트
|
||||
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/api-gateway:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/user-service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/bill-service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/product-service:${{ env.ENVIRONMENT }}-${{ env.IMAGE_TAG }}
|
||||
kustomize edit set image ${{ env.REGISTRY }}/${{ env.IMAGE_ORG }}/kos-mock:${{ 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/${{ env.ENVIRONMENT }}-api-gateway --timeout=300s
|
||||
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/${{ env.ENVIRONMENT }}-user-service --timeout=300s
|
||||
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/${{ env.ENVIRONMENT }}-bill-service --timeout=300s
|
||||
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/${{ env.ENVIRONMENT }}-product-service --timeout=300s
|
||||
kubectl -n ${{ env.NAMESPACE }} wait --for=condition=available deployment/${{ env.ENVIRONMENT }}-kos-mock --timeout=300s
|
||||
|
||||
```
|
||||
|
||||
- GitHub Actions 전용 환경별 설정 파일 작성
|
||||
`.github/config/deploy_env_vars_{환경}` 파일 생성 방법
|
||||
|
||||
**.github/config/deploy_env_vars_dev**
|
||||
```bash
|
||||
# dev Environment Configuration
|
||||
resource_group={RESOURCE_GROUP}
|
||||
cluster_name={AKS_CLUSTER}
|
||||
```
|
||||
|
||||
**.github/config/deploy_env_vars_staging**
|
||||
```bash
|
||||
# staging Environment Configuration
|
||||
resource_group={RESOURCE_GROUP}
|
||||
cluster_name={AKS_CLUSTER}
|
||||
```
|
||||
|
||||
**.github/config/deploy_env_vars_prod**
|
||||
```bash
|
||||
# prod Environment Configuration
|
||||
resource_group={RESOURCE_GROUP}
|
||||
cluster_name={AKS_CLUSTER}
|
||||
```
|
||||
|
||||
**참고**: Kustomize 방식에서는 namespace, replicas, resources 등은 kustomization.yaml과 patch 파일에서 관리됩니다.
|
||||
|
||||
- GitHub Actions 전용 수동 배포 스크립트 작성
|
||||
`.github/scripts/deploy-actions.sh` 파일 생성:
|
||||
```bash
|
||||
#!/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 {NAMESPACE}..."
|
||||
kubectl create namespace {NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# 환경별 이미지 태그 업데이트 (.github/kustomize 사용)
|
||||
cd .github/kustomize/overlays/${ENVIRONMENT}
|
||||
|
||||
echo "🔄 Updating image tags..."
|
||||
# 서비스 배열 정의
|
||||
services=({SERVICE_NAME1} {SERVICE_NAME2} {SERVICE_NAME3} {SERVICE_NAMEN})
|
||||
|
||||
# 각 서비스별 이미지 태그 업데이트
|
||||
for service in "${services[@]}"; do
|
||||
kustomize edit set image {ACR_NAME}.azurecr.io/{SYSTEM_NAME}/$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/${ENVIRONMENT}-$service -n {NAMESPACE} --timeout=300s
|
||||
done
|
||||
|
||||
echo "🔍 Health check..."
|
||||
# API Gateway Health Check (첫 번째 서비스가 API Gateway라고 가정)
|
||||
GATEWAY_SERVICE=${services[0]}
|
||||
GATEWAY_POD=$(kubectl get pod -n {NAMESPACE} -l app.kubernetes.io/name=${ENVIRONMENT}-$GATEWAY_SERVICE -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl -n {NAMESPACE} exec $GATEWAY_POD -- curl -f http://localhost:8080/actuator/health || echo "Health check failed, but deployment completed"
|
||||
|
||||
echo "📋 Service Information:"
|
||||
kubectl get pods -n {NAMESPACE}
|
||||
kubectl get services -n {NAMESPACE}
|
||||
kubectl get ingress -n {NAMESPACE}
|
||||
|
||||
echo "✅ GitHub Actions deployment completed successfully!"
|
||||
```
|
||||
|
||||
- SonarQube 프로젝트 설정 방법 작성
|
||||
- SonarQube에서 각 서비스별 프로젝트 생성
|
||||
- Quality Gate 설정:
|
||||
```
|
||||
Coverage: >= 80%
|
||||
Duplicated Lines: <= 3%
|
||||
Maintainability Rating: <= A
|
||||
Reliability Rating: <= A
|
||||
Security Rating: <= A
|
||||
```
|
||||
|
||||
- 롤백 방법 작성
|
||||
- GitHub Actions에서 이전 버전으로 롤백:
|
||||
```bash
|
||||
# 이전 워크플로우 실행으로 롤백
|
||||
1. GitHub > Actions > 성공한 이전 워크플로우 선택
|
||||
2. Re-run all jobs 클릭
|
||||
```
|
||||
- kubectl을 이용한 롤백:
|
||||
```bash
|
||||
# 특정 버전으로 롤백
|
||||
kubectl rollout undo deployment/{환경}-{서비스명} -n {NAMESPACE} --to-revision=2
|
||||
|
||||
# 롤백 상태 확인
|
||||
kubectl rollout status deployment/{환경}-{서비스명} -n {NAMESPACE}
|
||||
```
|
||||
- 수동 스크립트를 이용한 롤백:
|
||||
```bash
|
||||
# 이전 안정 버전 이미지 태그로 배포
|
||||
./deployment/cicd/scripts/deploy-actions.sh {환경} {이전태그}
|
||||
```
|
||||
|
||||
[체크리스트]
|
||||
GitHub Actions CI/CD 파이프라인 구축 작업을 누락 없이 진행하기 위한 체크리스트입니다.
|
||||
|
||||
## 📋 사전 준비 체크리스트
|
||||
- [ ] settings.gradle에서 시스템명과 서비스명 확인 완료
|
||||
- [ ] 실행정보 섹션에서 ACR명, 리소스 그룹, AKS 클러스터명 확인 완료
|
||||
|
||||
## 📂 GitHub Actions 전용 Kustomize 구조 생성 체크리스트
|
||||
- [ ] 디렉토리 구조 생성: `.github/kustomize/{base,overlays/{dev,staging,prod}}`
|
||||
- [ ] 서비스별 base 디렉토리 생성: `.github/kustomize/base/{common,{서비스명들}}`
|
||||
- [ ] 기존 k8s 매니페스트를 base로 복사 완료
|
||||
- [ ] **리소스 누락 방지 검증 완료**:
|
||||
- [ ] `ls .github/kustomize/base/*/` 명령으로 모든 서비스 디렉토리의 파일 확인
|
||||
- [ ] 각 서비스별 필수 파일 존재 확인 (deployment.yaml, service.yaml 필수)
|
||||
- [ ] ConfigMap 파일 존재 시 `cm-{서비스명}.yaml` 명명 규칙 준수 확인
|
||||
- [ ] Secret 파일 존재 시 `secret-{서비스명}.yaml` 명명 규칙 준수 확인
|
||||
- [ ] Base kustomization.yaml 파일 생성 완료
|
||||
- [ ] 모든 서비스의 deployment.yaml, service.yaml 포함 확인
|
||||
- [ ] 존재하는 모든 ConfigMap 파일 포함 확인 (`cm-{서비스명}.yaml`)
|
||||
- [ ] 존재하는 모든 Secret 파일 포함 확인 (`secret-{서비스명}.yaml`)
|
||||
- [ ] **검증 명령어 실행 완료**:
|
||||
- [ ] `kubectl kustomize .github/kustomize/base/` 정상 실행 확인
|
||||
- [ ] 에러 메시지 없이 모든 리소스 출력 확인
|
||||
|
||||
## 🔧 GitHub Actions 전용 환경별 Overlay 구성 체크리스트
|
||||
### 중요 체크 사항
|
||||
- Base Kustomization에서 존재하지 않는 Secret 파일들 제거
|
||||
|
||||
### 공통 체크 사항
|
||||
- **base 매니페스트에 없는 항목을 추가하지 않았는지 체크**
|
||||
- **base 매니페스트와 항목이 일치 하는지 체크**
|
||||
- Secret 매니페스트에 'data'가 아닌 'stringData'사용했는지 체크
|
||||
- **⚠️ Kustomize patch 방법 변경**: `patchesStrategicMerge` → `patches` (target 명시)
|
||||
|
||||
### DEV 환경
|
||||
- [ ] `.github/kustomize/overlays/dev/kustomization.yaml` 생성 완료
|
||||
- [ ] `.github/kustomize/overlays/dev/cm-common-patch.yaml` 생성 완료 (dev 프로파일, update DDL)
|
||||
- [ ] `.github/kustomize/overlays/dev/secret-common-patch.yaml` 생성 완료
|
||||
- [ ] `.github/kustomize/overlays/dev/ingress-patch.yaml` 생성 완료 (**Host 기본값은 base의 ingress.yaml과 동일**)
|
||||
- [ ] `.github/kustomize/overlays/dev/deployment-{서비스명}-patch.yaml` 생성 완료 (replicas, resources 지정)
|
||||
- [ ] 각 서비스별 `.github/kustomize/overlays/dev/secret-{서비스명}-patch.yaml` 생성 완료
|
||||
|
||||
### STAGING 환경
|
||||
- [ ] `.github/kustomize/overlays/staging/kustomization.yaml` 생성 완료
|
||||
- [ ] `.github/kustomize/overlays/staging/cm-common-patch.yaml` 생성 완료 (staging 프로파일, validate DDL)
|
||||
- [ ] `.github/kustomize/overlays/staging/secret-common-patch.yaml` 생성 완료
|
||||
- [ ] `.github/kustomize/overlays/staging/ingress-patch.yaml` 생성 완료 (prod 도메인, HTTPS, SSL 인증서)
|
||||
- [ ] `.github/kustomize/overlays/staging/deployment-{서비스명}-patch.yaml` 생성 완료 (replicas, resources 지정)
|
||||
- [ ] 각 서비스별 `.github/kustomize/overlays/staging/secret-{서비스명}-patch.yaml` 생성 완료
|
||||
|
||||
### PROD 환경
|
||||
- [ ] `.github/kustomize/overlays/prod/kustomization.yaml` 생성 완료
|
||||
- [ ] `.github/kustomize/overlays/prod/cm-common-patch.yaml` 생성 완료 (prod 프로파일, validate DDL, 짧은 JWT)
|
||||
- [ ] `.github/kustomize/overlays/prod/secret-common-patch.yaml` 생성 완료
|
||||
- [ ] `.github/kustomize/overlays/prod/ingress-patch.yaml` 생성 완료 (prod 도메인, HTTPS, SSL 인증서)
|
||||
- [ ] `.github/kustomize/overlays/prod/deployment-{서비스명}-patch.yaml` 생성 완료 (replicas, resources 지정)
|
||||
- [ ] 각 서비스별 `.github/kustomize/overlays/prod/secret-{서비스명}-patch.yaml` 생성 완료
|
||||
|
||||
## ⚙️ GitHub Actions 설정 및 스크립트 체크리스트
|
||||
- [ ] 환경별 설정 파일 생성: `.github/config/deploy_env_vars_{dev,staging,prod}`
|
||||
- [ ] GitHub Actions 워크플로우 파일 `.github/workflows/backend-cicd.yaml` 생성 완료
|
||||
- [ ] 워크플로우 주요 내용 확인
|
||||
- Build, SonarQube, Docker Build & Push, Deploy 단계 포함
|
||||
- JDK 버전 확인: `java-version: '{JDK버전}'`
|
||||
- 변수 참조 문법 확인: `${{ needs.build.outputs.* }}` 사용
|
||||
- 모든 서비스명이 실제 프로젝트 서비스명으로 치환되었는지 확인
|
||||
- **환경 변수 SKIP_SONARQUBE 처리 확인**: 기본값 'true', 조건부 실행
|
||||
- **플레이스홀더 사용 확인**: {ACR_NAME}, {SYSTEM_NAME}, {SERVICE_NAME} 등
|
||||
|
||||
- [ ] 수동 배포 스크립트 `.github/scripts/deploy-actions.sh` 생성 완료
|
||||
- [ ] 스크립트 실행 권한 설정 완료 (`chmod +x .github/scripts/*.sh`)
|
||||
|
||||
[결과파일]
|
||||
- 가이드: .github/actions-pipeline-guide.md
|
||||
- GitHub Actions 워크플로우: .github/workflows/backend-cicd.yaml
|
||||
- GitHub Actions 전용 Kustomize 매니페스트: .github/kustomize/*
|
||||
- GitHub Actions 전용 환경별 설정 파일: .github/config/*
|
||||
- GitHub Actions 전용 수동배포 스크립트: .github/scripts/deploy-actions.sh
|
||||
@ -1,454 +0,0 @@
|
||||
# 유저스토리 v2.2.0 → v2.3.0 변경사항 보고서
|
||||
|
||||
**작성일**: 2025-10-25
|
||||
**작성자**: 지수 (Product Designer), 민준 (Product Owner)
|
||||
**문서 버전**: 1.0
|
||||
|
||||
---
|
||||
|
||||
## 📋 개요
|
||||
|
||||
본 보고서는 AI기반 회의록 작성 및 이력 관리 개선 서비스의 유저스토리 문서가 v2.2.0에서 v2.3.0으로 업데이트되면서 변경된 내용과 그 의미를 분석합니다.
|
||||
|
||||
### 요약 통계
|
||||
|
||||
| 항목 | v2.2.0 | v2.3.0 | 변화 |
|
||||
|------|--------|--------|------|
|
||||
| **유저스토리 수** | 25개 | 27개 | +2개 (+8%) |
|
||||
| **신규 추가** | - | 5개 | UFR-USER-010, UFR-USER-020, UFR-MEET-015, UFR-AI-030, UFR-NOTI-010 |
|
||||
| **삭제/전환** | - | 2개 | AFR-USER-010, AFR-USER-020 → UFR로 전환 |
|
||||
| **AFR 코드** | 2개 | 0개 | -2개 (100% 제거) |
|
||||
| **UFR 코드** | 23개 | 27개 | +4개 (+17%) |
|
||||
| **평균 상세도** | 20-30줄 | 60-100줄 | **약 3배 증가** |
|
||||
| **프로토타입 연계** | 부분적 | 100% (10개 화면) | - |
|
||||
| **표준 형식 적용** | 0% | 100% (27개) | - |
|
||||
|
||||
---
|
||||
|
||||
## 📊 한눈에 보는 변경사항
|
||||
|
||||
```
|
||||
v2.2.0 (25개) v2.3.0 (27개)
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ AFR-USER-010 │ ──────────────────>│ UFR-USER-010 ✨ │ (로그인 상세화)
|
||||
│ AFR-USER-020 │ ──────────────────>│ UFR-USER-020 ✨ │ (대시보드 재설계)
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ UFR-MEET-010 │ ──────────────────>│ UFR-MEET-010 ✨ │ (회의예약 개선)
|
||||
│ │ │ UFR-MEET-015 🆕 │ (참석자 실시간 초대)
|
||||
│ UFR-MEET-020 │ ──────────────────>│ UFR-MEET-020 ✨ │ (템플릿선택 상세화)
|
||||
│ UFR-MEET-030 │ ──────────────────>│ UFR-MEET-030 ✨ │ (회의시작 4개 탭)
|
||||
│ UFR-MEET-040 │ ──────────────────>│ UFR-MEET-040 ✨ │ (회의종료 3가지 액션)
|
||||
│ UFR-MEET-050 │ ──────────────────>│ UFR-MEET-050 ✨ │ (최종확정 2가지 시나리오)
|
||||
│ UFR-MEET-046 │ ──────────────────>│ UFR-MEET-046 ✨ │ (목록조회 샘플 30개)
|
||||
│ UFR-MEET-047 │ ──────────────────>│ UFR-MEET-047 ✨ │ (상세조회 관련회의록)
|
||||
│ UFR-MEET-055 │ ──────────────────>│ UFR-MEET-055 ✨ │ (회의록수정 3가지 시나리오)
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ UFR-AI-010 │ ──────────────────>│ UFR-AI-010 │
|
||||
│ UFR-AI-020 │ ──────────────────>│ UFR-AI-020 │
|
||||
│ │ │ UFR-AI-030 🆕🎯 │ (실시간 AI 제안 - 차별화!)
|
||||
│ UFR-AI-035 │ ──────────────────>│ UFR-AI-035 │
|
||||
│ UFR-AI-036 │ ──────────────────>│ UFR-AI-036 │
|
||||
│ UFR-AI-040 │ ──────────────────>│ UFR-AI-040 │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ UFR-STT-010 │ ──────────────────>│ UFR-STT-010 │
|
||||
│ UFR-STT-020 │ ──────────────────>│ UFR-STT-020 │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ UFR-RAG-010 │ ──────────────────>│ UFR-RAG-010 │
|
||||
│ UFR-RAG-020 │ ──────────────────>│ UFR-RAG-020 │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ UFR-COLLAB-010 │ ──────────────────>│ UFR-COLLAB-010 │
|
||||
│ UFR-COLLAB-020 │ ──────────────────>│ UFR-COLLAB-020 │
|
||||
│ UFR-COLLAB-030 │ ──────────────────>│ UFR-COLLAB-030 │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ UFR-TODO-010 │ ──────────────────>│ UFR-TODO-010 │
|
||||
│ UFR-TODO-030 │ ──────────────────>│ UFR-TODO-030 │
|
||||
│ UFR-TODO-040 │ ──────────────────>│ UFR-TODO-040 │
|
||||
└─────────────────┘ ├─────────────────┤
|
||||
│ UFR-NOTI-010 🆕 │ (알림발송 - 폴링 방식)
|
||||
└─────────────────┘
|
||||
|
||||
범례:
|
||||
🆕 = 완전 신규 추가
|
||||
🎯 = 차별화 핵심 기능
|
||||
✨ = 대폭 개선 (프로토타입 기반 재작성)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 핵심 변경사항
|
||||
|
||||
### 1. 신규 추가된 유저스토리 (5개)
|
||||
|
||||
#### 1.1 UFR-USER-010: 로그인 🆕
|
||||
- **이전**: AFR-USER-010 (간략한 인증 설명)
|
||||
- **변경**: UFR-USER-010으로 전환 및 상세화
|
||||
- **의미**:
|
||||
- 로그인 프로세스 단계별 명시 (Enter 키 동작, 로딩 상태 등)
|
||||
- 예외처리 시나리오 구체화 (사번 미입력, 비밀번호 8자 미만 등)
|
||||
- 프로토타입 `01-로그인.html`과 1:1 매핑
|
||||
|
||||
#### 1.2 UFR-USER-020: 대시보드 🆕
|
||||
- **이전**: AFR-USER-020 (간략한 대시보드 설명)
|
||||
- **변경**: UFR-USER-020으로 전환 및 대폭 확장
|
||||
- **의미**:
|
||||
- 통계 블록, 최근 회의, 나의 Todo, 나의 회의록 위젯 상세 명세
|
||||
- FAB 버튼 2가지 액션 (회의예약/바로 시작) 명확화
|
||||
- 프로토타입 `02-대시보드.html`과 1:1 매핑
|
||||
|
||||
#### 1.3 UFR-MEET-015: 참석자 실시간 초대 🆕
|
||||
- **이전**: 없음
|
||||
- **변경**: 완전 신규 추가
|
||||
- **의미**:
|
||||
- 회의 진행 중 "참석자" 탭에서 실시간으로 참석자 추가 기능
|
||||
- 검색 모달 → 추가 → WebSocket 동기화 → 알림 발송 흐름 명시
|
||||
- **효과**: 회의 진행 중 동적 참석자 관리로 유연성 향상
|
||||
- 프로토타입 `05-회의진행.html`의 "참석자" 탭과 연계
|
||||
|
||||
#### 1.4 UFR-AI-030: 실시간 AI 제안 🆕🎯
|
||||
- **이전**: 없음
|
||||
- **변경**: 완전 신규 추가
|
||||
- **의미**:
|
||||
- **차별화 전략 "지능형 회의 진행 지원" 실현**
|
||||
- STT 텍스트 실시간 분석 → 주요 내용 감지 → AI 제안 카드 생성
|
||||
- 제안 카드에서 메모 탭으로 드래그 앤 드롭으로 추가
|
||||
- **효과**: 회의 중 놓치는 내용 최소화, 차별화 핵심 기능
|
||||
- 프로토타입 `05-회의진행.html`의 "AI 제안" 탭과 연계
|
||||
|
||||
#### 1.5 UFR-NOTI-010: 알림 발송 🆕
|
||||
- **이전**: 없음 (암묵적으로 Meeting Service에서 직접 발송)
|
||||
- **변경**: Notification 서비스의 독립적인 유저스토리로 추가
|
||||
- **의미**:
|
||||
- **알림 아키텍처를 폴링 방식으로 통일**
|
||||
- 1분 간격 폴링 → 이메일 발송 → 최대 3회 재시도
|
||||
- 6가지 알림 유형 명시 (Todo 할당, Todo 완료, 회의 시작, 회의록 확정, 참석자 초대, 회의록 수정)
|
||||
- **효과**: Notification 서비스 독립성 확보, 시스템 안정성 향상
|
||||
|
||||
---
|
||||
|
||||
### 2. 대폭 개선된 유저스토리 (주요 8개)
|
||||
|
||||
#### 2.1 UFR-MEET-010: 회의예약
|
||||
- **변경사항**:
|
||||
- 수행절차 10단계 명시 (FAB 버튼 → 입력 → 저장/완료)
|
||||
- 입력 필드별 상세 명세 (타입, 필수 여부, 최대/최소값, UI 요소)
|
||||
- 임시저장/예약 완료 2가지 시나리오 구분
|
||||
- 예외처리 7가지 추가 (제목 미입력, 과거 날짜, 참석자 미선택 등)
|
||||
- **의미**: 프로토타입 `03-회의예약.html` 기반 전면 재작성
|
||||
|
||||
#### 2.2 UFR-MEET-030: 회의시작
|
||||
- **변경사항**:
|
||||
- 회의 진행 화면 4개 탭 상세 명세 (녹음/메모, 참석자, AI 제안, 안건)
|
||||
- 녹음 시작/일시정지/재시작 플로우 명시
|
||||
- 참석자 상태 표시 (온라인/오프라인/참석중)
|
||||
- 탭별 UI 요소와 인터랙션 상세화
|
||||
- **의미**: 프로토타입 `05-회의진행.html` 4개 탭 구조 반영
|
||||
|
||||
#### 2.3 UFR-MEET-040: 회의종료
|
||||
- **변경사항**:
|
||||
- 회의 종료 후 3가지 액션 명시 (바로 확정, 나중에 확정, 검토 후 확정)
|
||||
- 각 액션별 이동 화면 명확화
|
||||
- 안건 요약 및 검증 상태 표시 추가
|
||||
- **의미**: 프로토타입 `07-회의종료.html` 반영, 사용자 선택권 강화
|
||||
|
||||
#### 2.4 UFR-MEET-050: 최종확정
|
||||
- **변경사항**:
|
||||
- 2가지 시나리오 분리 (검토 후 확정, 회의 종료 화면에서 바로 확정)
|
||||
- 안건별 검증 완료 여부 체크 로직 추가
|
||||
- 미검증 안건 있을 시 확정 불가 정책 명시
|
||||
- **의미**: 회의록 품질 보증 메커니즘 강화
|
||||
|
||||
#### 2.5 UFR-MEET-046: 회의록목록조회
|
||||
- **변경사항**:
|
||||
- 샘플 데이터 30개 명시 (제목, 날짜, 상태, 검증 현황 등)
|
||||
- 필터/정렬 기능 상세화 (기간, 상태, 폴더별)
|
||||
- 상태 배지 5종 추가 (진행중, 검토중, 확정완료 등)
|
||||
- **의미**: 프로토타입 `12-회의록목록조회.html` 반영
|
||||
|
||||
#### 2.6 UFR-MEET-047: 회의록상세조회
|
||||
- **변경사항**:
|
||||
- 관련 회의록 섹션 추가 (AI가 자동 연결한 회의록 3개 표시)
|
||||
- 안건별 검증 상태 표시 추가
|
||||
- 용어 팝업 연계 (UFR-RAG-010) 명시
|
||||
- **의미**: 프로토타입 `10-회의록상세조회.html` 반영, RAG 기능 연계
|
||||
|
||||
#### 2.7 UFR-MEET-055: 회의록수정
|
||||
- **변경사항**:
|
||||
- 3가지 진입 시나리오 명시 (회의종료 화면, 목록 화면, 상세조회 화면)
|
||||
- 실시간 협업 플로우 상세화 (UFR-COLLAB-010, UFR-COLLAB-020 연계)
|
||||
- 수정 저장/임시저장/취소 3가지 액션 구분
|
||||
- **의미**: 프로토타입 `11-회의록수정.html` 반영, 협업 기능 강화
|
||||
|
||||
#### 2.8 UFR-COLLAB-020: 충돌해결
|
||||
- **변경사항**:
|
||||
- 안건 기반 충돌 방지 메커니즘 상세화
|
||||
- 동일 안건 동시 수정 시 경고 표시 및 잠금 정책 명시
|
||||
- 충돌 해결 시나리오 3가지 (대기, 새 안건 작성, 취소)
|
||||
- **의미**: 실시간 협업 안정성 강화
|
||||
|
||||
---
|
||||
|
||||
### 3. 유지된 유저스토리 (14개)
|
||||
|
||||
다음 유저스토리들은 v2.2.0과 v2.3.0에서 ID와 핵심 내용이 유지되었습니다:
|
||||
|
||||
- UFR-AI-010 (회의록 자동 작성)
|
||||
- UFR-AI-020 (Todo 자동 추출)
|
||||
- UFR-AI-035 (섹션 AI 요약)
|
||||
- UFR-AI-036 (AI 한줄 요약)
|
||||
- UFR-AI-040 (관련 회의록 연결)
|
||||
- UFR-STT-010 (음성 녹음 인식)
|
||||
- UFR-STT-020 (텍스트 변환)
|
||||
- UFR-RAG-010 (전문용어 감지)
|
||||
- UFR-RAG-020 (맥락 기반 용어 설명)
|
||||
- UFR-COLLAB-010 (회의록 수정 동기화)
|
||||
- UFR-COLLAB-030 (검증 완료)
|
||||
- UFR-TODO-010 (Todo 할당)
|
||||
- UFR-TODO-030 (Todo 완료 처리)
|
||||
- UFR-TODO-040 (Todo 관리)
|
||||
|
||||
---
|
||||
|
||||
## 📈 문서 품질 개선
|
||||
|
||||
### 3.1 유저스토리 형식 표준화
|
||||
|
||||
#### Before (v2.2.0) - 자유 형식
|
||||
```
|
||||
UFR-MEET-010: [회의예약] 회의 생성자로서 | 나는, ...
|
||||
- 시나리오: 회의 예약 및 참석자 초대
|
||||
회의 예약 화면에 접근한 상황에서 | ...
|
||||
|
||||
[입력 요구사항]
|
||||
- 회의 제목: 최대 100자 (필수)
|
||||
...
|
||||
|
||||
[처리 결과]
|
||||
- 회의가 예약됨
|
||||
...
|
||||
|
||||
- M/13
|
||||
```
|
||||
|
||||
#### After (v2.3.0) - 표준 5단계 형식
|
||||
```
|
||||
### UFR-MEET-010: [회의예약] 회의 생성자로서 | 나는, ...
|
||||
|
||||
**수행절차:**
|
||||
1. 대시보드에서 "회의예약" FAB 버튼 클릭
|
||||
2. 회의 제목 입력 (최대 100자)
|
||||
3. 날짜 선택 (오늘 이후 날짜, 달력 UI)
|
||||
...
|
||||
10. "임시저장" 버튼 또는 "예약 완료" 버튼 클릭
|
||||
|
||||
**입력:**
|
||||
- 회의 제목: 텍스트 입력, 필수, 최대 100자, 문자 카운터 표시
|
||||
- 날짜: date 타입, 필수, 오늘 이후 날짜만 선택 가능
|
||||
...
|
||||
|
||||
**출력/결과:**
|
||||
- 예약 완료: "회의가 예약되었습니다" 토스트 메시지, 대시보드로 이동
|
||||
- 임시저장: "임시 저장되었습니다" 토스트 메시지
|
||||
...
|
||||
|
||||
**예외처리:**
|
||||
- 제목 미입력: "회의 제목을 입력해주세요" 토스트, 제목 필드 포커스
|
||||
- 과거 날짜 선택: "과거 날짜는 선택할 수 없습니다" 토스트
|
||||
...
|
||||
|
||||
**관련 유저스토리:**
|
||||
- UFR-USER-020: 대시보드 조회
|
||||
- UFR-MEET-020: 템플릿선택
|
||||
```
|
||||
|
||||
### 3.2 개선 효과
|
||||
|
||||
| 섹션 | 개선 효과 |
|
||||
|------|-----------|
|
||||
| **수행절차** | 단계별 명확한 작업 흐름, 개발자가 UI 플로우 이해 가능 |
|
||||
| **입력** | 필드 타입, 검증 규칙, UI 요소 상세 명세, API 명세서 작성 기준 제공 |
|
||||
| **출력/결과** | 성공/실패 시나리오별 응답 명시, 테스트 케이스 작성 기준 제공 |
|
||||
| **예외처리** | 에러 상황별 처리 방법 구체화, QA 시나리오 명확화 |
|
||||
| **관련 유저스토리** | 기능 간 연계성 추적, 통합 테스트 범위 파악 용이 |
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 프로토타입 연계 강화
|
||||
|
||||
v2.3.0에서는 모든 유저스토리가 프로토타입 화면과 명확하게 연계되었습니다.
|
||||
|
||||
| 프로토타입 화면 | 연계 유저스토리 | 상태 |
|
||||
|----------------|----------------|------|
|
||||
| 01-로그인.html | UFR-USER-010 | ✅ 1:1 매핑 |
|
||||
| 02-대시보드.html | UFR-USER-020 | ✅ 1:1 매핑 |
|
||||
| 03-회의예약.html | UFR-MEET-010 | ✅ 1:1 매핑 |
|
||||
| 04-템플릿선택.html | UFR-MEET-020 | ✅ 1:1 매핑 |
|
||||
| 05-회의진행.html | UFR-MEET-030, UFR-MEET-015 (신규), UFR-AI-030 (신규) | ✅ 1:N 매핑 |
|
||||
| 07-회의종료.html | UFR-MEET-040 | ✅ 1:1 매핑 |
|
||||
| 10-회의록상세조회.html | UFR-MEET-047 | ✅ 1:1 매핑 |
|
||||
| 11-회의록수정.html | UFR-MEET-055 | ✅ 1:1 매핑 |
|
||||
| 12-회의록목록조회.html | UFR-MEET-046 | ✅ 1:1 매핑 |
|
||||
| 08-최종확정.html | UFR-MEET-050 | ✅ 1:1 매핑 |
|
||||
|
||||
**결과**: 10개 프로토타입 화면 100% 유저스토리 연계 완료
|
||||
|
||||
---
|
||||
|
||||
## 🔑 핵심 아키텍처 변경
|
||||
|
||||
### 알림 아키텍처: 실시간 → 폴링 방식
|
||||
|
||||
#### Before (v2.2.0)
|
||||
```
|
||||
[Meeting Service] ──(실시간 발송)──> [Notification Service] ──> [Email]
|
||||
↓
|
||||
Todo 할당 발생 → 즉시 이메일 발송
|
||||
```
|
||||
|
||||
**문제점**:
|
||||
- Meeting Service와 Notification Service 간 강한 결합
|
||||
- 이메일 발송 실패 시 Meeting Service에 영향
|
||||
|
||||
#### After (v2.3.0)
|
||||
```
|
||||
[Meeting Service] ──(DB 레코드 생성)──> [Notification 테이블]
|
||||
↓
|
||||
(1분 간격 폴링)
|
||||
↓
|
||||
[Notification Service] ──> [Email]
|
||||
↓
|
||||
(발송 상태 업데이트)
|
||||
```
|
||||
|
||||
**개선 효과**:
|
||||
- ✅ **Notification 서비스 독립성 강화**: 마이크로서비스 간 느슨한 결합
|
||||
- ✅ **시스템 안정성 향상**: 이메일 발송 실패 시 자동 재시도 (최대 3회)
|
||||
- ✅ **확장성 확보**: 폴링 주기 조정으로 트래픽 제어 가능
|
||||
- ✅ **모니터링 용이**: 발송 대기/성공/실패 상태 DB에서 추적
|
||||
|
||||
---
|
||||
|
||||
## 💡 변경의 의미와 개선 효과
|
||||
|
||||
### 1. 사용자 경험 (UX) 개선
|
||||
|
||||
| 영역 | 개선 내용 | 효과 |
|
||||
|------|----------|------|
|
||||
| **회의 진행 중 유연성** | UFR-MEET-015 (참석자 실시간 초대) | 회의 중 동적 참석자 관리 가능 |
|
||||
| **회의 중 놓침 방지** | UFR-AI-030 (실시간 AI 제안) 🎯 | 차별화 핵심 기능, 회의 중 주요 내용 실시간 감지 |
|
||||
| **회의 종료 후 선택권** | UFR-MEET-040 (3가지 액션) | 바로 확정/나중에 확정/검토 후 확정 |
|
||||
| **회의록 품질 보증** | UFR-MEET-050 (검증 후 확정) | 미검증 안건 있을 시 확정 불가 정책 |
|
||||
| **실시간 협업 안정성** | UFR-COLLAB-020 (안건 기반 충돌 방지) | 동일 안건 동시 수정 시 경고 및 잠금 |
|
||||
|
||||
### 2. 기능적 개선
|
||||
|
||||
| 영역 | 개선 내용 | 효과 |
|
||||
|------|----------|------|
|
||||
| **알림 시스템 안정성** | UFR-NOTI-010 (폴링 방식) | Notification 서비스 독립성 확보, 재시도 메커니즘 |
|
||||
| **차별화 전략 실현** | UFR-AI-030 (실시간 AI 제안) 🎯 | "지능형 회의 진행 지원" 구체화 |
|
||||
| **프로토타입 정합성** | 10개 화면 100% 매핑 | 기획-디자인-개발 간 일관성 확보 |
|
||||
| **유저스토리 표준화** | 5단계 표준 형식 | 개발 가이드 역할 강화, API 명세서 작성 기준 제공 |
|
||||
|
||||
### 3. 문서화 개선
|
||||
|
||||
| 영역 | 개선 내용 | 효과 |
|
||||
|------|----------|------|
|
||||
| **상세도 3배 증가** | 20-30줄 → 60-100줄 | 개발자가 구현에 필요한 모든 정보 확보 |
|
||||
| **AFR 코드 폐지** | AFR → UFR 통일 | 유저스토리 체계 단순화 |
|
||||
| **예외처리 명시** | 각 유저스토리별 5-7개 예외 시나리오 | QA 테스트 케이스 작성 기준 제공 |
|
||||
| **관련 유저스토리 연계** | 기능 간 의존성 추적 | 통합 테스트 범위 명확화 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 권장 후속 조치
|
||||
|
||||
### 🔴 긴급 (1주 내)
|
||||
|
||||
- [ ] **신규 유저스토리 3개 기반 API 설계**
|
||||
- UFR-MEET-015: 참석자 실시간 초대 API
|
||||
- UFR-AI-030: 실시간 AI 제안 API (SSE 또는 WebSocket)
|
||||
- UFR-NOTI-010: 알림 폴링 및 발송 API
|
||||
|
||||
- [ ] **알림 아키텍처 폴링 방식 반영**
|
||||
- 물리 아키텍처 다이어그램 업데이트
|
||||
- Notification 테이블 스키마 정의
|
||||
- 폴링 스케줄러 설계
|
||||
|
||||
- [ ] **프로토타입 ↔ 유저스토리 1:1 매핑 검증**
|
||||
- 10개 화면별 유저스토리 매핑 검증
|
||||
- 누락된 화면 또는 유저스토리 확인
|
||||
|
||||
### 🟡 중요 (2주 내)
|
||||
|
||||
- [ ] **API 설계서 v2.3.0 기반 전면 업데이트**
|
||||
- 입력/출력 명세 반영 (타입, 필수 여부, 검증 규칙)
|
||||
- 예외처리 시나리오 → HTTP 상태 코드 및 에러 메시지 매핑
|
||||
- 관련 유저스토리 기반 API 그룹핑
|
||||
|
||||
- [ ] **예외처리 시나리오 → 테스트 케이스 전환**
|
||||
- 각 유저스토리의 예외처리 섹션을 테스트 케이스로 변환
|
||||
- 입력 검증 테스트 케이스 작성
|
||||
|
||||
- [ ] **관련 유저스토리 기반 통합 테스트 시나리오 작성**
|
||||
- 예: UFR-MEET-010 → UFR-MEET-020 → UFR-MEET-030 전체 플로우 테스트
|
||||
|
||||
### 🟢 일반 (3주 내)
|
||||
|
||||
- [ ] **유저스토리별 개발 우선순위 재평가**
|
||||
- 신규 유저스토리 3개 우선순위 결정
|
||||
- 차별화 핵심 기능 (UFR-AI-030) 우선 개발 검토
|
||||
|
||||
- [ ] **신규 기능 3개 개발 일정 수립**
|
||||
- UFR-MEET-015: 참석자 실시간 초대
|
||||
- UFR-AI-030: 실시간 AI 제안 (Sprint 목표로 권장)
|
||||
- UFR-NOTI-010: 알림 발송
|
||||
|
||||
- [ ] **프로토타입 기반 개발 가이드 작성**
|
||||
- 프로토타입 → 유저스토리 → API → 컴포넌트 매핑 가이드
|
||||
- 프론트엔드 개발자를 위한 프로토타입 활용 가이드
|
||||
|
||||
---
|
||||
|
||||
## 🔍 핵심 시사점 (Key Takeaways)
|
||||
|
||||
1. **v2.3.0은 프로토타입 분석을 통해 유저스토리를 전면 재정비한 버전**
|
||||
- 10개 프로토타입 화면과 100% 매핑
|
||||
- 실제 UI/UX 플로우를 유저스토리에 반영
|
||||
|
||||
2. **신규 기능 3개 추가로 차별화 강화**
|
||||
- 특히 UFR-AI-030 (실시간 AI 제안)은 차별화 핵심 기능
|
||||
|
||||
3. **알림 아키텍처 폴링 방식으로 통일하여 시스템 안정성 확보**
|
||||
- Notification 서비스 독립성 강화
|
||||
- 재시도 메커니즘으로 안정성 향상
|
||||
|
||||
4. **유저스토리 형식 표준화로 개발 가이드 역할 강화**
|
||||
- 5단계 표준 형식 (수행절차, 입력, 출력/결과, 예외처리, 관련 유저스토리)
|
||||
- API 명세서 및 테스트 케이스 작성 기준 제공
|
||||
|
||||
5. **평균 유저스토리 상세도 약 3배 증가로 품질 대폭 향상**
|
||||
- 개발자가 구현에 필요한 모든 정보 포함
|
||||
- 예외처리, 검증 규칙, UI 요소까지 상세 명시
|
||||
|
||||
6. **기존 24개 유저스토리 ID 승계하여 연속성 유지**
|
||||
- AFR-USER-010 → UFR-USER-010 전환
|
||||
- 기존 설계 문서와의 연계성 유지
|
||||
|
||||
7. **프로토타입-유저스토리 1:1 매핑으로 개발 명확성 확보**
|
||||
- 기획-디자인-개발 간 일관성 확보
|
||||
- 개발 우선순위 및 Sprint 계획 수립 용이
|
||||
|
||||
---
|
||||
|
||||
## 📎 참고 자료
|
||||
|
||||
- **상세 분석 (JSON)**: `claude/userstory-comparison-v2.2.0-to-v2.3.0.json` (19KB)
|
||||
- **상세 분석 (Markdown)**: `claude/userstory-comparison-v2.2.0-to-v2.3.0.md` (16KB)
|
||||
- **요약 분석**: `claude/userstory-comparison-summary.md` (11KB)
|
||||
- **유저스토리 v2.2.0 백업**: `design/userstory_v2.2.0_backup.md`
|
||||
- **유저스토리 v2.3.0 현재**: `design/userstory.md`
|
||||
|
||||
---
|
||||
|
||||
**보고서 작성**: 지수 (Product Designer), 민준 (Product Owner)
|
||||
**분석 일시**: 2025-10-25
|
||||
**문서 버전**: 1.0
|
||||
420
deploy/k8s/backend/README.md
Normal file
420
deploy/k8s/backend/README.md
Normal file
@ -0,0 +1,420 @@
|
||||
# HGZero 백엔드 서비스 Kubernetes 배포 가이드
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 HGZero 백엔드 서비스(User, Meeting, Notification)를 Azure Kubernetes Service(AKS)에 배포하는 방법을 설명합니다.
|
||||
|
||||
## 배포 환경
|
||||
|
||||
- **ACR(Azure Container Registry)**: acrdigitalgarage02
|
||||
- **AKS(Azure Kubernetes Service)**: aks-digitalgarage-02
|
||||
- **네임스페이스**: hgzero
|
||||
- **리소스 그룹**: rg-digitalgarage-02
|
||||
|
||||
## 서비스 구성
|
||||
|
||||
### 1. User Service
|
||||
- **포트**: 8080
|
||||
- **이미지**: acrdigitalgarage02.azurecr.io/hgzero/user-service:latest
|
||||
- **기능**: 사용자 인증 및 관리, LDAP 연동
|
||||
|
||||
### 2. Meeting Service
|
||||
- **포트**: 8081 (HTTP), 8082 (WebSocket)
|
||||
- **이미지**: acrdigitalgarage02.azurecr.io/hgzero/meeting-service:latest
|
||||
- **기능**: 회의 관리, WebSocket 통신
|
||||
|
||||
### 3. Notification Service
|
||||
- **포트**: 8082
|
||||
- **이미지**: acrdigitalgarage02.azurecr.io/hgzero/notification-service:latest
|
||||
- **기능**: 알림 전송, 이메일 발송
|
||||
|
||||
## 리소스 할당
|
||||
|
||||
각 서비스는 다음과 같은 리소스를 사용합니다:
|
||||
|
||||
- **Requests**:
|
||||
- CPU: 256m
|
||||
- Memory: 256Mi
|
||||
- **Limits**:
|
||||
- CPU: 1024m
|
||||
- Memory: 1024Mi
|
||||
- **Replicas**: 1 (각 서비스)
|
||||
|
||||
## 사전 요구사항
|
||||
|
||||
1. **Azure CLI 설치**
|
||||
```bash
|
||||
# macOS
|
||||
brew install azure-cli
|
||||
|
||||
# Windows
|
||||
# Download from https://aka.ms/installazurecliwindows
|
||||
|
||||
# Linux
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||
```
|
||||
|
||||
2. **kubectl 설치**
|
||||
```bash
|
||||
# macOS
|
||||
brew install kubectl
|
||||
|
||||
# Windows
|
||||
# Download from https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/
|
||||
|
||||
# Linux
|
||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
|
||||
```
|
||||
|
||||
3. **Azure 로그인**
|
||||
```bash
|
||||
az login
|
||||
```
|
||||
|
||||
4. **ACR 로그인**
|
||||
```bash
|
||||
az acr login --name acrdigitalgarage02
|
||||
```
|
||||
|
||||
## 배포 파일 구조
|
||||
|
||||
```
|
||||
deploy/k8s/backend/
|
||||
├── namespace.yaml # 네임스페이스 정의
|
||||
├── configmap.yaml # ConfigMap (Redis, Mail 설정)
|
||||
├── secret-template.yaml # Secret 템플릿 (민감 정보)
|
||||
├── user-service.yaml # User Service 배포 매니페스트
|
||||
├── meeting-service.yaml # Meeting Service 배포 매니페스트
|
||||
├── notification-service.yaml # Notification Service 배포 매니페스트
|
||||
├── deploy.sh # 배포 스크립트
|
||||
├── undeploy.sh # 배포 해제 스크립트
|
||||
├── create-secrets.sh # Secret 생성 스크립트
|
||||
└── README.md # 이 문서
|
||||
```
|
||||
|
||||
## 배포 절차
|
||||
|
||||
### 1단계: AKS 클러스터 접근 설정
|
||||
|
||||
```bash
|
||||
# AKS credentials 가져오기
|
||||
az aks get-credentials \
|
||||
--resource-group rg-digitalgarage-02 \
|
||||
--name aks-digitalgarage-02 \
|
||||
--overwrite-existing
|
||||
|
||||
# 클러스터 연결 확인
|
||||
kubectl cluster-info
|
||||
```
|
||||
|
||||
### 2단계: 네임스페이스 생성
|
||||
|
||||
```bash
|
||||
kubectl apply -f namespace.yaml
|
||||
```
|
||||
|
||||
### 3단계: ConfigMap 생성
|
||||
|
||||
```bash
|
||||
kubectl apply -f configmap.yaml
|
||||
```
|
||||
|
||||
### 4단계: Secret 생성
|
||||
|
||||
옵션 A: 대화형 스크립트 사용 (권장)
|
||||
```bash
|
||||
./create-secrets.sh
|
||||
```
|
||||
|
||||
옵션 B: 직접 생성
|
||||
```bash
|
||||
# Database Secret
|
||||
kubectl create secret generic db-secret \
|
||||
--from-literal=host=<DB_HOST> \
|
||||
--from-literal=username=<DB_USERNAME> \
|
||||
--from-literal=password=<DB_PASSWORD> \
|
||||
--namespace=hgzero
|
||||
|
||||
# Azure Secret
|
||||
kubectl create secret generic azure-secret \
|
||||
--from-literal=eventhub-connection-string=<EVENTHUB_CONN> \
|
||||
--from-literal=blob-connection-string=<BLOB_CONN> \
|
||||
--namespace=hgzero
|
||||
|
||||
# Mail Secret
|
||||
kubectl create secret generic mail-secret \
|
||||
--from-literal=username=<MAIL_USERNAME> \
|
||||
--from-literal=password=<MAIL_PASSWORD> \
|
||||
--namespace=hgzero
|
||||
```
|
||||
|
||||
### 5단계: 서비스 배포
|
||||
|
||||
옵션 A: 자동 배포 스크립트 사용 (권장)
|
||||
```bash
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
옵션 B: 수동 배포
|
||||
```bash
|
||||
# ACR 연동 설정
|
||||
az aks update \
|
||||
-n aks-digitalgarage-02 \
|
||||
-g rg-digitalgarage-02 \
|
||||
--attach-acr acrdigitalgarage02
|
||||
|
||||
# 서비스 배포
|
||||
kubectl apply -f user-service.yaml
|
||||
kubectl apply -f notification-service.yaml
|
||||
kubectl apply -f meeting-service.yaml
|
||||
```
|
||||
|
||||
### 6단계: 배포 상태 확인
|
||||
|
||||
```bash
|
||||
# Deployment 상태 확인
|
||||
kubectl get deployments -n hgzero
|
||||
|
||||
# Pod 상태 확인
|
||||
kubectl get pods -n hgzero
|
||||
|
||||
# Service 확인
|
||||
kubectl get services -n hgzero
|
||||
|
||||
# 상세 정보 확인
|
||||
kubectl describe deployment user-service -n hgzero
|
||||
```
|
||||
|
||||
## 배포 확인 및 테스트
|
||||
|
||||
### Pod 로그 확인
|
||||
|
||||
```bash
|
||||
# User Service 로그
|
||||
kubectl logs -f deployment/user-service -n hgzero
|
||||
|
||||
# Meeting Service 로그
|
||||
kubectl logs -f deployment/meeting-service -n hgzero
|
||||
|
||||
# Notification Service 로그
|
||||
kubectl logs -f deployment/notification-service -n hgzero
|
||||
```
|
||||
|
||||
### Health Check
|
||||
|
||||
```bash
|
||||
# User Service health check
|
||||
kubectl port-forward svc/user-service 8080:8080 -n hgzero
|
||||
curl http://localhost:8080/actuator/health
|
||||
|
||||
# Meeting Service health check
|
||||
kubectl port-forward svc/meeting-service 8081:8081 -n hgzero
|
||||
curl http://localhost:8081/actuator/health
|
||||
|
||||
# Notification Service health check
|
||||
kubectl port-forward svc/notification-service 8082:8082 -n hgzero
|
||||
curl http://localhost:8082/actuator/health
|
||||
```
|
||||
|
||||
### Pod 내부 접속
|
||||
|
||||
```bash
|
||||
# Pod 내부로 접속하여 디버깅
|
||||
kubectl exec -it deployment/user-service -n hgzero -- /bin/sh
|
||||
```
|
||||
|
||||
## 배포 해제
|
||||
|
||||
### 전체 서비스 삭제
|
||||
|
||||
```bash
|
||||
./undeploy.sh
|
||||
```
|
||||
|
||||
### 개별 서비스 삭제
|
||||
|
||||
```bash
|
||||
kubectl delete -f user-service.yaml
|
||||
kubectl delete -f meeting-service.yaml
|
||||
kubectl delete -f notification-service.yaml
|
||||
```
|
||||
|
||||
### Secret 및 ConfigMap 삭제
|
||||
|
||||
```bash
|
||||
kubectl delete configmap redis-config mail-config -n hgzero
|
||||
kubectl delete secret db-secret azure-secret mail-secret -n hgzero
|
||||
```
|
||||
|
||||
### 네임스페이스 삭제
|
||||
|
||||
```bash
|
||||
kubectl delete namespace hgzero
|
||||
```
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
### Pod가 시작되지 않는 경우
|
||||
|
||||
```bash
|
||||
# Pod 상태 확인
|
||||
kubectl get pods -n hgzero
|
||||
|
||||
# Pod 상세 정보 확인
|
||||
kubectl describe pod <pod-name> -n hgzero
|
||||
|
||||
# Pod 로그 확인
|
||||
kubectl logs <pod-name> -n hgzero
|
||||
|
||||
# 이전 컨테이너 로그 확인 (재시작된 경우)
|
||||
kubectl logs <pod-name> -n hgzero --previous
|
||||
```
|
||||
|
||||
### 이미지 Pull 오류
|
||||
|
||||
```bash
|
||||
# ACR 연동 상태 확인
|
||||
az aks show -n aks-digitalgarage-02 -g rg-digitalgarage-02 --query "servicePrincipalProfile"
|
||||
|
||||
# ACR 재연동
|
||||
az aks update -n aks-digitalgarage-02 -g rg-digitalgarage-02 --attach-acr acrdigitalgarage02
|
||||
|
||||
# ImagePullSecret 확인
|
||||
kubectl get serviceaccount default -n hgzero -o yaml
|
||||
```
|
||||
|
||||
### Secret 관련 오류
|
||||
|
||||
```bash
|
||||
# Secret 존재 확인
|
||||
kubectl get secrets -n hgzero
|
||||
|
||||
# Secret 내용 확인
|
||||
kubectl get secret db-secret -n hgzero -o yaml
|
||||
|
||||
# Secret 재생성
|
||||
kubectl delete secret db-secret -n hgzero
|
||||
./create-secrets.sh
|
||||
```
|
||||
|
||||
### 네트워크 연결 문제
|
||||
|
||||
```bash
|
||||
# Service Endpoint 확인
|
||||
kubectl get endpoints -n hgzero
|
||||
|
||||
# DNS 확인
|
||||
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup user-service.hgzero.svc.cluster.local
|
||||
|
||||
# 네트워크 정책 확인
|
||||
kubectl get networkpolicies -n hgzero
|
||||
```
|
||||
|
||||
### 리소스 부족 문제
|
||||
|
||||
```bash
|
||||
# 노드 리소스 사용량 확인
|
||||
kubectl top nodes
|
||||
|
||||
# Pod 리소스 사용량 확인
|
||||
kubectl top pods -n hgzero
|
||||
|
||||
# 리소스 제한 조정 (필요 시)
|
||||
kubectl edit deployment user-service -n hgzero
|
||||
```
|
||||
|
||||
## 업데이트 및 롤링 배포
|
||||
|
||||
### 이미지 업데이트
|
||||
|
||||
```bash
|
||||
# 새 이미지 태그로 업데이트
|
||||
kubectl set image deployment/user-service \
|
||||
user-service=acrdigitalgarage02.azurecr.io/hgzero/user-service:v2.0.0 \
|
||||
-n hgzero
|
||||
|
||||
# 롤아웃 상태 확인
|
||||
kubectl rollout status deployment/user-service -n hgzero
|
||||
|
||||
# 롤아웃 히스토리 확인
|
||||
kubectl rollout history deployment/user-service -n hgzero
|
||||
```
|
||||
|
||||
### 롤백
|
||||
|
||||
```bash
|
||||
# 이전 버전으로 롤백
|
||||
kubectl rollout undo deployment/user-service -n hgzero
|
||||
|
||||
# 특정 revision으로 롤백
|
||||
kubectl rollout undo deployment/user-service --to-revision=2 -n hgzero
|
||||
```
|
||||
|
||||
## 모니터링
|
||||
|
||||
### Kubernetes Dashboard
|
||||
|
||||
```bash
|
||||
# Dashboard 접근
|
||||
az aks browse -n aks-digitalgarage-02 -g rg-digitalgarage-02
|
||||
```
|
||||
|
||||
### 리소스 모니터링
|
||||
|
||||
```bash
|
||||
# 실시간 리소스 사용량 모니터링
|
||||
watch kubectl top pods -n hgzero
|
||||
|
||||
# 이벤트 모니터링
|
||||
kubectl get events -n hgzero --sort-by='.lastTimestamp'
|
||||
```
|
||||
|
||||
## 보안 권장사항
|
||||
|
||||
1. **Secret 관리**
|
||||
- Secret은 절대 Git에 커밋하지 마세요
|
||||
- Azure Key Vault 통합을 고려하세요
|
||||
- Secret rotation 정책을 수립하세요
|
||||
|
||||
2. **네트워크 보안**
|
||||
- Network Policy를 활용하여 Pod 간 통신을 제한하세요
|
||||
- Service Mesh 도입을 고려하세요
|
||||
|
||||
3. **이미지 보안**
|
||||
- 정기적으로 이미지 스캔을 수행하세요
|
||||
- 최소 권한 원칙을 적용하세요
|
||||
|
||||
## 성능 최적화
|
||||
|
||||
1. **Auto Scaling 설정**
|
||||
```bash
|
||||
kubectl autoscale deployment user-service \
|
||||
--cpu-percent=70 \
|
||||
--min=1 \
|
||||
--max=5 \
|
||||
-n hgzero
|
||||
```
|
||||
|
||||
2. **리소스 조정**
|
||||
- 실제 사용량에 따라 requests/limits를 조정하세요
|
||||
- HPA(Horizontal Pod Autoscaler) 설정을 고려하세요
|
||||
|
||||
## 참고 자료
|
||||
|
||||
- [Azure Kubernetes Service 문서](https://docs.microsoft.com/azure/aks/)
|
||||
- [Kubernetes 공식 문서](https://kubernetes.io/docs/)
|
||||
- [kubectl 치트시트](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)
|
||||
|
||||
## 지원 및 문의
|
||||
|
||||
문제 발생 시 다음 정보를 수집하여 보고해주세요:
|
||||
|
||||
```bash
|
||||
# 진단 정보 수집
|
||||
kubectl get all -n hgzero > hgzero-resources.txt
|
||||
kubectl describe pods -n hgzero > hgzero-pods-detail.txt
|
||||
kubectl logs deployment/user-service -n hgzero > user-service.log
|
||||
kubectl logs deployment/meeting-service -n hgzero > meeting-service.log
|
||||
kubectl logs deployment/notification-service -n hgzero > notification-service.log
|
||||
```
|
||||
20
deploy/k8s/backend/configmap.yaml
Normal file
20
deploy/k8s/backend/configmap.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
# Redis Configuration
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: redis-config
|
||||
namespace: hgzero
|
||||
data:
|
||||
host: "redis-service.hgzero.svc.cluster.local"
|
||||
port: "6379"
|
||||
---
|
||||
# Mail Configuration
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: mail-config
|
||||
namespace: hgzero
|
||||
data:
|
||||
host: "smtp.gmail.com"
|
||||
port: "587"
|
||||
103
deploy/k8s/backend/create-secrets.sh
Executable file
103
deploy/k8s/backend/create-secrets.sh
Executable file
@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
|
||||
# HGZero Backend Services Secrets Creation Script
|
||||
# This script helps create Kubernetes secrets for the backend services
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="hgzero"
|
||||
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN}HGZero Secrets Creation${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
|
||||
# Check if kubectl is installed
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo -e "${RED}Error: kubectl is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify connection to cluster
|
||||
echo -e "${YELLOW}Verifying connection to Kubernetes cluster...${NC}"
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if namespace exists
|
||||
if ! kubectl get namespace ${NAMESPACE} &> /dev/null; then
|
||||
echo -e "${RED}Error: Namespace '${NAMESPACE}' does not exist${NC}"
|
||||
echo -e "${YELLOW}Please run deploy.sh first to create the namespace${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to prompt for secret value
|
||||
prompt_secret() {
|
||||
local prompt_text=$1
|
||||
local secret_value
|
||||
echo -n -e "${YELLOW}${prompt_text}: ${NC}"
|
||||
read -s secret_value
|
||||
echo ""
|
||||
echo -n "$secret_value"
|
||||
}
|
||||
|
||||
# Create Database Secret
|
||||
echo -e "${GREEN}Creating Database Secret...${NC}"
|
||||
DB_HOST=$(prompt_secret "Enter Database Host")
|
||||
DB_USERNAME=$(prompt_secret "Enter Database Username")
|
||||
DB_PASSWORD=$(prompt_secret "Enter Database Password")
|
||||
|
||||
kubectl create secret generic db-secret \
|
||||
--from-literal=host="${DB_HOST}" \
|
||||
--from-literal=username="${DB_USERNAME}" \
|
||||
--from-literal=password="${DB_PASSWORD}" \
|
||||
--namespace=${NAMESPACE} \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
echo -e "${GREEN}✓ Database secret created${NC}"
|
||||
echo ""
|
||||
|
||||
# Create Azure Secret
|
||||
echo -e "${GREEN}Creating Azure Secret...${NC}"
|
||||
EVENTHUB_CONN=$(prompt_secret "Enter EventHub Connection String")
|
||||
BLOB_CONN=$(prompt_secret "Enter Blob Storage Connection String")
|
||||
|
||||
kubectl create secret generic azure-secret \
|
||||
--from-literal=eventhub-connection-string="${EVENTHUB_CONN}" \
|
||||
--from-literal=blob-connection-string="${BLOB_CONN}" \
|
||||
--namespace=${NAMESPACE} \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
echo -e "${GREEN}✓ Azure secret created${NC}"
|
||||
echo ""
|
||||
|
||||
# Create Mail Secret
|
||||
echo -e "${GREEN}Creating Mail Secret...${NC}"
|
||||
MAIL_USERNAME=$(prompt_secret "Enter Mail Username")
|
||||
MAIL_PASSWORD=$(prompt_secret "Enter Mail Password")
|
||||
|
||||
kubectl create secret generic mail-secret \
|
||||
--from-literal=username="${MAIL_USERNAME}" \
|
||||
--from-literal=password="${MAIL_PASSWORD}" \
|
||||
--namespace=${NAMESPACE} \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
echo -e "${GREEN}✓ Mail secret created${NC}"
|
||||
echo ""
|
||||
|
||||
# Verify secrets
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN}Secrets Created Successfully${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
kubectl get secrets -n ${NAMESPACE}
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Note: Secrets are stored in Kubernetes and can be viewed with:${NC}"
|
||||
echo -e " kubectl get secret <secret-name> -n ${NAMESPACE} -o yaml"
|
||||
133
deploy/k8s/backend/deploy.sh
Executable file
133
deploy/k8s/backend/deploy.sh
Executable file
@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
|
||||
# HGZero Backend Services Kubernetes Deployment Script
|
||||
# Azure Container Registry: acrdigitalgarage02
|
||||
# Azure Kubernetes Service: aks-digitalgarage-02
|
||||
# Namespace: hgzero
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
ACR_NAME="acrdigitalgarage02"
|
||||
AKS_NAME="aks-digitalgarage-02"
|
||||
RESOURCE_GROUP="rg-digitalgarage-02"
|
||||
NAMESPACE="hgzero"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN}HGZero Backend Services Deployment${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
|
||||
# Check if kubectl is installed
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo -e "${RED}Error: kubectl is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if Azure CLI is installed
|
||||
if ! command -v az &> /dev/null; then
|
||||
echo -e "${RED}Error: Azure CLI is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Login to Azure (if not already logged in)
|
||||
echo -e "${YELLOW}Checking Azure login status...${NC}"
|
||||
if ! az account show &> /dev/null; then
|
||||
echo -e "${YELLOW}Please login to Azure...${NC}"
|
||||
az login
|
||||
fi
|
||||
|
||||
# Get AKS credentials
|
||||
echo -e "${YELLOW}Getting AKS credentials...${NC}"
|
||||
az aks get-credentials --resource-group ${RESOURCE_GROUP} --name ${AKS_NAME} --overwrite-existing
|
||||
|
||||
# Verify connection to cluster
|
||||
echo -e "${YELLOW}Verifying connection to Kubernetes cluster...${NC}"
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Successfully connected to ${AKS_NAME}${NC}"
|
||||
|
||||
# Create namespace if it doesn't exist
|
||||
echo -e "${YELLOW}Creating namespace '${NAMESPACE}'...${NC}"
|
||||
kubectl apply -f ${SCRIPT_DIR}/namespace.yaml
|
||||
echo -e "${GREEN}✓ Namespace created/verified${NC}"
|
||||
|
||||
# Apply ConfigMaps
|
||||
echo -e "${YELLOW}Applying ConfigMaps...${NC}"
|
||||
kubectl apply -f ${SCRIPT_DIR}/configmap.yaml
|
||||
echo -e "${GREEN}✓ ConfigMaps applied${NC}"
|
||||
|
||||
# Check if secrets exist
|
||||
echo -e "${YELLOW}Checking for secrets...${NC}"
|
||||
if ! kubectl get secret db-secret -n ${NAMESPACE} &> /dev/null || \
|
||||
! kubectl get secret azure-secret -n ${NAMESPACE} &> /dev/null || \
|
||||
! kubectl get secret mail-secret -n ${NAMESPACE} &> /dev/null; then
|
||||
echo -e "${RED}Warning: One or more secrets are missing!${NC}"
|
||||
echo -e "${YELLOW}Please create secrets using secret-template.yaml as reference${NC}"
|
||||
echo -e "${YELLOW}Example:${NC}"
|
||||
echo -e " kubectl create secret generic db-secret -n ${NAMESPACE} \\"
|
||||
echo -e " --from-literal=host=<DB_HOST> \\"
|
||||
echo -e " --from-literal=username=<DB_USERNAME> \\"
|
||||
echo -e " --from-literal=password=<DB_PASSWORD>"
|
||||
echo ""
|
||||
read -p "Do you want to continue without secrets? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Deployment cancelled${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Configure ACR integration
|
||||
echo -e "${YELLOW}Configuring ACR integration...${NC}"
|
||||
az aks update -n ${AKS_NAME} -g ${RESOURCE_GROUP} --attach-acr ${ACR_NAME}
|
||||
echo -e "${GREEN}✓ ACR integration configured${NC}"
|
||||
|
||||
# Deploy services
|
||||
echo -e "${YELLOW}Deploying User Service...${NC}"
|
||||
kubectl apply -f ${SCRIPT_DIR}/user-service.yaml
|
||||
echo -e "${GREEN}✓ User Service deployed${NC}"
|
||||
|
||||
echo -e "${YELLOW}Deploying Notification Service...${NC}"
|
||||
kubectl apply -f ${SCRIPT_DIR}/notification-service.yaml
|
||||
echo -e "${GREEN}✓ Notification Service deployed${NC}"
|
||||
|
||||
echo -e "${YELLOW}Deploying Meeting Service...${NC}"
|
||||
kubectl apply -f ${SCRIPT_DIR}/meeting-service.yaml
|
||||
echo -e "${GREEN}✓ Meeting Service deployed${NC}"
|
||||
|
||||
# Wait for deployments to be ready
|
||||
echo -e "${YELLOW}Waiting for deployments to be ready...${NC}"
|
||||
kubectl wait --for=condition=available --timeout=300s \
|
||||
deployment/user-service \
|
||||
deployment/notification-service \
|
||||
deployment/meeting-service \
|
||||
-n ${NAMESPACE}
|
||||
|
||||
# Show deployment status
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN}Deployment Status${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
kubectl get deployments -n ${NAMESPACE}
|
||||
echo ""
|
||||
kubectl get pods -n ${NAMESPACE}
|
||||
echo ""
|
||||
kubectl get services -n ${NAMESPACE}
|
||||
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN}Deployment Completed Successfully!${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Useful commands:${NC}"
|
||||
echo -e " View logs: kubectl logs -f deployment/<service-name> -n ${NAMESPACE}"
|
||||
echo -e " View pods: kubectl get pods -n ${NAMESPACE}"
|
||||
echo -e " Describe pod: kubectl describe pod <pod-name> -n ${NAMESPACE}"
|
||||
echo -e " Port forward: kubectl port-forward svc/<service-name> <local-port>:<service-port> -n ${NAMESPACE}"
|
||||
112
deploy/k8s/backend/meeting-service.yaml
Normal file
112
deploy/k8s/backend/meeting-service.yaml
Normal file
@ -0,0 +1,112 @@
|
||||
---
|
||||
# Meeting Service Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: meeting-service
|
||||
namespace: hgzero
|
||||
labels:
|
||||
app: meeting-service
|
||||
tier: backend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: meeting-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: meeting-service
|
||||
tier: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: meeting-service
|
||||
image: acrdigitalgarage02.azurecr.io/hgzero/meeting-service:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8081
|
||||
name: http
|
||||
- containerPort: 8082
|
||||
name: websocket
|
||||
env:
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: "prod"
|
||||
- name: SERVER_PORT
|
||||
value: "8081"
|
||||
- name: DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: host
|
||||
- name: DB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: username
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: password
|
||||
- name: REDIS_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: redis-config
|
||||
key: host
|
||||
- name: REDIS_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: redis-config
|
||||
key: port
|
||||
- name: AZURE_EVENTHUB_CONNECTION_STRING
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: azure-secret
|
||||
key: eventhub-connection-string
|
||||
- name: NOTIFICATION_SERVICE_URL
|
||||
value: "http://notification-service:8082"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 256m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 1024m
|
||||
memory: 1024Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/liveness
|
||||
port: 8081
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 8081
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
---
|
||||
# Meeting Service Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: meeting-service
|
||||
namespace: hgzero
|
||||
labels:
|
||||
app: meeting-service
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8081
|
||||
targetPort: 8081
|
||||
protocol: TCP
|
||||
name: http
|
||||
- port: 8082
|
||||
targetPort: 8082
|
||||
protocol: TCP
|
||||
name: websocket
|
||||
selector:
|
||||
app: meeting-service
|
||||
9
deploy/k8s/backend/namespace.yaml
Normal file
9
deploy/k8s/backend/namespace.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
# Namespace for HGZero application
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: hgzero
|
||||
labels:
|
||||
name: hgzero
|
||||
environment: production
|
||||
129
deploy/k8s/backend/notification-service.yaml
Normal file
129
deploy/k8s/backend/notification-service.yaml
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
# Notification Service Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notification-service
|
||||
namespace: hgzero
|
||||
labels:
|
||||
app: notification-service
|
||||
tier: backend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: notification-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: notification-service
|
||||
tier: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: notification-service
|
||||
image: acrdigitalgarage02.azurecr.io/hgzero/notification-service:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8082
|
||||
name: http
|
||||
env:
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: "prod"
|
||||
- name: SERVER_PORT
|
||||
value: "8082"
|
||||
- name: DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: host
|
||||
- name: DB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: username
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: password
|
||||
- name: REDIS_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: redis-config
|
||||
key: host
|
||||
- name: REDIS_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: redis-config
|
||||
key: port
|
||||
- name: AZURE_EVENTHUB_CONNECTION_STRING
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: azure-secret
|
||||
key: eventhub-connection-string
|
||||
- name: AZURE_BLOB_CONNECTION_STRING
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: azure-secret
|
||||
key: blob-connection-string
|
||||
- name: MAIL_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: mail-config
|
||||
key: host
|
||||
- name: MAIL_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: mail-config
|
||||
key: port
|
||||
- name: MAIL_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mail-secret
|
||||
key: username
|
||||
- name: MAIL_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mail-secret
|
||||
key: password
|
||||
resources:
|
||||
requests:
|
||||
cpu: 256m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 1024m
|
||||
memory: 1024Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/liveness
|
||||
port: 8082
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 8082
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
---
|
||||
# Notification Service Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notification-service
|
||||
namespace: hgzero
|
||||
labels:
|
||||
app: notification-service
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8082
|
||||
targetPort: 8082
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: notification-service
|
||||
36
deploy/k8s/backend/secret-template.yaml
Normal file
36
deploy/k8s/backend/secret-template.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
# Database Secret Template
|
||||
# Note: Replace base64 encoded values with your actual credentials
|
||||
# To encode: echo -n 'your-value' | base64
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: db-secret
|
||||
namespace: hgzero
|
||||
type: Opaque
|
||||
data:
|
||||
host: <BASE64_ENCODED_DB_HOST>
|
||||
username: <BASE64_ENCODED_DB_USERNAME>
|
||||
password: <BASE64_ENCODED_DB_PASSWORD>
|
||||
---
|
||||
# Azure Secret Template
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: azure-secret
|
||||
namespace: hgzero
|
||||
type: Opaque
|
||||
data:
|
||||
eventhub-connection-string: <BASE64_ENCODED_EVENTHUB_CONNECTION_STRING>
|
||||
blob-connection-string: <BASE64_ENCODED_BLOB_CONNECTION_STRING>
|
||||
---
|
||||
# Mail Secret Template
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mail-secret
|
||||
namespace: hgzero
|
||||
type: Opaque
|
||||
data:
|
||||
username: <BASE64_ENCODED_MAIL_USERNAME>
|
||||
password: <BASE64_ENCODED_MAIL_PASSWORD>
|
||||
89
deploy/k8s/backend/undeploy.sh
Executable file
89
deploy/k8s/backend/undeploy.sh
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
|
||||
# HGZero Backend Services Kubernetes Undeployment Script
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
NAMESPACE="hgzero"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
echo -e "${YELLOW}======================================${NC}"
|
||||
echo -e "${YELLOW}HGZero Backend Services Undeployment${NC}"
|
||||
echo -e "${YELLOW}======================================${NC}"
|
||||
|
||||
# Check if kubectl is installed
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo -e "${RED}Error: kubectl is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify connection to cluster
|
||||
echo -e "${YELLOW}Verifying connection to Kubernetes cluster...${NC}"
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo -e "${RED}Error: Cannot connect to Kubernetes cluster${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if namespace exists
|
||||
if ! kubectl get namespace ${NAMESPACE} &> /dev/null; then
|
||||
echo -e "${YELLOW}Namespace '${NAMESPACE}' does not exist. Nothing to undeploy.${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}This will delete all services in namespace '${NAMESPACE}'${NC}"
|
||||
read -p "Are you sure you want to continue? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${YELLOW}Undeployment cancelled${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Delete services
|
||||
echo -e "${YELLOW}Deleting Meeting Service...${NC}"
|
||||
kubectl delete -f ${SCRIPT_DIR}/meeting-service.yaml --ignore-not-found=true
|
||||
echo -e "${GREEN}✓ Meeting Service deleted${NC}"
|
||||
|
||||
echo -e "${YELLOW}Deleting Notification Service...${NC}"
|
||||
kubectl delete -f ${SCRIPT_DIR}/notification-service.yaml --ignore-not-found=true
|
||||
echo -e "${GREEN}✓ Notification Service deleted${NC}"
|
||||
|
||||
echo -e "${YELLOW}Deleting User Service...${NC}"
|
||||
kubectl delete -f ${SCRIPT_DIR}/user-service.yaml --ignore-not-found=true
|
||||
echo -e "${GREEN}✓ User Service deleted${NC}"
|
||||
|
||||
# Delete ConfigMaps
|
||||
echo -e "${YELLOW}Deleting ConfigMaps...${NC}"
|
||||
kubectl delete -f ${SCRIPT_DIR}/configmap.yaml --ignore-not-found=true
|
||||
echo -e "${GREEN}✓ ConfigMaps deleted${NC}"
|
||||
|
||||
# Ask about secrets deletion
|
||||
echo ""
|
||||
echo -e "${YELLOW}Do you want to delete secrets as well?${NC}"
|
||||
echo -e "${RED}Warning: This will delete all database and Azure credentials${NC}"
|
||||
read -p "Delete secrets? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
kubectl delete secret db-secret azure-secret mail-secret -n ${NAMESPACE} --ignore-not-found=true
|
||||
echo -e "${GREEN}✓ Secrets deleted${NC}"
|
||||
fi
|
||||
|
||||
# Ask about namespace deletion
|
||||
echo ""
|
||||
echo -e "${YELLOW}Do you want to delete the namespace '${NAMESPACE}'?${NC}"
|
||||
read -p "Delete namespace? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
kubectl delete namespace ${NAMESPACE}
|
||||
echo -e "${GREEN}✓ Namespace deleted${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
echo -e "${GREEN}Undeployment Completed${NC}"
|
||||
echo -e "${GREEN}======================================${NC}"
|
||||
102
deploy/k8s/backend/user-service.yaml
Normal file
102
deploy/k8s/backend/user-service.yaml
Normal file
@ -0,0 +1,102 @@
|
||||
---
|
||||
# User Service Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: user-service
|
||||
namespace: hgzero
|
||||
labels:
|
||||
app: user-service
|
||||
tier: backend
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: user-service
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: user-service
|
||||
tier: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: user-service
|
||||
image: acrdigitalgarage02.azurecr.io/hgzero/user-service:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
env:
|
||||
- name: SPRING_PROFILES_ACTIVE
|
||||
value: "prod"
|
||||
- name: DB_HOST
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: host
|
||||
- name: DB_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: username
|
||||
- name: DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: db-secret
|
||||
key: password
|
||||
- name: REDIS_HOST
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: redis-config
|
||||
key: host
|
||||
- name: REDIS_PORT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: redis-config
|
||||
key: port
|
||||
- name: AZURE_EVENTHUB_CONNECTION_STRING
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: azure-secret
|
||||
key: eventhub-connection-string
|
||||
resources:
|
||||
requests:
|
||||
cpu: 256m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 1024m
|
||||
memory: 1024Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/liveness
|
||||
port: 8080
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health/readiness
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
---
|
||||
# User Service Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: user-service
|
||||
namespace: hgzero
|
||||
labels:
|
||||
app: user-service
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: user-service
|
||||
25
deployment/container/Dockerfile-backend
Normal file
25
deployment/container/Dockerfile-backend
Normal file
@ -0,0 +1,25 @@
|
||||
# Build stage
|
||||
FROM openjdk:23-oraclelinux8 AS builder
|
||||
ARG BUILD_LIB_DIR
|
||||
ARG ARTIFACTORY_FILE
|
||||
COPY ${BUILD_LIB_DIR}/${ARTIFACTORY_FILE} app.jar
|
||||
|
||||
# Run stage
|
||||
FROM openjdk:23-slim
|
||||
ENV USERNAME=k8s
|
||||
ENV ARTIFACTORY_HOME=/home/${USERNAME}
|
||||
ENV JAVA_OPTS=""
|
||||
|
||||
# Add a non-root user
|
||||
RUN adduser --system --group ${USERNAME} && \
|
||||
mkdir -p ${ARTIFACTORY_HOME} && \
|
||||
chown ${USERNAME}:${USERNAME} ${ARTIFACTORY_HOME}
|
||||
|
||||
WORKDIR ${ARTIFACTORY_HOME}
|
||||
COPY --from=builder app.jar app.jar
|
||||
RUN chown ${USERNAME}:${USERNAME} app.jar
|
||||
|
||||
USER ${USERNAME}
|
||||
|
||||
ENTRYPOINT [ "sh", "-c" ]
|
||||
CMD ["java ${JAVA_OPTS} -jar app.jar"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user