# 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) — 환경변수 및 시크릿 관리
