티스토리 뷰
쿠버네티스 실습: SaaS형 API 플랫폼 구축 — 테넌트별 인증, OIDC, Redis, Gateway 통합
octo54 2025. 11. 12. 17:12쿠버네티스 실습: SaaS형 API 플랫폼 구축 — 테넌트별 인증, OIDC, Redis, Gateway 통합
앞선 글에서는 멀티 테넌트 SaaS 아키텍처를 통해
팀(또는 고객) 단위의 리소스·비용·보안 정책을 완벽히 분리한 Multi-Tenant Kubernetes Cluster를 구축했습니다.
이제 우리는 실제 “서비스 형태의 SaaS 플랫폼”으로 발전시켜야 합니다.
이번 글의 목표는 다음과 같습니다.
“각 테넌트별로 고유한 API Key와 인증 토큰을 발급받고,
Istio Gateway를 통해 접근 제어와 트래픽 모니터링을 수행하는 SaaS형 API 서비스 플랫폼을 구축한다.”
1) 전체 아키텍처
[Client (Tenant A/B)]
│
▼
[Istio Ingress Gateway]
│
├── [Auth Service (NestJS + OIDC)]
├── [Redis Cache (API Key / Token)]
├── [Backend APIs (Tenant-Specific Namespace)]
└── [PostgreSQL + Prisma]
핵심 구성 요소
구성 요소 역할
| NestJS Auth Service | 테넌트별 로그인 및 토큰 발급 |
| OIDC Provider (Keycloak / Auth0 / Google) | 인증 연동 |
| Redis | API Key 캐싱 및 만료 관리 |
| Istio Gateway + VirtualService | API 라우팅 및 인증 필터 |
| Prisma + PostgreSQL | 테넌트 및 사용자 메타데이터 저장 |
2) Auth Service 설계 (NestJS + OIDC)
OIDC 기반 로그인 + API Key 발급 기능을 구현합니다.
auth.module.ts
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1h' },
}),
],
providers: [AuthService],
controllers: [AuthController],
})
export class AuthModule {}
auth.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
async login(@Body() { tenantId, email, password }: any) {
return await this.authService.login(tenantId, email, password);
}
@Post('apikey')
async generateApiKey(@Body('tenantId') tenantId: string) {
return await this.authService.issueApiKey(tenantId);
}
}
auth.service.ts
import { Injectable } from '@nestjs/common';
import * as jwt from 'jsonwebtoken';
import { v4 as uuid } from 'uuid';
import { RedisService } from './redis.service';
@Injectable()
export class AuthService {
constructor(private redis: RedisService) {}
async login(tenantId: string, email: string, password: string) {
// OIDC 연동 부분 (예: Keycloak)
const payload = { tenantId, email, role: 'user' };
const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });
await this.redis.set(`token:${tenantId}:${email}`, token, 3600);
return { access_token: token };
}
async issueApiKey(tenantId: string) {
const apiKey = uuid();
await this.redis.set(`apikey:${tenantId}`, apiKey, 86400);
return { apiKey };
}
}
3) Redis 구성 (API Key 캐시)
redis-values.yaml
architecture: standalone
auth:
enabled: false
설치:
helm install redis bitnami/redis -f redis-values.yaml -n team-a
테넌트별로 키 네임스페이스 구분:
apikey:tenant-a
apikey:tenant-b
token:tenant-a:user1
4) Istio Gateway + VirtualService 설정
api-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: saas-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*.api.fuelstation.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tenant-route
namespace: istio-system
spec:
hosts:
- "*.api.fuelstation.com"
http:
- match:
- headers:
x-api-key:
exact: "VALID_API_KEY"
route:
- destination:
host: backend.team-a.svc.cluster.local
port:
number: 3000
- match:
- headers:
x-api-key:
exact: "ANOTHER_KEY"
route:
- destination:
host: backend.team-b.svc.cluster.local
port:
number: 3000
→ 각 테넌트는 자신의 API Key로만 접근 가능.
5) Prisma 스키마 — 테넌트별 계정 관리
schema.prisma
model Tenant {
id String @id @default(uuid())
name String
apiKey String @unique
users User[]
}
model User {
id String @id @default(uuid())
email String @unique
password String
tenantId String
Tenant Tenant @relation(fields: [tenantId], references: [id])
}
6) 요청 흐름
1️⃣ 클라이언트가 /auth/login 요청 → OIDC 인증 수행
2️⃣ NestJS가 JWT를 발급하고 Redis에 캐시
3️⃣ 클라이언트가 x-api-key를 포함하여 API 호출
4️⃣ Istio Gateway가 API Key 매칭 후 해당 테넌트 네임스페이스로 라우팅
5️⃣ 각 Backend는 Vault에서 테넌트별 Secret 로드
6️⃣ Kubecost와 AIOps가 테넌트 단위 자원 사용량 모니터링
7) 보안 강화
- Vault Secret Engine → 테넌트별 DB 비밀번호 분리
- OPA Policy → 잘못된 API Key 요청 즉시 차단
- JWT Expiration + Refresh Token Flow → 세션 탈취 방지
- Redis TTL → API Key 자동 만료 관리
8) API Key 회수 및 만료 자동화
cleanup-job.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: api-key-cleanup
spec:
schedule: "0 */6 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: cleanup
image: bitnami/redis
command: ["redis-cli"]
args: ["--scan", "--pattern", "apikey:*", "|", "xargs", "del"]
restartPolicy: OnFailure
→ 만료된 키를 자동 삭제하여 보안 유지.
9) 실습 결과
시나리오 결과
| Tenant A 유효한 API Key로 요청 | 200 OK |
| Tenant B API Key로 A의 API 호출 | 403 Forbidden |
| 만료된 Key로 접근 | 401 Unauthorized |
| 잘못된 OIDC 토큰 | Istio AuthFilter 차단 |
| Vault Secret Scope 위반 | 접근 거부 |
10) 정리
- NestJS + OIDC: 사용자 인증 및 JWT 발급
- Redis: Token 및 API Key 캐시
- Istio Gateway: API Key 기반 라우팅
- Vault: Secret 스코프 분리
- Prisma + PostgreSQL: 테넌트 데이터 관리
결과적으로,
단일 쿠버네티스 클러스터에서 여러 SaaS 고객이 각자 인증, 데이터, 비용, 보안을 완전히 분리된 상태로 사용할 수 있는
Multi-Tenant SaaS API 플랫폼이 완성되었습니다.
다음 글에서는 이 플랫폼 위에 실시간 모니터링 대시보드 (Grafana + Prometheus + Loki + Redis Stream) 를 연결해
테넌트별 API 사용량, 요청 속도, 에러율, 비용지표 시각화를 구현하겠습니다.
쿠버네티스,SaaS,API플랫폼,NestJS,OIDC,Redis,Istio,Prisma,멀티테넌트,보안,K8s실습
✅ 참고할 최신 자료
- Kubernetes 공식 문서의 멀티테넌시 개념: 네임스페이스 기반 분리, 자원 격리, 보안 등 다양한 구현 전략 정리되어 있습니다. (Kubernetes)
- 멀티테넌트 SaaS 설계의 베스트 프랙티스: 네임스페이스 분리, 리소스쿼터, 네트워크폴리시 등을 통해 격리하는 방법이 실제 사례로 설명되어 있습니다. (Gcore)
- 멀티테넌시 아키텍처 3가지 접근법 (클러스터 별, 네임스페이스 별, 하이브리드) 설명된 글입니다. (Spectro Cloud)
⚠️ 보완/강화 제안
- API Key 인증 흐름: API Key와 JWT 혼용 설계는 좋지만, 실제 운영 시에는 “Key 발급/만료/재발급/회수” 프로세스, Key 유출 시 대응 전략 등을 추가하면 더 완성도 높습니다.
- 테넌트별 namespace 설계: 네임스페이스 기반 격리는 좋지만 “노이즈 네이버(noisy neighbor)”, “리소스 고갈 테넌트가 다른 테넌트에 영향 주는 리스크” 등이 있어서 리소스쿼터나 노드셋 격리 전략을 언급하면 좋습니다. (Spectro Cloud)
- Gateway 라우팅 및 인증 필터링: API Key 헤더 기반 매칭 외에도 테넌트별 리밋(rate limit), IP 제한, 서브도메인 분리 등 추가 보완이 있으면 좋습니다.
- 보안 및 데이터 격리: Redis 캐시, PostgreSQL 데이터베이스, Vault 시크릿 등이 테넌트별로 명확히 분리되어야 하며, “한 테넌트의 데이터가 다른 테넌트로 넘어가는 리스크”에 대해 경고하면 좋습니다.
- 운영 자동화: 신규 테넌트 온보딩(네임스페이스 + 리소스쿼터 + Argo CD 프로젝트 자동 생성) 흐름도 포함하면 독자가 따라하기 쉬워집니다.
'project > 맥미니로 시작하는 쿠버네티스' 카테고리의 다른 글
| 쿠버네티스 실습: 테넌트별 Rate Limiting & Quota Enforcement (0) | 2025.11.17 |
|---|---|
| 쿠버네티스 실습: 테넌트별 실시간 관측(Observability) 대시보드 구축 (0) | 2025.11.13 |
| 쿠버네티스 실습: 멀티 테넌트 SaaS 아키텍처 — 팀별 리소스 격리, 청구, 정책기반 자동화 (0) | 2025.11.06 |
| 쿠버네티스 실습: AI 기반 자율 확장(Auto-Scaling)과 비용 최적화 — 예측형 인프라 운영 설계 (0) | 2025.11.04 |
| 쿠버네티스 실습: AI 기반 이상 탐지(AIOps) — Prometheus + Loki + Falco 로그로 학습하는 지능형 운영 자동화 (0) | 2025.11.03 |
- Total
- Today
- Yesterday
- rag
- 쿠버네티스
- Express
- JAX
- Next.js
- seo 최적화 10개
- llm
- Redis
- SEO최적화
- NestJS
- Prisma
- REACT
- LangChain
- 백엔드개발
- DevOps
- 웹개발
- Docker
- fastapi
- JWT
- Python
- nextJS
- 개발블로그
- 딥러닝
- CI/CD
- node.js
- PostgreSQL
- kotlin
- ai철학
- 생성형AI
- flax
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |

