- Add 파인다이닝/코스 cuisine type to 한식/일식/중식/양식 categories - Change cuisine filter from flat list to grouped optgroup with subcategories - Fix remap-foods/remap-cuisine: add jdbcType=CLOB, fix CLOB LISTAGG, improve retry logic (3 attempts, batch size 5), add error logging - Add OKE deployment: Dockerfiles, K8s manifests, deploy.sh, deployment guide - Add Next.js standalone output for Docker builds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
344 lines
8.0 KiB
Markdown
344 lines
8.0 KiB
Markdown
# Tasteby OKE 배포 가이드
|
||
|
||
## 아키텍처
|
||
|
||
```
|
||
Internet
|
||
│
|
||
▼
|
||
OCI Load Balancer (Nginx Ingress가 자동 생성)
|
||
│
|
||
├─ / → frontend Service (Next.js :3001)
|
||
├─ /api/ → backend Service (Spring Boot :8000)
|
||
│
|
||
├─ cert-manager (Let's Encrypt 인증서 자동 발급/갱신)
|
||
└─ Redis (in-cluster 캐시 :6379)
|
||
```
|
||
|
||
## 인프라 정보
|
||
|
||
| 항목 | 값 |
|
||
|------|-----|
|
||
| 클러스터 | tasteby-cluster |
|
||
| 리전 | ap-seoul-1 (Seoul) |
|
||
| 노드 | ARM64 × 2대 (2 CPU / 8GB 각) |
|
||
| K8s 버전 | v1.34.2 |
|
||
| OCI 프로필 | JOUNGMINKOAWS |
|
||
| OCIR | icn.ocir.io/idyhsdamac8c/tasteby |
|
||
| 도메인 | www.tasteby.net (Namecheap DNS) |
|
||
| SSL | Let's Encrypt (cert-manager + HTTP-01) |
|
||
|
||
## 파일 구조
|
||
|
||
```
|
||
tasteby/
|
||
├── backend-java/Dockerfile
|
||
├── frontend/Dockerfile
|
||
├── k8s/
|
||
│ ├── namespace.yaml
|
||
│ ├── configmap.yaml
|
||
│ ├── secrets.yaml.template ← 실제 secrets.yaml은 .gitignore
|
||
│ ├── redis-deployment.yaml
|
||
│ ├── backend-deployment.yaml
|
||
│ ├── frontend-deployment.yaml
|
||
│ ├── ingress.yaml
|
||
│ └── cert-manager/
|
||
│ └── cluster-issuer.yaml
|
||
└── deploy.sh
|
||
```
|
||
|
||
## 리소스 배분
|
||
|
||
| 파드 | replicas | CPU req/lim | 메모리 req/lim |
|
||
|------|----------|-------------|----------------|
|
||
| backend (Java) | 1 | 500m / 1 | 768Mi / 1536Mi |
|
||
| frontend (Next.js) | 1 | 200m / 500m | 256Mi / 512Mi |
|
||
| redis | 1 | 100m / 200m | 128Mi / 256Mi |
|
||
| ingress-controller | 1 | 100m / 200m | 128Mi / 256Mi |
|
||
| cert-manager (×3) | 1씩 | 50m / 100m | 64Mi / 128Mi |
|
||
| **합계** | | **~1.2 CPU** | **~1.6GB** |
|
||
|
||
전체 클러스터: 4 CPU / 16GB → 여유 충분
|
||
|
||
---
|
||
|
||
## 1단계: 사전 준비
|
||
|
||
### 1.1 kubectl 설정
|
||
|
||
```bash
|
||
# OKE kubeconfig 가져오기
|
||
oci ce cluster create-kubeconfig \
|
||
--cluster-id ocid1.cluster.oc1.ap-seoul-1.aaaaaaaaoqgd2sh6754m5zrwfqaxwrtlqon3dxtdwbbc2dvzbcbou3pf75rq \
|
||
--profile JOUNGMINKOAWS \
|
||
--region ap-seoul-1 \
|
||
--token-version 2.0.0 \
|
||
--kube-endpoint PUBLIC_ENDPOINT
|
||
|
||
# ~/.kube/config의 user args에 --profile JOUNGMINKOAWS 추가 필요
|
||
# 확인
|
||
kubectl get nodes
|
||
```
|
||
|
||
### 1.2 Helm 설치 (없으면)
|
||
|
||
```bash
|
||
brew install helm
|
||
```
|
||
|
||
---
|
||
|
||
## 2단계: 인프라 설치 (1회성)
|
||
|
||
### 2.1 Nginx Ingress Controller
|
||
|
||
```bash
|
||
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
|
||
helm repo update
|
||
|
||
helm install ingress-nginx ingress-nginx/ingress-nginx \
|
||
--namespace ingress-nginx --create-namespace \
|
||
--set controller.service.type=LoadBalancer \
|
||
--set controller.service.annotations."oci\.oraclecloud\.com/load-balancer-type"=nlb
|
||
```
|
||
|
||
설치 후 External IP 확인:
|
||
```bash
|
||
kubectl get svc -n ingress-nginx ingress-nginx-controller -w
|
||
# EXTERNAL-IP가 나오면 Namecheap에서 A 레코드 업데이트
|
||
# www.tasteby.net → <EXTERNAL-IP>
|
||
# tasteby.net → <EXTERNAL-IP>
|
||
```
|
||
|
||
### 2.2 cert-manager
|
||
|
||
```bash
|
||
helm repo add jetstack https://charts.jetstack.io
|
||
helm repo update
|
||
|
||
helm install cert-manager jetstack/cert-manager \
|
||
--namespace cert-manager --create-namespace \
|
||
--set crds.enabled=true
|
||
```
|
||
|
||
### 2.3 ClusterIssuer 생성
|
||
|
||
```bash
|
||
kubectl apply -f k8s/cert-manager/cluster-issuer.yaml
|
||
```
|
||
|
||
---
|
||
|
||
## 3단계: 네임스페이스 및 시크릿 생성 (1회성)
|
||
|
||
### 3.1 네임스페이스
|
||
|
||
```bash
|
||
kubectl apply -f k8s/namespace.yaml
|
||
```
|
||
|
||
### 3.2 OCIR 이미지 Pull Secret
|
||
|
||
```bash
|
||
# OCI Auth Token 필요 (콘솔 > User Settings > Auth Tokens에서 생성)
|
||
kubectl create secret docker-registry ocir-secret \
|
||
--docker-server=icn.ocir.io \
|
||
--docker-username="idyhsdamac8c/<oci-username>" \
|
||
--docker-password="<auth-token>" \
|
||
--docker-email="<email>" \
|
||
-n tasteby
|
||
```
|
||
|
||
### 3.3 앱 시크릿
|
||
|
||
```bash
|
||
# secrets.yaml.template을 복사하여 실제 값 입력
|
||
cp k8s/secrets.yaml.template k8s/secrets.yaml
|
||
# 편집 후
|
||
kubectl apply -f k8s/secrets.yaml
|
||
```
|
||
|
||
### 3.4 Oracle Wallet Secret
|
||
|
||
```bash
|
||
# Wallet 디렉토리의 파일들을 Secret으로 생성
|
||
kubectl create secret generic oracle-wallet \
|
||
--from-file=cwallet.sso=<wallet-dir>/cwallet.sso \
|
||
--from-file=tnsnames.ora=<wallet-dir>/tnsnames.ora \
|
||
--from-file=sqlnet.ora=<wallet-dir>/sqlnet.ora \
|
||
--from-file=keystore.jks=<wallet-dir>/keystore.jks \
|
||
--from-file=truststore.jks=<wallet-dir>/truststore.jks \
|
||
--from-file=ojdbc.properties=<wallet-dir>/ojdbc.properties \
|
||
-n tasteby
|
||
```
|
||
|
||
### 3.5 OCI Config Secret
|
||
|
||
```bash
|
||
# OCI API key config 파일과 PEM 키를 Secret으로 생성
|
||
# config 파일은 K8s용으로 수정 필요 (key_file 경로를 /root/.oci/oci_api_key.pem으로)
|
||
kubectl create secret generic oci-config \
|
||
--from-file=config=<oci-config-for-k8s> \
|
||
--from-file=oci_api_key.pem=<pem-key-file> \
|
||
-n tasteby
|
||
```
|
||
|
||
**참고**: OCI config 파일에서 `key_file` 경로를 컨테이너 내부 마운트 경로로 수정:
|
||
```ini
|
||
[DEFAULT]
|
||
user=ocid1.user.oc1..xxx
|
||
fingerprint=xx:xx:xx
|
||
key_file=/root/.oci/oci_api_key.pem
|
||
tenancy=ocid1.tenancy.oc1..xxx
|
||
region=ap-seoul-1
|
||
```
|
||
|
||
### 3.6 ConfigMap 적용
|
||
|
||
```bash
|
||
kubectl apply -f k8s/configmap.yaml
|
||
```
|
||
|
||
---
|
||
|
||
## 4단계: 앱 배포
|
||
|
||
### 4.1 기본 리소스 배포
|
||
|
||
```bash
|
||
kubectl apply -f k8s/redis-deployment.yaml
|
||
kubectl apply -f k8s/backend-deployment.yaml
|
||
kubectl apply -f k8s/frontend-deployment.yaml
|
||
kubectl apply -f k8s/ingress.yaml
|
||
```
|
||
|
||
### 4.2 OCIR 로그인 (이미지 푸시용)
|
||
|
||
```bash
|
||
docker login icn.ocir.io \
|
||
-u "idyhsdamac8c/<oci-username>" \
|
||
-p "<auth-token>"
|
||
```
|
||
|
||
### 4.3 이미지 빌드 & 배포
|
||
|
||
```bash
|
||
# 전체 배포
|
||
./deploy.sh "초기 배포"
|
||
|
||
# 백엔드만 배포
|
||
./deploy.sh --backend-only "API 버그 수정"
|
||
|
||
# 프론트엔드만 배포
|
||
./deploy.sh --frontend-only "UI 개선"
|
||
|
||
# 드라이런 (실제 실행 안 함)
|
||
./deploy.sh --dry-run "테스트"
|
||
```
|
||
|
||
---
|
||
|
||
## 5단계: DNS 설정
|
||
|
||
Namecheap에서 A 레코드 변경:
|
||
|
||
| Type | Host | Value | TTL |
|
||
|------|------|-------|-----|
|
||
| A | @ | `<LB External IP>` | Automatic |
|
||
| A | www | `<LB External IP>` | Automatic |
|
||
|
||
DNS 전파 후 cert-manager가 자동으로 Let's Encrypt 인증서를 발급합니다.
|
||
|
||
---
|
||
|
||
## 운영 명령어
|
||
|
||
### 상태 확인
|
||
|
||
```bash
|
||
# 파드 상태
|
||
kubectl get pods -n tasteby
|
||
|
||
# 로그 확인
|
||
kubectl logs -f deployment/backend -n tasteby
|
||
kubectl logs -f deployment/frontend -n tasteby
|
||
|
||
# 인증서 상태
|
||
kubectl get certificate -n tasteby
|
||
kubectl describe certificate tasteby-tls -n tasteby
|
||
```
|
||
|
||
### 롤백
|
||
|
||
```bash
|
||
# 이전 버전으로 롤백
|
||
kubectl rollout undo deployment/backend -n tasteby
|
||
kubectl rollout undo deployment/frontend -n tasteby
|
||
|
||
# 특정 리비전으로 롤백
|
||
kubectl rollout history deployment/backend -n tasteby
|
||
kubectl rollout undo deployment/backend --to-revision=2 -n tasteby
|
||
```
|
||
|
||
### 시크릿 업데이트
|
||
|
||
```bash
|
||
# secrets.yaml 수정 후
|
||
kubectl apply -f k8s/secrets.yaml
|
||
# 파드 재시작 (시크릿 변경 반영)
|
||
kubectl rollout restart deployment/backend -n tasteby
|
||
```
|
||
|
||
### 스케일링
|
||
|
||
```bash
|
||
# 백엔드 2개로 확장
|
||
kubectl scale deployment/backend --replicas=2 -n tasteby
|
||
```
|
||
|
||
---
|
||
|
||
## 배포 태그 규칙
|
||
|
||
- 형식: `v0.1.X` (patch 버전 자동 증가)
|
||
- `deploy.sh`가 빌드 → 푸시 → K8s 업데이트 → git tag 생성 → 태그 푸시까지 자동 처리
|
||
- 태그 메시지에 배포 대상(backend/frontend)과 이미지 태그 포함
|
||
|
||
```bash
|
||
# 태그 목록 확인
|
||
git tag -l 'v*' --sort=-v:refname
|
||
|
||
# 특정 태그의 배포 내역 확인
|
||
git tag -n20 v0.1.5
|
||
```
|
||
|
||
---
|
||
|
||
## 트러블슈팅
|
||
|
||
### 이미지 Pull 실패
|
||
```bash
|
||
kubectl describe pod <pod-name> -n tasteby
|
||
# Events에서 ImagePullBackOff 확인 → ocir-secret 점검
|
||
```
|
||
|
||
### DB 연결 실패
|
||
```bash
|
||
kubectl exec -it deployment/backend -n tasteby -- env | grep ORACLE
|
||
# Oracle Wallet 마운트 확인
|
||
kubectl exec -it deployment/backend -n tasteby -- ls /etc/oracle/wallet/
|
||
```
|
||
|
||
### 인증서 발급 안 됨
|
||
```bash
|
||
kubectl get challenges -n tasteby
|
||
kubectl describe challenge -n tasteby
|
||
# DNS A 레코드가 LB IP로 설정되었는지, 80 포트가 열려있는지 확인
|
||
```
|
||
|
||
### OCI GenAI 연결 실패
|
||
```bash
|
||
kubectl exec -it deployment/backend -n tasteby -- cat /root/.oci/config
|
||
# key_file 경로가 /root/.oci/oci_api_key.pem 인지 확인
|
||
```
|