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

19 KiB
Raw Blame History

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 GroupIAM 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에서 생성)
# Git remote 추가 예시
git remote add oci https://devops.scmservice.ap-seoul-1.oci.oraclecloud.com/namespaces/<namespace>/projects/tasteby/repositories/tasteby

build_spec.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 없이 로컬에서 직접 배포할 때 사용합니다.

# 전체 배포
./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 참고.


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)과 이미지 태그 포함
# 태그 목록 확인
git tag -l 'v*' --sort=-v:refname

# 특정 태그 상세 확인
git tag -n20 v0.1.5

# 롤백
kubectl rollout undo deployment/backend -n tasteby

관련 문서