티스토리 뷰

반응형

쿠버네티스 실습: 테넌트별 실시간 관측(Observability) 대시보드 구축

Grafana + Prometheus + Loki + Redis Stream 기반 API 사용량/에러율/비용 모니터링

앞선 글에서는 테넌트별 인증·API Key·Gateway 라우팅으로 구성된
SaaS API 플랫폼 핵심 구조를 완성했습니다.

이제는 실제 운영 단계에서 반드시 필요한 기능인 Observability(관측성) 을 구축합니다.
이번 글의 목표는 다음과 같습니다.

“각 테넌트(Tenant A, Tenant B…)가 자신의 API 사용량, 에러율, 응답 속도, 비용을
실시간으로 시각화하여 확인할 수 있는 대시보드를 제공하는 것.”

즉, SaaS 고객 포털에서 제공하는 ‘Usage Dashboard’ 를 직접 만드는 단계입니다.
(API Gateway 사용량 분석, Rate Limit 추적, 비용 모니터링까지 포함)


1. 전체 아키텍처

[Istio Gateway Access Log]
        │
        ▼
   [Loki / Promtail] ───────▶ Grafana “Tenant Logs”
        │
        ├────────────────────▶ Redis Stream Queue → 실시간 처리
        │
        └────────────────────▶ Prometheus (latency, RPS, error rate)
                                   │
                                   ▼
                             Grafana Dashboards

또한 Kubecost 데이터를 함께 연동해
→ “테넌트별 비용” 까지 하나의 대시보드에서 보여주는 구조를 목표로 합니다.


2. Istio Access Log 활성화

Istio Gateway에서 API 호출 로그를 일관된 형식(JSON)으로 출력하도록 설정합니다.

istio-telemetry.yaml

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: access-logs
  namespace: istio-system
spec:
  accessLogging:
  - providers:
    - name: envoy
    disabled: false

일반적으로 Istio는 Envoy Proxy를 사용하므로
Access Log는 다음 항목을 자동으로 포함합니다:

  • requestPath
  • responseCode
  • responseDuration
  • apiKey (x-api-key 값)
  • tenant (커스텀 라벨링)

3. Promtail로 Istio 로그 수집 → Loki 저장

반응형

promtail-config.yaml

server:
  http_listen_port: 9080
positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki.monitoring.svc:3100/loki/api/v1/push

scrape_configs:
- job_name: istio-gateway-logs
  static_configs:
  - targets:
    - localhost
    labels:
      job: istio-gateway
      __path__: /var/log/containers/istio-ingressgateway-*.log

설치:

helm repo add grafana https://grafana.github.io/helm-charts
helm install promtail grafana/promtail -f promtail-config.yaml -n monitoring

Loki로 전송된 로그는 Grafana에서 실시간 쿼리 가능해집니다.


4. 로그 라벨링: 테넌트별 분리

Istio VirtualService에서 API Key와 테넌트 정보를 로그 라벨로 포함합니다.

envoyFilter.yaml

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: add-tenant-label
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: HTTP_FILTER
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.lua
        typed_config:
          "@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
          inlineCode: |
            function envoy_on_request(request_handle)
              local apiKey = request_handle:headers():get("x-api-key") or "none"
              request_handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua", "tenant", apiKey)
            end

결과:
Loki 로그에서 tenant="apikey-xxx" 형식으로 필터링 가능.


5. Prometheus 메트릭 수집 (RPS / Latency / Error)

Istio 기본 메트릭:

Metric 의미

istio_requests_total 서비스별 요청 수
istio_request_duration_milliseconds 지연시간
istio_requests_error_rate 에러율

테넌트 라벨별로 분리하려면 EnvoyFilter에서 tenant 라벨을 추가합니다.

metrics-config.yaml

apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: tenant-metrics
  namespace: istio-system
spec:
  metrics:
  - providers:
    - name: prometheus
    overrides:
    - match:
        metric: REQUEST_COUNT
      tagOverrides:
        tenant:
          operation: UPSERT
          value: apiKey

이제 Prometheus 메트릭에 다음처럼 라벨이 추가됩니다:

istio_requests_total{tenant="tenant-a", response_code="200"}

6. Redis Stream으로 이벤트 스트리밍

API 호출 기록을 Redis Stream으로 흘려보내면
→ 실시간 API 사용량 분석이 가능합니다.

NestJS Gateway에서 Hook 적용:

middleware.ts

import { Injectable, NestMiddleware } from "@nestjs/common";
import { Redis } from "ioredis";

@Injectable()
export class ApiLogMiddleware implements NestMiddleware {
  private redis = new Redis(process.env.REDIS_URL);

  use(req: any, res: any, next: () => void) {
    const start = Date.now();
    res.on("finish", () => {
      const log = {
        method: req.method,
        path: req.originalUrl,
        tenant: req.headers["x-api-key"] || "none",
        status: res.statusCode,
        duration: Date.now() - start,
        timestamp: Date.now()
      };
      this.redis.xadd("api_stream", "*",
        "tenant", log.tenant,
        "path", log.path,
        "status", log.status,
        "duration", log.duration
      );
    });
    next();
  }
}

Redis Stream 사용 목적:

  • 초당 호출 수(RPS) 실시간 분석
  • Top Endpoints 계산
  • 테넌트별 Rate Limit 적용 관계형 데이터 저장 없이 가능

7. Grafana 대시보드 구성

다음과 같은 Panel을 구성합니다:


① 테넌트별 API 호출 수

PromQL:

sum by (tenant) (rate(istio_requests_total[1m]))

② 테넌트별 평균 응답시간

histogram_quantile(
  0.95,
  sum(rate(istio_request_duration_milliseconds_bucket[5m])) by (tenant, le)
)

③ 에러율

sum(rate(istio_requests_total{response_code=~"5.."}[5m])) 
/
sum(rate(istio_requests_total[5m]))

④ API 별 Top Usage (Loki Query)

{job="istio-gateway"} | json | stats count() by (tenant, requestPath)

⑤ 테넌트별 비용 (Kubecost)

Kubecost Data Source 활용:

kubecost_namespace_cost{namespace="team-a"}

8. SaaS 포털에 ‘내 API 현황’ 기능 제공

테넌트는 자신의 API 사용 내역을 확인할 수 있어야 합니다.

NestJS에서 Redis Stream → GraphQL API 구현:

usage.resolver.ts

@Query(() => [Usage])
async apiUsage(@Args("tenantId") tenantId: string) {
  const entries = await this.redis.xrange("api_stream", "-", "+");
  return entries
    .filter(([_, fields]) => fields.includes(tenantId))
    .map(([id, fields]) => ({
      id,
      tenant: fields[1],
      path: fields[3],
      status: fields[5],
      duration: Number(fields[7])
    }));
}

9. 활용 시나리오

시나리오 처리

Tenant A가 RPS 급증 Grafana 알림 + 자동 Rate Limit 조정
API Key 유실 Loki 로그에서 추적 → 즉시 비활성화
Tenant B 비용 급증 Kubecost 알림 → AIOps가 Pod 규모 조정
특정 API 오류률 증가 Prometheus Alert → DevOps Slack 알림

10. 정리

이번 글에서 구축한 것은 실제 SaaS 업체들이 반드시 제공하는 “Usage Dashboard” 입니다.

✔ 테넌트별 API 사용량
✔ 응답 속도 / 에러율
✔ API Gateway 라우팅 현황
✔ 실시간 스트림 기반 사용 분석
✔ 비용 통계(Kubecost)
✔ 로그 검색(Loki)

결과적으로 하나의 Kubernetes 클러스터에서
“각 고객에게 독립적인 관측/청구/보안 환경”을 제공할 수 있는
프로덕션급 SaaS Observability Platform이 완성되었습니다.


다음 글에서는 이 기반 위에 테넌트별 Rate Limiting & Quota Enforcement 시스템을 구축합니다.
(API Gateway + Redis + EnvoyFilter + AIOps 예측 기반 Rate Control)


 

쿠버네티스,Observability,Grafana,Prometheus,Loki,RedisStream,SaaS플랫폼,APIUsage,Kubecost,모니터링

 


✅ 참고할 최신 자료

  • Grafana Cloud에서 싱글 스택 vs 멀티 스택 구조로 테넌시를 관리하는 방법론이 정리되어 있습니다. (Grafana Labs)
  • “How to Achieve Multi-tenant Observability with Grafana” 글에서는 로그 / 메트릭을 테넌트별로 분리하고 접근 제어하는 구체적인 아키텍처가 설명되어 있습니다. (gepardec.com)
  • Kubecost를 이용해 네임스페이스 기반 비용 분리 및 테넌트별 청구 구조를 운영하는 사례도 존재합니다. (InfraCloud)

⚠️ 보완/강화 제안

  • 로그/메트릭 테넌시 분리: 단순히 네임스페이스를 분리하는 것 외에도, 테넌트별로 데이터 소스(예: Prometheus 데이터베이스, Loki 인스턴스), 라벨 필터링, 권한 제어(RBAC/Label-Based Access Control)를 고려하면 좋습니다. 예컨대, Grafana 내에서 각 테넌트가 “자신의 라벨 tenant=…만 조회 가능”하도록 설계하는 방식.
  • 데이터 지연/카디널리티 문제: 테넌트가 많아지면 로그 및 메트릭 데이터가 급증하고 쿼리 성능 저하될 수 있습니다. 이를 대비해 라벨 압축, 기간 제한, 별도 인제스팅 인프라 설계 고려사항을 넣으면 좋습니다.
  • 비용 + 사용량 연동: 단순히 비용만 보여주는 것이 아니라 “사용량 증가 → 비용 급증” 흐름을 시각화하고, 알림 트리거(예: 예산 초과)도 포함하면 운영가치가 올라갑니다.
  • 테넌트 온보딩 자동화 흐름: 새로운 테넌트가 들어올 때 Namespace 생성, 리소스쿼터 설정, 관측 대시보드 자동 생성, 비용 청구 그룹 연동 등을 스크립트로 자동화하면 SaaS 플랫폼으로서 완성도가 높아집니다.
  • 보안 및 접근 제어: 관측 대시보드에서도 테넌트간 데이터 노출이 없도록 “테넌트별 로그인 → 해당 데이터만 조회 가능” 구조를 설명하는 것이 좋습니다.

 

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
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
글 보관함
반응형