Merge branch 'develop' into feature/analytics

This commit is contained in:
Hyowon Yang 2025-10-29 19:31:10 +09:00 committed by GitHub
commit 108ee10293
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
193 changed files with 25489 additions and 426 deletions

186
.github/README.md vendored Normal file
View File

@ -0,0 +1,186 @@
# KT Event Marketing - CI/CD Infrastructure
이 디렉토리는 KT Event Marketing 백엔드 서비스의 CI/CD 인프라를 포함합니다.
## 디렉토리 구조
```
.github/
├── README.md # 이 파일
├── workflows/
│ └── backend-cicd.yaml # GitHub Actions 워크플로우
├── kustomize/ # Kubernetes 매니페스트 관리
│ ├── base/ # 기본 리소스 정의
│ │ ├── kustomization.yaml
│ │ ├── cm-common.yaml
│ │ ├── secret-common.yaml
│ │ ├── secret-imagepull.yaml
│ │ ├── ingress.yaml
│ │ └── {service}-*.yaml # 각 서비스별 리소스
│ └── overlays/ # 환경별 설정
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── *-patch.yaml # 1 replica, 256Mi-1024Mi
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── *-patch.yaml # 2 replicas, 512Mi-2048Mi
│ └── prod/
│ ├── kustomization.yaml
│ └── *-patch.yaml # 3 replicas, 1024Mi-4096Mi
├── config/
│ ├── deploy_env_vars_dev # Dev 환경 변수
│ ├── deploy_env_vars_staging # Staging 환경 변수
│ └── deploy_env_vars_prod # Prod 환경 변수
└── scripts/
├── deploy.sh # 수동 배포 스크립트
├── generate-patches.sh # 패치 파일 생성 스크립트
└── copy-manifests-to-base.py # 매니페스트 복사 스크립트
```
## 주요 파일 설명
### workflows/backend-cicd.yaml
GitHub Actions 워크플로우 정의 파일입니다.
**트리거**:
- develop 브랜치 push → dev 환경 배포
- main 브랜치 push → prod 환경 배포
- Manual workflow dispatch → 원하는 환경과 서비스 선택
**Jobs**:
1. `detect-changes`: 변경된 서비스 감지
2. `build-and-push`: 서비스 빌드 및 ACR 푸시
3. `deploy`: AKS에 배포
4. `notify`: 배포 결과 알림
### kustomize/base/kustomization.yaml
모든 환경에서 공통으로 사용하는 기본 리소스를 정의합니다.
**포함 리소스**:
- Common ConfigMaps and Secrets
- Ingress
- 7개 서비스의 Deployment, Service, ConfigMap, Secret
### kustomize/overlays/{env}/kustomization.yaml
환경별 설정을 오버라이드합니다.
**주요 차이점**:
- 이미지 태그 (dev/staging/prod)
- Replica 수 (1/2/3)
- 리소스 할당량 (작음/중간/큼)
### scripts/deploy.sh
로컬에서 수동 배포를 위한 스크립트입니다.
**사용법**:
```bash
# 모든 서비스를 dev 환경에 배포
./scripts/deploy.sh dev
# 특정 서비스만 prod 환경에 배포
./scripts/deploy.sh prod user-service
```
## 배포 프로세스
### 자동 배포 (GitHub Actions)
1. **Dev 환경**:
```bash
git checkout develop
git push origin develop
```
2. **Prod 환경**:
```bash
git checkout main
git merge develop
git push origin main
```
3. **수동 배포**:
- GitHub Actions UI → Run workflow
- Environment 선택 (dev/staging/prod)
- Service 선택 (all 또는 특정 서비스)
### 수동 배포 (로컬)
```bash
# 사전 요구사항: Azure CLI, kubectl, kustomize 설치
# Azure 로그인 필요
# Dev 환경에 모든 서비스 배포
./.github/scripts/deploy.sh dev
# Prod 환경에 user-service만 배포
./.github/scripts/deploy.sh prod user-service
```
## 환경별 설정
| 환경 | 브랜치 | 이미지 태그 | Replicas | CPU Request | Memory Request |
|------|--------|-------------|----------|-------------|----------------|
| Dev | develop | dev | 1 | 256m | 256Mi |
| Staging | manual | staging | 2 | 512m | 512Mi |
| Prod | main | prod | 3 | 1024m | 1024Mi |
## 서비스 목록
1. **user-service** (8081) - 사용자 관리
2. **event-service** (8082) - 이벤트 관리
3. **ai-service** (8083) - AI 기반 콘텐츠 생성
4. **content-service** (8084) - 콘텐츠 관리
5. **distribution-service** (8085) - 경품 배포
6. **participation-service** (8086) - 이벤트 참여
7. **analytics-service** (8087) - 분석 및 통계
## 모니터링
### Pod 상태 확인
```bash
kubectl get pods -n kt-event-marketing
```
### 로그 확인
```bash
# 실시간 로그
kubectl logs -n kt-event-marketing -l app=user-service -f
# 이전 컨테이너 로그
kubectl logs -n kt-event-marketing <pod-name> --previous
```
### 리소스 사용량
```bash
# Pod 리소스
kubectl top pods -n kt-event-marketing
# Node 리소스
kubectl top nodes
```
## 트러블슈팅
상세한 트러블슈팅 가이드는 [deployment/cicd/CICD-GUIDE.md](../../deployment/cicd/CICD-GUIDE.md)를 참조하세요.
**주요 문제 해결**:
- ImagePullBackOff → ACR Secret 확인
- CrashLoopBackOff → 로그 확인 및 환경 변수 검증
- Readiness Probe Failed → Context Path 및 Actuator 경로 확인
## 롤백
```bash
# 이전 버전으로 롤백
kubectl rollout undo deployment/user-service -n kt-event-marketing
# 특정 리비전으로 롤백
kubectl rollout undo deployment/user-service --to-revision=2 -n kt-event-marketing
```
## 참고 자료
- [CI/CD 가이드 (한글)](../../deployment/cicd/CICD-GUIDE.md)
- [GitHub Actions 공식 문서](https://docs.github.com/en/actions)
- [Kustomize 공식 문서](https://kustomize.io/)
- [Azure AKS 공식 문서](https://docs.microsoft.com/en-us/azure/aks/)

11
.github/config/deploy_env_vars_dev vendored Normal file
View File

@ -0,0 +1,11 @@
# Development Environment Variables
ENVIRONMENT=dev
ACR_NAME=acrdigitalgarage01
RESOURCE_GROUP=rg-digitalgarage-01
AKS_CLUSTER=aks-digitalgarage-01
NAMESPACE=kt-event-marketing
REPLICAS=1
CPU_REQUEST=256m
MEMORY_REQUEST=256Mi
CPU_LIMIT=1024m
MEMORY_LIMIT=1024Mi

11
.github/config/deploy_env_vars_prod vendored Normal file
View File

@ -0,0 +1,11 @@
# Production Environment Variables
ENVIRONMENT=prod
ACR_NAME=acrdigitalgarage01
RESOURCE_GROUP=rg-digitalgarage-01
AKS_CLUSTER=aks-digitalgarage-01
NAMESPACE=kt-event-marketing
REPLICAS=3
CPU_REQUEST=1024m
MEMORY_REQUEST=1024Mi
CPU_LIMIT=4096m
MEMORY_LIMIT=4096Mi

11
.github/config/deploy_env_vars_staging vendored Normal file
View File

@ -0,0 +1,11 @@
# Staging Environment Variables
ENVIRONMENT=staging
ACR_NAME=acrdigitalgarage01
RESOURCE_GROUP=rg-digitalgarage-01
AKS_CLUSTER=aks-digitalgarage-01
NAMESPACE=kt-event-marketing
REPLICAS=2
CPU_REQUEST=512m
MEMORY_REQUEST=512Mi
CPU_LIMIT=2048m
MEMORY_LIMIT=2048Mi

View File

@ -0,0 +1,55 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-ai-service
data:
# Server Configuration
SERVER_PORT: "8083"
# Redis Configuration (service-specific)
REDIS_DATABASE: "3"
REDIS_TIMEOUT: "3000"
REDIS_POOL_MIN: "2"
# Kafka Configuration (service-specific)
KAFKA_CONSUMER_GROUP: "ai-service-consumers"
# Kafka Topics Configuration
KAFKA_TOPICS_AI_JOB: "ai-event-generation-job"
KAFKA_TOPICS_AI_JOB_DLQ: "ai-event-generation-job-dlq"
# AI Provider Configuration
AI_PROVIDER: "CLAUDE"
AI_CLAUDE_API_URL: "https://api.anthropic.com/v1/messages"
AI_CLAUDE_ANTHROPIC_VERSION: "2023-06-01"
AI_CLAUDE_MODEL: "claude-sonnet-4-5-20250929"
AI_CLAUDE_MAX_TOKENS: "4096"
AI_CLAUDE_TEMPERATURE: "0.7"
AI_CLAUDE_TIMEOUT: "300000"
# Circuit Breaker Configuration
RESILIENCE4J_CIRCUITBREAKER_FAILURE_RATE_THRESHOLD: "50"
RESILIENCE4J_CIRCUITBREAKER_SLOW_CALL_RATE_THRESHOLD: "50"
RESILIENCE4J_CIRCUITBREAKER_SLOW_CALL_DURATION_THRESHOLD: "60s"
RESILIENCE4J_CIRCUITBREAKER_PERMITTED_CALLS_HALF_OPEN: "3"
RESILIENCE4J_CIRCUITBREAKER_SLIDING_WINDOW_SIZE: "10"
RESILIENCE4J_CIRCUITBREAKER_MINIMUM_CALLS: "5"
RESILIENCE4J_CIRCUITBREAKER_WAIT_DURATION_OPEN: "60s"
RESILIENCE4J_TIMELIMITER_TIMEOUT_DURATION: "300s"
# Redis Cache TTL Configuration (seconds)
CACHE_TTL_RECOMMENDATION: "86400"
CACHE_TTL_JOB_STATUS: "86400"
CACHE_TTL_TREND: "3600"
CACHE_TTL_FALLBACK: "604800"
# Logging Configuration
LOG_LEVEL_ROOT: "INFO"
LOG_LEVEL_AI: "DEBUG"
LOG_LEVEL_KAFKA: "INFO"
LOG_LEVEL_REDIS: "INFO"
LOG_LEVEL_RESILIENCE4J: "DEBUG"
LOG_FILE_NAME: "logs/ai-service.log"
LOG_FILE_MAX_SIZE: "10MB"
LOG_FILE_MAX_HISTORY: "7"
LOG_FILE_TOTAL_CAP: "100MB"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ai-service
labels:
app: ai-service
spec:
replicas: 1
selector:
matchLabels:
app: ai-service
template:
metadata:
labels:
app: ai-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: ai-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8083
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-ai-service
- secretRef:
name: secret-common
- secretRef:
name: secret-ai-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health
port: 8083
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8083
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8083
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-ai-service
type: Opaque
stringData:
# Claude API Key
AI_CLAUDE_API_KEY: "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"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: ai-service
labels:
app: ai-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8083
protocol: TCP
name: http
selector:
app: ai-service

View File

@ -0,0 +1,37 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-analytics-service
data:
# Server Configuration
SERVER_PORT: "8086"
# Database Configuration
DB_HOST: "analytic-postgresql"
DB_PORT: "5432"
DB_NAME: "analytics_db"
DB_USERNAME: "eventuser"
# Redis Configuration (service-specific)
REDIS_DATABASE: "5"
# Kafka Configuration (service-specific)
KAFKA_ENABLED: "true"
KAFKA_CONSUMER_GROUP_ID: "analytics-service"
# Sample Data Configuration (MVP only)
SAMPLE_DATA_ENABLED: "true"
# Batch Scheduler Configuration
BATCH_REFRESH_INTERVAL: "300000" # 5분 (밀리초)
BATCH_INITIAL_DELAY: "30000" # 30초 (밀리초)
BATCH_ENABLED: "true"
# Logging Configuration
LOG_LEVEL_APP: "INFO"
LOG_LEVEL_WEB: "INFO"
LOG_LEVEL_SQL: "WARN"
LOG_LEVEL_SQL_TYPE: "WARN"
SHOW_SQL: "false"
DDL_AUTO: "update"
LOG_FILE: "logs/analytics-service.log"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: analytics-service
labels:
app: analytics-service
spec:
replicas: 1
selector:
matchLabels:
app: analytics-service
template:
metadata:
labels:
app: analytics-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: analytics-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/analytics-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8086
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-analytics-service
- secretRef:
name: secret-common
- secretRef:
name: secret-analytics-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8086
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 30
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8086
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8086
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-analytics-service
type: Opaque
stringData:
DB_PASSWORD: "Hi5Jessica!"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: analytics-service
labels:
app: analytics-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8086
protocol: TCP
name: http
selector:
app: analytics-service

46
.github/kustomize/base/cm-common.yaml vendored Normal file
View File

@ -0,0 +1,46 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-common
data:
# Redis Configuration
REDIS_ENABLED: "true"
REDIS_HOST: "redis"
REDIS_PORT: "6379"
REDIS_TIMEOUT: "2000ms"
REDIS_POOL_MAX: "8"
REDIS_POOL_IDLE: "8"
REDIS_POOL_MIN: "0"
REDIS_POOL_WAIT: "-1ms"
# Kafka Configuration
KAFKA_BOOTSTRAP_SERVERS: "20.249.182.13:9095,4.217.131.59:9095"
EXCLUDE_KAFKA: ""
EXCLUDE_REDIS: ""
# CORS Configuration
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://kt-event-marketing.20.214.196.128.nip.io"
CORS_ALLOWED_METHODS: "GET,POST,PUT,DELETE,OPTIONS,PATCH"
CORS_ALLOWED_HEADERS: "*"
CORS_ALLOW_CREDENTIALS: "true"
CORS_MAX_AGE: "3600"
# JWT Configuration
JWT_ACCESS_TOKEN_VALIDITY: "604800000"
JWT_REFRESH_TOKEN_VALIDITY: "86400000"
# JPA Configuration
DDL_AUTO: "update"
SHOW_SQL: "false"
JPA_DIALECT: "org.hibernate.dialect.PostgreSQLDialect"
H2_CONSOLE_ENABLED: "false"
# Logging Configuration
LOG_LEVEL_APP: "INFO"
LOG_LEVEL_WEB: "INFO"
LOG_LEVEL_SQL: "WARN"
LOG_LEVEL_SQL_TYPE: "WARN"
LOG_LEVEL_ROOT: "INFO"
LOG_FILE_MAX_SIZE: "10MB"
LOG_FILE_MAX_HISTORY: "7"
LOG_FILE_TOTAL_CAP: "100MB"

View File

@ -0,0 +1,24 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-content-service
data:
# Server Configuration
SERVER_PORT: "8084"
# Redis Configuration (service-specific)
REDIS_DATABASE: "1"
# Replicate API Configuration (Stable Diffusion)
REPLICATE_API_URL: "https://api.replicate.com"
REPLICATE_MODEL_VERSION: "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b"
# HuggingFace API Configuration
HUGGINGFACE_API_URL: "https://api-inference.huggingface.co"
HUGGINGFACE_MODEL: "runwayml/stable-diffusion-v1-5"
# Azure Blob Storage Configuration
AZURE_CONTAINER_NAME: "content-images"
# Logging Configuration
LOG_FILE_PATH: "logs/content-service.log"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: content-service
labels:
app: content-service
spec:
replicas: 1
selector:
matchLabels:
app: content-service
template:
metadata:
labels:
app: content-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: content-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/content-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8084
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-content-service
- secretRef:
name: secret-common
- secretRef:
name: secret-content-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /api/v1/content/actuator/health
port: 8084
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /api/v1/content/actuator/health/readiness
port: 8084
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /api/v1/content/actuator/health/liveness
port: 8084
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,14 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-content-service
type: Opaque
stringData:
# Azure Blob Storage Connection String
AZURE_STORAGE_CONNECTION_STRING: "DefaultEndpointsProtocol=https;AccountName=blobkteventstorage;AccountKey=tcBN7mAfojbl0uGsOpU7RNuKNhHnzmwDiWjN31liSMVSrWaEK+HHnYKZrjBXXAC6ZPsuxUDlsf8x+AStd++QYg==;EndpointSuffix=core.windows.net"
# Replicate API Token
REPLICATE_API_TOKEN: ""
# HuggingFace API Token
HUGGINGFACE_API_TOKEN: ""

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: content-service
labels:
app: content-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8084
protocol: TCP
name: http
selector:
app: content-service

View File

@ -0,0 +1,28 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-distribution-service
data:
# Server Configuration
SERVER_PORT: "8085"
# Database Configuration
DB_HOST: "distribution-postgresql"
DB_PORT: "5432"
DB_NAME: "distributiondb"
DB_USERNAME: "eventuser"
# Kafka Configuration
KAFKA_ENABLED: "true"
KAFKA_CONSUMER_GROUP: "distribution-service"
# External Channel APIs
URIDONGNETV_API_URL: "http://localhost:9001/api/uridongnetv"
RINGOBIZ_API_URL: "http://localhost:9002/api/ringobiz"
GINITV_API_URL: "http://localhost:9003/api/ginitv"
INSTAGRAM_API_URL: "http://localhost:9004/api/instagram"
NAVER_API_URL: "http://localhost:9005/api/naver"
KAKAO_API_URL: "http://localhost:9006/api/kakao"
# Logging Configuration
LOG_FILE: "logs/distribution-service.log"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: distribution-service
labels:
app: distribution-service
spec:
replicas: 1
selector:
matchLabels:
app: distribution-service
template:
metadata:
labels:
app: distribution-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: distribution-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/distribution-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8085
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-distribution-service
- secretRef:
name: secret-common
- secretRef:
name: secret-distribution-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /api/v1/distribution/actuator/health
port: 8085
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /api/v1/distribution/actuator/health/readiness
port: 8085
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /api/v1/distribution/actuator/health/liveness
port: 8085
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-distribution-service
type: Opaque
stringData:
DB_PASSWORD: "Hi5Jessica!"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: distribution-service
labels:
app: distribution-service
spec:
type: ClusterIP
selector:
app: distribution-service
ports:
- name: http
port: 80
targetPort: 8085
protocol: TCP

View File

@ -0,0 +1,28 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-event-service
data:
# Server Configuration
SERVER_PORT: "8080"
# Database Configuration
DB_HOST: "event-postgresql"
DB_PORT: "5432"
DB_NAME: "eventdb"
DB_USERNAME: "eventuser"
# Redis Configuration (service-specific)
REDIS_DATABASE: "2"
# Kafka Configuration (service-specific)
KAFKA_CONSUMER_GROUP: "event-service-consumers"
# Service URLs
CONTENT_SERVICE_URL: "http://content-service"
DISTRIBUTION_SERVICE_URL: "http://distribution-service"
# Logging Configuration
LOG_LEVEL: "INFO"
SQL_LOG_LEVEL: "WARN"
LOG_FILE: "logs/event-service.log"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-service
labels:
app: event-service
spec:
replicas: 1
selector:
matchLabels:
app: event-service
template:
metadata:
labels:
app: event-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: event-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-event-service
- secretRef:
name: secret-common
- secretRef:
name: secret-event-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-event-service
type: Opaque
stringData:
# Database Password
DB_PASSWORD: "Hi5Jessica!"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: event-service
labels:
app: event-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: event-service

116
.github/kustomize/base/ingress.yaml vendored Normal file
View File

@ -0,0 +1,116 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kt-event-marketing
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
ingressClassName: nginx
rules:
- host: kt-event-marketing-api.20.214.196.128.nip.io
http:
paths:
# User Service
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
# Content Service
- path: /api/v1/content
pathType: Prefix
backend:
service:
name: content-service
port:
number: 80
# Event Service
- path: /api/v1/events
pathType: Prefix
backend:
service:
name: event-service
port:
number: 80
- path: /api/v1/jobs
pathType: Prefix
backend:
service:
name: event-service
port:
number: 80
- path: /api/v1/redis-test
pathType: Prefix
backend:
service:
name: event-service
port:
number: 80
# AI Service
- path: /api/v1/ai-service
pathType: Prefix
backend:
service:
name: ai-service
port:
number: 80
# Participation Service
- path: /api/v1/participations
pathType: Prefix
backend:
service:
name: participation-service
port:
number: 80
- path: /api/v1/winners
pathType: Prefix
backend:
service:
name: participation-service
port:
number: 80
- path: /debug
pathType: Prefix
backend:
service:
name: participation-service
port:
number: 80
# Analytics Service - Event Analytics
- path: /api/v1/events/([0-9]+)/analytics
pathType: ImplementationSpecific
backend:
service:
name: analytics-service
port:
number: 80
# Analytics Service - User Analytics
- path: /api/v1/users/([0-9]+)/analytics
pathType: ImplementationSpecific
backend:
service:
name: analytics-service
port:
number: 80
# Distribution Service
- path: /distribution
pathType: Prefix
backend:
service:
name: distribution-service
port:
number: 80

View File

@ -0,0 +1,76 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Common resources
resources:
# Common ConfigMaps and Secrets
- cm-common.yaml
- secret-common.yaml
- secret-imagepull.yaml
# Ingress
- ingress.yaml
# user-service
- user-service-deployment.yaml
- user-service-service.yaml
- user-service-cm-user-service.yaml
- user-service-secret-user-service.yaml
# event-service
- event-service-deployment.yaml
- event-service-service.yaml
- event-service-cm-event-service.yaml
- event-service-secret-event-service.yaml
# ai-service
- ai-service-deployment.yaml
- ai-service-service.yaml
- ai-service-cm-ai-service.yaml
- ai-service-secret-ai-service.yaml
# content-service
- content-service-deployment.yaml
- content-service-service.yaml
- content-service-cm-content-service.yaml
- content-service-secret-content-service.yaml
# distribution-service
- distribution-service-deployment.yaml
- distribution-service-service.yaml
- distribution-service-cm-distribution-service.yaml
- distribution-service-secret-distribution-service.yaml
# participation-service
- participation-service-deployment.yaml
- participation-service-service.yaml
- participation-service-cm-participation-service.yaml
- participation-service-secret-participation-service.yaml
# analytics-service
- analytics-service-deployment.yaml
- analytics-service-service.yaml
- analytics-service-cm-analytics-service.yaml
- analytics-service-secret-analytics-service.yaml
# Common labels for all resources
commonLabels:
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/part-of: kt-event-marketing
# Image tag replacement (will be overridden by overlays)
images:
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service
newTag: latest
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service
newTag: latest
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service
newTag: latest
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/content-service
newTag: latest
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/distribution-service
newTag: latest
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/participation-service
newTag: latest
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/analytics-service
newTag: latest

View File

@ -0,0 +1,24 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-participation-service
data:
# Server Configuration
SERVER_PORT: "8084"
# Database Configuration
DB_HOST: "participation-postgresql"
DB_PORT: "5432"
DB_NAME: "participationdb"
DB_USERNAME: "eventuser"
# Redis Configuration (service-specific)
REDIS_DATABASE: "4"
# Kafka Configuration (service-specific)
KAFKA_CONSUMER_GROUP: "participation-service-consumers"
# Logging Configuration
LOG_LEVEL: "INFO"
SHOW_SQL: "false"
LOG_FILE: "logs/participation-service.log"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: participation-service
labels:
app: participation-service
spec:
replicas: 1
selector:
matchLabels:
app: participation-service
template:
metadata:
labels:
app: participation-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: participation-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/participation-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8084
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-participation-service
- secretRef:
name: secret-common
- secretRef:
name: secret-participation-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8084
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 30
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8084
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8084
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,7 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-participation-service
type: Opaque
stringData:
DB_PASSWORD: "Hi5Jessica!"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: participation-service
labels:
app: participation-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8084
protocol: TCP
name: http
selector:
app: participation-service

View File

@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-common
type: Opaque
stringData:
# Redis Password
REDIS_PASSWORD: "Hi5Jessica!"
# JWT Secret
JWT_SECRET: "QL0czzXckz18kHnxpaTDoWFkq+3qKO7VQXeNvf2bOoU="

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
name: kt-event-marketing
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{
"auths": {
"acrdigitalgarage01.azurecr.io": {
"username": "acrdigitalgarage01",
"password": "+OY+rmOagorjWvQe/tTk6oqvnZI8SmNbY/Y2o5EDcY+ACRDCDbYk",
"auth": "YWNyZGlnaXRhbGdhcmFnZTAxOitPWStybU9hZ29yald2UWUvdFRrNm9xdm5aSThTbU5iWS9ZMm81RURjWStBQ1JEQ0RiWWs="
}
}
}

View File

@ -0,0 +1,31 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-user-service
data:
# Server Configuration
SERVER_PORT: "8081"
# Database Configuration
DB_URL: "jdbc:postgresql://user-postgresql:5432/userdb"
DB_HOST: "user-postgresql"
DB_PORT: "5432"
DB_NAME: "userdb"
DB_USERNAME: "eventuser"
DB_DRIVER: "org.postgresql.Driver"
DB_KIND: "postgresql"
DB_POOL_MAX: "20"
DB_POOL_MIN: "5"
DB_CONN_TIMEOUT: "30000"
DB_IDLE_TIMEOUT: "600000"
DB_MAX_LIFETIME: "1800000"
DB_LEAK_THRESHOLD: "60000"
# Redis Configuration (service-specific)
REDIS_DATABASE: "0"
# Kafka Configuration (service-specific)
KAFKA_CONSUMER_GROUP: "user-service-consumers"
# Logging Configuration
LOG_FILE_PATH: "logs/user-service.log"

View File

@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 1
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
imagePullSecrets:
- name: kt-event-marketing
containers:
- name: user-service
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8081
name: http
envFrom:
- configMapRef:
name: cm-common
- configMapRef:
name: cm-user-service
- secretRef:
name: secret-common
- secretRef:
name: secret-user-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8081
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8081
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3

View File

@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: secret-user-service
type: Opaque
stringData:
# Database Password
DB_PASSWORD: "Hi5Jessica!"

View File

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: user-service
labels:
app: user-service
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8081
protocol: TCP
name: http
selector:
app: user-service

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ai-service
spec:
replicas: 1
template:
spec:
containers:
- name: ai-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: analytics-service
spec:
replicas: 1
template:
spec:
containers:
- name: analytics-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: content-service
spec:
replicas: 1
template:
spec:
containers:
- name: content-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: distribution-service
spec:
replicas: 1
template:
spec:
containers:
- name: distribution-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-service
spec:
replicas: 1
template:
spec:
containers:
- name: event-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,38 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kt-event-marketing
bases:
- ../../base
# Environment-specific labels
commonLabels:
environment: dev
# Environment-specific patches
patchesStrategicMerge:
- user-service-patch.yaml
- event-service-patch.yaml
- ai-service-patch.yaml
- content-service-patch.yaml
- distribution-service-patch.yaml
- participation-service-patch.yaml
- analytics-service-patch.yaml
# Override image tags for dev environment
images:
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service
newTag: dev
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service
newTag: dev
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service
newTag: dev
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/content-service
newTag: dev
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/distribution-service
newTag: dev
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/participation-service
newTag: dev
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/analytics-service
newTag: dev

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: participation-service
spec:
replicas: 1
template:
spec:
containers:
- name: participation-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 1
template:
spec:
containers:
- name: user-service
resources:
requests:
cpu: "256m"
memory: "256Mi"
limits:
cpu: "1024m"
memory: "1024Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ai-service
spec:
replicas: 3
template:
spec:
containers:
- name: ai-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: analytics-service
spec:
replicas: 3
template:
spec:
containers:
- name: analytics-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: content-service
spec:
replicas: 3
template:
spec:
containers:
- name: content-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: distribution-service
spec:
replicas: 3
template:
spec:
containers:
- name: distribution-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-service
spec:
replicas: 3
template:
spec:
containers:
- name: event-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View File

@ -0,0 +1,38 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kt-event-marketing
bases:
- ../../base
# Environment-specific labels
commonLabels:
environment: prod
# Environment-specific patches
patchesStrategicMerge:
- user-service-patch.yaml
- event-service-patch.yaml
- ai-service-patch.yaml
- content-service-patch.yaml
- distribution-service-patch.yaml
- participation-service-patch.yaml
- analytics-service-patch.yaml
# Override image tags for prod environment
images:
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service
newTag: prod
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service
newTag: prod
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service
newTag: prod
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/content-service
newTag: prod
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/distribution-service
newTag: prod
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/participation-service
newTag: prod
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/analytics-service
newTag: prod

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: participation-service
spec:
replicas: 3
template:
spec:
containers:
- name: participation-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
template:
spec:
containers:
- name: user-service
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"

View 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:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: analytics-service
spec:
replicas: 2
template:
spec:
containers:
- name: analytics-service
resources:
requests:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: content-service
spec:
replicas: 2
template:
spec:
containers:
- name: content-service
resources:
requests:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: distribution-service
spec:
replicas: 2
template:
spec:
containers:
- name: distribution-service
resources:
requests:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: event-service
spec:
replicas: 2
template:
spec:
containers:
- name: event-service
resources:
requests:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View File

@ -0,0 +1,38 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kt-event-marketing
bases:
- ../../base
# Environment-specific labels
commonLabels:
environment: staging
# Environment-specific patches
patchesStrategicMerge:
- user-service-patch.yaml
- event-service-patch.yaml
- ai-service-patch.yaml
- content-service-patch.yaml
- distribution-service-patch.yaml
- participation-service-patch.yaml
- analytics-service-patch.yaml
# Override image tags for staging environment
images:
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service
newTag: staging
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service
newTag: staging
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service
newTag: staging
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/content-service
newTag: staging
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/distribution-service
newTag: staging
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/participation-service
newTag: staging
- name: acrdigitalgarage01.azurecr.io/kt-event-marketing/analytics-service
newTag: staging

View File

@ -0,0 +1,17 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: participation-service
spec:
replicas: 2
template:
spec:
containers:
- name: participation-service
resources:
requests:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View 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:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"

View File

@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""
Copy K8s manifests to Kustomize base directory and remove namespace declarations
"""
import os
import shutil
import yaml
from pathlib import Path
# Service names
SERVICES = [
'user-service',
'event-service',
'ai-service',
'content-service',
'distribution-service',
'participation-service',
'analytics-service'
]
# Base directories
SOURCE_DIR = Path('deployment/k8s')
BASE_DIR = Path('.github/kustomize/base')
def remove_namespace_from_yaml(content):
"""Remove namespace field from YAML content"""
docs = list(yaml.safe_load_all(content))
for doc in docs:
if doc and isinstance(doc, dict):
if 'metadata' in doc and 'namespace' in doc['metadata']:
del doc['metadata']['namespace']
return yaml.dump_all(docs, default_flow_style=False, sort_keys=False)
def copy_and_process_file(source_path, dest_path):
"""Copy file and remove namespace declaration"""
with open(source_path, 'r', encoding='utf-8') as f:
content = f.read()
# Remove namespace from YAML
processed_content = remove_namespace_from_yaml(content)
# Write to destination
dest_path.parent.mkdir(parents=True, exist_ok=True)
with open(dest_path, 'w', encoding='utf-8') as f:
f.write(processed_content)
print(f"✓ Copied and processed: {source_path} -> {dest_path}")
def main():
print("Starting manifest copy to Kustomize base...")
# Copy common resources
print("\n[Common Resources]")
common_dir = SOURCE_DIR / 'common'
for file in ['cm-common.yaml', 'secret-common.yaml', 'secret-imagepull.yaml', 'ingress.yaml']:
source = common_dir / file
if source.exists():
dest = BASE_DIR / file
copy_and_process_file(source, dest)
# Copy service-specific resources
print("\n[Service Resources]")
for service in SERVICES:
service_dir = SOURCE_DIR / service
if not service_dir.exists():
print(f"⚠ Service directory not found: {service_dir}")
continue
print(f"\nProcessing {service}...")
for file in service_dir.glob('*.yaml'):
dest = BASE_DIR / f"{service}-{file.name}"
copy_and_process_file(file, dest)
print("\n✅ All manifests copied to base directory!")
if __name__ == '__main__':
main()

181
.github/scripts/deploy.sh vendored Normal file
View File

@ -0,0 +1,181 @@
#!/bin/bash
set -e
###############################################################################
# Backend Services Deployment Script for AKS
#
# Usage:
# ./deploy.sh <environment> [service-name]
#
# Arguments:
# environment - Target environment (dev, staging, prod)
# service-name - Specific service to deploy (optional, deploys all if not specified)
#
# Examples:
# ./deploy.sh dev # Deploy all services to dev
# ./deploy.sh prod user-service # Deploy only user-service to prod
###############################################################################
# Color output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Validate arguments
if [ $# -lt 1 ]; then
log_error "Usage: $0 <environment> [service-name]"
log_error "Environment must be one of: dev, staging, prod"
exit 1
fi
ENVIRONMENT=$1
SERVICE=${2:-all}
# Validate environment
if [[ ! "$ENVIRONMENT" =~ ^(dev|staging|prod)$ ]]; then
log_error "Invalid environment: $ENVIRONMENT"
log_error "Must be one of: dev, staging, prod"
exit 1
fi
# Load environment variables
ENV_FILE=".github/config/deploy_env_vars_${ENVIRONMENT}"
if [ ! -f "$ENV_FILE" ]; then
log_error "Environment file not found: $ENV_FILE"
exit 1
fi
source "$ENV_FILE"
log_info "Loaded environment configuration: $ENVIRONMENT"
# Service list
SERVICES=(
"user-service"
"event-service"
"ai-service"
"content-service"
"distribution-service"
"participation-service"
"analytics-service"
)
# Validate service if specified
if [ "$SERVICE" != "all" ]; then
if [[ ! " ${SERVICES[@]} " =~ " ${SERVICE} " ]]; then
log_error "Invalid service: $SERVICE"
log_error "Must be one of: ${SERVICES[*]}"
exit 1
fi
SERVICES=("$SERVICE")
fi
log_info "Services to deploy: ${SERVICES[*]}"
# Check prerequisites
log_info "Checking prerequisites..."
if ! command -v az &> /dev/null; then
log_error "Azure CLI not found. Please install Azure CLI."
exit 1
fi
if ! command -v kubectl &> /dev/null; then
log_error "kubectl not found. Please install kubectl."
exit 1
fi
if ! command -v kustomize &> /dev/null; then
log_error "kustomize not found. Please install kustomize."
exit 1
fi
# Azure login check
log_info "Checking Azure authentication..."
if ! az account show &> /dev/null; then
log_error "Not logged in to Azure. Please run 'az login'"
exit 1
fi
# Get AKS credentials
log_info "Getting AKS credentials..."
az aks get-credentials \
--resource-group "$RESOURCE_GROUP" \
--name "$AKS_CLUSTER" \
--overwrite-existing
# Check namespace
log_info "Checking namespace: $NAMESPACE"
if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then
log_warn "Namespace $NAMESPACE does not exist. Creating..."
kubectl create namespace "$NAMESPACE"
fi
# Build and deploy with Kustomize
OVERLAY_DIR=".github/kustomize/overlays/${ENVIRONMENT}"
if [ ! -d "$OVERLAY_DIR" ]; then
log_error "Kustomize overlay directory not found: $OVERLAY_DIR"
exit 1
fi
log_info "Building Kustomize manifests for $ENVIRONMENT..."
cd "$OVERLAY_DIR"
# Update image tags
log_info "Updating image tags to: $ENVIRONMENT"
kustomize edit set image \
${ACR_NAME}.azurecr.io/kt-event-marketing/user-service:${ENVIRONMENT} \
${ACR_NAME}.azurecr.io/kt-event-marketing/event-service:${ENVIRONMENT} \
${ACR_NAME}.azurecr.io/kt-event-marketing/ai-service:${ENVIRONMENT} \
${ACR_NAME}.azurecr.io/kt-event-marketing/content-service:${ENVIRONMENT} \
${ACR_NAME}.azurecr.io/kt-event-marketing/distribution-service:${ENVIRONMENT} \
${ACR_NAME}.azurecr.io/kt-event-marketing/participation-service:${ENVIRONMENT} \
${ACR_NAME}.azurecr.io/kt-event-marketing/analytics-service:${ENVIRONMENT}
# Apply manifests
log_info "Applying manifests to AKS..."
kustomize build . | kubectl apply -f -
cd - > /dev/null
# Wait for deployments
log_info "Waiting for deployments to be ready..."
for service in "${SERVICES[@]}"; do
log_info "Waiting for $service deployment..."
if ! kubectl rollout status deployment/"$service" -n "$NAMESPACE" --timeout=5m; then
log_error "Deployment of $service failed!"
exit 1
fi
log_info "$service is ready"
done
# Verify deployment
log_info "Verifying deployment..."
echo ""
echo "=== Pods Status ==="
kubectl get pods -n "$NAMESPACE" -l app.kubernetes.io/part-of=kt-event-marketing
echo ""
echo "=== Services ==="
kubectl get svc -n "$NAMESPACE"
echo ""
echo "=== Ingress ==="
kubectl get ingress -n "$NAMESPACE"
log_info "Deployment completed successfully!"
log_info "Environment: $ENVIRONMENT"
log_info "Services: ${SERVICES[*]}"

51
.github/scripts/generate-patches.sh vendored Normal file
View File

@ -0,0 +1,51 @@
#!/bin/bash
SERVICES=(user-service event-service ai-service content-service distribution-service participation-service analytics-service)
# Staging patches (2 replicas, increased resources)
for service in "${SERVICES[@]}"; do
cat > ".github/kustomize/overlays/staging/${service}-patch.yaml" << YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${service}
spec:
replicas: 2
template:
spec:
containers:
- name: ${service}
resources:
requests:
cpu: "512m"
memory: "512Mi"
limits:
cpu: "2048m"
memory: "2048Mi"
YAML
done
# Prod patches (3 replicas, maximum resources)
for service in "${SERVICES[@]}"; do
cat > ".github/kustomize/overlays/prod/${service}-patch.yaml" << YAML
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${service}
spec:
replicas: 3
template:
spec:
containers:
- name: ${service}
resources:
requests:
cpu: "1024m"
memory: "1024Mi"
limits:
cpu: "4096m"
memory: "4096Mi"
YAML
done
echo "✅ Generated all patch files for staging and prod"

207
.github/workflows/backend-cicd.yaml vendored Normal file
View File

@ -0,0 +1,207 @@
name: Backend CI/CD Pipeline
on:
# push:
# branches:
# - develop
# - main
# paths:
# - '*-service/**'
# - '.github/workflows/backend-cicd.yaml'
# - '.github/kustomize/**'
pull_request:
branches:
- develop
- main
paths:
- '*-service/**'
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
type: choice
options:
- dev
- staging
- prod
service:
description: 'Service to deploy (all for all services)'
required: true
default: 'all'
env:
ACR_NAME: acrdigitalgarage01
RESOURCE_GROUP: rg-digitalgarage-01
AKS_CLUSTER: aks-digitalgarage-01
NAMESPACE: kt-event-marketing
JDK_VERSION: '21'
jobs:
detect-changes:
name: Detect Changed Services
runs-on: ubuntu-latest
outputs:
services: ${{ steps.detect.outputs.services }}
environment: ${{ steps.env.outputs.environment }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine environment
id: env
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
elif [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "environment=prod" >> $GITHUB_OUTPUT
elif [ "${{ github.ref }}" = "refs/heads/develop" ]; then
echo "environment=dev" >> $GITHUB_OUTPUT
else
echo "environment=dev" >> $GITHUB_OUTPUT
fi
- name: Detect changed services
id: detect
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ github.event.inputs.service }}" != "all" ]; then
echo "services=[\"${{ github.event.inputs.service }}\"]" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ github.event.inputs.service }}" = "all" ]; then
echo "services=[\"user-service\",\"event-service\",\"ai-service\",\"content-service\",\"distribution-service\",\"participation-service\",\"analytics-service\"]" >> $GITHUB_OUTPUT
else
CHANGED_SERVICES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | \
grep -E '^(user|event|ai|content|distribution|participation|analytics)-service/' | \
cut -d'/' -f1 | sort -u | \
jq -R -s -c 'split("\n") | map(select(length > 0))')
if [ "$CHANGED_SERVICES" = "[]" ] || [ -z "$CHANGED_SERVICES" ]; then
echo "services=[\"user-service\",\"event-service\",\"ai-service\",\"content-service\",\"distribution-service\",\"participation-service\",\"analytics-service\"]" >> $GITHUB_OUTPUT
else
echo "services=$CHANGED_SERVICES" >> $GITHUB_OUTPUT
fi
fi
build-and-push:
name: Build and Push - ${{ matrix.service }}
needs: detect-changes
runs-on: ubuntu-latest
strategy:
matrix:
service: ${{ fromJson(needs.detect-changes.outputs.services) }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK ${{ env.JDK_VERSION }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.JDK_VERSION }}
distribution: 'temurin'
cache: 'gradle'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew ${{ matrix.service }}:build -x test
# - name: Run tests
# run: ./gradlew ${{ matrix.service }}:test
- name: Build JAR
run: ./gradlew ${{ matrix.service }}:bootJar
- name: Log in to Azure Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.ACR_NAME }}.azurecr.io
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./${{ matrix.service }}
file: ./${{ matrix.service }}/Dockerfile
push: true
tags: |
${{ env.ACR_NAME }}.azurecr.io/kt-event-marketing/${{ matrix.service }}:${{ needs.detect-changes.outputs.environment }}
${{ env.ACR_NAME }}.azurecr.io/kt-event-marketing/${{ matrix.service }}:${{ github.sha }}
${{ env.ACR_NAME }}.azurecr.io/kt-event-marketing/${{ matrix.service }}:latest
deploy:
name: Deploy to AKS - ${{ needs.detect-changes.outputs.environment }}
needs: [detect-changes, build-and-push]
runs-on: ubuntu-latest
environment: ${{ needs.detect-changes.outputs.environment }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Azure login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Get AKS credentials
run: |
az aks get-credentials \
--resource-group ${{ env.RESOURCE_GROUP }} \
--name ${{ env.AKS_CLUSTER }} \
--overwrite-existing
- name: Setup Kustomize
uses: imranismail/setup-kustomize@v2
- name: Deploy with Kustomize
run: |
cd .github/kustomize/overlays/${{ needs.detect-changes.outputs.environment }}
kustomize edit set image \
acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service:${{ needs.detect-changes.outputs.environment }} \
acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service:${{ needs.detect-changes.outputs.environment }} \
acrdigitalgarage01.azurecr.io/kt-event-marketing/ai-service:${{ needs.detect-changes.outputs.environment }} \
acrdigitalgarage01.azurecr.io/kt-event-marketing/content-service:${{ needs.detect-changes.outputs.environment }} \
acrdigitalgarage01.azurecr.io/kt-event-marketing/distribution-service:${{ needs.detect-changes.outputs.environment }} \
acrdigitalgarage01.azurecr.io/kt-event-marketing/participation-service:${{ needs.detect-changes.outputs.environment }} \
acrdigitalgarage01.azurecr.io/kt-event-marketing/analytics-service:${{ needs.detect-changes.outputs.environment }}
kustomize build . | kubectl apply -f -
- name: Wait for deployment rollout
run: |
for service in $(echo '${{ needs.detect-changes.outputs.services }}' | jq -r '.[]'); do
echo "Waiting for ${service} deployment..."
kubectl rollout status deployment/${service} -n ${{ env.NAMESPACE }} --timeout=5m
done
- name: Verify deployment
run: |
echo "=== Pods Status ==="
kubectl get pods -n ${{ env.NAMESPACE }} -l app.kubernetes.io/part-of=kt-event-marketing
echo "=== Services ==="
kubectl get svc -n ${{ env.NAMESPACE }}
echo "=== Ingress ==="
kubectl get ingress -n ${{ env.NAMESPACE }}
notify:
name: Notify Deployment Result
needs: [detect-changes, deploy]
runs-on: ubuntu-latest
if: always()
steps:
- name: Deployment Success
if: needs.deploy.result == 'success'
run: |
echo "✅ Deployment to ${{ needs.detect-changes.outputs.environment }} succeeded!"
echo "Services: ${{ needs.detect-changes.outputs.services }}"
- name: Deployment Failure
if: needs.deploy.result == 'failure'
run: |
echo "❌ Deployment to ${{ needs.detect-changes.outputs.environment }} failed!"
echo "Services: ${{ needs.detect-changes.outputs.services }}"
exit 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

244
API-TEST-RESULT.md Normal file
View File

@ -0,0 +1,244 @@
# API 연동 테스트 결과 보고서
**테스트 일시**: 2025-10-29
**테스트 대상**: 프론트엔드(localhost:3000)와 event-service(localhost:8080) API 연동
---
## ✅ 테스트 결과 요약
### 1. 서비스 실행 상태
- **프론트엔드**: ✅ Next.js 서버 (port 3000) 정상 실행
- **백엔드**: ✅ Event-service (port 8080) 정상 실행
- **데이터베이스**: ✅ PostgreSQL 연결 정상 (헬스체크 통과)
### 2. API 연동 테스트
#### 2.1 백엔드 API 직접 호출 테스트
**테스트 명령어**:
```bash
curl -X GET "http://localhost:8080/api/v1/events?page=0&size=20" \
-H "Authorization: Bearer <JWT_TOKEN>" \
-H "Content-Type: application/json"
```
**결과**: ✅ **성공**
- 응답 코드: 200 OK
- 조회된 이벤트: 8개
- 응답 형식: JSON (표준 API 응답 포맷)
- 페이지네이션: 정상 작동
**샘플 응답 데이터**:
```json
{
"success": true,
"data": {
"content": [
{
"eventId": "2a91c77c-9276-49d3-94d5-0ab8f0b3d343",
"userId": "11111111-1111-1111-1111-111111111111",
"storeId": "22222222-2222-2222-2222-222222222222",
"objective": "awareness",
"status": "DRAFT",
"createdAt": "2025-10-29T11:08:38.556326"
}
// ... 7개 더
],
"page": 0,
"size": 20,
"totalElements": 8,
"totalPages": 1
}
}
```
#### 2.2 인증 테스트
**JWT 토큰**: ✅ 정상 작동
- 토큰 생성 스크립트: `generate-test-token.py`
- 유효 기간: 365일 (테스트용)
- 알고리즘: HS256
- Secret: 백엔드와 일치
#### 2.3 프론트엔드 설정
**환경 변수 파일**: `.env.local` 생성 완료
```env
NEXT_PUBLIC_API_BASE_URL=http://localhost:8081
NEXT_PUBLIC_EVENT_HOST=http://localhost:8080
NEXT_PUBLIC_API_VERSION=v1
```
**현재 상태**: ⚠️ **Mock 데이터 사용 중**
- 파일: `src/app/(main)/events/page.tsx`
- 이벤트 목록 페이지가 하드코딩된 Mock 데이터 표시
- 실제 API 연동 코드 미구현 상태
---
## 📊 API 엔드포인트 정보
### Event Service (localhost:8080)
#### 1. 이벤트 목록 조회
- **URL**: `GET /api/v1/events`
- **인증**: Bearer Token 필수
- **파라미터**:
- `status`: EventStatus (optional) - DRAFT, PUBLISHED, ENDED
- `search`: String (optional) - 검색어
- `objective`: String (optional) - 목적 필터
- `page`: int (default: 0)
- `size`: int (default: 20)
- `sort`: String (default: createdAt)
- `order`: String (default: desc)
#### 2. 이벤트 상세 조회
- **URL**: `GET /api/v1/events/{eventId}`
- **인증**: Bearer Token 필수
#### 3. 이벤트 생성 (목적 선택)
- **URL**: `POST /api/v1/events/objectives`
- **인증**: Bearer Token 필수
- **Request Body**:
```json
{
"objective": "CUSTOMER_ACQUISITION"
}
```
#### 4. 추가 엔드포인트
- `DELETE /api/v1/events/{eventId}` - 이벤트 삭제
- `POST /api/v1/events/{eventId}/publish` - 이벤트 배포
- `POST /api/v1/events/{eventId}/end` - 이벤트 종료
- `POST /api/v1/events/{eventId}/ai-recommendations` - AI 추천 요청
- `POST /api/v1/events/{eventId}/images` - 이미지 생성 요청
- `PUT /api/v1/events/{eventId}` - 이벤트 수정
---
## 🔍 발견 사항
### ✅ 정상 작동 항목
1. **백엔드 서비스**
- Event-service 정상 실행 (port 8080)
- PostgreSQL 데이터베이스 연결 정상
- API 엔드포인트 정상 응답
- JWT 인증 시스템 작동
2. **프론트엔드 서비스**
- Next.js 개발 서버 정상 실행 (port 3000)
- 페이지 렌더링 정상
- 환경 변수 설정 완료
### ⚠️ 개선 필요 항목
#### 1. 프론트엔드 API 연동 미구현
**현재 상태**:
- `src/app/(main)/events/page.tsx` 파일이 Mock 데이터 사용
- 실제 API 호출 코드 없음
**권장 수정 사항**:
```typescript
// src/entities/event/api/eventApi.ts (신규 생성 필요)
import { apiClient } from '@/shared/api';
export const eventApi = {
getEvents: async (params) => {
const response = await apiClient.get('/api/v1/events', { params });
return response.data;
},
// ... 기타 메서드
};
```
#### 2. API 클라이언트 설정 개선
**현재**:
- `apiClient` 기본 URL이 user-service(8081)를 가리킴
- Event API는 별도 서비스(8080)
**개선 방안**:
```typescript
// 서비스별 클라이언트 분리 또는
// NEXT_PUBLIC_EVENT_HOST 환경 변수 활용
const eventApiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_EVENT_HOST || 'http://localhost:8080',
// ...
});
```
---
## 📝 테스트 체크리스트
### 완료된 항목 ✅
- [x] 백엔드 서비스 실행 상태 확인
- [x] 프론트엔드 서비스 실행 상태 확인
- [x] Event Service API 직접 호출 테스트
- [x] JWT 인증 토큰 생성 및 테스트
- [x] 환경 변수 설정 (`.env.local`)
- [x] API 응답 형식 확인
- [x] 페이지네이션 동작 확인
- [x] 데이터베이스 연결 확인
### 추가 작업 필요 ⏳
- [ ] 프론트엔드 API 연동 코드 작성
- [ ] Event API 클라이언트 구현
- [ ] React Query 또는 SWR 통합
- [ ] 에러 핸들링 구현
- [ ] 로딩 상태 UI 구현
- [ ] 실제 데이터 렌더링 테스트
- [ ] E2E 테스트 작성
---
## 🎯 다음 단계 권장사항
### 1단계: Event API 클라이언트 작성
```bash
# 파일 생성
src/entities/event/api/eventApi.ts
src/entities/event/model/types.ts
```
### 2단계: React Query 설정
```bash
# useEvents 훅 작성
src/entities/event/model/useEvents.ts
```
### 3단계: 페이지 수정
```bash
# Mock 데이터를 실제 API 호출로 교체
src/app/(main)/events/page.tsx
```
### 4단계: 통합 테스트
- 브라우저에서 실제 데이터 렌더링 확인
- 필터링 및 검색 기능 테스트
- 페이지네이션 동작 확인
---
## 📌 참고 정보
### 테스트 토큰 정보
- User ID: `6db043d0-b303-4577-b9dd-6d366cc59fa0`
- Store ID: `34000028-01fd-4ed1-975c-35f7c88b6547`
- Email: `test@example.com`
- 유효 기간: 2026-10-29까지
### 서비스 포트 매핑
| 서비스 | 포트 | 상태 |
|--------|------|------|
| 프론트엔드 | 3000 | ✅ Running |
| User Service | 8081 | ⚠️ 미확인 |
| Event Service | 8080 | ✅ Running |
| Content Service | 8082 | ⚠️ 미확인 |
| AI Service | 8083 | ⚠️ 미확인 |
| Participation Service | 8084 | ⚠️ 미확인 |
---
## ✨ 결론
**백엔드 API는 정상적으로 작동하고 있으며, 프론트엔드와의 연동을 위한 환경은 준비되었습니다.**
다음 작업은 프론트엔드에서 Mock 데이터를 실제 API 호출로 교체하는 것입니다.

24
ai-service/Dockerfile Normal file
View File

@ -0,0 +1,24 @@
# Multi-stage build for Spring Boot application
FROM eclipse-temurin:21-jre-alpine AS builder
WORKDIR /app
COPY build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# Create non-root user
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
# Copy layers from builder
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8083/api/v1/ai-service/actuator/health || exit 1
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

View File

@ -37,7 +37,7 @@ spring:
server:
port: ${SERVER_PORT:8083}
servlet:
context-path: /
context-path: /api/v1/ai-service
encoding:
charset: UTF-8
enabled: true

View File

@ -39,7 +39,7 @@
<entry key="JWT_REFRESH_TOKEN_VALIDITY" value="86400" />
<!-- CORS Configuration -->
<entry key="CORS_ALLOWED_ORIGINS" value="http://localhost:*" />
<entry key="CORS_ALLOWED_ORIGINS" value="http://localhost:*,http://*.nip.io:*" />
<!-- Logging Configuration -->
<entry key="LOG_FILE" value="logs/analytics-service.log" />

View File

@ -0,0 +1,24 @@
# Multi-stage build for Spring Boot application
FROM eclipse-temurin:21-jre-alpine AS builder
WORKDIR /app
COPY build/libs/*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# Create non-root user
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
# Copy layers from builder
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8086/api/v1/analytics/actuator/health || exit 1
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

View File

@ -80,6 +80,7 @@ server:
charset: UTF-8
enabled: true
force: true
context-path: /api/v1/analytics
# JWT
jwt:
@ -89,7 +90,11 @@ jwt:
# CORS Configuration
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:*}
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://kt-event-marketing.20.214.196.128.nip.io}
allowed-methods: ${CORS_ALLOWED_METHODS:GET,POST,PUT,DELETE,OPTIONS,PATCH}
allowed-headers: ${CORS_ALLOWED_HEADERS:*}
allow-credentials: ${CORS_ALLOW_CREDENTIALS:true}
max-age: ${CORS_MAX_AGE:3600}
# Actuator
management:

25
check-event-service.sh Normal file
View File

@ -0,0 +1,25 @@
#!/bin/bash
echo "================================"
echo "Event Service 시작 확인"
echo "================================"
echo ""
echo "1. 프로세스 확인..."
jps -l | grep -i "EventServiceApplication" || echo "❌ 프로세스 없음"
echo ""
echo "2. 포트 확인..."
netstat -ano | findstr "LISTENING" | findstr ":8082" || echo "⚠️ 8082 포트 리스닝 없음"
echo ""
echo "3. Health Check (8082 포트)..."
curl -s http://localhost:8082/actuator/health 2>&1 | head -5
echo ""
echo "4. 로그 확인 (최근 10줄)..."
tail -10 logs/event-service.log
echo ""
echo "================================"
echo "확인 완료"
echo "================================"

68
claude/class-design.md Normal file
View File

@ -0,0 +1,68 @@
# 클래스설계가이드
[요청사항]
- <작성원칙>을 준용하여 설계
- <작성순서>에 따라 설계
- [결과파일] 안내에 따라 파일 작성
[가이드]
<작성원칙>
- **유저스토리와 매칭**되어야 함. **불필요한 추가 설계 금지**
- API설계서와 일관성 있게 설계. Controller에 API를 누락하지 말고 모두 설계
- Controller 클래스는 API로 정의하지 않은 메소드 생성 안함. 단, 필요한 Private 메소드는 추가함
- {service-name}-simple.puml파일에 Note로 Controller 클래스 메소드와 API 매핑표 작성: {Methond}: {API Path} {API 제목}
예) login: /login 로그인
- 내부시퀀스설계서와 일관성 있게 설계
- 각 서비스별 지정된 {설계 아키텍처 패턴}을 적용
- Clean아키텍처 적용 시 Port/Adapter라는 용어 대신 Clean 아키텍처에 맞는 용어 사용
- 클래스의 프라퍼티와 메소드를 모두 기술할 것. 단 "Getter/Setter 메소드"는 작성하지 않음
- 클래스 간의 관계를 표현: Generalization, Realization, Dependency, Association, Aggregation, Composition
<작성순서>
- **서브 에이전트를 활용한 병렬 작성 필수**
- **3단계 하이브리드 접근법 적용**
- **마이크로서비스 아키텍처 기반 설계**
- 1단계: 공통 컴포넌트 설계 (순차적)
- 결과: design/backend/class/common-base.puml
- 2단계: 서비스별 병렬 설계 (병렬 실행)
- 1단계 공통 컴포넌트 참조
- '!include'는 사용하지 말고 필요한 인터페이스 직접 정의
- 클래스 설계 후 프라퍼티와 메소드를 생략한 간단한 클래스설계서도 추가로 작성
- 결과:
- design/backend/class/{service-name}.puml
- design/backend/class/{service-name}-simple.puml
- 병렬 처리 기준
- 서비스 간 의존성이 없는 경우: 모든 서비스 동시 실행
- 의존성이 있는 경우: 의존성 그룹별로 묶어서 실행
- 예: A→B 의존 시, A 완료 후 B 실행
- 독립 서비스 C,D는 A,B와 병렬 실행
- 3단계: 통합 및 검증 (순차적)
- '패키지구조표준'의 예시를 참조하여 모든 클래스와 파일이 포함된 패키지 구조도를 작성
(plantuml 스크립트가 아니라 트리구조 텍스트로 작성)
- 인터페이스 일치성 검증
- 명명 규칙 통일성 확인
- 의존성 검증
- 크로스 서비스 참조 검증
- **PlantUML 스크립트 파일 검사 실행**: 'PlantUML문법검사가이드' 준용
[참고자료]
- 유저스토리
- API설계서
- 내부시퀀스설계서
- 패키지구조표준
- PlantUML문법검사가이드
[예시]
- 링크: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/sample-클래스설계서.puml
[결과파일]
- 패키지 구조도: design/backend/class/package-structure.md
- 클래스설계서:
- design/backend/class/common-base.puml
- design/backend/class/{service-name}.puml
- 클래스설계서(요약): design/backend/class/{service-name}-simple.puml
- service-name은 영어로 작성 (예: profile, location, itinerary)

55
claude/data-design.md Normal file
View File

@ -0,0 +1,55 @@
# 데이터설계가이드
[요청사항]
- <작성원칙>을 준용하여 설계
- <작성순서>에 따라 설계
- [결과파일] 안내에 따라 파일 작성
[가이드]
<작성원칙>
- **클래스설계서의 각 서비스별 Entity정의와 일치**해야 함. **불필요한 추가 설계 금지**
- <데이터독립성원칙>에 따라 각 서비스마다 데이터베이스를 분리
<작성순서>
- 준비:
- 유저스토리, API설계서, 외부시퀀스설계서, 내부시퀀스설계서, 패키지구조표준 분석 및 이해
- 실행:
- <병렬처리>안내에 따라 각 서비스별 병렬 수행
- 데이터설계서 작성
- 캐시 사용 시 캐시DB 설계 포함
- 시작 부분에 '데이터설계 요약' 제공
- 결과: {service-name}.md
- ERD 작성
- 결과: {service-name}-erd.puml
- **PlantUML 스크립트 파일 생성 즉시 검사 실행**: 'PlantUML 문법 검사 가이드' 준용
- 데이터베이스 스키마 스크립트 작성
- 실행 가능한 SQL 스크립트 작성
- 결과: {service-name}-schema.psql
- 검토:
- <작성원칙> 준수 검토
- 스쿼드 팀원 리뷰: 누락 및 개선 사항 검토
- 수정 사항 선택 및 반영
<병렬처리>
Agent 1~N: 각 서비스별 데이터베이스 설계
- 서비스별 독립적인 스키마 설계
- Entity 클래스와 1:1 매핑
- 서비스 간 데이터 공유 금지
- FK 관계는 서비스 내부에서만 설정
<데이터독립성원칙>
- **데이터 소유권**: 각 서비스가 자신의 데이터 완전 소유
- **크로스 서비스 조인 금지**: 서비스 간 DB 조인 불가
- **이벤트 기반 동기화**: 필요시 이벤트/메시지로 데이터 동기화
- **캐시 활용**: 타 서비스 데이터는 캐시로만 참조
[참고자료]
- 클래스설계서
[예시]
- 링크: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/sample-데이터설계서.puml
[결과파일]
- design/backend/database/{service-name}.md
- design/backend/database/{service-name}-erd.puml
- design/backend/database/{service-name}-schema.psql
- service-name은 영어로 작성

View 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

View File

@ -0,0 +1,425 @@
# High Level 아키텍처 정의서
## 1. 개요 (Executive Summary)
### 1.1 프로젝트 개요
- **비즈니스 목적**:
- **핵심 기능**:
- **대상 사용자**:
- **예상 사용자 규모**:
### 1.2 아키텍처 범위 및 경계
- **시스템 범위**:
- **포함되는 시스템**:
- **제외되는 시스템**:
- **외부 시스템 연동**:
### 1.3 문서 구성
이 문서는 4+1 뷰 모델을 기반으로 구성되며, 논리적/물리적/프로세스/개발 관점에서 아키텍처를 정의합니다.
---
## 2. 아키텍처 요구사항
### 2.1 기능 요구사항 요약
| 영역 | 주요 기능 | 우선순위 |
|------|-----------|----------|
| | | |
### 2.2 비기능 요구사항 (NFRs)
#### 2.2.1 성능 요구사항
- **응답시간**:
- **처리량**:
- **동시사용자**:
- **데이터 처리량**:
#### 2.2.2 확장성 요구사항
- **수평 확장**:
- **수직 확장**:
- **글로벌 확장**:
#### 2.2.3 가용성 요구사항
- **목표 가용성**: 99.9% / 99.99% / 99.999%
- **다운타임 허용**:
- **재해복구 목표**: RTO/RPO
#### 2.2.4 보안 요구사항
- **인증/인가**:
- **데이터 암호화**:
- **네트워크 보안**:
- **컴플라이언스**:
### 2.3 아키텍처 제약사항
- **기술적 제약**:
- **비용 제약**:
- **시간 제약**:
- **조직적 제약**:
---
## 3. 아키텍처 설계 원칙
### 3.1 핵심 설계 원칙
1. **확장성 우선**: 수평적 확장이 가능한 구조
2. **장애 격리**: 단일 장애점 제거 및 Circuit Breaker 패턴
3. **느슨한 결합**: 마이크로서비스 간 독립성 보장
4. **관측 가능성**: 로깅, 모니터링, 추적 체계 구축
5. **보안 바이 데자인**: 설계 단계부터 보안 고려
### 3.2 아키텍처 품질 속성 우선순위
| 순위 | 품질 속성 | 중요도 | 전략 |
|------|-----------|--------|------|
| 1 | | High | |
| 2 | | Medium | |
| 3 | | Low | |
---
## 4. 논리 아키텍처 (Logical View)
### 4.1 시스템 컨텍스트 다이어그램
```
{논리아키텍처 경로}
```
### 4.2 도메인 아키텍처
#### 4.2.1 도메인 모델
| 도메인 | 책임 | 주요 엔티티 |
|--------|------|-------------|
| | | |
#### 4.2.2 바운디드 컨텍스트
```
[도메인별 바운디드 컨텍스트 다이어그램]
```
### 4.3 서비스 아키텍처
#### 4.3.1 마이크로서비스 구성
| 서비스명 | 책임 |
|----------|------|
| | |
#### 4.3.2 서비스 간 통신 패턴
- **동기 통신**: REST API, GraphQL
- **비동기 통신**: Event-driven, Message Queue
- **데이터 일관성**: Saga Pattern, Event Sourcing
---
## 5. 프로세스 아키텍처 (Process View)
### 5.1 주요 비즈니스 프로세스
#### 5.1.1 핵심 사용자 여정
```
[사용자 여정별 프로세스 플로우]
```
#### 5.1.2 시스템 간 통합 프로세스
```
[시스템 통합 시퀀스 다이어그램]
```
### 5.2 동시성 및 동기화
- **동시성 처리 전략**:
- **락 관리**:
- **이벤트 순서 보장**:
---
## 6. 개발 아키텍처 (Development View)
### 6.1 개발 언어 및 프레임워크 선정
#### 6.1.1 백엔드 기술스택
| 서비스 | 언어 | 프레임워크 | 선정이유 |
|----------|------|---------------|----------|
#### 6.1.2 프론트엔드 기술스택
- **언어**:
- **프레임워크**:
- **선정 이유**:
### 6.2 서비스별 개발 아키텍처 패턴
| 서비스 | 아키텍처 패턴 | 선정 이유 |
|--------|---------------|-----------|
| | Clean/Layered/Hexagonal | |
### 6.3 개발 가이드라인
- **코딩 표준**:
- **테스트 전략**:
---
## 7. 물리 아키텍처 (Physical View)
### 7.1 클라우드 아키텍처 패턴
#### 7.1.1 선정된 클라우드 패턴
- **패턴명**:
- **적용 이유**:
- **예상 효과**:
#### 7.1.2 클라우드 제공자
- **주 클라우드**: Azure/AWS/GCP
- **멀티 클라우드 전략**:
- **하이브리드 구성**:
### 7.2 인프라스트럭처 구성
#### 7.2.1 컴퓨팅 리소스
| 구성요소 | 사양 | 스케일링 전략 |
|----------|------|---------------|
| 웹서버 | | |
| 앱서버 | | |
| 데이터베이스 | | |
#### 7.2.2 네트워크 구성
```
[네트워크 토폴로지 다이어그램]
```
#### 7.2.3 보안 구성
- **방화벽**:
- **WAF**:
- **DDoS 방어**:
- **VPN/Private Link**:
---
## 8. 기술 스택 아키텍처
### 8.1 API Gateway & Service Mesh
#### 8.1.1 API Gateway
- **제품**:
- **주요 기능**: 인증, 라우팅, 레이트 리미팅, 모니터링
- **설정 전략**:
#### 8.1.2 Service Mesh
- **제품**: Istio/Linkerd/Consul Connect
- **적용 범위**:
- **트래픽 관리**:
### 8.2 데이터 아키텍처
#### 8.2.1 데이터베이스 전략
| 용도 | 데이터베이스 | 타입 | 특징 |
|------|-------------|------|------|
| 트랜잭션 | | RDBMS | |
| 캐시 | | In-Memory | |
| 검색 | | Search Engine | |
| 분석 | | Data Warehouse | |
#### 8.2.2 데이터 파이프라인
```
[데이터 플로우 다이어그램]
```
### 8.3 백킹 서비스 (Backing Services)
#### 8.3.1 메시징 & 이벤트 스트리밍
- **메시지 큐**:
- **이벤트 스트리밍**:
- **이벤트 스토어**:
#### 8.3.2 스토리지 서비스
- **객체 스토리지**:
- **블록 스토리지**:
- **파일 스토리지**:
### 8.4 관측 가능성 (Observability)
#### 8.4.1 로깅 전략
- **로그 수집**:
- **로그 저장**:
- **로그 분석**:
#### 8.4.2 모니터링 & 알람
- **메트릭 수집**:
- **시각화**:
- **알람 정책**:
#### 8.4.3 분산 추적
- **추적 도구**:
- **샘플링 전략**:
- **성능 분석**:
---
## 9. AI/ML 아키텍처
### 9.1 AI API 통합 전략
#### 9.1.1 AI 서비스/모델 매핑
| 목적 | 서비스 | 모델 | Input 데이터 | Output 데이터 | SLA |
|------|--------|-------|-------------|-------------|-----|
| | | | | | |
#### 9.1.2 AI 파이프라인
```
[AI 데이터 처리 파이프라인]
```
### 9.2 데이터 과학 플랫폼
- **모델 개발 환경**:
- **모델 배포 전략**:
- **모델 모니터링**:
---
## 10. 개발 운영 (DevOps)
### 10.1 CI/CD 파이프라인
#### 10.1.1 지속적 통합 (CI)
- **도구**:
- **빌드 전략**:
- **테스트 자동화**:
#### 10.1.2 지속적 배포 (CD)
- **배포 도구**:
- **배포 전략**: Blue-Green/Canary/Rolling
- **롤백 정책**:
### 10.2 컨테이너 오케스트레이션
#### 10.2.1 Kubernetes 구성
- **클러스터 전략**:
- **네임스페이스 설계**:
- **리소스 관리**:
#### 10.2.2 헬름 차트 관리
- **차트 구조**:
- **환경별 설정**:
- **의존성 관리**:
---
## 11. 보안 아키텍처
### 11.1 보안 전략
#### 11.1.1 보안 원칙
- **Zero Trust**:
- **Defense in Depth**:
- **Least Privilege**:
#### 11.1.2 위협 모델링
| 위협 | 영향도 | 대응 방안 |
|------|--------|-----------|
| | | |
### 11.2 보안 구현
#### 11.2.1 인증 & 인가
- **ID 제공자**:
- **토큰 전략**: JWT/OAuth2/SAML
- **권한 모델**: RBAC/ABAC
#### 11.2.2 데이터 보안
- **암호화 전략**:
- **키 관리**:
- **데이터 마스킹**:
---
## 12. 품질 속성 구현 전략
### 12.1 성능 최적화
#### 12.1.1 캐싱 전략
| 계층 | 캐시 유형 | TTL | 무효화 전략 |
|------|-----------|-----|-------------|
| | | | |
#### 12.1.2 데이터베이스 최적화
- **인덱싱 전략**:
- **쿼리 최적화**:
- **커넥션 풀링**:
### 12.2 확장성 구현
#### 12.2.1 오토스케일링
- **수평 확장**: HPA/VPA
- **수직 확장**:
- **예측적 스케일링**:
#### 12.2.2 부하 분산
- **로드 밸런서**:
- **트래픽 분산 정책**:
- **헬스체크**:
### 12.3 가용성 및 복원력
#### 12.3.1 장애 복구 전략
- **Circuit Breaker**:
- **Retry Pattern**:
- **Bulkhead Pattern**:
#### 12.3.2 재해 복구
- **백업 전략**:
- **RTO/RPO**:
- **DR 사이트**:
---
## 13. 아키텍처 의사결정 기록 (ADR)
### 13.1 주요 아키텍처 결정
| ID | 결정 사항 | 결정 일자 | 상태 | 결정 이유 |
|----|-----------|-----------|------|-----------|
| ADR-001 | | | | |
### 13.2 트레이드오프 분석
#### 13.2.1 성능 vs 확장성
- **고려사항**:
- **선택**:
- **근거**:
#### 13.2.2 일관성 vs 가용성 (CAP 정리)
- **고려사항**:
- **선택**: AP/CP
- **근거**:
---
## 14. 구현 로드맵
### 14.1 개발 단계
| 단계 | 기간 | 주요 산출물 | 마일스톤 |
|------|------|-------------|-----------|
| Phase 1 | | | |
| Phase 2 | | | |
| Phase 3 | | | |
### 14.2 마이그레이션 전략 (레거시 시스템이 있는 경우)
- **데이터 마이그레이션**:
- **기능 마이그레이션**:
- **병행 운영**:
---
## 15. 위험 관리
### 15.1 아키텍처 위험
| 위험 | 영향도 | 확률 | 완화 방안 |
|------|--------|------|-----------|
| | | | |
### 15.2 기술 부채 관리
- **식별된 기술 부채**:
- **해결 우선순위**:
- **해결 계획**:
---
## 16. 부록
### 16.1 참조 아키텍처
- **업계 표준**:
- **내부 표준**:
- **외부 참조**:
### 16.2 용어집
| 용어 | 정의 |
|------|------|
| | |
### 16.3 관련 문서
- {문서명}: {파일 위치}
- ...
---
## 문서 이력
| 버전 | 일자 | 작성자 | 변경 내용 | 승인자 |
|------|------|--------|-----------|-------|
| v1.0 | | | 초기 작성 | |

View File

@ -0,0 +1,230 @@
# 물리아키텍처설계가이드
[요청사항]
- <작성원칙>을 준용하여 설계
- <작성순서>에 따라 설계
- [결과파일] 안내에 따라 파일 작성
- 완료 후 mermaid 스크립트 테스트 방법 안내
- https://mermaid.live/edit 에 접근
- 스크립트 내용을 붙여넣어 확인
[가이드]
<작성원칙>
- 클라우드 기반의 물리 아키텍처 설계
- HighLevel아키텍처정의서와 일치해야 함
- 백킹서비스설치방법에 있는 제품을 우선적으로 사용
- 환경별 특성에 맞는 차별화 전략 적용
- 비용 효율성과 운영 안정성의 균형 고려
- 선정된 아키텍처 패턴 반영 및 최적화
<작성순서>
- 준비:
- 아키텍처패턴, 논리아키텍처, 외부시퀀스설계서, 데이터설계서, HighLevel아키텍처정의서 분석 및 이해
- 실행:
- 물리아키텍처 다이어그램 작성
- 서브에이전트로 병렬 수행
- Mermaid 형식으로 작성
- Mermaid 스크립트 파일 검사 실행
- 개발환경 물리아키텍처 다이어그램
- '<예시>의 '개발환경 물리아키텍처 다이어그램'의 내용을 읽어 참조
- 사용자 → Ingress → 서비스 → 데이터베이스 플로우만 표시
- 클라우드 서비스는 최소한으로만 포함
- 부가 설명은 문서에만 기록, 다이어그램에서 제거
- 네트워크, 보안, 운영 관련 아키텍처는 생략
- 모니터링/로깅/보안과 관련된 제품/서비스 생략함
- 운영환경 물리아키텍처 다이어그램
- '<예시>의 '운영환경 물리아키텍처 다이어그램'의 내용을 읽어 참조
- 결과:
- 개발환경: physical-architecture-dev.mmd
- 운영환경: physical-architecture-prod.mmd
- 네트워크 아키텍처 다이어그램 작성
- 서브에이전트로 병렬 수행
- Mermaid 형식으로 작성
- Mermaid 스크립트 파일 검사 실행
- 개발환경 네트워크 다이어그램: '<예시>의 '개발환경 네트워크 다이어그램'의 내용을 읽어 참조
- 운영환경 네트워크 다이어그램: '<예시>의 '운영환경 네트워크 다이어그램'의 내용을 읽어 참조
- 결과:
- 개발환경: network-dev.mmd
- 운영환경: network-prod.mmd
- 개발환경 물리아키텍처 설계서 작성
- <개발환경가이드>의 항목별 작성
- '<예시>의 '개발환경 물리아키텍처 설계서'의 내용을 읽어 참조
- 비용 최적화 중심의 개발 친화적 환경 구성
- 빠른 배포와 테스트를 위한 단순화된 아키텍처
- Pod 기반 백킹서비스와 기본 보안 설정
- 개발팀 규모와 워크플로우에 최적화
- 제품/서비스 구성
- Application Gateway: Kubernetes Ingress
- Database: "백킹서비스설치방법"에 있는 오픈소스 DB사용
- Message Queue: "백킹서비스설치방법"에 있는 {CLOUD}에서 제공하는 제품
- CI/CD: 'HighLevel아키텍처정의서'에 있는 CI/CD 제품
- 결과: physical-architecture-dev.md
- 운영환경 물리아키텍처 설계서 작성
- <운영환경가이드>의 항목별 작성
- '<예시>의 '운영환경 물리아키텍처 설계서'의 내용을 읽어 참조
- 고가용성과 확장성을 고려한 프로덕션 환경
- 관리형 서비스 중심의 안정적인 구성
- 엔터프라이즈급 보안과 모니터링 체계
- 실사용자 규모에 따른 성능 최적화
- 결과: physical-architecture-prod.md
- 마스터 아키텍처 설계서 작성
- <마스터가이드>의 항목별 작성
- '<예시>의 '마스터 물리아키텍처 설계서'의 내용을 읽어 참조
- 환경별 아키텍처 비교 및 통합 관리
- 단계별 전환 전략과 확장 로드맵
- 비용 분석과 운영 가이드라인
- 전체 시스템 거버넌스 체계
- 결과: physical-architecture.md
- 검토:
- <작성원칙> 준수 검토
- 선정 아키텍처 패턴 적용 확인
- 환경별 비용 효율성 검증
- 확장성 및 성능 요구사항 충족 확인
- 프로젝트 팀원 리뷰 및 피드백 반영
- 수정 사항 선택 및 최종 반영
<개발환경가이드>
```
대분류|중분류|소분류|작성가이드
---|---|---|---
1. 개요|1.1 설계 목적||개발환경 물리 아키텍처의 설계 범위, 목적, 대상을 명확히 기술
1. 개요|1.2 설계 원칙||개발환경에 적합한 핵심 설계 원칙 4가지 정의 (MVP 우선, 비용 최적화, 개발 편의성, 단순성)
1. 개요|1.3 참조 아키텍처||관련 아키텍처 문서들의 연관관계와 참조 방법 명시
2. 개발환경 아키텍처 개요|2.1 환경 특성||개발환경의 목적, 사용자 규모, 가용성 목표, 확장성, 보안 수준 등 특성 정의
2. 개발환경 아키텍처 개요|2.2 전체 아키텍처||전체 시스템 구성도와 주요 컴포넌트 간 연결 관계 설명 및 다이어그램 링크
3. 컴퓨팅 아키텍처|3.1 Kubernetes 클러스터 구성|3.1.1 클러스터 설정|Kubernetes 버전, 서비스 계층, 네트워크 플러그인, DNS 등 클러스터 기본 설정값 표 작성
3. 컴퓨팅 아키텍처|3.1 Kubernetes 클러스터 구성|3.1.2 노드 풀 구성|인스턴스 크기, 노드 수, 스케일링 설정, 가용영역, 가격 정책 등 노드 풀 상세 설정 표 작성
3. 컴퓨팅 아키텍처|3.2 서비스별 리소스 할당|3.2.1 애플리케이션 서비스|각 마이크로서비스별 CPU/Memory requests, limits, replicas 상세 리소스 할당 표 작성
3. 컴퓨팅 아키텍처|3.2 서비스별 리소스 할당|3.2.2 백킹 서비스|데이터베이스, 캐시 등 백킹서비스 Pod의 리소스 할당 및 스토리지 설정 표 작성 (PVC 사용 시 클라우드 스토리지 클래스 지정: standard, premium-ssd 등)
3. 컴퓨팅 아키텍처|3.2 서비스별 리소스 할당|3.2.3 스토리지 클래스 구성|클라우드 제공자별 스토리지 클래스 설정 (hostPath 사용 금지, 관리형 디스크만 사용)
4. 네트워크 아키텍처|4.1 네트워크 구성|4.1.1 네트워크 토폴로지|네트워크 구성도 및 서브넷 구조 설명, 네트워크 다이어그램 링크 제공
4. 네트워크 아키텍처|4.1 네트워크 구성|4.1.2 네트워크 보안|Network Policy 설정, 접근 제한 규칙, 보안 정책 표 작성
4. 네트워크 아키텍처|4.2 서비스 디스커버리||각 서비스의 내부 DNS 주소, 포트, 용도를 정리한 서비스 디스커버리 표 작성
5. 데이터 아키텍처|5.1 데이터베이스 구성|5.1.1 주 데이터베이스 Pod 구성|컨테이너 이미지, 리소스 설정, 스토리지 구성, 데이터베이스 설정값 상세 표 작성 (스토리지는 클라우드 제공자별 관리형 스토리지 사용: Azure Disk, AWS EBS, GCP Persistent Disk 등)
5. 데이터 아키텍처|5.1 데이터베이스 구성|5.1.2 캐시 Pod 구성|캐시 컨테이너 이미지, 리소스, 메모리 설정, 캐시 정책 등 상세 설정 표 작성 (영구 저장이 필요한 경우 클라우드 스토리지 클래스 사용)
5. 데이터 아키텍처|5.2 데이터 관리 전략|5.2.1 데이터 초기화|Kubernetes Job을 이용한 데이터 초기화 프로세스, 실행 절차, 검증 방법 상세 기술
5. 데이터 아키텍처|5.2 데이터 관리 전략|5.2.2 백업 전략|백업 방법, 주기, 보존 전략, 복구 절차를 서비스별로 정리한 표 작성
6. 메시징 아키텍처|6.1 Message Queue 구성|6.1.1 Basic Tier 설정|Message Queue 전체 설정값과 큐별 상세 설정을 표로 정리하여 작성
6. 메시징 아키텍처|6.1 Message Queue 구성|6.1.2 연결 설정|인증 방식, 연결 풀링, 재시도 정책 등 연결 관련 설정 표 작성
7. 보안 아키텍처|7.1 개발환경 보안 정책|7.1.1 기본 보안 설정|보안 계층별 설정값과 수준을 정리한 표와 관리 대상 시크릿 목록 작성
7. 보안 아키텍처|7.1 개발환경 보안 정책|7.1.2 시크릿 관리|시크릿 관리 방식, 순환 정책, 저장소 등 시크릿 관리 전략 표 작성
7. 보안 아키텍처|7.2 Network Policies|7.2.1 기본 정책|Network Policy 설정 상세 내용과 보안 정책 적용 범위 표 작성
8. 모니터링 및 로깅|8.1 기본 모니터링|8.1.1 Kubernetes 기본 모니터링|모니터링 스택 구성과 기본 알림 설정 임계값을 표로 정리
8. 모니터링 및 로깅|8.1 기본 모니터링|8.1.2 애플리케이션 모니터링|헬스체크 설정과 수집 메트릭 유형을 표로 정리하여 작성
8. 모니터링 및 로깅|8.2 로깅|8.2.1 로그 수집|로그 수집 방식, 저장 방식, 보존 기간과 로그 레벨 설정을 표로 작성
9. 배포 관련 컴포넌트|||CI/CD 파이프라인 구성 요소들과 각각의 역할을 표 형태로 정리
10. 비용 최적화|10.1 개발환경 비용 구조|10.1.1 주요 비용 요소|구성요소별 사양과 월간 예상 비용, 절약 방안을 상세 표로 작성
10. 비용 최적화|10.1 개발환경 비용 구조|10.1.2 비용 절약 전략|컴퓨팅, 스토리지, 네트워킹 영역별 절약 방안과 절약률을 표로 정리
11. 개발환경 운영 가이드|11.1 일상 운영|11.1.1 환경 시작/종료|일상적인 환경 관리를 위한 kubectl 명령어와 절차를 코드 블록으로 제공
11. 개발환경 운영 가이드|11.1 일상 운영|11.1.2 데이터 관리|데이터 초기화, 백업, 복원을 위한 구체적 명령어와 절차를 코드 블록으로 작성
11. 개발환경 운영 가이드|11.2 트러블슈팅|11.2.1 일반적인 문제 해결|자주 발생하는 문제 유형별 원인과 해결방안, 예방법을 표로 정리
12. 개발환경 특성 요약|||개발환경의 핵심 설계 원칙, 주요 제약사항, 최적화 목표를 요약하여 기술
```
<운영환경가이드>
```
대분류|중분류|소분류|작성가이드
---|---|---|---
1. 개요|1.1 설계 목적||운영환경 물리 아키텍처의 설계 범위, 목적, 대상을 명확히 기술
1. 개요|1.2 설계 원칙||고가용성, 확장성, 보안 우선, 관측 가능성, 재해복구 등 5대 핵심 원칙 정의
1. 개요|1.3 참조 아키텍처||관련 아키텍처 문서들의 연관관계와 참조 방법 명시
2. 운영환경 아키텍처 개요|2.1 환경 특성||운영환경의 목적, 사용자 규모, 가용성 목표, 확장성, 보안 수준 등 특성 정의
2. 운영환경 아키텍처 개요|2.2 전체 아키텍처||전체 시스템 구성도와 주요 컴포넌트 간 연결 관계 설명 및 다이어그램 링크
3. 컴퓨팅 아키텍처|3.1 Kubernetes 클러스터 구성|3.1.1 클러스터 설정|Kubernetes 버전, Standard 서비스 티어, CNI 플러그인, RBAC 등 클러스터 기본 설정값 표 작성
3. 컴퓨팅 아키텍처|3.1 Kubernetes 클러스터 구성|3.1.2 노드 풀 구성|시스템 노드 풀과 애플리케이션 노드 풀의 인스턴스 크기, 노드 수, Multi-Zone 배포 설정 표 작성
3. 컴퓨팅 아키텍처|3.2 고가용성 구성|3.2.1 Multi-Zone 배포|가용성 전략과 Pod Disruption Budget 설정을 표로 정리
3. 컴퓨팅 아키텍처|3.3 서비스별 리소스 할당|3.3.1 애플리케이션 서비스|각 마이크로서비스별 CPU/Memory requests, limits, replicas, HPA 설정 상세 표 작성
3. 컴퓨팅 아키텍처|3.3 서비스별 리소스 할당|3.3.2 HPA 구성|Horizontal Pod Autoscaler 설정을 YAML 코드 블록으로 상세 작성
4. 네트워크 아키텍처|4.1 네트워크 토폴로지||네트워크 흐름도와 VNet 구성, 서브넷 세부 구성을 표로 정리하고 다이어그램 링크 제공
4. 네트워크 아키텍처|4.1 네트워크 토폴로지|4.1.1 Virtual Network 구성|VPC/VNet 기본 설정과 서브넷별 주소 대역, 용도, 특별 설정을 상세 표로 작성
4. 네트워크 아키텍처|4.1 네트워크 토폴로지|4.1.2 네트워크 보안 그룹|보안 그룹 규칙을 방향, 규칙 이름, 포트, 소스/대상별로 정리한 표 작성
4. 네트워크 아키텍처|4.2 트래픽 라우팅|4.2.1 Application Gateway 구성|기본 설정, 프론트엔드 구성, 백엔드 및 라우팅 설정을 표로 정리
4. 네트워크 아키텍처|4.2 트래픽 라우팅|4.2.2 WAF 구성|WAF 정책과 커스텀 규칙, 관리 규칙을 YAML 코드 블록으로 작성
4. 네트워크 아키텍처|4.3 Network Policies|4.3.1 마이크로서비스 간 통신 제어|Network Policy 기본 설정과 Ingress/Egress 규칙을 표로 정리
4. 네트워크 아키텍처|4.4 서비스 디스커버리||각 서비스의 내부 DNS 주소, 포트, 용도를 정리한 서비스 디스커버리 표 작성
5. 데이터 아키텍처|5.1 관리형 주 데이터베이스|5.1.1 데이터베이스 구성|기본 설정, 고가용성, 백업 및 보안 설정을 표로 정리하여 작성 (클라우드 제공자의 관리형 데이터베이스 서비스 사용: Azure Database, AWS RDS, Google Cloud SQL 등)
5. 데이터 아키텍처|5.1 관리형 주 데이터베이스|5.1.2 읽기 전용 복제본|읽기 복제본 구성을 YAML 코드 블록으로 상세 작성
5. 데이터 아키텍처|5.2 관리형 캐시 서비스|5.2.1 캐시 클러스터 구성|기본 설정, 클러스터 구성, 지속성 및 보안 설정을 표로 정리 (관리형 캐시 서비스 사용: Azure Cache for Redis, AWS ElastiCache, Google Cloud Memorystore 등)
5. 데이터 아키텍처|5.2 관리형 캐시 서비스|5.2.2 캐시 전략|운영 최적화된 캐시 전략과 패턴을 YAML 코드 블록으로 작성
5. 데이터 아키텍처|5.3 데이터 백업 및 복구|5.3.1 자동 백업 전략|주 데이터베이스와 캐시의 자동 백업 전략을 YAML 코드 블록으로 상세 작성
6. 메시징 아키텍처|6.1 관리형 Message Queue|6.1.1 Message Queue 구성|Premium 티어 설정과 네임스페이스, 보안 설정을 YAML 코드 블록으로 작성
6. 메시징 아키텍처|6.1 관리형 Message Queue|6.1.2 큐 및 토픽 설계|큐와 토픽의 상세 설정을 YAML 코드 블록으로 작성
7. 보안 아키텍처|7.1 다층 보안 아키텍처|7.1.1 보안 계층 구조|L1-L4 보안 계층별 구성 요소를 YAML 코드 블록으로 상세 작성
7. 보안 아키텍처|7.2 인증 및 권한 관리|7.2.1 클라우드 Identity 통합|클라우드 Identity 구성과 애플리케이션 등록을 YAML 코드 블록으로 작성
7. 보안 아키텍처|7.2 인증 및 권한 관리|7.2.2 RBAC 구성|클러스터 역할과 서비스 계정을 YAML 코드 블록으로 상세 작성
7. 보안 아키텍처|7.3 네트워크 보안|7.3.1 Private Endpoints|각 서비스별 Private Endpoint 설정을 YAML 코드 블록으로 작성
7. 보안 아키텍처|7.4 암호화 및 키 관리|7.4.1 관리형 Key Vault 구성|Key Vault 설정과 액세스 정책, 순환 정책을 YAML 코드 블록으로 작성
8. 모니터링 및 관측 가능성|8.1 종합 모니터링 스택|8.1.1 클라우드 모니터링 통합|Log Analytics, Application Insights, Container Insights 설정을 YAML 코드 블록으로 작성
8. 모니터링 및 관측 가능성|8.1 종합 모니터링 스택|8.1.2 메트릭 및 알림|중요 알림과 리소스 알림 설정을 YAML 코드 블록으로 상세 작성
8. 모니터링 및 관측 가능성|8.2 로깅 및 추적|8.2.1 중앙집중식 로깅|로그 수집 설정과 중앙 로그 시스템 쿼리를 YAML 코드 블록으로 작성
8. 모니터링 및 관측 가능성|8.2 로깅 및 추적|8.2.2 애플리케이션 성능 모니터링|APM 설정과 커스텀 메트릭을 YAML 코드 블록으로 작성
9. 배포 관련 컴포넌트|||CI/CD 파이프라인 구성 요소들과 각각의 역할, 보안 스캔, 롤백 정책을 표 형태로 정리
10. 재해복구 및 고가용성|10.1 재해복구 전략|10.1.1 백업 및 복구 목표|RTO, RPO와 백업 전략을 YAML 코드 블록으로 상세 작성
10. 재해복구 및 고가용성|10.1 재해복구 전략|10.1.2 자동 장애조치|데이터베이스, 캐시, 애플리케이션별 장애조치 설정을 YAML 코드 블록으로 작성
10. 재해복구 및 고가용성|10.2 비즈니스 연속성|10.2.1 운영 절차|인시던트 대응, 유지보수 윈도우, 변경 관리를 YAML 코드 블록으로 작성
11. 비용 최적화|11.1 운영환경 비용 구조|11.1.1 월간 비용 분석|구성요소별 사양, 예상 비용, 최적화 방안을 상세 표로 작성
11. 비용 최적화|11.1 운영환경 비용 구조|11.1.2 비용 최적화 전략|컴퓨팅, 스토리지, 네트워크 영역별 최적화 방안을 YAML 코드 블록으로 작성
11. 비용 최적화|11.2 성능 대비 비용 효율성|11.2.1 Auto Scaling 최적화|예측 스케일링과 비용 인식 스케일링을 YAML 코드 블록으로 작성
12. 운영 가이드|12.1 일상 운영 절차|12.1.1 정기 점검 항목|일일, 주간, 월간 운영 체크리스트를 YAML 코드 블록으로 작성
12. 운영 가이드|12.2 인시던트 대응|12.2.1 장애 대응 절차|심각도별 대응 절차를 YAML 코드 블록으로 상세 작성
12. 운영 가이드|12.2 인시던트 대응|12.2.2 자동 복구 메커니즘|Pod 재시작, 노드 교체, 트래픽 라우팅 등 자동 복구를 YAML 코드 블록으로 작성
13. 확장 계획|13.1 단계별 확장 로드맵|13.1.1 Phase 1-3|각 단계별 목표, 대상, 결과물을 YAML 코드 블록으로 상세 작성
13. 확장 계획|13.2 기술적 확장성|13.2.1 수평 확장 전략|애플리케이션, 데이터베이스, 캐시 티어별 확장 전략을 YAML 코드 블록으로 작성
14. 운영환경 특성 요약|||운영환경의 핵심 설계 원칙, 주요 성과 목표, 최적화 목표를 요약하여 기술
```
<마스터가이드>
```
대분류|중분류|소분류|작성가이드
---|---|---|---
1. 개요|1.1 설계 목적||전체 물리 아키텍처의 통합 관리 체계와 마스터 인덱스 역할 기술
1. 개요|1.2 아키텍처 분리 원칙||개발환경과 운영환경 분리 원칙과 단계적 발전 전략 정의
1. 개요|1.3 문서 구조||마스터 인덱스와 환경별 상세 문서 구조 및 참조 관계 명시
1. 개요|1.4 참조 아키텍처||관련 아키텍처 문서들(HighLevel, 논리, 패턴, API)의 연관관계 명시
2. 환경별 아키텍처 개요|2.1 환경별 특성 비교||목적, 가용성, 사용자, 확장성, 보안, 비용 등 환경별 특성을 비교 표로 작성
2. 환경별 아키텍처 개요|2.2 환경별 세부 문서|2.2.1 개발환경 아키텍처|개발환경 문서 링크와 주요 특징, 핵심 구성을 요약하여 기술
2. 환경별 아키텍처 개요|2.2 환경별 세부 문서|2.2.2 운영환경 아키텍처|운영환경 문서 링크와 주요 특징, 핵심 구성을 요약하여 기술
2. 환경별 아키텍처 개요|2.3 핵심 아키텍처 결정사항|2.3.1 공통 아키텍처 원칙|서비스 메시 제거, 비동기 통신, 관리형 Identity, 다층 보안 등 공통 원칙 기술
2. 환경별 아키텍처 개요|2.3 핵심 아키텍처 결정사항|2.3.2 환경별 차별화 전략|개발환경과 운영환경의 최적화 전략 차이점을 비교하여 기술
3. 네트워크 아키텍처 비교|3.1 환경별 네트워크 전략|3.1.1 환경별 네트워크 전략 비교|인그레스, 네트워크, 보안, 접근 방식을 환경별로 비교한 표 작성
3. 네트워크 아키텍처 비교|3.2 네트워크 보안 전략|3.2.1 공통 보안 원칙|Network Policies, 관리형 Identity, Private Endpoints, TLS 암호화 등 공통 보안 원칙 기술
3. 네트워크 아키텍처 비교|3.2 네트워크 보안 전략|3.2.2 환경별 보안 수준|Network Policy, 시크릿 관리, 암호화, 웹 보안 수준을 환경별로 비교한 표 작성
4. 데이터 아키텍처 비교|4.1 환경별 데이터 전략|4.1.1 환경별 데이터 구성 비교|주 데이터베이스와 캐시의 환경별 구성, 가용성, 비용을 비교한 상세 표 작성 (개발환경: Pod 기반 + 클라우드 스토리지, 운영환경: 관리형 서비스)
4. 데이터 아키텍처 비교|4.2 캐시 전략 비교|4.2.1 다층 캐시 아키텍처|L1 애플리케이션 캐시와 L2 분산 캐시의 계층별 설정을 표로 정리
4. 데이터 아키텍처 비교|4.2 캐시 전략 비교|4.2.2 환경별 캐시 특성 비교|캐시 구성, 데이터 지속성, 성능 특성을 환경별로 비교한 표 작성
5. 보안 아키텍처 비교|5.1 다층 보안 아키텍처|5.1.1 공통 보안 계층|L1-L4 보안 계층의 보안 기술, 적용 범위, 보안 목적을 표로 정리
5. 보안 아키텍처 비교|5.2 환경별 보안 수준|5.2.1 환경별 보안 수준 비교|인증, 네트워크, 시크릿, 암호화 영역별 보안 수준과 강화 방안을 비교한 표 작성
6. 모니터링 및 운영|6.1 환경별 모니터링 전략|6.1.1 환경별 모니터링 도구 비교|모니터링 도구, 메트릭, 알림, 로그 수집 방식을 환경별로 비교한 표 작성
6. 모니터링 및 운영|6.2 CI/CD 및 배포 전략|6.2.1 환경별 배포 방식 비교|배포 방식, 자동화, 테스트, 다운타임 허용도를 환경별로 비교한 표 작성
7. 비용 분석|7.1 환경별 비용 구조|7.1.1 월간 비용 비교|구성요소별 개발환경과 운영환경 비용을 상세 비교한 표 작성
7. 비용 분석|7.1 환경별 비용 구조|7.1.2 환경별 비용 최적화 전략 비교|컴퓨팅, 백킹서비스, 리소스 관리 최적화 방안을 환경별로 비교한 표 작성
8. 전환 및 확장 계획|8.1 개발환경 → 운영환경 전환 체크리스트||데이터 마이그레이션, 설정 변경, 모니터링 등 전환 체크리스트를 카테고리별로 표 작성
8. 전환 및 확장 계획|8.2 단계별 확장 로드맵||Phase 1-3 단계별 기간, 핵심 목표, 주요 작업, 사용자 지원, 가용성을 표로 정리
9. 핵심 SLA 지표|9.1 환경별 서비스 수준 목표||가용성, 응답시간, 배포시간, 복구시간, 동시사용자, 월간비용을 환경별로 비교한 표 작성
```
[참고자료]
- 아키텍처패턴
- 논리아키텍처
- 외부시퀀스설계서
- 데이터설계서
- HighLevel아키텍처정의서
[예시]
- 개발환경 물리아키텍처 설계서: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-physical-architecture-dev.md
- 운영환경 물리아키텍처 설계서: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-physical-architecture-prod.md
- 마스터 물리아키텍처 설계서: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-physical-architecture.md
- 개발환경 물리아키텍처 다이어그램: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-physical-architecture-dev.mmd
- 운영환경 물리아키텍처 다이어그램: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-physical-architecture-prod.mmd
- 개발환경 네트워크 다이어그램: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-network-dev.mmd
- 운영환경 네트워크 다이어그램: https://raw.githubusercontent.com/cna-bootcamp/clauding-guide/refs/heads/main/samples/physical/sample-network-prod.mmd
[결과파일]
- design/backend/physical/physical-architecture.md
- design/backend/physical/physical-architecture-dev.md
- design/backend/physical/physical-architecture-prod.md
- design/backend/physical/physical-architecture-dev.mmd
- design/backend/physical/physical-architecture-prod.mmd
- design/backend/physical/network-dev.mmd
- design/backend/physical/network-prod.mmd

View File

@ -0,0 +1,138 @@
graph TB
%% 개발환경 네트워크 다이어그램
%% AI 기반 여행 일정 생성 서비스 - 개발환경
%% 외부 영역
subgraph Internet["🌐 인터넷"]
Developer["👨‍💻 개발자"]
QATester["🧪 QA팀"]
end
%% Azure 클라우드 영역
subgraph AzureCloud["☁️ Azure Cloud"]
%% Virtual Network
subgraph VNet["🏢 Virtual Network (VNet)<br/>주소 공간: 10.0.0.0/16"]
%% AKS 서브넷
subgraph AKSSubnet["🎯 AKS Subnet<br/>10.0.1.0/24"]
%% Kubernetes 클러스터
subgraph AKSCluster["⚙️ AKS Cluster"]
%% Ingress Controller
subgraph IngressController["🚪 NGINX Ingress Controller"]
LoadBalancer["⚖️ LoadBalancer Service<br/>(External IP)"]
IngressPod["📦 Ingress Controller Pod"]
end
%% Application Tier
subgraph AppTier["🚀 Application Tier"]
UserService["👤 User Service<br/>Pod"]
TripService["🗺️ Trip Service<br/>Pod"]
AIService["🤖 AI Service<br/>Pod"]
LocationService["📍 Location Service<br/>Pod"]
end
%% Database Tier
subgraph DBTier["🗄️ Database Tier"]
PostgreSQL["🐘 PostgreSQL<br/>Pod"]
PostgreSQLStorage["💾 hostPath Volume<br/>(/data/postgresql)"]
end
%% Cache Tier
subgraph CacheTier["⚡ Cache Tier"]
Redis["🔴 Redis<br/>Pod"]
end
%% Cluster Internal Services
subgraph ClusterServices["🔗 ClusterIP Services"]
UserServiceDNS["user-service:8080"]
TripServiceDNS["trip-service:8080"]
AIServiceDNS["ai-service:8080"]
LocationServiceDNS["location-service:8080"]
PostgreSQLDNS["postgresql:5432"]
RedisDNS["redis:6379"]
end
end
end
%% Service Bus 서브넷
subgraph ServiceBusSubnet["📨 Service Bus Subnet<br/>10.0.2.0/24"]
ServiceBus["📮 Azure Service Bus<br/>(Basic Tier)"]
subgraph Queues["📬 Message Queues"]
AIQueue["🤖 ai-schedule-generation"]
LocationQueue["📍 location-search"]
NotificationQueue["🔔 notification"]
end
end
end
end
%% 네트워크 연결 관계
%% 외부에서 클러스터로의 접근
Developer -->|"HTTPS:443<br/>(개발용 도메인)"| LoadBalancer
QATester -->|"API 호출/테스트"| LoadBalancer
%% Ingress Controller 내부 흐름
LoadBalancer -->|"트래픽 라우팅"| IngressPod
%% Ingress에서 Application Services로
IngressPod -->|"/api/users/**"| UserServiceDNS
IngressPod -->|"/api/trips/**"| TripServiceDNS
IngressPod -->|"/api/ai/**"| AIServiceDNS
IngressPod -->|"/api/locations/**"| LocationServiceDNS
%% ClusterIP Services에서 실제 Pod로
UserServiceDNS -->|"내부 로드밸런싱"| UserService
TripServiceDNS -->|"내부 로드밸런싱"| TripService
AIServiceDNS -->|"내부 로드밸런싱"| AIService
LocationServiceDNS -->|"내부 로드밸런싱"| LocationService
%% Application Services에서 Database로
UserService -->|"DB 연결<br/>TCP:5432"| PostgreSQLDNS
TripService -->|"DB 연결<br/>TCP:5432"| PostgreSQLDNS
AIService -->|"DB 연결<br/>TCP:5432"| PostgreSQLDNS
LocationService -->|"DB 연결<br/>TCP:5432"| PostgreSQLDNS
%% Application Services에서 Cache로
UserService -->|"캐시 연결<br/>TCP:6379"| RedisDNS
TripService -->|"캐시 연결<br/>TCP:6379"| RedisDNS
AIService -->|"캐시 연결<br/>TCP:6379"| RedisDNS
LocationService -->|"캐시 연결<br/>TCP:6379"| RedisDNS
%% ClusterIP Services에서 실제 Pod로 (Database/Cache)
PostgreSQLDNS -->|"DB 요청 처리"| PostgreSQL
RedisDNS -->|"캐시 요청 처리"| Redis
%% Storage 연결
PostgreSQL -->|"데이터 영속화"| PostgreSQLStorage
%% Service Bus 연결
AIService -->|"비동기 메시징<br/>HTTPS/AMQP"| ServiceBus
LocationService -->|"비동기 메시징<br/>HTTPS/AMQP"| ServiceBus
TripService -->|"알림 메시징<br/>HTTPS/AMQP"| ServiceBus
ServiceBus --> AIQueue
ServiceBus --> LocationQueue
ServiceBus --> NotificationQueue
%% 스타일 정의
classDef azureStyle fill:#0078D4,stroke:#fff,stroke-width:2px,color:#fff
classDef k8sStyle fill:#326CE5,stroke:#fff,stroke-width:2px,color:#fff
classDef appStyle fill:#28A745,stroke:#fff,stroke-width:2px,color:#fff
classDef dbStyle fill:#DC3545,stroke:#fff,stroke-width:2px,color:#fff
classDef cacheStyle fill:#FF6B35,stroke:#fff,stroke-width:2px,color:#fff
classDef serviceStyle fill:#6610F2,stroke:#fff,stroke-width:2px,color:#fff
classDef queueStyle fill:#FD7E14,stroke:#fff,stroke-width:2px,color:#fff
%% 스타일 적용
class AzureCloud,VNet azureStyle
class AKSCluster,AKSSubnet,IngressController k8sStyle
class AppTier,UserService,TripService,AIService,LocationService appStyle
class DBTier,PostgreSQL,PostgreSQLStorage dbStyle
class CacheTier,Redis cacheStyle
class ClusterServices,UserServiceDNS,TripServiceDNS,AIServiceDNS,LocationServiceDNS,PostgreSQLDNS,RedisDNS serviceStyle
class ServiceBus,ServiceBusSubnet,Queues,AIQueue,LocationQueue,NotificationQueue queueStyle

View File

@ -0,0 +1,190 @@
graph TB
%% 운영환경 네트워크 다이어그램
%% AI 기반 여행 일정 생성 서비스 - 운영환경
%% 외부 영역
subgraph Internet["🌐 인터넷"]
Users["👥 실사용자<br/>(1만~10만 명)"]
CDN["🌍 Azure Front Door<br/>+ CDN"]
end
%% Azure 클라우드 영역
subgraph AzureCloud["☁️ Azure Cloud (운영환경)"]
%% Virtual Network
subgraph VNet["🏢 Virtual Network (VNet)<br/>주소 공간: 10.0.0.0/16"]
%% Gateway Subnet
subgraph GatewaySubnet["🚪 Gateway Subnet<br/>10.0.4.0/24"]
subgraph AppGateway["🛡️ Application Gateway + WAF"]
PublicIP["📍 Public IP<br/>(고정)"]
PrivateIP["📍 Private IP<br/>(10.0.4.10)"]
WAF["🛡️ WAF<br/>(OWASP CRS 3.2)"]
RateLimiter["⏱️ Rate Limiting<br/>(100 req/min/IP)"]
end
end
%% Application Subnet
subgraph AppSubnet["🎯 Application Subnet<br/>10.0.1.0/24"]
%% AKS 클러스터
subgraph AKSCluster["⚙️ AKS Premium Cluster<br/>(Multi-Zone)"]
%% System Node Pool
subgraph SystemNodes["🔧 System Node Pool"]
SystemNode1["📦 System Node 1<br/>(Zone 1)"]
SystemNode2["📦 System Node 2<br/>(Zone 2)"]
SystemNode3["📦 System Node 3<br/>(Zone 3)"]
end
%% Application Node Pool
subgraph AppNodes["🚀 Application Node Pool"]
AppNode1["📦 App Node 1<br/>(Zone 1)"]
AppNode2["📦 App Node 2<br/>(Zone 2)"]
AppNode3["📦 App Node 3<br/>(Zone 3)"]
end
%% Application Services (High Availability)
subgraph AppServices["🚀 Application Services"]
UserServiceHA["👤 User Service<br/>(3 replicas, HPA)"]
TripServiceHA["🗺️ Trip Service<br/>(3 replicas, HPA)"]
AIServiceHA["🤖 AI Service<br/>(2 replicas, HPA)"]
LocationServiceHA["📍 Location Service<br/>(2 replicas, HPA)"]
end
%% Internal Load Balancer
subgraph InternalLB["⚖️ Internal Services"]
UserServiceLB["user-service:8080"]
TripServiceLB["trip-service:8080"]
AIServiceLB["ai-service:8080"]
LocationServiceLB["location-service:8080"]
end
end
end
%% Database Subnet
subgraph DBSubnet["🗄️ Database Subnet<br/>10.0.2.0/24"]
subgraph AzurePostgreSQL["🐘 Azure PostgreSQL Flexible Server"]
PGPrimary["📊 Primary Server<br/>(Zone 1)"]
PGSecondary["📊 Read Replica<br/>(Zone 2)"]
PGBackup["💾 Automated Backup<br/>(Point-in-time Recovery)"]
end
end
%% Cache Subnet
subgraph CacheSubnet["⚡ Cache Subnet<br/>10.0.3.0/24"]
subgraph AzureRedis["🔴 Azure Cache for Redis Premium"]
RedisPrimary["⚡ Primary Cache<br/>(Zone 1)"]
RedisSecondary["⚡ Secondary Cache<br/>(Zone 2)"]
RedisCluster["🔗 Redis Cluster<br/>(High Availability)"]
end
end
end
%% Service Bus (Premium)
subgraph ServiceBus["📨 Azure Service Bus Premium"]
ServiceBusHA["📮 Service Bus Namespace<br/>(sb-tripgen-prod)"]
subgraph QueuesHA["📬 Premium Message Queues"]
AIQueueHA["🤖 ai-schedule-generation<br/>(Partitioned, 16GB)"]
LocationQueueHA["📍 location-search<br/>(Partitioned, 16GB)"]
NotificationQueueHA["🔔 notification<br/>(Partitioned, 16GB)"]
end
end
%% Private Endpoints
subgraph PrivateEndpoints["🔒 Private Endpoints"]
PGPrivateEndpoint["🔐 PostgreSQL<br/>Private Endpoint"]
RedisPrivateEndpoint["🔐 Redis<br/>Private Endpoint"]
ServiceBusPrivateEndpoint["🔐 Service Bus<br/>Private Endpoint"]
end
end
%% 네트워크 연결 관계
%% 외부에서 Azure로의 접근
Users -->|"HTTPS 요청"| CDN
CDN -->|"글로벌 가속"| PublicIP
%% Application Gateway 내부 흐름
PublicIP --> WAF
WAF --> RateLimiter
RateLimiter --> PrivateIP
%% Application Gateway에서 AKS로
PrivateIP -->|"/api/users/**<br/>NodePort 30080"| UserServiceLB
PrivateIP -->|"/api/trips/**<br/>NodePort 30081"| TripServiceLB
PrivateIP -->|"/api/ai/**<br/>NodePort 30082"| AIServiceLB
PrivateIP -->|"/api/locations/**<br/>NodePort 30083"| LocationServiceLB
%% Load Balancer에서 실제 서비스로
UserServiceLB -->|"고가용성 라우팅"| UserServiceHA
TripServiceLB -->|"고가용성 라우팅"| TripServiceHA
AIServiceLB -->|"고가용성 라우팅"| AIServiceHA
LocationServiceLB -->|"고가용성 라우팅"| LocationServiceHA
%% 서비스 배치 (Multi-Zone)
UserServiceHA -.-> AppNode1
UserServiceHA -.-> AppNode2
UserServiceHA -.-> AppNode3
TripServiceHA -.-> AppNode1
TripServiceHA -.-> AppNode2
TripServiceHA -.-> AppNode3
%% Application Services에서 Database로 (Private Endpoint)
UserServiceHA -->|"Private Link<br/>TCP:5432"| PGPrivateEndpoint
TripServiceHA -->|"Private Link<br/>TCP:5432"| PGPrivateEndpoint
AIServiceHA -->|"Private Link<br/>TCP:5432"| PGPrivateEndpoint
LocationServiceHA -->|"Private Link<br/>TCP:5432"| PGPrivateEndpoint
%% Private Endpoint에서 실제 서비스로
PGPrivateEndpoint --> PGPrimary
PGPrivateEndpoint --> PGSecondary
%% Application Services에서 Cache로 (Private Endpoint)
UserServiceHA -->|"Private Link<br/>TCP:6379"| RedisPrivateEndpoint
TripServiceHA -->|"Private Link<br/>TCP:6379"| RedisPrivateEndpoint
AIServiceHA -->|"Private Link<br/>TCP:6379"| RedisPrivateEndpoint
LocationServiceHA -->|"Private Link<br/>TCP:6379"| RedisPrivateEndpoint
%% Private Endpoint에서 Redis로
RedisPrivateEndpoint --> RedisPrimary
RedisPrivateEndpoint --> RedisSecondary
%% High Availability 연결
PGPrimary -.->|"복제"| PGSecondary
RedisPrimary -.->|"HA 동기화"| RedisSecondary
PGPrimary -.->|"자동 백업"| PGBackup
%% Service Bus 연결 (Private Endpoint)
AIServiceHA -->|"Private Link<br/>HTTPS/AMQP"| ServiceBusPrivateEndpoint
LocationServiceHA -->|"Private Link<br/>HTTPS/AMQP"| ServiceBusPrivateEndpoint
TripServiceHA -->|"Private Link<br/>HTTPS/AMQP"| ServiceBusPrivateEndpoint
ServiceBusPrivateEndpoint --> ServiceBusHA
ServiceBusHA --> AIQueueHA
ServiceBusHA --> LocationQueueHA
ServiceBusHA --> NotificationQueueHA
%% 스타일 정의
classDef azureStyle fill:#0078D4,stroke:#fff,stroke-width:2px,color:#fff
classDef k8sStyle fill:#326CE5,stroke:#fff,stroke-width:2px,color:#fff
classDef appStyle fill:#28A745,stroke:#fff,stroke-width:2px,color:#fff
classDef dbStyle fill:#DC3545,stroke:#fff,stroke-width:2px,color:#fff
classDef cacheStyle fill:#FF6B35,stroke:#fff,stroke-width:2px,color:#fff
classDef serviceStyle fill:#6610F2,stroke:#fff,stroke-width:2px,color:#fff
classDef queueStyle fill:#FD7E14,stroke:#fff,stroke-width:2px,color:#fff
classDef securityStyle fill:#E83E8C,stroke:#fff,stroke-width:2px,color:#fff
classDef haStyle fill:#20C997,stroke:#fff,stroke-width:2px,color:#fff
%% 스타일 적용
class AzureCloud,VNet azureStyle
class AKSCluster,AppSubnet,SystemNodes,AppNodes k8sStyle
class AppServices,UserServiceHA,TripServiceHA,AIServiceHA,LocationServiceHA appStyle
class DBSubnet,AzurePostgreSQL,PGPrimary,PGSecondary,PGBackup dbStyle
class CacheSubnet,AzureRedis,RedisPrimary,RedisSecondary,RedisCluster cacheStyle
class InternalLB,UserServiceLB,TripServiceLB,AIServiceLB,LocationServiceLB serviceStyle
class ServiceBus,ServiceBusHA,QueuesHA,AIQueueHA,LocationQueueHA,NotificationQueueHA queueStyle
class AppGateway,WAF,RateLimiter,PrivateEndpoints,PGPrivateEndpoint,RedisPrivateEndpoint,ServiceBusPrivateEndpoint securityStyle
class CDN,SystemNode1,SystemNode2,SystemNode3,AppNode1,AppNode2,AppNode3 haStyle

View File

@ -0,0 +1,49 @@
graph TB
%% Development Environment Physical Architecture
%% Core Flow: Users → Ingress → Services → Database
Users[Mobile/Web Users] --> Ingress[Kubernetes Ingress Controller]
subgraph "Azure Kubernetes Service - Development"
Ingress --> UserService[User Service Pod]
Ingress --> TravelService[Travel Service Pod]
Ingress --> ScheduleService[AI Service Pod]
Ingress --> LocationService[Location Service Pod]
UserService --> PostgreSQL[PostgreSQL Pod<br/>16GB Storage]
TravelService --> PostgreSQL
ScheduleService --> PostgreSQL
LocationService --> PostgreSQL
UserService --> Redis[Redis Pod<br/>Memory Cache]
TravelService --> Redis
ScheduleService --> Redis
LocationService --> Redis
TravelService --> ServiceBus[Azure Service Bus<br/>Basic Tier]
ScheduleService --> ServiceBus
LocationService --> ServiceBus
end
%% External APIs
ExternalAPI[External APIs<br/>OpenAI, Maps, Weather] --> ScheduleService
ExternalAPI --> LocationService
%% Essential Azure Services
AKS --> ContainerRegistry[Azure Container Registry]
%% Node Configuration
subgraph "Node Pool"
NodePool[2x Standard B2s<br/>2 vCPU, 4GB RAM]
end
%% Styling
classDef azureService fill:#0078d4,stroke:#333,stroke-width:2px,color:#fff
classDef microservice fill:#ff6b6b,stroke:#333,stroke-width:2px,color:#fff
classDef database fill:#4ecdc4,stroke:#333,stroke-width:2px,color:#fff
classDef external fill:#95e1d3,stroke:#333,stroke-width:2px,color:#333
class Ingress,ServiceBus,ContainerRegistry azureService
class UserService,TravelService,ScheduleService,LocationService microservice
class PostgreSQL,Redis database
class Users,ExternalAPI external

View File

@ -0,0 +1,184 @@
graph TB
%% Production Environment Physical Architecture
%% Enterprise-grade Azure Cloud Architecture
Users[Mobile/Web Users<br/>1만~10만 명] --> CDN[Azure Front Door<br/>+ CDN]
subgraph "Azure Cloud - Production Environment"
CDN --> AppGateway[Application Gateway<br/>+ WAF v2<br/>Zone Redundant]
subgraph "VNet (10.0.0.0/16)"
subgraph "Gateway Subnet (10.0.4.0/24)"
AppGateway
end
subgraph "Application Subnet (10.0.1.0/24)"
subgraph "AKS Premium Cluster - Multi-Zone"
direction TB
subgraph "System Node Pool"
SystemNode1[System Node 1<br/>Zone 1<br/>D2s_v3]
SystemNode2[System Node 2<br/>Zone 2<br/>D2s_v3]
SystemNode3[System Node 3<br/>Zone 3<br/>D2s_v3]
end
subgraph "Application Node Pool"
AppNode1[App Node 1<br/>Zone 1<br/>D4s_v3]
AppNode2[App Node 2<br/>Zone 2<br/>D4s_v3]
AppNode3[App Node 3<br/>Zone 3<br/>D4s_v3]
end
subgraph "Application Services"
UserService[User Service<br/>3 replicas, HPA<br/>2-10 replicas]
TripService[Trip Service<br/>3 replicas, HPA<br/>3-15 replicas]
AIService[AI Service<br/>2 replicas, HPA<br/>2-8 replicas]
LocationService[Location Service<br/>2 replicas, HPA<br/>2-10 replicas]
end
end
end
AppGateway -->|NodePort 30080-30083| UserService
AppGateway -->|NodePort 30080-30083| TripService
AppGateway -->|NodePort 30080-30083| AIService
AppGateway -->|NodePort 30080-30083| LocationService
subgraph "Database Subnet (10.0.2.0/24)"
PostgreSQLPrimary[Azure PostgreSQL<br/>Flexible Server<br/>Primary - Zone 1<br/>GP_Standard_D4s_v3]
PostgreSQLReplica[PostgreSQL<br/>Read Replica<br/>Zone 2]
PostgreSQLBackup[Automated Backup<br/>Point-in-time Recovery<br/>35 days retention]
end
subgraph "Cache Subnet (10.0.3.0/24)"
RedisPrimary[Azure Redis Premium<br/>P2 - 6GB<br/>Primary - Zone 1]
RedisSecondary[Redis Secondary<br/>Zone 2<br/>HA Enabled]
end
end
subgraph "Service Bus Premium"
ServiceBusPremium[Azure Service Bus<br/>Premium Tier<br/>sb-tripgen-prod]
subgraph "Message Queues"
AIQueue[ai-schedule-generation<br/>Partitioned, 16GB]
LocationQueue[location-search<br/>Partitioned, 16GB]
NotificationQueue[notification<br/>Partitioned, 16GB]
end
end
subgraph "Private Endpoints"
PostgreSQLEndpoint[PostgreSQL<br/>Private Endpoint<br/>10.0.2.10]
RedisEndpoint[Redis<br/>Private Endpoint<br/>10.0.3.10]
ServiceBusEndpoint[Service Bus<br/>Private Endpoint<br/>10.0.5.10]
KeyVaultEndpoint[Key Vault<br/>Private Endpoint<br/>10.0.6.10]
end
subgraph "Security & Management"
KeyVault[Azure Key Vault<br/>Premium<br/>HSM-backed]
AAD[Azure Active Directory<br/>RBAC Integration]
Monitor[Azure Monitor<br/>+ Application Insights<br/>Log Analytics]
end
%% Private Link Connections
UserService -->|Private Link| PostgreSQLEndpoint
TripService -->|Private Link| PostgreSQLEndpoint
AIService -->|Private Link| PostgreSQLEndpoint
LocationService -->|Private Link| PostgreSQLEndpoint
PostgreSQLEndpoint --> PostgreSQLPrimary
PostgreSQLEndpoint --> PostgreSQLReplica
UserService -->|Private Link| RedisEndpoint
TripService -->|Private Link| RedisEndpoint
AIService -->|Private Link| RedisEndpoint
LocationService -->|Private Link| RedisEndpoint
RedisEndpoint --> RedisPrimary
RedisEndpoint --> RedisSecondary
AIService -->|Private Link| ServiceBusEndpoint
LocationService -->|Private Link| ServiceBusEndpoint
TripService -->|Private Link| ServiceBusEndpoint
ServiceBusEndpoint --> ServiceBusPremium
ServiceBusPremium --> AIQueue
ServiceBusPremium --> LocationQueue
ServiceBusPremium --> NotificationQueue
%% High Availability Connections
PostgreSQLPrimary -.->|Replication| PostgreSQLReplica
PostgreSQLPrimary -.->|Auto Backup| PostgreSQLBackup
RedisPrimary -.->|HA Sync| RedisSecondary
%% Security Connections
UserService -.->|Managed Identity| KeyVaultEndpoint
TripService -.->|Managed Identity| KeyVaultEndpoint
AIService -.->|Managed Identity| KeyVaultEndpoint
LocationService -.->|Managed Identity| KeyVaultEndpoint
KeyVaultEndpoint --> KeyVault
UserService -.->|RBAC| AAD
TripService -.->|RBAC| AAD
AIService -.->|RBAC| AAD
LocationService -.->|RBAC| AAD
%% Monitoring Connections
UserService -.->|Telemetry| Monitor
TripService -.->|Telemetry| Monitor
AIService -.->|Telemetry| Monitor
LocationService -.->|Telemetry| Monitor
end
%% External Integrations
subgraph "External Services"
ExternalAPI[External APIs<br/>OpenAI GPT-4 Turbo<br/>Google Maps API<br/>OpenWeatherMap API]
end
%% External Connections
ExternalAPI -->|HTTPS/TLS 1.3| AIService
ExternalAPI -->|HTTPS/TLS 1.3| LocationService
%% DevOps & CI/CD
subgraph "DevOps Infrastructure"
GitHubActions[GitHub Actions<br/>Enterprise CI/CD]
ArgoCD[ArgoCD<br/>GitOps Deployment<br/>HA Mode]
ContainerRegistry[Azure Container Registry<br/>Premium Tier<br/>Geo-replicated]
end
%% DevOps Connections
GitHubActions -->|Build & Push| ContainerRegistry
ArgoCD -->|Deploy| UserService
ArgoCD -->|Deploy| TripService
ArgoCD -->|Deploy| AIService
ArgoCD -->|Deploy| LocationService
%% Backup & DR
subgraph "Backup & Disaster Recovery"
BackupVault[Azure Backup Vault<br/>GRS - 99.999999999%]
DRSite[DR Site<br/>Secondary Region<br/>Korea Central]
end
PostgreSQLPrimary -.->|Automated Backup| BackupVault
RedisPrimary -.->|Data Persistence| BackupVault
ContainerRegistry -.->|Image Backup| BackupVault
BackupVault -.->|Geo-replication| DRSite
%% Styling
classDef azureService fill:#0078d4,stroke:#333,stroke-width:2px,color:#fff
classDef microservice fill:#28a745,stroke:#333,stroke-width:2px,color:#fff
classDef database fill:#dc3545,stroke:#333,stroke-width:2px,color:#fff
classDef security fill:#ffc107,stroke:#333,stroke-width:2px,color:#333
classDef external fill:#17a2b8,stroke:#333,stroke-width:2px,color:#fff
classDef devops fill:#6f42c1,stroke:#333,stroke-width:2px,color:#fff
classDef backup fill:#e83e8c,stroke:#333,stroke-width:2px,color:#fff
classDef privateEndpoint fill:#fd7e14,stroke:#333,stroke-width:2px,color:#fff
classDef nodePool fill:#20c997,stroke:#333,stroke-width:2px,color:#fff
class CDN,AppGateway,ServiceBusPremium,ContainerRegistry,Monitor,AAD azureService
class UserService,TripService,AIService,LocationService microservice
class PostgreSQLPrimary,PostgreSQLReplica,PostgreSQLBackup,RedisPrimary,RedisSecondary database
class KeyVault,KeyVaultEndpoint security
class Users,ExternalAPI external
class GitHubActions,ArgoCD devops
class BackupVault,DRSite backup
class PostgreSQLEndpoint,RedisEndpoint,ServiceBusEndpoint privateEndpoint
class SystemNode1,SystemNode2,SystemNode3,AppNode1,AppNode2,AppNode3 nodePool

View File

@ -0,0 +1,268 @@
# 물리 아키텍처 설계서 - 마스터 인덱스
## 1. 개요
### 1.1 설계 목적
- AI 기반 여행 일정 생성 서비스의 Azure Cloud 기반 물리 아키텍처 설계
- 개발환경과 운영환경의 체계적인 아키텍처 분리 및 관리
- 환경별 특화 구성과 단계적 확장 전략 제시
### 1.2 아키텍처 분리 원칙
- **환경별 특화**: 개발환경과 운영환경의 목적에 맞는 최적화
- **단계적 발전**: 개발→운영 단계적 아키텍처 진화
- **비용 효율성**: 환경별 비용 최적화 전략
- **운영 단순성**: 환경별 복잡도 적정 수준 유지
### 1.3 문서 구조
```
physical-architecture.md (마스터 인덱스)
├── physical-architecture-dev.md (개발환경)
└── physical-architecture-prod.md (운영환경)
```
### 1.4 참조 아키텍처
- HighLevel아키텍처정의서: design/high-level-architecture.md
- 논리아키텍처: design/backend/logical/logical-architecture.md
- 아키텍처패턴: design/pattern/아키텍처패턴.md
- API설계서: design/backend/api/*.yaml
## 2. 환경별 아키텍처 개요
### 2.1 환경별 특성 비교
| 구분 | 개발환경 | 운영환경 |
|------|----------|----------|
| **목적** | MVP 개발/검증 | 실제 서비스 운영 |
| **가용성** | 95% | 99.9% |
| **사용자** | 개발팀(5명) | 실사용자(1만~10만) |
| **확장성** | 고정 리소스 | 자동 스케일링 |
| **보안** | 기본 수준 | 엔터프라이즈급 |
| **비용** | 최소화($150/월) | 최적화($2,650/월) |
| **복잡도** | 단순 | 고도화 |
### 2.2 환경별 세부 문서
#### 2.2.1 개발환경 아키텍처
📄 **[물리 아키텍처 설계서 - 개발환경](./physical-architecture-dev.md)**
**주요 특징:**
- **비용 최적화**: Spot Instance, 로컬 스토리지 활용
- **개발 편의성**: 복잡한 설정 최소화, 빠른 배포
- **단순한 보안**: 기본 Network Policy, JWT 검증
- **Pod 기반 백킹서비스**: PostgreSQL, Redis Pod 배포
**핵심 구성:**
📄 **[개발환경 물리 아키텍처 다이어그램](./physical-architecture-dev.mmd)**
- NGINX Ingress → AKS Basic → Pod Services 구조
- Application Pods, PostgreSQL Pod, Redis Pod 배치
#### 2.2.2 운영환경 아키텍처
📄 **[물리 아키텍처 설계서 - 운영환경](./physical-architecture-prod.md)**
**주요 특징:**
- **고가용성**: Multi-Zone 배포, 자동 장애조치
- **확장성**: HPA 기반 자동 스케일링 (10배 확장)
- **엔터프라이즈 보안**: 다층 보안, Private Endpoint
- **관리형 서비스**: Azure Database, Cache for Redis
**핵심 구성:**
📄 **[운영환경 물리 아키텍처 다이어그램](./physical-architecture-prod.mmd)**
- Azure Front Door → App Gateway + WAF → AKS Premium 구조
- Multi-Zone Apps, Azure PostgreSQL, Azure Redis Premium 배치
### 2.3 핵심 아키텍처 결정사항
#### 2.3.1 공통 아키텍처 원칙
- **서비스 메시 제거**: Istio 대신 Kubernetes Network Policies 사용
- **비동기 통신 중심**: 직접적인 서비스 간 호출 최소화
- **Managed Identity**: 키 없는 인증으로 보안 강화
- **다층 보안**: L1(Network) → L2(Gateway) → L3(Identity) → L4(Data)
#### 2.3.2 환경별 차별화 전략
**개발환경 최적화:**
- 개발 속도와 비용 효율성 우선
- 단순한 구성으로 운영 부담 최소화
- Pod 기반 백킹서비스로 의존성 제거
**운영환경 최적화:**
- 가용성과 확장성 우선
- 관리형 서비스로 운영 안정성 확보
- 엔터프라이즈급 보안 및 모니터링
## 3. 네트워크 아키텍처 비교
### 3.1 환경별 네트워크 전략
#### 3.1.1 환경별 네트워크 전략 비교
| 구성 요소 | 개발환경 | 운영환경 | 비교 |
|-----------|----------|----------|------|
| **인그레스** | NGINX Ingress Controller | Azure Application Gateway + WAF | 운영환경에서 WAF 보안 강화 |
| **네트워크** | 단일 서브넷 구성 | 다중 서브넷 (Application/Database/Cache) | 운영환경에서 계층적 분리 |
| **보안** | 기본 Network Policy | Private Endpoint, NSG 강화 | 운영환경에서 엔터프라이즈급 보안 |
| **접근** | 인터넷 직접 접근 허용 | Private Link 기반 보안 접근 | 운영환경에서 보안 접근 제한 |
### 3.2 네트워크 보안 전략
#### 3.2.1 공통 보안 원칙
- **Network Policies**: Pod 간 통신 제어
- **Managed Identity**: 키 없는 인증
- **Private Endpoints**: Azure 서비스 보안 접근
- **TLS 암호화**: 모든 외부 통신
#### 3.2.2 환경별 보안 수준
| 보안 요소 | 개발환경 | 운영환경 | 보안 수준 |
|-----------|----------|----------|----------|
| **Network Policy** | 기본 (개발 편의성 고려) | 엄격한 적용 | 운영환경에서 강화 |
| **시크릿 관리** | Kubernetes Secrets | Azure Key Vault | 운영환경에서 HSM 보안 |
| **암호화** | HTTPS 인그레스 레벨 | End-to-End TLS 1.3 | 운영환경에서 완전 암호화 |
| **웹 보안** | - | WAF + DDoS 보호 | 운영환경 전용 |
## 4. 데이터 아키텍처 비교
### 4.1 환경별 데이터 전략
#### 4.1.1 환경별 데이터 구성 비교
| 데이터 서비스 | 개발환경 | 운영환경 | 가용성 | 비용 |
|-------------|----------|----------|---------|------|
| **PostgreSQL** | Kubernetes Pod (hostPath) | Azure Database Flexible Server | 95% vs 99.9% | 무료 vs $450/월 |
| **Redis** | Memory Only Pod | Azure Cache Premium (Cluster) | 단일 vs 클러스터 | 무료 vs $250/월 |
| **백업** | 수동 (주 1회) | 자동 (35일 보존) | 로컬 vs 지역간 복제 | - |
| **데이터 지속성** | 재시작 시 손실 가능 | Zone Redundant | - | - |
### 4.2 캐시 전략 비교
#### 4.2.1 다층 캐시 아키텍처
| 캐시 계층 | 캐시 타입 | TTL | 개발환경 설정 | 운영환경 설정 | 용도 |
|----------|----------|-----|-------------|-------------|------|
| **L1_Application** | Caffeine Cache | 5분 | max_entries: 1000 | max_entries: 2000 | 애플리케이션 레벨 로컬 캐시 |
| **L2_Distributed** | Redis | 30분 | cluster_mode: false | cluster_mode: true | 분산 캐시, eviction_policy: allkeys-lru |
#### 4.2.2 환경별 캐시 특성 비교
| 캐시 특성 | 개발환경 | 운영환경 | 비고 |
|-----------|----------|----------|------|
| **Redis 구성** | 단일 Pod | Premium 클러스터 | 운영환경에서 고가용성 |
| **데이터 지속성** | 메모리 전용 | 지속성 백업 | 운영환경에서 데이터 보장 |
| **성능** | 기본 성능 | 최적화된 성능 | 운영환경에서 향상된 처리 능력 |
## 5. 보안 아키텍처 비교
### 5.1 다층 보안 아키텍처
#### 5.1.1 공통 보안 계층
| 보안 계층 | 보안 기술 | 적용 범위 | 보안 목적 |
|----------|----------|----------|----------|
| **L1_Network** | Kubernetes Network Policies | Pod-to-Pod 통신 제어 | 내부 네트워크 마이크로 세그먼테이션 |
| **L2_Gateway** | API Gateway JWT 검증 | 외부 요청 인증/인가 | API 레벨 인증 및 인가 제어 |
| **L3_Identity** | Azure Managed Identity | Azure 서비스 접근 | 클라우드 리소스 안전한 접근 |
| **L4_Data** | Private Link + Key Vault | 데이터 암호화 및 비밀 관리 | 엔드투엔드 데이터 보호 |
### 5.2 환경별 보안 수준
#### 5.2.1 환경별 보안 수준 비교
| 보안 영역 | 개발환경 | 운영환경 | 보안 강화 |
|-----------|----------|----------|----------|
| **인증** | JWT (고정 시크릿) | Azure AD + Managed Identity | 운영환경에서 엔터프라이즈 인증 |
| **네트워크** | 기본 Network Policy | 엄격한 Network Policy + Private Endpoint | 운영환경에서 네트워크 격리 강화 |
| **시크릿** | Kubernetes Secrets | Azure Key Vault (HSM) | 운영환경에서 하드웨어 보안 모듈 |
| **암호화** | HTTPS (인그레스 레벨) | End-to-End TLS 1.3 | 운영환경에서 전 구간 암호화 |
## 6. 모니터링 및 운영
### 6.1 환경별 모니터링 전략
#### 6.1.1 환경별 모니터링 도구 비교
| 모니터링 요소 | 개발환경 | 운영환경 | 기능 차이 |
|-------------|----------|----------|----------|
| **도구** | Kubernetes Dashboard, kubectl logs | Azure Monitor, Application Insights | 운영환경에서 전문 APM 도구 |
| **메트릭** | 기본 Pod/Node 메트릭 | 포괄적 APM, 비즈니스 메트릭 | 운영환경에서 비즈니스 인사이트 |
| **알림** | 기본 알림 (Pod 재시작) | 다단계 알림 (PagerDuty, Teams) | 운영환경에서 전문 알림 체계 |
| **로그** | 로컬 파일시스템 (7일) | Log Analytics (90일) | 운영환경에서 장기 보존 |
### 6.2 CI/CD 및 배포 전략
#### 6.2.1 환별별 배포 방식
#### 6.2.1 환경별 배포 방식 비교
| 배포 요소 | 개발환경 | 운영환경 | 안정성 차이 |
|-----------|----------|----------|----------|
| **배포 방식** | Rolling Update | Blue-Green Deployment | 운영환경에서 무중단 배포 |
| **자동화** | develop 브랜치 자동 | tag 생성 + 수동 승인 | 운영환경에서 더 신중한 배포 |
| **테스트** | 기본 헬스체크 | 종합 품질 게이트 (80% 커버리지) | 운영환경에서 더 엄격한 테스트 |
| **다운타임** | 허용 (1-2분) | Zero Downtime | 운영환경에서 서비스 연속성 보장 |
## 7. 비용 분석
### 7.1 환경별 비용 구조
#### 7.1.1 월간 비용 비교 (USD)
| 구성요소 | 개발환경 | 운영환경 | 차이 |
|----------|----------|----------|------|
| **컴퓨팅** | | | |
| AKS 노드 | $120 (Spot) | $1,200 (Reserved) | 10배 |
| **데이터** | | | |
| PostgreSQL | $0 (Pod) | $450 (Managed) | 무제한 |
| Redis | $0 (Pod) | $250 (Premium) | 무제한 |
| **네트워킹** | | | |
| Load Balancer | $20 | $150 | 7.5배 |
| **기타** | | | |
| Service Bus | $10 | $150 | 15배 |
| 모니터링 | $0 | $100 | 무제한 |
| **총합** | **$150** | **$2,650** | **17.7배** |
#### 7.1.2 비용 최적화 전략
#### 7.1.2 환경별 비용 최적화 전략 비교
| 최적화 영역 | 개발환경 | 운영환경 | 절약 효과 |
|-------------|----------|----------|----------|
| **컴퓨팅 비용** | Spot Instances 사용 | Reserved Instances | 70% vs 30% 절약 |
| **백킹서비스** | Pod 기반 (무료) | 관리형 서비스 | 100% 절약 vs 안정성 |
| **리소스 관리** | 비업무시간 자동 종료 | 자동 스케일링 | 시간 절약 vs 효율성 |
| **사이징 전략** | 고정 리소스 | 성능 기반 적정 sizing | 단순 vs 최적화 |
## 8. 전환 및 확장 계획
### 8.1 개발환경 → 운영환경 전환 체크리스트
| 카테고리 | 체크 항목 | 상태 | 우선순위 | 비고 |
|---------|-----------|------|----------|------|
| **데이터 마이그레이션** | 개발 데이터 백업 | ☐ | 높음 | pg_dump 사용 |
| **데이터 마이그레이션** | 스키마 마이그레이션 스크립트 | ☐ | 높음 | Flyway/Liquibase 고려 |
| **데이터 마이그레이션** | Azure Database 프로비저닝 | ☐ | 높음 | Flexible Server 구성 |
| **설정 변경** | 환경 변수 분리 | ☐ | 높음 | ConfigMap/Secret 분리 |
| **설정 변경** | Azure Key Vault 설정 | ☐ | 높음 | HSM 보안 모듈 |
| **설정 변경** | Managed Identity 구성 | ☐ | 높음 | 키 없는 인증 |
| **모니터링** | Azure Monitor 설정 | ☐ | 중간 | Log Analytics 연동 |
| **모니터링** | 알림 정책 수립 | ☐ | 중간 | PagerDuty/Teams 연동 |
| **모니터링** | 대시보드 구축 | ☐ | 낮음 | Application Insights |
### 8.2 단계별 확장 로드맵
| 단계 | 기간 | 핵심 목표 | 주요 작업 | 사용자 지원 | 가용성 |
|------|------|----------|----------|-------------|----------|
| **Phase 1** | 현재-6개월 | 안정화 | 개발환경 → 운영환경 전환<br/>기본 모니터링 및 알림 구축 | 1만 사용자 | 99.9% |
| **Phase 2** | 6-12개월 | 최적화 | 성능 최적화 및 비용 효율화<br/>고급 모니터링 (APM) 도입 | 10만 동시 사용자 | 99.9% |
| **Phase 3** | 12-18개월 | 글로벌 확장 | 다중 리전 배포<br/>글로벌 CDN 및 로드 밸런싱 | 100만 사용자 | 99.95% |
## 9. 핵심 SLA 지표
### 9.1 환경별 서비스 수준 목표
| SLA 항목 | 개발환경 | 운영환경 | 글로벌환경 (Phase 3) |
|---------|----------|----------|---------------------|
| **가용성** | 95% | 99.9% | 99.95% |
| **응답시간** | < 10초 | < 5초 | < 2초 |
| **배포시간** | 30분 | 10분 | 5분 |
| **복구시간** | 수동 복구 | < 5분 | < 2분 |
| **동시사용자** | 개발팀 (5명) | 10만명 | 100만명 |
| **월간비용** | $150 | $2,650 | $15,000+ |
| **보안인시던트** | 모니터링 없음 | 0건 목표 | 0건 목표 |

View File

@ -12,6 +12,8 @@
- UI/UX설계서의 '사용자 플로우'참조하여 설계
- 마이크로서비스 내부의 처리 흐름을 표시
- **각 서비스-시나리오별로 분리하여 각각 작성**
- 요청/응답을 **한글로 표시**
- Repository CRUD 처리를 한글로 설명하고 SQL은 사용하지 말것
- 각 서비스별 주요 시나리오마다 독립적인 시퀀스 설계 수행
- 프론트엔드와 백엔드 책임 분리: 프론트엔드에서 할 수 있는 것은 백엔드로 요청 안하게 함
- 표현 요소

View File

@ -40,7 +40,11 @@ replicate:
# CORS Configuration
cors:
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:*}
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://kt-event-marketing.20.214.196.128.nip.io}
allowed-methods: ${CORS_ALLOWED_METHODS:GET,POST,PUT,DELETE,OPTIONS,PATCH}
allowed-headers: ${CORS_ALLOWED_HEADERS:*}
allow-credentials: ${CORS_ALLOW_CREDENTIALS:true}
max-age: ${CORS_MAX_AGE:3600}
# Actuator
management:

View File

@ -0,0 +1,582 @@
# KT Event Marketing - Backend CI/CD Guide
## 목차
1. [개요](#개요)
2. [아키텍처](#아키텍처)
3. [사전 요구사항](#사전-요구사항)
4. [GitHub Secrets 설정](#github-secrets-설정)
5. [배포 환경](#배포-환경)
6. [파이프라인 구조](#파이프라인-구조)
7. [사용 방법](#사용-방법)
8. [트러블슈팅](#트러블슈팅)
---
## 개요
이 문서는 KT Event Marketing 백엔드 마이크로서비스의 CI/CD 파이프라인 구축 및 운영 가이드입니다.
### 시스템 정보
- **시스템명**: kt-event-marketing
- **JDK 버전**: 21
- **빌드 도구**: Gradle
- **컨테이너 레지스트리**: Azure Container Registry (ACR)
- **배포 대상**: Azure Kubernetes Service (AKS)
### 서비스 목록
1. user-service (8081)
2. event-service (8082)
3. ai-service (8083)
4. content-service (8084)
5. distribution-service (8085)
6. participation-service (8086)
7. analytics-service (8087)
---
## 아키텍처
### CI/CD 파이프라인 흐름
```
┌─────────────────┐
│ Code Push │
│ (GitHub) │
└────────┬────────┘
┌─────────────────────────────────────────────┐
│ GitHub Actions Workflow │
│ │
│ 1. Detect Changed Services │
│ 2. Build & Test (Gradle) │
│ 3. Build Docker Image │
│ 4. Push to ACR │
│ 5. Deploy to AKS (Kustomize) │
│ 6. Verify Deployment │
└─────────────────────────────────────────────┘
┌─────────────────┐
│ AKS Cluster │
│ (Running Pods) │
└─────────────────┘
```
### Kustomize 디렉토리 구조
```
.github/
├── kustomize/
│ ├── base/ # 기본 매니페스트
│ │ ├── kustomization.yaml
│ │ ├── cm-common.yaml
│ │ ├── secret-common.yaml
│ │ ├── ingress.yaml
│ │ └── *-service-*.yaml # 각 서비스별 리소스
│ └── overlays/ # 환경별 설정
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── *-service-patch.yaml # Dev 환경 패치
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── *-service-patch.yaml # Staging 환경 패치
│ └── prod/
│ ├── kustomization.yaml
│ └── *-service-patch.yaml # Prod 환경 패치
├── workflows/
│ └── backend-cicd.yaml # GitHub Actions 워크플로우
├── config/
│ ├── deploy_env_vars_dev # Dev 환경 변수
│ ├── deploy_env_vars_staging # Staging 환경 변수
│ └── deploy_env_vars_prod # Prod 환경 변수
└── scripts/
└── deploy.sh # 수동 배포 스크립트
```
---
## 사전 요구사항
### 1. Azure 리소스
- **Azure Container Registry (ACR)**
- 이름: acrdigitalgarage01
- SKU: Standard 이상
- **Azure Kubernetes Service (AKS)**
- 클러스터명: aks-digitalgarage-01
- 리소스그룹: rg-digitalgarage-01
- Kubernetes 버전: 1.28 이상
### 2. GitHub Repository Secrets
다음 Secrets를 GitHub Repository에 등록해야 합니다:
- `ACR_USERNAME`: ACR 사용자명
- `ACR_PASSWORD`: ACR 패스워드
- `AZURE_CREDENTIALS`: Azure Service Principal JSON
### 3. 로컬 개발 환경 (수동 배포 시)
- Azure CLI 2.50 이상
- kubectl 1.28 이상
- kustomize 5.0 이상
- JDK 21
---
## GitHub Secrets 설정
### 1. Azure Service Principal 생성
```bash
# Azure 로그인
az login
# Service Principal 생성
az ad sp create-for-rbac \
--name "github-actions-kt-event-marketing" \
--role contributor \
--scopes /subscriptions/{subscription-id}/resourceGroups/rg-digitalgarage-01 \
--sdk-auth
# 출력된 JSON을 AZURE_CREDENTIALS Secret에 등록
```
### 2. ACR 자격증명 가져오기
```bash
# ACR 사용자명 확인
az acr credential show --name acrdigitalgarage01 --query username
# ACR 패스워드 확인
az acr credential show --name acrdigitalgarage01 --query passwords[0].value
```
### 3. GitHub Secrets 등록
GitHub Repository → Settings → Secrets and variables → Actions → New repository secret
```
Name: ACR_USERNAME
Value: [ACR 사용자명]
Name: ACR_PASSWORD
Value: [ACR 패스워드]
Name: AZURE_CREDENTIALS
Value: [Service Principal JSON]
```
---
## 배포 환경
### Dev 환경
- **브랜치**: develop
- **이미지 태그**: dev
- **네임스페이스**: kt-event-marketing
- **리소스**:
- Replicas: 1
- CPU Request: 256m, Limit: 1024m
- Memory Request: 256Mi, Limit: 1024Mi
### Staging 환경
- **브랜치**: staging (Manual workflow dispatch)
- **이미지 태그**: staging
- **네임스페이스**: kt-event-marketing
- **리소스**:
- Replicas: 2
- CPU Request: 512m, Limit: 2048m
- Memory Request: 512Mi, Limit: 2048Mi
### Prod 환경
- **브랜치**: main
- **이미지 태그**: prod
- **네임스페이스**: kt-event-marketing
- **리소스**:
- Replicas: 3
- CPU Request: 1024m, Limit: 4096m
- Memory Request: 1024Mi, Limit: 4096Mi
---
## 파이프라인 구조
### Job 1: detect-changes
변경된 서비스를 감지하고 배포 환경을 결정합니다.
**Output**:
- `services`: 배포할 서비스 목록 (JSON 배열)
- `environment`: 배포 대상 환경 (dev/staging/prod)
**로직**:
- Workflow dispatch: 사용자가 지정한 서비스와 환경
- Push to main: 모든 서비스를 prod에 배포
- Push to develop: 변경된 서비스를 dev에 배포
### Job 2: build-and-push
각 서비스를 병렬로 빌드하고 ACR에 푸시합니다.
**단계**:
1. 코드 체크아웃
2. JDK 21 설정
3. Gradle 빌드 (테스트 제외)
4. 단위 테스트 실행
5. bootJar 빌드
6. ACR 로그인
7. Docker 이미지 빌드 및 푸시
**생성되는 이미지 태그**:
- `{environment}`: 환경별 태그 (dev/staging/prod)
- `{git-sha}`: Git 커밋 해시
- `latest`: 최신 이미지
### Job 3: deploy
Kustomize를 사용하여 AKS에 배포합니다.
**단계**:
1. Azure 로그인
2. AKS 자격증명 가져오기
3. Kustomize 설치
4. 이미지 태그 업데이트
5. Kustomize 빌드 및 적용
6. Deployment 롤아웃 대기
7. 배포 검증
### Job 4: notify
배포 결과를 알립니다.
---
## 사용 방법
### 1. 자동 배포 (Push)
#### Dev 환경 배포
```bash
git checkout develop
git add .
git commit -m "feat: 새로운 기능 추가"
git push origin develop
```
#### Prod 환경 배포
```bash
git checkout main
git merge develop
git push origin main
```
### 2. 수동 배포 (Workflow Dispatch)
GitHub Actions 웹 UI에서:
1. Actions 탭 클릭
2. "Backend CI/CD Pipeline" 워크플로우 선택
3. "Run workflow" 클릭
4. 환경과 서비스 선택:
- Environment: dev/staging/prod
- Service: all 또는 특정 서비스명
### 3. 로컬에서 수동 배포
```bash
# Dev 환경에 모든 서비스 배포
./.github/scripts/deploy.sh dev
# Prod 환경에 특정 서비스만 배포
./.github/scripts/deploy.sh prod user-service
# 스크립트 도움말
./.github/scripts/deploy.sh
```
### 4. 단일 서비스 배포
특정 서비스만 배포하려면:
```bash
# GitHub Actions UI에서 Workflow Dispatch 사용
# 또는 로컬 스크립트 사용
./.github/scripts/deploy.sh dev user-service
```
---
## 배포 검증
### 1. Pod 상태 확인
```bash
kubectl get pods -n kt-event-marketing
```
모든 Pod가 `Running` 상태이고 `READY` 컬럼이 `1/1`이어야 합니다.
### 2. Service 확인
```bash
kubectl get svc -n kt-event-marketing
```
### 3. Ingress 확인
```bash
kubectl get ingress -n kt-event-marketing
```
### 4. 로그 확인
```bash
# 특정 Pod의 로그
kubectl logs -n kt-event-marketing <pod-name>
# 최근 로그 스트리밍
kubectl logs -n kt-event-marketing <pod-name> -f
# 이전 컨테이너 로그
kubectl logs -n kt-event-marketing <pod-name> --previous
```
### 5. 애플리케이션 헬스 체크
```bash
# Ingress를 통한 헬스 체크
curl http://kt-event-marketing-api.20.214.196.128.nip.io/api/v1/users/actuator/health
# 각 서비스별 엔드포인트
curl http://kt-event-marketing-api.20.214.196.128.nip.io/api/v1/events/actuator/health
curl http://kt-event-marketing-api.20.214.196.128.nip.io/api/v1/content/actuator/health
curl http://kt-event-marketing-api.20.214.196.128.nip.io/api/v1/ai-service/actuator/health
curl http://kt-event-marketing-api.20.214.196.128.nip.io/distribution/actuator/health
curl http://kt-event-marketing-api.20.214.196.128.nip.io/api/v1/participations/actuator/health
```
---
## 트러블슈팅
### 문제 1: ImagePullBackOff
**증상**:
```
kubectl get pods -n kt-event-marketing
NAME READY STATUS RESTARTS AGE
user-service-xxx 0/1 ImagePullBackOff 0 2m
```
**원인**:
- ACR 인증 실패
- 이미지가 ACR에 존재하지 않음
**해결**:
```bash
# Secret 확인
kubectl get secret kt-event-marketing -n kt-event-marketing
# Secret 재생성
kubectl create secret docker-registry kt-event-marketing \
--docker-server=acrdigitalgarage01.azurecr.io \
--docker-username=<ACR_USERNAME> \
--docker-password=<ACR_PASSWORD> \
-n kt-event-marketing \
--dry-run=client -o yaml | kubectl apply -f -
# ACR에 이미지 존재 확인
az acr repository list --name acrdigitalgarage01 --output table
az acr repository show-tags --name acrdigitalgarage01 --repository kt-event-marketing/user-service
```
### 문제 2: CrashLoopBackOff
**증상**:
```
kubectl get pods -n kt-event-marketing
NAME READY STATUS RESTARTS AGE
user-service-xxx 0/1 CrashLoopBackOff 5 5m
```
**원인**:
- 애플리케이션 시작 실패
- 환경 변수 오류
- 데이터베이스 연결 실패
**해결**:
```bash
# Pod 로그 확인
kubectl logs -n kt-event-marketing user-service-xxx
# 이전 컨테이너 로그 확인
kubectl logs -n kt-event-marketing user-service-xxx --previous
# ConfigMap 확인
kubectl get cm -n kt-event-marketing cm-common -o yaml
kubectl get cm -n kt-event-marketing cm-user-service -o yaml
# Secret 확인 (값은 base64 인코딩)
kubectl get secret -n kt-event-marketing secret-common -o yaml
# Pod describe로 상세 정보 확인
kubectl describe pod -n kt-event-marketing user-service-xxx
```
### 문제 3: Readiness Probe Failed
**증상**:
```
kubectl get pods -n kt-event-marketing
NAME READY STATUS RESTARTS AGE
content-service-xxx 0/1 Running 0 3m
```
Pod는 Running이지만 READY가 0/1입니다.
**원인**:
- Actuator 엔드포인트 경로 오류
- 애플리케이션 Context Path 설정 문제
**해결**:
```bash
# Pod 이벤트 확인
kubectl describe pod -n kt-event-marketing content-service-xxx
# 수동으로 헬스 체크 테스트
kubectl exec -n kt-event-marketing content-service-xxx -- \
curl -f http://localhost:8084/api/v1/content/actuator/health
# Deployment의 probe 설정 확인 및 수정
kubectl edit deployment content-service -n kt-event-marketing
```
### 문제 4: Database Connection Failed
**증상**:
로그에서 데이터베이스 연결 오류 발생
**해결**:
```bash
# PostgreSQL Pod 확인
kubectl get pods -n kt-event-marketing | grep postgres
# PostgreSQL 연결 테스트
kubectl exec -it distribution-postgresql-0 -n kt-event-marketing -- \
psql -U eventuser -d postgres -c "\l"
# 데이터베이스 생성
kubectl exec distribution-postgresql-0 -n kt-event-marketing -- \
bash -c "PGPASSWORD=Hi5Jessica! psql -U eventuser -d postgres -c 'CREATE DATABASE analytics_db;'"
# ConfigMap과 Secret의 DB 설정 확인
kubectl get cm cm-analytics-service -n kt-event-marketing -o yaml
kubectl get secret secret-analytics-service -n kt-event-marketing -o yaml
```
### 문제 5: Workflow 실패
**증상**:
GitHub Actions 워크플로우가 실패합니다.
**해결**:
1. **Build 단계 실패**:
```bash
# 로컬에서 빌드 테스트
./gradlew clean build
# 특정 서비스만 빌드
./gradlew user-service:build
```
2. **Docker push 실패**:
- ACR_USERNAME, ACR_PASSWORD Secrets 확인
- ACR에 로그인 권한 확인
3. **Deploy 단계 실패**:
- AZURE_CREDENTIALS Secret 확인
- Service Principal 권한 확인
- AKS 클러스터 접근 권한 확인
### 문제 6: Kustomize 빌드 실패
**증상**:
```
Error: unable to find one or more resources
```
**해결**:
```bash
# 로컬에서 Kustomize 빌드 테스트
cd .github/kustomize/overlays/dev
kustomize build .
# 리소스 파일 존재 확인
ls -la ../../base/
# kustomization.yaml의 resources 경로 확인
cat kustomization.yaml
```
---
## 모니터링 및 로깅
### 1. 실시간 로그 모니터링
```bash
# 모든 서비스 로그 스트리밍
kubectl logs -n kt-event-marketing -l app.kubernetes.io/part-of=kt-event-marketing -f
# 특정 서비스 로그
kubectl logs -n kt-event-marketing -l app=user-service -f
```
### 2. 리소스 사용량 모니터링
```bash
# Pod 리소스 사용량
kubectl top pods -n kt-event-marketing
# Node 리소스 사용량
kubectl top nodes
```
### 3. 이벤트 확인
```bash
# 네임스페이스 이벤트
kubectl get events -n kt-event-marketing --sort-by='.lastTimestamp'
# 특정 Pod 이벤트
kubectl describe pod -n kt-event-marketing user-service-xxx
```
---
## 롤백
### 1. Deployment 롤백
```bash
# 이전 버전으로 롤백
kubectl rollout undo deployment/user-service -n kt-event-marketing
# 특정 리비전으로 롤백
kubectl rollout history deployment/user-service -n kt-event-marketing
kubectl rollout undo deployment/user-service --to-revision=2 -n kt-event-marketing
```
### 2. 이미지 태그로 롤백
```bash
# 특정 이미지 버전으로 변경
kubectl set image deployment/user-service \
user-service=acrdigitalgarage01.azurecr.io/kt-event-marketing/user-service:{previous-sha} \
-n kt-event-marketing
# 롤아웃 상태 확인
kubectl rollout status deployment/user-service -n kt-event-marketing
```
---
## 참고 자료
- [GitHub Actions 공식 문서](https://docs.github.com/en/actions)
- [Kustomize 공식 문서](https://kustomize.io/)
- [Azure AKS 공식 문서](https://docs.microsoft.com/en-us/azure/aks/)
- [Kubernetes 공식 문서](https://kubernetes.io/docs/)
---
## 변경 이력
| 날짜 | 버전 | 변경 내용 | 작성자 |
|------|------|-----------|--------|
| 2025-10-29 | 1.0.0 | 초기 CI/CD 파이프라인 구축 | DevOps Team |

View File

@ -0,0 +1,288 @@
# GitHub Actions CI/CD 파이프라인 구축 완료
## 작업 일시
2025-10-29
## 작업 내용
### 1. Kustomize 디렉토리 구조 생성 ✅
```
.github/kustomize/
├── base/ # 기본 매니페스트 (35개 파일)
│ ├── kustomization.yaml # 기본 리소스 정의
│ ├── cm-common.yaml
│ ├── secret-common.yaml
│ ├── secret-imagepull.yaml
│ ├── ingress.yaml
│ └── {service}-*.yaml # 7개 서비스 × 4개 리소스
└── overlays/ # 환경별 설정
├── dev/ # Dev 환경 (9개 파일)
│ ├── kustomization.yaml
│ └── *-service-patch.yaml # 7개 서비스
├── staging/ # Staging 환경 (9개 파일)
│ ├── kustomization.yaml
│ └── *-service-patch.yaml # 7개 서비스
└── prod/ # Prod 환경 (9개 파일)
├── kustomization.yaml
└── *-service-patch.yaml # 7개 서비스
```
**총 파일 수**: 62개
### 2. GitHub Actions 워크플로우 생성 ✅
**파일**: `.github/workflows/backend-cicd.yaml`
**주요 기능**:
- 변경된 서비스 자동 감지
- 병렬 빌드 및 테스트
- ACR에 Docker 이미지 푸시
- Kustomize를 사용한 AKS 배포
- 배포 검증 및 알림
**트리거**:
- develop 브랜치 push → dev 환경 자동 배포
- main 브랜치 push → prod 환경 자동 배포
- Manual workflow dispatch → 원하는 환경/서비스 선택 배포
**Jobs**:
1. `detect-changes`: 변경된 서비스 및 환경 감지
2. `build-and-push`: 서비스별 병렬 빌드 및 푸시
3. `deploy`: Kustomize 기반 AKS 배포
4. `notify`: 배포 결과 알림
### 3. 배포 스크립트 생성 ✅
**디렉토리**: `.github/scripts/`
1. **deploy.sh**
- 로컬에서 수동 배포를 위한 메인 스크립트
- 환경별 배포 설정 자동 적용
- 사용법: `./deploy.sh <env> [service]`
2. **generate-patches.sh**
- Staging과 Prod 환경의 패치 파일 자동 생성
- 리소스 할당량을 환경에 맞게 설정
3. **copy-manifests-to-base.py**
- 기존 K8s 매니페스트를 base 디렉토리로 복사
- namespace 선언 자동 제거
### 4. 환경별 설정 파일 생성 ✅
**디렉토리**: `.github/config/`
1. **deploy_env_vars_dev**
- Dev 환경 변수 정의
- Replicas: 1, CPU: 256m-1024m, Memory: 256Mi-1024Mi
2. **deploy_env_vars_staging**
- Staging 환경 변수 정의
- Replicas: 2, CPU: 512m-2048m, Memory: 512Mi-2048Mi
3. **deploy_env_vars_prod**
- Prod 환경 변수 정의
- Replicas: 3, CPU: 1024m-4096m, Memory: 1024Mi-4096Mi
### 5. 문서화 완료 ✅
1. **deployment/cicd/CICD-GUIDE.md** (15KB)
- 전체 CI/CD 파이프라인 가이드
- 사전 요구사항 및 설정 방법
- 상세한 트러블슈팅 가이드
- 모니터링 및 롤백 방법
2. **.github/README.md** (6KB)
- CI/CD 인프라 구조 설명
- 배포 프로세스 가이드
- 환경별 설정 요약
3. **deployment/cicd/SETUP-SUMMARY.md** (이 파일)
- 구축 완료 요약
## 시스템 정보
| 항목 | 내용 |
|------|------|
| 시스템명 | kt-event-marketing |
| JDK 버전 | 21 |
| 빌드 도구 | Gradle |
| ACR | acrdigitalgarage01.azurecr.io |
| AKS 클러스터 | aks-digitalgarage-01 |
| 리소스 그룹 | rg-digitalgarage-01 |
| 네임스페이스 | kt-event-marketing |
| 서비스 수 | 7개 |
## 서비스 목록
1. **user-service** (8081)
2. **event-service** (8082)
3. **ai-service** (8083)
4. **content-service** (8084)
5. **distribution-service** (8085)
6. **participation-service** (8086)
7. **analytics-service** (8087)
## 환경별 설정
| 환경 | 브랜치 | 이미지 태그 | Replicas | CPU Limit | Memory Limit |
|------|--------|-------------|----------|-----------|--------------|
| Dev | develop | dev | 1 | 1024m | 1024Mi |
| Staging | manual | staging | 2 | 2048m | 2048Mi |
| Prod | main | prod | 3 | 4096m | 4096Mi |
## 다음 단계 (Required)
### 1. GitHub Secrets 설정 필수
다음 Secrets를 GitHub Repository에 등록해야 합니다:
```bash
# ACR 자격증명 확인
az acr credential show --name acrdigitalgarage01
# Service Principal 생성
az ad sp create-for-rbac \
--name "github-actions-kt-event-marketing" \
--role contributor \
--scopes /subscriptions/{subscription-id}/resourceGroups/rg-digitalgarage-01 \
--sdk-auth
```
**필수 Secrets**:
1. `ACR_USERNAME` - ACR 사용자명
2. `ACR_PASSWORD` - ACR 패스워드
3. `AZURE_CREDENTIALS` - Service Principal JSON
**등록 경로**:
GitHub Repository → Settings → Secrets and variables → Actions → New repository secret
### 2. 초기 배포 테스트
```bash
# 로컬에서 Kustomize 빌드 테스트
cd .github/kustomize/overlays/dev
kustomize build .
# Azure 로그인
az login
# AKS 자격증명 가져오기
az aks get-credentials \
--resource-group rg-digitalgarage-01 \
--name aks-digitalgarage-01
# Dev 환경 배포 테스트
./.github/scripts/deploy.sh dev
```
### 3. GitHub Actions 워크플로우 테스트
```bash
# develop 브랜치에 커밋하여 자동 배포 트리거
git checkout develop
git add .
git commit -m "ci: Add GitHub Actions CI/CD pipeline"
git push origin develop
```
또는 GitHub Actions UI에서 Manual workflow dispatch 실행
## 주요 특징
### 1. Kustomize 기반 환경 관리
- Base + Overlays 패턴으로 중복 최소화
- 환경별 리소스 할당량 자동 적용
- 이미지 태그 환경별 분리 (dev/staging/prod)
### 2. 자동 변경 감지
- Git diff를 통한 변경된 서비스 자동 감지
- 변경된 서비스만 빌드 및 배포하여 시간 절약
- Manual trigger 시 전체 또는 특정 서비스 선택 가능
### 3. 병렬 처리
- 7개 서비스를 병렬로 빌드하여 시간 단축
- Matrix strategy를 사용한 효율적인 CI
### 4. 안전한 배포
- Startup, Readiness, Liveness probe 설정
- 롤아웃 상태 자동 확인
- 배포 실패 시 자동 알림
### 5. 다단계 이미지 태깅
- 환경별 태그: dev/staging/prod
- Git SHA 태그: 추적성 확보
- latest 태그: 최신 버전 유지
## 파일 통계
| 카테고리 | 파일 수 | 설명 |
|----------|---------|------|
| Kustomize Base | 35 | 기본 매니페스트 |
| Kustomize Dev Overlay | 9 | Dev 환경 설정 |
| Kustomize Staging Overlay | 9 | Staging 환경 설정 |
| Kustomize Prod Overlay | 9 | Prod 환경 설정 |
| Workflows | 1 | GitHub Actions 워크플로우 |
| Scripts | 3 | 배포 및 유틸리티 스크립트 |
| Config | 3 | 환경별 설정 파일 |
| Documentation | 3 | 가이드 문서 |
| **Total** | **72** | **전체 파일** |
## 기술 스택
- **CI/CD**: GitHub Actions
- **Container Registry**: Azure Container Registry
- **Orchestration**: Azure Kubernetes Service (AKS)
- **Manifest Management**: Kustomize
- **Build Tool**: Gradle
- **Runtime**: OpenJDK 21
- **Containerization**: Docker (multi-stage builds)
## 참고 사항
### 현재 AKS 배포 상태
현재 7개 서비스가 모두 AKS에 배포되어 Running 상태입니다:
- user-service: 1/1 Running
- event-service: 1/1 Running
- ai-service: 1/1 Running
- content-service: 1/1 Running
- distribution-service: 1/1 Running
- participation-service: 1/1 Running
- analytics-service: 1/1 Running
### 기존 배포 방식과의 호환성
- 기존 K8s 매니페스트 (`deployment/k8s/`)는 그대로 유지
- Kustomize는 별도 경로 (`.github/kustomize/`)에 구성
- 두 방식 모두 사용 가능하나 CI/CD에서는 Kustomize 사용 권장
### 모니터링 및 로깅
- Kubernetes 기본 모니터링 사용
- Azure Monitor 통합 가능 (별도 설정 필요)
- Application Insights 연동 가능 (별도 설정 필요)
## 문의 및 지원
- CI/CD 관련 문제: [deployment/cicd/CICD-GUIDE.md](./CICD-GUIDE.md) 참조
- 인프라 구조: [.github/README.md](../../.github/README.md) 참조
- 트러블슈팅: CICD-GUIDE.md의 트러블슈팅 섹션 참조
## 완료 체크리스트
- [x] Kustomize base 디렉토리 생성 및 매니페스트 복사
- [x] 환경별 overlay 디렉토리 생성 (dev/staging/prod)
- [x] 환경별 패치 파일 생성
- [x] GitHub Actions 워크플로우 작성
- [x] 배포 스크립트 작성
- [x] 환경별 설정 파일 작성
- [x] CI/CD 가이드 문서 작성
- [x] README 문서 작성
- [ ] GitHub Secrets 설정 (사용자 작업 필요)
- [ ] 초기 배포 테스트 (사용자 작업 필요)
- [ ] 워크플로우 동작 확인 (사용자 작업 필요)
---
**작성일**: 2025-10-29
**작성자**: Claude (DevOps Assistant)
**버전**: 1.0.0

View File

@ -0,0 +1,76 @@
# Event Service 환경변수 설정 템플릿
# 이 파일을 .env.event로 복사하고 실제 값으로 수정하세요
# 사용법: docker-compose --env-file .env.event -f docker-compose-event.yml up -d
# =============================================================================
# 서버 설정
# =============================================================================
SERVER_PORT=8082
# =============================================================================
# PostgreSQL 데이터베이스 설정 (필수)
# =============================================================================
DB_HOST=your-postgresql-host
DB_PORT=5432
DB_NAME=eventdb
DB_USERNAME=eventuser
DB_PASSWORD=your-db-password
DDL_AUTO=update
# 개발 환경 예시:
# DB_HOST=localhost
# DB_PORT=5432
# DB_NAME=eventdb
# DB_USERNAME=eventuser
# DB_PASSWORD=eventpass123
# =============================================================================
# Redis 설정 (필수)
# =============================================================================
REDIS_HOST=your-redis-host
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password
# 개발 환경 예시 (비밀번호 없음):
# REDIS_HOST=localhost
# REDIS_PORT=6379
# REDIS_PASSWORD=
# =============================================================================
# Kafka 설정 (필수)
# =============================================================================
KAFKA_BOOTSTRAP_SERVERS=your-kafka-host:9092
# 개발 환경 예시:
# KAFKA_BOOTSTRAP_SERVERS=localhost:9092
# 운영 환경 예시 (다중 브로커):
# KAFKA_BOOTSTRAP_SERVERS=kafka1:9092,kafka2:9092,kafka3:9092
# =============================================================================
# JWT 설정 (필수 - 최소 32자)
# =============================================================================
JWT_SECRET=your-jwt-secret-key-minimum-32-characters-required
# 주의: 운영 환경에서는 반드시 강력한 시크릿 키를 사용하세요
# 예시: JWT_SECRET=kt-event-marketing-prod-jwt-secret-2025-secure-random-key
# =============================================================================
# 마이크로서비스 URL (선택)
# =============================================================================
CONTENT_SERVICE_URL=http://content-service:8083
DISTRIBUTION_SERVICE_URL=http://distribution-service:8086
# Kubernetes 환경 예시:
# CONTENT_SERVICE_URL=http://content-service.default.svc.cluster.local:8083
# DISTRIBUTION_SERVICE_URL=http://distribution-service.default.svc.cluster.local:8086
# =============================================================================
# 로깅 설정 (선택)
# =============================================================================
LOG_LEVEL=INFO
SQL_LOG_LEVEL=WARN
# 개발 환경에서는 DEBUG로 설정 가능:
# LOG_LEVEL=DEBUG
# SQL_LOG_LEVEL=DEBUG

View File

@ -0,0 +1,291 @@
# Event Service 외부 서비스 연결 가이드
## 📋 연결 설정 검토 결과
### ✅ 현재 설정 상태
Event Service는 다음 외부 서비스들과 연동됩니다:
1. **PostgreSQL** - 이벤트 데이터 저장
2. **Redis** - AI 생성 결과 및 이미지 캐싱
3. **Kafka** - 비동기 작업 큐 (AI 생성, 이미지 생성)
4. **Content Service** - 콘텐츠 생성 서비스 (선택)
5. **Distribution Service** - 배포 서비스 (선택)
### 📁 설정 파일
#### application.yml
모든 연결 정보는 환경변수를 통해 주입되도록 설정되어 있습니다:
```yaml
# PostgreSQL
spring.datasource.url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:eventdb}
spring.datasource.username: ${DB_USERNAME:eventuser}
spring.datasource.password: ${DB_PASSWORD:eventpass}
# Redis
spring.data.redis.host: ${REDIS_HOST:localhost}
spring.data.redis.port: ${REDIS_PORT:6379}
spring.data.redis.password: ${REDIS_PASSWORD:}
# Kafka
spring.kafka.bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:localhost:9092}
# JWT
jwt.secret: ${JWT_SECRET:default-jwt-secret-key-for-development-minimum-32-bytes-required}
```
## 🔧 필수 환경변수
### PostgreSQL (필수)
```bash
DB_HOST=your-postgresql-host # PostgreSQL 호스트
DB_PORT=5432 # PostgreSQL 포트
DB_NAME=eventdb # 데이터베이스 이름
DB_USERNAME=eventuser # 데이터베이스 사용자
DB_PASSWORD=your-password # 데이터베이스 비밀번호
DDL_AUTO=update # Hibernate DDL 모드
```
### Redis (필수)
```bash
REDIS_HOST=your-redis-host # Redis 호스트
REDIS_PORT=6379 # Redis 포트
REDIS_PASSWORD=your-password # Redis 비밀번호 (없으면 빈 문자열)
```
### Kafka (필수)
```bash
KAFKA_BOOTSTRAP_SERVERS=kafka-host:9092 # Kafka 브로커 주소
```
### JWT (필수)
```bash
JWT_SECRET=your-jwt-secret-key # 최소 32자 이상
```
### 마이크로서비스 연동 (선택)
```bash
CONTENT_SERVICE_URL=http://content-service:8083
DISTRIBUTION_SERVICE_URL=http://distribution-service:8086
```
## 🚀 배포 방법
### 방법 1: Docker Run
```bash
docker run -d \
--name event-service \
-p 8082:8082 \
-e DB_HOST=your-postgresql-host \
-e DB_PORT=5432 \
-e DB_NAME=eventdb \
-e DB_USERNAME=eventuser \
-e DB_PASSWORD=your-password \
-e REDIS_HOST=your-redis-host \
-e REDIS_PORT=6379 \
-e REDIS_PASSWORD=your-redis-password \
-e KAFKA_BOOTSTRAP_SERVERS=your-kafka:9092 \
-e JWT_SECRET=your-jwt-secret-minimum-32-chars \
acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service:latest
```
### 방법 2: Docker Compose
1. **환경변수 파일 생성**:
```bash
cp .env.event.example .env.event
vi .env.event # 실제 값으로 수정
```
2. **컨테이너 실행**:
```bash
docker-compose --env-file .env.event -f docker-compose-event.yml up -d
```
3. **로그 확인**:
```bash
docker-compose -f docker-compose-event.yml logs -f event-service
```
### 방법 3: 스크립트 실행
```bash
# run-event-service.sh 파일 수정 후 실행
chmod +x run-event-service.sh
./run-event-service.sh
```
## 🔍 연결 상태 확인
### 헬스체크
```bash
curl http://localhost:8082/actuator/health
```
**예상 응답**:
```json
{
"status": "UP",
"components": {
"ping": {"status": "UP"},
"db": {"status": "UP"},
"redis": {"status": "UP"}
}
}
```
### 개별 서비스 연결 확인
#### PostgreSQL 연결
```bash
docker logs event-service | grep -i "hikari"
```
성공 시: `HikariPool-1 - Start completed.`
#### Redis 연결
```bash
docker logs event-service | grep -i "redis"
```
성공 시: `Lettuce driver initialized`
#### Kafka 연결
```bash
docker logs event-service | grep -i "kafka"
```
성공 시: `Kafka version: ...`
## ⚠️ 주의사항
### 1. localhost 주의
- Docker 컨테이너 내에서 `localhost`는 컨테이너 자신을 의미
- 호스트 머신의 서비스에 접근하려면:
- Linux/Mac: `host.docker.internal` 사용
- 또는 호스트 IP 직접 사용
### 2. JWT Secret
- 최소 32자 이상 필수
- 운영 환경에서는 강력한 랜덤 키 사용
- 예시: `kt-event-marketing-prod-jwt-secret-2025-secure-random-key`
### 3. DDL_AUTO 설정
- 개발: `update` (스키마 자동 업데이트)
- 운영: `validate` (스키마 검증만 수행)
- 초기 설치: `create` (스키마 생성, 주의: 기존 데이터 삭제)
### 4. Kafka 토픽
Event Service가 사용하는 토픽들이 미리 생성되어 있어야 합니다:
- `ai-event-generation-job`
- `image-generation-job`
- `event-created`
### 5. Redis 캐시 키
다음 키 프리픽스를 사용합니다:
- `ai:recommendation:*` - AI 추천 결과 (TTL: 24시간)
- `image:generation:*` - 이미지 생성 결과 (TTL: 7일)
- `job:status:*` - 작업 상태
## 🐛 트러블슈팅
### PostgreSQL 연결 실패
**증상**: `Connection refused` 또는 `Connection timeout`
**해결**:
```bash
# 1. PostgreSQL 서버 상태 확인
psql -h $DB_HOST -U $DB_USERNAME -d $DB_NAME
# 2. 방화벽 확인
telnet $DB_HOST 5432
# 3. 환경변수 확인
docker exec event-service env | grep DB_
```
### Redis 연결 실패
**증상**: `Unable to connect to Redis`
**해결**:
```bash
# 1. Redis 서버 상태 확인
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD ping
# 2. 환경변수 확인
docker exec event-service env | grep REDIS_
```
### Kafka 연결 실패
**증상**: `Connection to node ... could not be established`
**해결**:
```bash
# 1. Kafka 서버 상태 확인
kafka-topics.sh --bootstrap-server $KAFKA_BOOTSTRAP_SERVERS --list
# 2. 토픽 존재 확인
kafka-topics.sh --bootstrap-server $KAFKA_BOOTSTRAP_SERVERS --describe --topic ai-event-generation-job
# 3. 환경변수 확인
docker exec event-service env | grep KAFKA_
```
### JWT 오류
**증상**: `JWT secret key must be at least 32 characters`
**해결**:
```bash
# JWT_SECRET 길이 확인 (32자 이상이어야 함)
docker exec event-service env | grep JWT_SECRET | awk -F'=' '{print length($2)}'
```
## 📊 연결 풀 설정
### HikariCP (PostgreSQL)
```yaml
maximum-pool-size: 5 # 최대 연결 수
minimum-idle: 2 # 최소 유휴 연결 수
connection-timeout: 30000 # 연결 타임아웃 (30초)
```
### Lettuce (Redis)
```yaml
max-active: 5 # 최대 활성 연결 수
max-idle: 3 # 최대 유휴 연결 수
min-idle: 1 # 최소 유휴 연결 수
```
## 🔐 보안 권장사항
1. **환경변수 보안**
- `.env` 파일은 `.gitignore`에 추가
- 운영 환경에서는 Kubernetes Secrets 또는 AWS Secrets Manager 사용
2. **네트워크 보안**
- 프로덕션 환경에서는 전용 네트워크 사용
- 불필요한 포트 노출 금지
3. **인증 정보 관리**
- 비밀번호 정기적 변경
- 강력한 비밀번호 사용
## 📝 체크리스트
배포 전 확인 사항:
- [ ] PostgreSQL 서버 준비 및 데이터베이스 생성
- [ ] Redis 서버 준비 및 연결 확인
- [ ] Kafka 클러스터 준비 및 토픽 생성
- [ ] JWT Secret 생성 (32자 이상)
- [ ] 환경변수 파일 작성 및 검증
- [ ] 네트워크 방화벽 설정 확인
- [ ] Docker 이미지 pull 권한 확인
- [ ] 헬스체크 엔드포인트 접근 가능 확인
## 📚 관련 문서
- [Event Service 컨테이너 이미지 빌드 가이드](build-image.md)
- [Docker Compose 설정](docker-compose-event.yml)
- [환경변수 템플릿](.env.event.example)
- [실행 스크립트](run-event-service.sh)
- [IntelliJ 실행 프로파일](../.run/EventServiceApplication.run.xml)

View File

@ -1,68 +1,57 @@
# 백엔드 컨테이너 이미지 작성 결과
# 백엔드 컨테이너 이미지 빌드 결과
## 작업 개요
- **작업일시**: 2025-10-29
- **작성자**: DevOps Engineer (송근정 "데브옵스 마스터")
- **대상 서비스**: 6개 백엔드 마이크로서비스
## 개요
KT 이벤트 마케팅 서비스의 백엔드 마이크로서비스들에 대한 컨테이너 이미지를 생성하였습니다.
## 1. 서비스 확인
## 작업 일시
- 날짜: 2025-10-29
- 빌드 환경: Windows (MINGW64_NT-10.0-19045)
### settings.gradle 분석
```gradle
## 서비스 목록 확인
settings.gradle에서 확인한 서비스 목록:
```
rootProject.name = 'kt-event-marketing'
// Common module
include 'common'
// Microservices
include 'user-service'
include 'event-service'
include 'ai-service'
include 'content-service'
include 'distribution-service'
include 'participation-service'
include 'analytics-service'
```
### 빌드 가능한 서비스 (6개)
Main Application 클래스가 존재하는 서비스:
1. **user-service** - `UserServiceApplication.java`
2. **event-service** - `EventServiceApplication.java`
3. **ai-service** - `AiServiceApplication.java`
4. **content-service** - `ContentApplication.java`
5. **participation-service** - `ParticipationServiceApplication.java`
6. **analytics-service** - `AnalyticsServiceApplication.java`
**빌드 대상 서비스 (6개):**
- user-service (Java/Spring Boot)
- event-service (Java/Spring Boot)
- ai-service (Java/Spring Boot)
- distribution-service (Java/Spring Boot)
- participation-service (Java/Spring Boot)
- analytics-service (Java/Spring Boot)
### 제외된 서비스
- **distribution-service**: 소스 코드 미구현 상태 (src/main/java 디렉토리 없음)
**제외 대상:**
- common: 공통 라이브러리 모듈 (독립 실행 서비스 아님)
- content-service: Python 기반 서비스 (별도 빌드 필요)
## 2. bootJar 설정
## bootJar 설정 확인
각 서비스의 `build.gradle`에 bootJar 설정 추가/수정:
모든 Java 서비스의 build.gradle에 bootJar 설정이 올바르게 구성되어 있음을 확인:
### 설정 추가된 서비스 (5개)
```gradle
bootJar {
archiveFileName = '{service-name}.jar'
}
```
| 서비스명 | JAR 파일명 | 경로 |
|---------|-----------|------|
| user-service | user-service.jar | user-service/build/libs/user-service.jar |
| event-service | event-service.jar | event-service/build/libs/event-service.jar |
| ai-service | ai-service.jar | ai-service/build/libs/ai-service.jar |
| distribution-service | distribution-service.jar | distribution-service/build/libs/distribution-service.jar |
| participation-service | participation-service.jar | participation-service/build/libs/participation-service.jar |
| analytics-service | analytics-service.jar | analytics-service/build/libs/analytics-service.jar |
- user-service/build.gradle
- ai-service/build.gradle
- distribution-service/build.gradle (향후 구현 대비)
- participation-service/build.gradle
- analytics-service/build.gradle
## Dockerfile 생성
### 기존 설정 확인된 서비스 (2개)
- event-service/build.gradle ✅
- content-service/build.gradle ✅
**파일 위치:** `deployment/container/Dockerfile-backend`
## 3. Dockerfile 생성
### 파일 경로
`deployment/container/Dockerfile-backend`
### Dockerfile 내용
**Dockerfile 구성:**
```dockerfile
# Build stage
FROM openjdk:23-oraclelinux8 AS builder
@ -91,58 +80,34 @@ ENTRYPOINT [ "sh", "-c" ]
CMD ["java ${JAVA_OPTS} -jar app.jar"]
```
### Dockerfile 특징
- **Multi-stage build**: 빌드와 실행 스테이지 분리
- **Non-root user**: 보안을 위한 k8s 사용자 실행
- **플랫폼**: linux/amd64 (K8s 클러스터 호환)
- **Java 버전**: OpenJDK 23
**주요 특징:**
- Multi-stage 빌드: 빌드 이미지와 런타임 이미지 분리
- Base Image: openjdk:23-slim (경량화)
- 보안: 비root 사용자(k8s)로 실행
- 플랫폼: linux/amd64
## 4. JAR 파일 빌드
## Gradle 빌드 실행
### 빌드 명령어
**실행 명령:**
```bash
./gradlew user-service:bootJar ai-service:bootJar event-service:bootJar \
content-service:bootJar participation-service:bootJar analytics-service:bootJar
./gradlew clean build -x test
```
### 빌드 결과
```
BUILD SUCCESSFUL in 27s
33 actionable tasks: 15 executed, 18 up-to-date
```
**빌드 결과:**
- 상태: ✅ BUILD SUCCESSFUL
- 소요 시간: 33초
- 실행된 태스크: 56개
### 생성된 JAR 파일
```bash
$ ls -lh */build/libs/*.jar
## 컨테이너 이미지 빌드
-rw-r--r-- 1 KTDS 197121 94M 10월 29 09:49 ai-service/build/libs/ai-service.jar
-rw-r--r-- 1 KTDS 197121 95M 10월 29 09:48 analytics-service/build/libs/analytics-service.jar
-rw-r--r-- 1 KTDS 197121 78M 10월 29 09:49 content-service/build/libs/content-service.jar
-rw-r--r-- 1 KTDS 197121 94M 10월 29 09:49 event-service/build/libs/event-service.jar
-rw-r--r-- 1 KTDS 197121 85M 10월 29 09:49 participation-service/build/libs/participation-service.jar
-rw-r--r-- 1 KTDS 197121 96M 10월 29 09:49 user-service/build/libs/user-service.jar
```
### 병렬 빌드 전략
서브 에이전트를 활용하여 6개 서비스를 동시에 빌드하여 시간 단축
## 5. Docker 이미지 빌드
### 1. user-service
### 사전 준비사항
⚠️ **Docker Desktop이 실행 중이어야 합니다**
Docker Desktop 시작 확인:
```bash
# Docker 상태 확인
docker version
docker ps
# Docker Desktop이 정상 실행되면 위 명령들이 정상 동작합니다
```
### 빌드 명령어
#### 5.1 user-service
**빌드 명령:**
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="user-service/build/libs" \
@ -151,22 +116,17 @@ docker build \
-t user-service:latest .
```
#### 5.2 ai-service
**결과:**
- 상태: ✅ SUCCESS
- 이미지 ID: fb07547604be
- 이미지 크기: 1.09GB
- Image SHA: sha256:fb07547604bee7e8ff69e56e8423299b7dec277e80d865ee5013ddd876a0b4c6
### 2. event-service
**빌드 명령:**
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="ai-service/build/libs" \
--build-arg ARTIFACTORY_FILE="ai-service.jar" \
-f ${DOCKER_FILE} \
-t ai-service:latest .
```
#### 5.3 event-service
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="event-service/build/libs" \
@ -175,22 +135,56 @@ docker build \
-t event-service:latest .
```
#### 5.4 content-service
**결과:**
- 상태: ✅ SUCCESS
- 이미지 ID: 191a9882a628
- 이미지 크기: 1.08GB
- 빌드 시간: ~20초
### 3. ai-service
**빌드 명령:**
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="content-service/build/libs" \
--build-arg ARTIFACTORY_FILE="content-service.jar" \
--build-arg BUILD_LIB_DIR="ai-service/build/libs" \
--build-arg ARTIFACTORY_FILE="ai-service.jar" \
-f ${DOCKER_FILE} \
-t content-service:latest .
-t ai-service:latest .
```
#### 5.5 participation-service
**결과:**
- 상태: ✅ SUCCESS
- 이미지 ID: 498feb888dc5
- 이미지 크기: 1.08GB
- Image SHA: sha256:498feb888dc58a98715841c4e50f191bc8434eccd12baefa79e82b0e44a5bc40
### 4. distribution-service
**빌드 명령:**
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="distribution-service/build/libs" \
--build-arg ARTIFACTORY_FILE="distribution-service.jar" \
-f ${DOCKER_FILE} \
-t distribution-service:latest .
```
**결과:**
- 상태: ✅ SUCCESS
- 이미지 ID: e0ad31c51b63
- 이미지 크기: 1.08GB
- Image SHA: sha256:e0ad31c51b63b44d67f017cca8a729ae9cbb5e9e9503feddb308c09f19b70fba
- 빌드 시간: ~60초
### 5. participation-service
**빌드 명령:**
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="participation-service/build/libs" \
@ -199,10 +193,18 @@ docker build \
-t participation-service:latest .
```
#### 5.6 analytics-service
**결과:**
- 상태: ✅ SUCCESS
- 이미지 ID: 9bd60358659b
- 이미지 크기: 1.04GB
- Image SHA: sha256:9bd60358659b528190edcab699152b5126dc906070e05d355310303ac292f02b
- 빌드 시간: ~37초
### 6. analytics-service
**빌드 명령:**
```bash
DOCKER_FILE=deployment/container/Dockerfile-backend
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="analytics-service/build/libs" \
@ -211,186 +213,55 @@ docker build \
-t analytics-service:latest .
```
### 빌드 스크립트 (일괄 실행)
**결과:**
- 상태: ✅ SUCCESS
- 이미지 ID: 33b53299ec16
- 이미지 크기: 1.08GB
- Image SHA: sha256:33b53299ec16e0021a9adca4fb32535708021073df03c30b8a0ea335348547de
## 생성된 이미지 확인
**확인 명령:**
```bash
#!/bin/bash
# build-all-images.sh
DOCKER_FILE=deployment/container/Dockerfile-backend
services=(
"user-service"
"ai-service"
"event-service"
"content-service"
"participation-service"
"analytics-service"
)
for service in "${services[@]}"; do
echo "Building ${service}..."
docker build \
--platform linux/amd64 \
--build-arg BUILD_LIB_DIR="${service}/build/libs" \
--build-arg ARTIFACTORY_FILE="${service}.jar" \
-f ${DOCKER_FILE} \
-t ${service}:latest .
if [ $? -eq 0 ]; then
echo "✅ ${service} build successful"
else
echo "❌ ${service} build failed"
exit 1
fi
done
echo "🎉 All images built successfully!"
docker images | grep -E "(user-service|event-service|ai-service|distribution-service|participation-service|analytics-service)" | grep latest
```
## 6. 이미지 확인
### 생성된 이미지 확인 명령어
```bash
# 모든 서비스 이미지 확인
docker images | grep -E "(user-service|ai-service|event-service|content-service|participation-service|analytics-service)"
# 개별 서비스 확인
docker images user-service:latest
docker images ai-service:latest
docker images event-service:latest
docker images content-service:latest
docker images participation-service:latest
docker images analytics-service:latest
**확인 결과:**
```
event-service latest 191a9882a628 39 seconds ago 1.08GB
ai-service latest 498feb888dc5 46 seconds ago 1.08GB
analytics-service latest 33b53299ec16 46 seconds ago 1.08GB
user-service latest fb07547604be 47 seconds ago 1.09GB
participation-service latest 9bd60358659b 48 seconds ago 1.04GB
distribution-service latest e0ad31c51b63 48 seconds ago 1.08GB
```
### 빌드 결과 ✅
```
REPOSITORY TAG IMAGE ID CREATED SIZE
user-service latest 91c511ef86bd About a minute ago 1.09GB
ai-service latest 9477022fa493 About a minute ago 1.08GB
event-service latest add81de69536 About a minute ago 1.08GB
content-service latest aa9cc16ad041 About a minute ago 1.01GB
participation-service latest 9b044a3854dd About a minute ago 1.04GB
analytics-service latest ac569de42545 About a minute ago 1.08GB
```
## 빌드 결과 요약
**빌드 일시**: 2025-10-29 09:50 KST
**빌드 소요 시간**: 약 13초 (병렬 빌드)
**총 이미지 크기**: 6.48GB
| 서비스명 | 이미지 태그 | 이미지 ID | 크기 | 상태 |
|---------|-----------|----------|------|------|
| user-service | user-service:latest | fb07547604be | 1.09GB | ✅ |
| event-service | event-service:latest | 191a9882a628 | 1.08GB | ✅ |
| ai-service | ai-service:latest | 498feb888dc5 | 1.08GB | ✅ |
| distribution-service | distribution-service:latest | e0ad31c51b63 | 1.08GB | ✅ |
| participation-service | participation-service:latest | 9bd60358659b | 1.04GB | ✅ |
| analytics-service | analytics-service:latest | 33b53299ec16 | 1.08GB | ✅ |
## 7. 이미지 테스트
**총 6개 서비스 이미지 빌드 성공**
### 로컬 실행 테스트 (예시: user-service)
```bash
# 컨테이너 실행
docker run -d \
--name user-service-test \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=dev \
user-service:latest
## 다음 단계
# 로그 확인
docker logs -f user-service-test
생성된 이미지를 사용하여 다음 작업을 진행할 수 있습니다:
# 헬스체크
curl http://localhost:8080/actuator/health
1. **로컬 테스트:** Docker Compose 또는 개별 컨테이너 실행
2. **ACR 푸시:** Azure Container Registry에 이미지 업로드
3. **AKS 배포:** Kubernetes 클러스터에 배포
4. **CI/CD 통합:** GitHub Actions 또는 Jenkins 파이프라인 연동
# 정리
docker stop user-service-test
docker rm user-service-test
```
## 참고사항
## 8. 다음 단계
### 8.1 컨테이너 레지스트리 푸시
```bash
# Docker Hub 예시
docker tag user-service:latest <your-registry>/user-service:latest
docker push <your-registry>/user-service:latest
# Azure Container Registry 예시
docker tag user-service:latest <acr-name>.azurecr.io/user-service:latest
docker push <acr-name>.azurecr.io/user-service:latest
```
### 8.2 Kubernetes 배포
- Kubernetes Deployment 매니페스트 작성
- Service 리소스 정의
- ConfigMap/Secret 설정
- Ingress 구성
### 8.3 CI/CD 파이프라인 구성
- GitHub Actions 또는 Jenkins 파이프라인 작성
- 자동 빌드 및 배포 설정
- 이미지 태깅 전략 수립 (semantic versioning)
## 9. 트러블슈팅
### Issue 1: Docker Desktop 미실행
**증상**:
```
ERROR: error during connect: open //./pipe/dockerDesktopLinuxEngine:
The system cannot find the file specified.
```
**해결**:
1. Docker Desktop 애플리케이션 시작
2. 시스템 트레이의 Docker 아이콘이 안정화될 때까지 대기
3. `docker ps` 명령으로 정상 동작 확인
### Issue 2: JAR 파일 없음
**증상**:
```
COPY failed: file not found in build context
```
**해결**:
```bash
# JAR 파일 재빌드
./gradlew {service-name}:clean {service-name}:bootJar
# 생성 확인
ls -l {service-name}/build/libs/{service-name}.jar
```
### Issue 3: 플랫폼 불일치
**증상**: K8s 클러스터에서 실행 안됨
**해결**: `--platform linux/amd64` 옵션 사용 (이미 적용됨)
## 10. 요약
### ✅ 완료된 작업
1. ✅ 6개 서비스의 bootJar 설정 완료
2. ✅ Dockerfile-backend 생성 완료
3. ✅ 6개 서비스 JAR 파일 빌드 완료 (총 542MB)
4. ✅ 6개 서비스 Docker 이미지 빌드 완료 (총 6.48GB)
### 📊 최종 서비스 현황
| 서비스 | JAR 빌드 | Docker 이미지 | 이미지 크기 | Image ID | 상태 |
|--------|---------|--------------|-----------|----------|------|
| user-service | ✅ 96MB | ✅ | 1.09GB | 91c511ef86bd | ✅ Ready |
| ai-service | ✅ 94MB | ✅ | 1.08GB | 9477022fa493 | ✅ Ready |
| event-service | ✅ 94MB | ✅ | 1.08GB | add81de69536 | ✅ Ready |
| content-service | ✅ 78MB | ✅ | 1.01GB | aa9cc16ad041 | ✅ Ready |
| participation-service | ✅ 85MB | ✅ | 1.04GB | 9b044a3854dd | ✅ Ready |
| analytics-service | ✅ 95MB | ✅ | 1.08GB | ac569de42545 | ✅ Ready |
| distribution-service | ❌ | ❌ | - | - | 소스 미구현 |
### 🎯 빌드 성능 메트릭
- **JAR 빌드 시간**: 27초
- **Docker 이미지 빌드**: 병렬 실행으로 약 13초
- **총 소요 시간**: 약 40초
- **빌드 성공률**: 100% (6/6 서비스)
### 🚀 다음 단계 권장사항
1. **컨테이너 레지스트리 푸시** (예: Azure ACR, Docker Hub)
2. **Kubernetes 배포 매니페스트 작성**
3. **CI/CD 파이프라인 구성** (GitHub Actions 또는 Jenkins)
4. **모니터링 및 로깅 설정**
---
**작성일**: 2025-10-29 09:50 KST
**작성자**: DevOps Engineer (송근정 "데브옵스 마스터")
**빌드 완료**: ✅ 모든 서비스 이미지 빌드 성공
- 모든 이미지는 linux/amd64 플랫폼용으로 빌드됨
- 보안을 위해 비root 사용자(k8s)로 실행 구성
- Multi-stage 빌드로 이미지 크기 최적화
- Java 23 (OpenJDK) 기반 런타임 사용
- content-service(Python)는 별도의 Dockerfile로 빌드 필요

View File

@ -0,0 +1,52 @@
version: '3.8'
services:
event-service:
image: acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service:latest
container_name: event-service
ports:
- "8082:8082"
environment:
# Server Configuration
- SERVER_PORT=8082
# PostgreSQL Configuration (필수)
- DB_HOST=${DB_HOST:-your-postgresql-host}
- DB_PORT=${DB_PORT:-5432}
- DB_NAME=${DB_NAME:-eventdb}
- DB_USERNAME=${DB_USERNAME:-eventuser}
- DB_PASSWORD=${DB_PASSWORD:-your-db-password}
- DDL_AUTO=${DDL_AUTO:-update}
# Redis Configuration (필수)
- REDIS_HOST=${REDIS_HOST:-your-redis-host}
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_PASSWORD=${REDIS_PASSWORD:-your-redis-password}
# Kafka Configuration (필수)
- KAFKA_BOOTSTRAP_SERVERS=${KAFKA_BOOTSTRAP_SERVERS:-your-kafka-host:9092}
# JWT Configuration (필수 - 최소 32자)
- JWT_SECRET=${JWT_SECRET:-your-jwt-secret-key-minimum-32-characters-required}
# Microservice URLs (선택)
- CONTENT_SERVICE_URL=${CONTENT_SERVICE_URL:-http://content-service:8083}
- DISTRIBUTION_SERVICE_URL=${DISTRIBUTION_SERVICE_URL:-http://distribution-service:8086}
# Logging Configuration (선택)
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- SQL_LOG_LEVEL=${SQL_LOG_LEVEL:-WARN}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8082/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
- kt-event-network
networks:
kt-event-network:
driver: bridge

View File

@ -0,0 +1,46 @@
#!/bin/bash
# Event Service Docker 실행 스크립트
# 실제 환경에 맞게 환경변수 값을 수정하세요
docker run -d \
--name event-service \
-p 8082:8082 \
--restart unless-stopped \
\
# 서버 설정
-e SERVER_PORT=8082 \
\
# PostgreSQL 설정 (필수)
-e DB_HOST=your-postgresql-host \
-e DB_PORT=5432 \
-e DB_NAME=eventdb \
-e DB_USERNAME=eventuser \
-e DB_PASSWORD=your-db-password \
-e DDL_AUTO=update \
\
# Redis 설정 (필수)
-e REDIS_HOST=your-redis-host \
-e REDIS_PORT=6379 \
-e REDIS_PASSWORD=your-redis-password \
\
# Kafka 설정 (필수)
-e KAFKA_BOOTSTRAP_SERVERS=your-kafka-host:9092 \
\
# JWT 설정 (필수 - 최소 32자)
-e JWT_SECRET=your-jwt-secret-key-minimum-32-characters-required \
\
# 마이크로서비스 연동 (선택)
-e CONTENT_SERVICE_URL=http://content-service:8083 \
-e DISTRIBUTION_SERVICE_URL=http://distribution-service:8086 \
\
# 로깅 설정 (선택)
-e LOG_LEVEL=INFO \
-e SQL_LOG_LEVEL=WARN \
\
# 이미지
acrdigitalgarage01.azurecr.io/kt-event-marketing/event-service:latest
echo "Event Service 컨테이너 시작됨"
echo "헬스체크: curl http://localhost:8082/actuator/health"
echo "로그 확인: docker logs -f event-service"

View File

@ -42,21 +42,21 @@ spec:
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health
path: /api/v1/ai-service/actuator/health
port: 8083
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
path: /api/v1/ai-service/actuator/health/readiness
port: 8083
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/ai-service/actuator/health/liveness
port: 8083
initialDelaySeconds: 30
periodSeconds: 10

View File

@ -42,21 +42,21 @@ spec:
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/analytics/actuator/health/liveness
port: 8086
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 30
livenessProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/analytics/actuator/health/liveness
port: 8086
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
path: /api/v1/analytics/actuator/health/readiness
port: 8086
initialDelaySeconds: 0
periodSeconds: 10

View File

@ -6,7 +6,7 @@ metadata:
data:
# Redis Configuration
REDIS_ENABLED: "true"
REDIS_HOST: "redis"
REDIS_HOST: "redis-node-0.redis-headless"
REDIS_PORT: "6379"
REDIS_TIMEOUT: "2000ms"
REDIS_POOL_MAX: "8"
@ -20,7 +20,7 @@ data:
EXCLUDE_REDIS: ""
# CORS Configuration
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://kt-event-marketing.20.214.196.128.nip.io"
CORS_ALLOWED_ORIGINS: "http://localhost:8081,http://localhost:8082,http://localhost:8083,http://localhost:8084,http://kt-event-marketing.20.214.196.128.nip.io,http://kt-event-marketing-api.20.214.196.128.nip.io,http://*.20.214.196.128.nip.io,https://kt-event-marketing.20.214.196.128.nip.io,https://kt-event-marketing-api.20.214.196.128.nip.io,https://*.20.214.196.128.nip.io"
CORS_ALLOWED_METHODS: "GET,POST,PUT,DELETE,OPTIONS,PATCH"
CORS_ALLOWED_HEADERS: "*"
CORS_ALLOW_CREDENTIALS: "true"

View File

@ -89,18 +89,9 @@ spec:
port:
number: 80
# Analytics Service - Event Analytics
- path: /api/v1/events/([0-9]+)/analytics
pathType: ImplementationSpecific
backend:
service:
name: analytics-service
port:
number: 80
# Analytics Service - User Analytics
- path: /api/v1/users/([0-9]+)/analytics
pathType: ImplementationSpecific
# Analytics Service
- path: /api/v1/analytics
pathType: Prefix
backend:
service:
name: analytics-service
@ -108,7 +99,7 @@ spec:
number: 80
# Distribution Service
- path: /distribution
- path: /api/v1/distribution
pathType: Prefix
backend:
service:

View File

@ -9,7 +9,7 @@ stringData:
AZURE_STORAGE_CONNECTION_STRING: "DefaultEndpointsProtocol=https;AccountName=blobkteventstorage;AccountKey=tcBN7mAfojbl0uGsOpU7RNuKNhHnzmwDiWjN31liSMVSrWaEK+HHnYKZrjBXXAC6ZPsuxUDlsf8x+AStd++QYg==;EndpointSuffix=core.windows.net"
# Replicate API Token
REPLICATE_API_TOKEN: ""
REPLICATE_API_TOKEN: "r8_BsGCJtAg5U5kkMBXSe3pgMkPufSKnUR4NY9gJ"
# HuggingFace API Token
HUGGINGFACE_API_TOKEN: ""

View File

@ -42,21 +42,21 @@ spec:
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health
path: /api/v1/distribution/actuator/health
port: 8085
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
path: /api/v1/distribution/actuator/health/readiness
port: 8085
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/distribution/actuator/health/liveness
port: 8085
initialDelaySeconds: 30
periodSeconds: 10

View File

@ -42,21 +42,21 @@ spec:
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health
path: /api/v1/events/actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 30
readinessProbe:
httpGet:
path: /actuator/health/readiness
path: /api/v1/events/actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/events/actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10

View File

@ -42,21 +42,21 @@ spec:
memory: "1024Mi"
startupProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/participations/actuator/health/liveness
port: 8084
initialDelaySeconds: 60
periodSeconds: 10
failureThreshold: 30
livenessProbe:
httpGet:
path: /actuator/health/liveness
path: /api/v1/participations/actuator/health/liveness
port: 8084
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
path: /api/v1/participations/actuator/health/readiness
port: 8084
initialDelaySeconds: 0
periodSeconds: 10

Some files were not shown because too many files have changed in this diff Show More