Files
tasteby/docs/cicd-architecture.md
2026-03-09 23:53:02 +09:00

406 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasteby CI/CD 파이프라인 & 전체 아키텍처
## 전체 시스템 아키텍처
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Internet │
│ www.tasteby.net │
└──────────────────────────────┬──────────────────────────────────────────┘
┌─────────▼──────────┐
│ Namecheap DNS │
│ A → LB External IP │
└─────────┬──────────┘
┌──────────────────────────────▼──────────────────────────────────────────┐
│ OCI Load Balancer (NLB) │
│ (Nginx Ingress Controller가 자동 생성) │
└──────────────────────────────┬──────────────────────────────────────────┘
┌──────────────────────────────▼──────────────────────────────────────────┐
│ OKE Cluster (tasteby-cluster) │
│ ap-seoul-1 │ ARM64 × 2 노드 (2CPU/8GB 각) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Nginx Ingress Controller (ingress-nginx) │ │
│ │ │ │
│ │ TLS termination (Let's Encrypt via cert-manager) │ │
│ │ tasteby.net → www.tasteby.net 리다이렉트 │ │
│ │ │ │
│ │ /api/* ──→ backend Service :8000 │ │
│ │ /* ──→ frontend Service :3001 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── namespace: tasteby ─────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌────────────────┐ │ │
│ │ │ backend (×1) │ │ frontend (×1) │ │ redis (×1) │ │ │
│ │ │ Spring Boot 3 │ │ Next.js 15 │ │ Redis 7 │ │ │
│ │ │ Java 21 │ │ standalone mode │ │ alpine │ │ │
│ │ │ :8000 │ │ :3001 │ │ :6379 │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ ┌─ Volumes ─────┐│ └──────────────────┘ └────────────────┘ │ │
│ │ │ │ oracle-wallet ││ │ │
│ │ │ │ oci-config ││ │ │
│ │ │ └───────────────┘│ │ │
│ │ └──────────────────┘ │ │
│ │ │ │
│ │ ┌─── ConfigMap / Secrets ──────────────────────────────────────┐ │ │
│ │ │ tasteby-config : REDIS_HOST, OCI endpoints, 등 │ │ │
│ │ │ tasteby-secrets : DB credentials, API keys, JWT │ │ │
│ │ │ oracle-wallet : cwallet.sso, tnsnames.ora, keystore.jks │ │ │
│ │ │ oci-config : OCI API config + PEM key │ │ │
│ │ │ ocir-secret : OCIR 이미지 Pull 인증 │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── namespace: cert-manager ──┐ ┌─── namespace: ingress-nginx ──┐ │
│ │ cert-manager (×3 pods) │ │ ingress-nginx-controller │ │
│ │ ClusterIssuer: letsencrypt │ │ (NLB 자동 생성) │ │
│ └──────────────────────────────┘ └────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────▼──────────┐
│ Oracle ADB 23ai │
│ (mTLS via Wallet) │
└────────────────────┘
```
## 외부 서비스 연동
```
backend (Spring Boot)
├─→ Oracle ADB 23ai : JDBC + mTLS (Wallet)
├─→ OCI GenAI (Chat) : 식당 정보 추출, 음식 태그, 평가 생성
├─→ OCI GenAI (Embed) : 벡터 임베딩 (Cohere embed-v4)
├─→ Google Maps Places API : 식당 검색, 좌표 조회
├─→ YouTube Data API v3 : 채널/영상 정보 조회
└─→ Redis : 캐시 (API 응답, 검색 결과)
frontend (Next.js)
├─→ Google Maps JavaScript API : 지도 렌더링
└─→ Google OAuth 2.0 : 사용자 인증
```
---
## CI/CD 파이프라인
### 파이프라인 개요
```
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐ ┌─────────┐
│ 개발자 │────→│ OCI Code Repo │────→│ OCI DevOps Build │────→│ OKE │
│ git push │ │ (tasteby) │ │ Pipeline │ │ 배포 │
└──────────┘ └──────────────────┘ └──────────────────────┘ └─────────┘
```
### 상세 흐름
```
1. 코드 푸시
┌──────────────┐
│ git push oci │ ← OCI Code Repository remote
└──────┬───────┘
2. 빌드 파이프라인 (OCI DevOps Build Pipeline)
┌───────────────────────────────────────────────────────────┐
│ │
│ Stage 1: Managed Build │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ build_spec.yaml 실행 │ │
│ │ │ │
│ │ 1) IMAGE_TAG 생성 (BuildRunID + timestamp) │ │
│ │ 2) docker build --platform linux/arm64 │ │
│ │ - backend-java/Dockerfile → backend image │ │
│ │ - frontend/Dockerfile → frontend image │ │
│ │ 3) Output: BACKEND_IMAGE, FRONTEND_IMAGE │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Stage 2: Deliver Artifacts │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Docker images → OCIR 푸시 │ │
│ │ │ │
│ │ icn.ocir.io/idyhsdamac8c/tasteby/backend:TAG │ │
│ │ icn.ocir.io/idyhsdamac8c/tasteby/frontend:TAG │ │
│ └──────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
3. 배포 (수동 또는 deploy.sh)
┌───────────────────────────────────────────────────────────┐
│ kubectl set image deployment/backend backend=IMAGE:TAG │
│ kubectl set image deployment/frontend frontend=IMAGE:TAG │
│ kubectl rollout status ... │
│ │
│ git tag -a "vX.Y.Z" -m "Deploy: ..." │
│ git push origin "vX.Y.Z" │
└───────────────────────────────────────────────────────────┘
```
### Git Remote 구성
```
origin → Gitea (gittea.cloud-handson.com) ← 소스 코드 관리
oci → OCI Code Repository ← CI/CD 트리거용
```
두 리모트에 모두 push하여 소스와 빌드를 동기화합니다.
### OCI IAM 권한 설정 (빌드/배포용)
OCI DevOps Build Pipeline이 코드 레포, OCIR, 시크릿 등에 접근하려면 **Dynamic Group**과 **IAM Policy**가 필요합니다.
#### Dynamic Group
| 이름 | 설명 |
|------|------|
| `tasteby-build-pipeline` | DevOps 빌드/배포 파이프라인 리소스 |
**Matching Rule:**
```
ANY {
resource.type = 'devopsbuildpipeline',
resource.type = 'devopsrepository',
resource.type = 'devopsdeploypipeline',
resource.type = 'devopsconnection'
}
```
#### IAM Policy
| 이름 | 설명 |
|------|------|
| `tasteby-devops-policy` | DevOps 파이프라인 리소스 접근 권한 |
**Policy Statements:**
```
Allow dynamic-group tasteby-build-pipeline to manage devops-family in tenancy
Allow dynamic-group tasteby-build-pipeline to manage repos in tenancy
Allow dynamic-group tasteby-build-pipeline to read secret-family in tenancy
Allow dynamic-group tasteby-build-pipeline to manage generic-artifacts in tenancy
Allow dynamic-group tasteby-build-pipeline to use ons-topics in tenancy
```
> **참고**: IAM 정책은 적용 후 전파에 최대 수 분이 걸릴 수 있습니다.
> 빌드 실행 시 `RelatedResourceNotAuthorizedOrNotFound` 오류가 나면 정책 전파를 기다린 후 재시도하세요.
#### OCI Code Repository 인증 (HTTPS)
```
Username: <tenancy-name>/oracleidentitycloudservice/<oci-username>
Password: OCI Auth Token (User Settings에서 생성)
```
```bash
# Git remote 추가 예시
git remote add oci https://devops.scmservice.ap-seoul-1.oci.oraclecloud.com/namespaces/<namespace>/projects/tasteby/repositories/tasteby
```
### build_spec.yaml 구조
```yaml
# OCI DevOps Build Pipeline 설정
version: 0.1
component: build
shell: bash
env:
variables:
REGISTRY: "icn.ocir.io/idyhsdamac8c/tasteby"
exportedVariables:
- IMAGE_TAG # 다음 stage에서 사용
- BACKEND_IMAGE
- FRONTEND_IMAGE
steps:
- Set image tag # BuildRunID + timestamp 조합
- Build backend image # backend-java/Dockerfile, ARM64
- Build frontend image # frontend/Dockerfile, ARM64
outputArtifacts:
- backend-image (DOCKER_IMAGE)
- frontend-image (DOCKER_IMAGE)
```
---
## 로컬 배포 (deploy.sh)
OCI DevOps 없이 로컬에서 직접 배포할 때 사용합니다.
```bash
# 전체 배포
./deploy.sh "초기 배포"
# 백엔드만
./deploy.sh --backend-only "API 버그 수정"
# 프론트엔드만
./deploy.sh --frontend-only "UI 개선"
# 드라이런
./deploy.sh --dry-run "테스트"
```
**deploy.sh 동작:**
1. 최신 git tag에서 다음 버전 계산 (v0.1.X → v0.1.X+1)
2. Docker build (ARM64) + OCIR push
3. `kubectl set image` → rollout 대기
4. git tag 생성 + push
---
## Docker 이미지 빌드
### Backend (Spring Boot)
```
eclipse-temurin:21-jdk (build)
└─ gradlew bootJar
└─ app.jar
eclipse-temurin:21-jre (runtime)
└─ java -XX:MaxRAMPercentage=75.0 -jar app.jar
└─ EXPOSE 8000
```
### Frontend (Next.js)
```
node:22-alpine (build)
└─ npm ci + npm run build
└─ NEXT_PUBLIC_* 빌드 시 주입 (build args)
node:22-alpine (runtime)
└─ .next/standalone + .next/static + public/
└─ node server.js
└─ EXPOSE 3001
```
---
## K8s 리소스 구성
### 파일 구조
```
k8s/
├── namespace.yaml # tasteby namespace
├── configmap.yaml # 비밀이 아닌 설정
├── secrets.yaml.template # 시크릿 템플릿 (실제 파일은 .gitignore)
├── redis-deployment.yaml # Redis 7 + Service
├── backend-deployment.yaml # Spring Boot + Service
├── frontend-deployment.yaml # Next.js + Service
├── ingress.yaml # Nginx Ingress + TLS
└── cert-manager/
└── cluster-issuer.yaml # Let's Encrypt ClusterIssuer
```
### 리소스 할당
| Pod | replicas | CPU req/lim | Memory req/lim |
|-----|----------|-------------|----------------|
| backend | 1 | 500m / 1 | 768Mi / 1536Mi |
| frontend | 1 | 200m / 500m | 256Mi / 512Mi |
| redis | 1 | 100m / 200m | 128Mi / 256Mi |
| ingress-controller | 1 | 100m / 200m | 128Mi / 256Mi |
| cert-manager (×3) | 1 each | 50m / 100m | 64Mi / 128Mi |
| **합계** | | **~1.2 CPU** | **~1.6GB** |
클러스터: ARM64 × 2 노드 (4 CPU / 16GB 총) → 여유 충분
### 네트워크 흐름
```
Client → NLB:443 → Ingress Controller → /api/* → backend:8000
→ /* → frontend:3001
backend → redis:6379 (K8s Service DNS, 클러스터 내부)
backend → Oracle ADB (mTLS, Wallet Volume Mount)
backend → OCI GenAI (OCI SDK, oci-config Volume Mount)
backend → Google APIs (API Key, 환경변수)
```
### Volume Mounts (backend)
```
/etc/oracle/wallet/ ← Secret: oracle-wallet
├── cwallet.sso
├── tnsnames.ora
├── sqlnet.ora
├── keystore.jks
├── truststore.jks
└── ojdbc.properties
/root/.oci/ ← Secret: oci-config
├── config
└── oci_api_key.pem
```
---
## 환경 분리
```
┌────────────────────────────┬──────────────────────────────────────┐
│ 개발 (Local) │ 운영 (OKE) │
├────────────────────────────┼──────────────────────────────────────┤
│ backend/.env │ ConfigMap + Secret │
│ frontend/.env.local │ Dockerfile build args │
│ ~/.oci/config │ Secret: oci-config → /root/.oci/ │
│ 로컬 Wallet 디렉토리 │ Secret: oracle-wallet → /etc/oracle/ │
│ Redis: 192.168.0.147:6379 │ Redis: redis:6379 (K8s DNS) │
│ PM2로 프로세스 관리 │ K8s Deployment로 관리 │
│ nginx + certbot (SSL) │ Ingress + cert-manager (SSL) │
└────────────────────────────┴──────────────────────────────────────┘
```
**변수명이 동일**하므로 코드 변경 없이 환경만 교체 가능합니다.
자세한 환경변수 목록은 [environment-guide.md](./environment-guide.md) 참고.
---
## OCI 리소스 정보
| 항목 | 값 |
|------|-----|
| 리전 | ap-seoul-1 (Seoul) |
| OCI 프로필 | JOUNGMINKOAWS |
| OKE 클러스터 | tasteby-cluster |
| OCIR Registry | icn.ocir.io/idyhsdamac8c/tasteby |
| DevOps 프로젝트 | tasteby |
| Code Repository | tasteby (OCI DevOps SCM) |
| 도메인 | www.tasteby.net (Namecheap) |
| SSL | Let's Encrypt (cert-manager HTTP-01) |
| 노드 | ARM64 × 2대 (2 CPU / 8GB 각) |
---
## 배포 버전 관리
- 태그 형식: `v0.1.X` (patch 자동 증가)
- deploy.sh가 자동으로 git tag 생성 + push
- 태그 메시지에 배포 대상(backend/frontend)과 이미지 태그 포함
```bash
# 태그 목록 확인
git tag -l 'v*' --sort=-v:refname
# 특정 태그 상세 확인
git tag -n20 v0.1.5
# 롤백
kubectl rollout undo deployment/backend -n tasteby
```
---
## 관련 문서
- [OKE 배포 가이드](./oke-deployment-guide.md) — 인프라 설치 및 배포 절차
- [환경 관리 가이드](./environment-guide.md) — 환경변수 및 시크릿 관리