티스토리 뷰
쿠버네티스 실습: SaaS 운영관리(Admin Console) 구축
Next.js + NestJS + RBAC + Grafana API + Stripe Dashboard 통합
앞선 글에서는 SaaS Billing(청구/과금) 시스템을 완벽히 구축해
테넌트별 사용량 → 가격 정책 → Stripe 자동결제까지 이어지는
실제 SaaS 비즈니스 수준의 프로덕션 아키텍처를 완성했습니다.
이번 글에서는 이 모든 구성을 하나의 Admin Console(운영 대시보드) 로 통합합니다.
즉, 운영 관리자가 웹 UI에서:
- 테넌트 생성/삭제
- Price Plan 등록/변경
- API Key 발급/비활성화
- RateLimit 정책 수정
- 사용량 그래프 조회(실시간 Grafana)
- 청구내역 조회 및 Stripe invoice 연동
- 보안정책 변경(OPA/Kyverno 템플릿 적용)
까지 모두 관리할 수 있게 만드는 실습입니다.
1. 전체 구조
[Admin Portal (Next.js)]
│
▼
[NestJS Admin API] ─── Prisma ─── PostgreSQL
│ │
│ ├── 테넌트, 요금제, API Key, 정책 저장
│
├── Stripe API ↔ Billing
│
├── Grafana API ↔ 실시간 모니터링
│
├── ArgoCD API ↔ 테넌트별 배포 제어
│
└── Vault API ↔ Secret, Token 관리
Admin Console은 단순 UI가 아니라
SaaS 전체를 운영 가능한 통합 제어판(Control Plane) 역할입니다.
2. 관리자를 위한 RBAC 설계
관리자 계층도 권한을 분리해야 합니다.
Role 권한
| super_admin | 모든 기능(정책/요금/시스템) |
| billing_admin | 요금제, 인보이스 관리 |
| ops_admin | RateLimit/Quota, 인프라 관리 |
| viewer | 대시보드 & 조회만 |
NestJS에서 RBAC Guard 구현:
roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(ctx: ExecutionContext): boolean {
const required = this.reflector.get<string[]>('roles', ctx.getHandler());
if (!required) return true;
const req = ctx.switchToHttp().getRequest();
const role = req.user.role;
return required.includes(role);
}
}
@Roles(‘super_admin’) 같은 데코레이터로 제어.
3. Admin API: 테넌트 생성
테넌트를 생성하면 동시에 다음을 자동화해야 합니다.
- DB에 Tenant row 생성
- Stripe Customer 생성
- Namespace(team-a) 생성(Kubernetes)
- ArgoCD Project 생성
- Vault Secret Path 생성
- 기본 RateLimit/Quota 정책 설정
- 기본 Price Plan 적용
tenant.controller.ts
@Post()
@Roles('super_admin')
async createTenant(@Body() dto: CreateTenantDto) {
const tenant = await this.tenantService.create(dto);
await this.infraService.createNamespace(tenant.id);
await this.infraService.createArgoProject(tenant.id);
await this.billingService.createStripeCustomer(tenant.id, dto.email);
await this.policyService.applyDefaultRateLimit(tenant.id);
return tenant;
}
4. Kubernetes 연동 — 관리자 페이지에서 Namespace 생성
infra.service.ts
import k8s = require('@kubernetes/client-node');
@Injectable()
export class InfraService {
private kc = new k8s.KubeConfig();
private k8sApi: k8s.CoreV1Api;
constructor() {
this.kc.loadFromDefault();
this.k8sApi = this.kc.makeApiClient(k8s.CoreV1Api);
}
async createNamespace(tenant: string) {
const ns = { metadata: { name: `tenant-${tenant}` }};
await this.k8sApi.createNamespace(ns);
}
}
Kubernetes API 사용은 공식 라이브러리(@kubernetes/client-node) 기준.
5. API Key 발급/회수
Admin Console에서 버튼 클릭 → Key 재발급/비활성화
apikey.service.ts
async rotateKey(tenantId: string) {
const newKey = randomUUID();
await this.redis.set(`apikey:${tenantId}`, newKey);
return newKey;
}
NestJS Controller:
@Post('rotate/:id')
@Roles('ops_admin')
async rotate(@Param('id') tenantId: string) {
return this.apikeyService.rotateKey(tenantId);
}
Next.js UI:
<button onClick={() => rotateKey(tenantId)}>Rotate API Key</button>
6. Price Plan CRUD 화면
NestJS:
@Post("plan")
@Roles('billing_admin')
async createPlan(@Body() dto: CreatePlanDto) {
return this.prisma.plan.create({ data: dto });
}
Next.js에서는 표(View) + 수정UI + 저장:
<form onSubmit={handleSubmit}>
<input value={plan.monthlyBaseFee}/>
<input value={plan.pricePerRequest}/>
<button type="submit">Save</button>
</form>
7. Stripe Invoice 조회
NestJS:
@Get('invoices/:tenantId')
@Roles('billing_admin','viewer')
async getInvoices(@Param('tenantId') tid: string) {
const tenant = await this.prisma.tenant.findUnique({ where: { id: tid }});
return stripe.invoices.list({ customer: tenant.stripeCustomerId });
}
Next.js에서 인보이스 목록 렌더:
{invoices.data.map(inv => (
<tr key={inv.id}>
<td>{inv.number}</td>
<td>{inv.amount_due / 100}</td>
<td>{inv.status}</td>
</tr>
))}
8. Grafana API 연동(사용량/지표 실시간 조회)
Grafana API Token 필요.
NestJS Service:
async getGrafanaPanel(panelId: number) {
return axios.get(`https://grafana/api/panels/${panelId}`, {
headers: { Authorization: `Bearer ${process.env.GRAFANA_TOKEN}` }
});
}
Next.js:
<img src={`/admin/grafana/panel/${panelId}`} />
또는 iframe:
<iframe src={`${GRAFANA_URL}/d/${dashboardId}?orgId=1&refresh=5s`} />
9. RateLimit 정책 수정(Admin UI → Istio EnvoyFilter Patch)
관리 UI에서 JSON Patch로 EnvoyFilter 수정.
NestJS:
async updateRateLimit(tenantId: string, tokens: number, perMinutes: number) {
return this.k8sApi.patchNamespacedCustomObject(
'networking.istio.io','v1alpha3','istio-system','envoyfilters','tenant-local-ratelimit',
[{ op: "replace", path: "/spec/configPatches/0/patch/value/token_bucket/max_tokens", value: tokens }]
);
}
→ 관리 UI에서 토큰 수 조정 → 즉시 반영됨.
10. Vault Secret 관리
테넌트별 DB 비밀번호/접속 토큰 저장:
await vault.write(`secret/data/${tenantId}/db`, { data: { password: pw }});
Admin UI에서 rotate 버튼 제공 → 즉시 Vault 갱신 → Deployment rollout.
11. 관리자용 로그/이벤트 뷰어 (Loki)
NestJS → Loki API:
async getTenantLogs(tenantId: string) {
const query = `{tenant="${tenantId}"}`;
return axios.get(`${LOKI_URL}/loki/api/v1/query_range`, { params: { query }});
}
Next.js에서 테이블 렌더링:
<pre>{JSON.stringify(logs, null, 2)}</pre>
12. SaaS 운영에서 실제 필요한 메뉴
최종 Admin Console 메뉴 구성 예시:
Dashboard
├── System Status
├── Tenant Overview
Tenants
├── Create Tenant
├── Tenant Settings
├── API Keys
Plans & Billing
├── Price Plan Editor
├── Stripe Invoices
Rate Limit / Quota
├── Current Policies
├── Adjust Rate Limit
├── Quota Setup
Monitoring
├── Grafana Usage Charts
├── Logs (Loki)
├── Alerts (Prometheus)
Security
├── Vault Secrets
├── OPA / Kyverno Policies
Infrastructure
├── Namespace / ArgoCD Projects
├── Deployments & Rollouts
이 구조는 실제 기업용 SaaS 플랫폼 관리자 페이지의 구성과 거의 동일합니다.
13. 운영 시나리오 예시
상황 Admin Console에서 수행
| 신규 고객 온보딩 | 버튼 1번 → Tenant 생성 → Namespace/Plan/Key 자동 생성 |
| 고객이 요금제 변경 요청 | Price Plan 수정 → 다음 결제주기부터 적용 |
| API Key 유출 | Admin → “Key Rotate” 클릭 즉시 반영 |
| 사용량 폭주 | RateLimit 조정 / KEDA로 확장 트리거 |
| 보안 문제 발생 | 로그 조회 → OPA 정책 강화 버튼 실행 |
| 백엔드 장애 | ArgoCD에서 재배포(ReSync) 버튼 클릭 |
14. 정리
이번 글에서 구축한 Admin Console은
단순한 UI가 아니라 SaaS 시스템 전체의 Control Plane 입니다.
✔ 테넌트 관리
✔ API Key 관리
✔ 청구/결제 Stripe 연동
✔ Grafana/Loki/Prometheus 통합 모니터링
✔ Rate Limit & Quota 관리
✔ Vault Secret Management
✔ ArgoCD & Kubernetes 배포 제어
✔ RBAC 기반 관리자 권한 체계
이제 진짜 ‘서비스’로서의 기능이 모두 갖춰졌습니다.
앞으로 DevOps/SRE/보안/비즈니스 의사결정 모두 이 Admin Console에서 이루어지게 됩니다.
다음 글 예고
다음 편에서는 “완전 자동화된 Multi-Tenant Provisioning Pipeline” 을 구축합니다.
신규 고객이 가입하면 → Namespace 생성 → API Key 발급 → RateLimit 적용 → Billing 연동까지
Terraform + ArgoCD + NestJS Provisioner + Vault 로 자동 실행되는
진짜 SaaS 수준의 자동 프로비저닝 시스템을 만들어냅니다.
쿠버네티스,SaaS,AdminConsole,NextJS,NestJS,Stripe,Grafana,Loki,ArgoCD,Vault
✅ 참고할 최신 자료
- Stripe 공식 가이드: SaaS 과금 모델(구독, 사용량 기반 등)을 설명해 있으며, “요금 모델 선택”, “자동 청구”, “안정적 수익 구조” 측면을 잘 정리하고 있습니다. (Stripe)
- Stripe 블로그: SaaS 지불 처리에서 직면하는 문제 및 해결책을 설명한 글 (예: 반복 결제, 실패 대응, 지역별 과금) (Stripe)
- Stripe 활용 사례: 사용 기반 요금 모델(usage-based billing)에서의 설계와 유의사항에 대해 정리되어 있습니다. (iteratorshq.com)
⚠️ 보완 / 강화 제안
아래 항목들을 글에 추가하시면 독자가 실무 구현 시 놓치기 쉬운 부분까지 대비할 수 있어요.
- 요금 모델 선택 및 구조화
글에서 기본요금 + 사용량 단가 구조를 예시로 들었는데, 추가로 “Tiered Pricing”, “Volume Pricing”, “Add-on 기반 요금” 등 실제 SaaS에서 자주 쓰이는 모델을 간단히 정리하면 좋습니다. 참고자료에서도 이 부분이 강조되어 있어요. (iteratorshq.com) - 청구 처리 및 실패 대응 흐름
예: 결제 실패 시 재시도, 카드 유효기간 만료, 구독 해지 시의 처리, 영수증 발행과 세금 계산. Stripe 관련 자료에서 이 부분이 중요하게 다뤄집니다. (Stripe) - 사용량 집계 정확성 및 지연 고려
API 사용량을 Redis Stream → Worker로 집계하는 구조인데, 실제 운영에서는 이벤트 누락, 중복 집계, 지연(latency) 등이 문제가 됩니다. 이 부분을 “데이터 청소(Cleaning) + 중복 제거 + 타임존 고려” 등으로 보완하면 좋습니다. - 투명성 있는 과금 제공
고객(테넌트)에게 과금 내역을 명확히 보여주는 것도 중요합니다 — “사용량 단위(예: API 호출 수) vs 청구 금액” 비교, 무료 티어, 할인 적용 등. Stripe 자료에서도 “사용 단위 정의(clear usage unit)”가 강조되어 있어요. (iteratorshq.com) - 보안/컴플라이언스 측면
결제정보, 고객 정보가 포함되므로 PCI-DSS, 지역별 세금(예: 부가가치세 VAT) 등 준수사항을 간단히 언급하면 신뢰도가 올라갑니다. - 테넌트별 과금 자동화 및 확장 고려사항
다수의 테넌트를 운용할 때 “사용량 증가 → 요금 상승”이 자동으로 반영되도록 설계해야 하며, 테넌트별 과금 이력 저장, 과금 리포트 제공, 테넌트별 요금 정책 변경(업그레이드/다운그레이드) 대응 흐름이 들어가면 더 좋습니다.
'project > 맥미니로 시작하는 쿠버네티스' 카테고리의 다른 글
| 쿠버네티스 실습: AI 기반 AIOps 자동 확장(Auto-Scaling) 엔진 구축 (0) | 2025.12.01 |
|---|---|
| 쿠버네티스 실습: 완전 자동화된 Multi-Tenant Provisioning Pipeline 구축 (0) | 2025.11.25 |
| 쿠버네티스 실습: SaaS 청구·과금(Billing) 시스템 구축 (0) | 2025.11.19 |
| 쿠버네티스 실습: 테넌트별 Rate Limiting & Quota Enforcement (0) | 2025.11.17 |
| 쿠버네티스 실습: 테넌트별 실시간 관측(Observability) 대시보드 구축 (0) | 2025.11.13 |
- Total
- Today
- Yesterday
- REACT
- 딥러닝
- 쿠버네티스
- Python
- CI/CD
- seo 최적화 10개
- Prisma
- node.js
- PostgreSQL
- Express
- rag
- JAX
- 생성형AI
- NestJS
- SEO최적화
- llm
- 웹개발
- kotlin
- nextJS
- ai철학
- flax
- Docker
- Next.js
- LangChain
- DevOps
- fastapi
- 백엔드개발
- JWT
- Redis
- 개발블로그
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
