-
이코에코(Eco²) ext-authz: AuthN/AuthZ 검증 엔진 Stress Test이코에코(Eco²)/Auth Offloading (ext-authz) 2025. 12. 14. 15:29

Go-! ext-authz 서버 개발기에서 이코에코 서비스 API(GET)로 ext-authz의 대략적인 성능을 테스트했다.
당시 유저수 200명->1000명으로 증가하도록 테스트해도 서비스 API의 RPS가 250-280선에서 증가하지 않아, ext-authz 서버가 부하를 온전히 받지 못했다. 서비스 API(GET)엔 도메인별 DB 조회 로직이 포함된만큼 변수가 많았기에, ext-authz 서버의 임계 성능을 알아보고자 더미 엔드포인트(/api/v1/character/ping)를 만들고 AuthN/AuthZ 검증 필터를 통과하도록 구성했다.주요 리소스 스펙
이코에코 클러스터 내 ext-authz 부하 테스트에 영향을 받는 리소스들의 스펙은 아래와 같다.
EC2 인스턴스

k8s-api-character t3.small 2 2GB character-api k8s-redis t3.medium 2 4GB Redis (cache/blacklist) k8s-ingress-gateway t3.medium 2 4GB Istio Ingress Gateway Pod 리소스 할당
character-api k8s-api-character 100m / 300m 128MB / 1GB Redis k8s-redis 100m / 500m 128MB / 1GB istio-ingressgateway k8s-ingress-gateway 100m / 2000m 128MB / 1GB Istio 리소스
EnvoyFilter cookie-to-header istio-system s_access 쿠키 → Authorization 헤더 AuthorizationPolicy ext-authz-policy istio-system /api/v1/* 경로 ext-authz 호출 VirtualService {domain}-vs 각 ns 경로 기반 라우팅 ext-authz 기본 설정
Port 50051 (gRPC) Timeout 0.25s failOpen false cpu / memory (limits) 0.2 core / 128Mi 검증 대상 헤더 authorization, x-refresh-token, x-request-id 패킷 플로우

테스트용 더미 엔드포인트(/character/ping)는 별도의 로직없이 AuthN/AuthZ 검증 필터만 통과한다.
Istio GW Route로 ext-authz 서버를 거쳐 검증을 진행한 뒤, 더미 엔드포인트로 라우팅된다.Observability: Grafana, Prometheus
항목 쿼리 p99 Latency histogram_quantile(0.99, sum(rate(ext_authz_request_duration_seconds_bucket[5m])) by (le))Avg Latency sum(rate(ext_authz_request_duration_seconds_sum[5m])) / sum(rate(ext_authz_request_duration_seconds_count[5m])) p99 JWT 검증 histogram_quantile(0.99, sum(rate(ext_authz_jwt_verify_duration_seconds_bucket[5m])) by (le))p99 Redis 조회 histogram_quantile(0.99, sum(rate(ext_authz_redis_lookup_duration_seconds_bucket[5m])) by (le))Success Rate (%) sum(rate(ext_authz_requests_total{result="allow"}[5m])) / sum(rate(ext_authz_requests_total[5m])) * 100CPU (cores) sum(rate(container_cpu_usage_seconds_total{namespace="auth", pod=~"ext-authz.*"}[5m]))Memory (GB) sum(container_memory_working_set_bytes{namespace="auth", pod=~"ext-authz.*"}) / 1024 / 1024 / 1024RPS sum(rate(ext_authz_requests_total[5m]))동시 요청 수 ext_authz_requests_in_flight에러 원인 sum by (reason) (rate(ext_authz_requests_total{result="deny"}[5m])) ext-authz의 HTTP 1.1 서버에 부착된 메트릭을 기반으로 성능 지표를 수집한다.
HTTP 1.1 서버와 gRPC 서버는 별개 goroutine이지만 동일한 스레드(OS 레벨)를 공유해서 노이즈 없이 메트릭이 수집된다.
Locust의 RPS는 전체 플로우를 기준으로 잡히기에 보다 정밀한 측정을 위해 ext-authz의 RPS를 측정하는 쿼리를 추가했다.
5분 단위로 측정되며 동시 접속자 2000명->2500명, 200명/s -> 250명/s 으로 테스트를 진행했다. 지표와 함께 살펴보자Stress Test, users: 2000, ramp-ups: 200, 5m

users: 2000, ramp-ups: 200, 5m, p99 latency, 431 - 484ms 
users: 2000, ramp-ups: 200, 5m, avg latency, 57.4-80.1 ms 
users: 2000, ramp-ups: 200, 5m, p99 latency(JWT): 0.7-0.95ms 
users: 2000, ramp-ups: 200, 5m, p99(Redis): 100+ms 
users: 2000, ramp-ups: 200, 5m, success rate: 90.4-94.0% 
users: 2000, ramp-ups: 200, 5m, CPU usage, 0.0247 - 0.194 cores 
users: 2000, ramp-ups: 200, 5m, memory usage, 17.5 - 25.1 MB 
users: 2000, ramp-ups: 200, 5m, RPS: 200-884 
users: 2000, ramp-ups: 200, 5m, 최대 동시 처리: 230 
users: 2000, ramp-ups: 200, 5m, reason_error, redis_error: 11.5 - 54.4 req/s
동시 접속자 1000명, 100명/s에서 도메인 API(GET)와 ext-authz가 다운되지 않았기에 2000명, 200명/s부터 측정했다.
p99 431 - 484ms, avg latency 57.4 - 80.1ms로 sparse한 환경에서의 속도(14.3ms)를 감안하면 부하가 반영된 상황이다.
JWT 검증 연산은 p99 1ms 미만으로 매우 빠른 속도로 처리되고 있으며 유의미한 부하가 관측되진 않았다.
주된 병목 요인은 Redis 조회로 p99 100ms, p99 기준 총 처리시간의 25%를 소비한다.ubuntu@k8s-master:~$ kubectl exec -it -n auth deployment/auth-api -- python3 -c " import redis import time r = redis.Redis(host='dev-redis.redis.svc.cluster.local', port=6379, db=0) # Warm-up r.ping() # 측정 (100회) ... " Min: 1.02ms Avg: 1.22ms p99: 1.78ms Max: 3.02ms성공률 90.4-94%다. 최대 초당 55.4개의 요청이 Redis 조회에서 실패했다(redis_error).
RPS가 200-884인 걸 감안하면 초당 11.5 - 54.4 요청 실패는 에러율인 6-10%의 수치와 일치한다.
현재 Redis의 poolsize는 0-10 이다.
동시 처리수는 230이니, 초당 210개 요청이 레디스 풀 대기로 진입하며 이중 PoolTimeout(4s)을 초과한 요청이 드랍됐다.
동일한 네임스페이스인 auth-api(FastAPI)의 redis 조회시간은 2ms 남짓이다. p99 100ms 중 98%가 풀 대기시간이다..
이론상으론 Redis에서 20 connections × (1000ms / 2ms) = 10,000 req/s의 요청을 커버할 수 있지만, 실제 측정값에 따르면 RPS 200-884임에도 버스트 트래픽(p99)에서 풀 대기가 발생했다.Stress Test (breaking point), users: 2500, ramp-ups: 250, 5m

users: 2500, ramp-ups: 250, 5m, p99 latency: ~470ms 
users: 2500, ramp-ups: 250, 5m, avg latency: ~125ms 
users: 2500, ramp-ups: 250, 5m, p99 JWT verify: ~0.7ms 
users: 2500, ramp-ups: 250, 5m, p99 Redis latency: 100+ms 
users: 2500, ramp-ups: 250, 5m, success rate: ~86% 
users: 2500, ramp-ups: 250, 5m, CPU usage: 0.008-0.012 cores 
users: 2500, ramp-ups: 250, 5m, memory usage: ~22MB 
users: 2500, ramp-ups: 250, 5m, RPS: ~42 
users: 2500, ramp-ups: 250, 5m, 최대 동시 처리: 180 
users: 2500, ramp-ups: 250, 5m, redis_error: ~6 req/s 2000명 테스트에서 시스템이 다운되지 않았기에 부하를 25% 증가시켜 2500명, 250명/s로 측정했다. p99 전체 응답시간 470ms, 평균 125ms로 이전 테스트(p99 431-484ms, avg 57-80ms) 대비 평균 응답시간이 56% 증가했다. JWT 검증은 p99 0.7ms로 여전히 빠르며 병목이 아니다. p99 Redis 조회시간은 100ms 초과로 관측된다. Go 서버의 redis latency metrics 최대치가 100ms로 걸려있던 상황이라.. 보강이 필요한 상황이다. auth-api에서 측정한 Redis 조회시간(p99 1.78ms)과 비교하면 98ms가 풀 대기 시간이다.
에러율 = redis_error / RPS = 6 / 42 ≈ 14%성공률은 86%로 이전(90-94%)보다 4-8%p 하락했다. redis_error rate가 6 req/s로 이전(11-54)보다 낮아 보이지만, 전체 RPS가 42로 급감했기 때문에 실 에러율은 14%로 증가했다.
RPS가 이전(200-884)에서 42로 10-20배 감소한 것은 시스템이 포화 상태에 도달했음을 의미한다.
동시 처리 요청은 약 180개로 이전(230)보다 감소했으며, 메모리 22MB로 리소스는 충분히 여유가 있다.
CPU 사용량의 경우 최대 0.196 cores로 미미해보이나, 현재 ext-authz의 cpu limits가 0.2 cores로 부하 지점까지 도달한 상황이다.
현재로선 ext-authz의 CPU limits와 Redis 커넥션 풀(PoolSize=20)이 주요 병목 지점이다.주요 지표 요약
p99 Latency 431-484ms ~470ms 유사 Avg Latency 57-80ms ~125ms 56% 증가 p99 JWT 0.7-0.95ms ~0.7ms 유사 p99 Redis 100ms 초과 100ms 초과 메트릭 보강 필요 Success Rate 90-94% ~86% 4-8% 하락 redis_error 11-54 req/s ~6 req/s 감소 RPS 200-884 ~42 대폭 감소 동시 처리 230 ~180 감소 CPU 0.01 cores 0.01 cores 유사 Memory 17-25MB ~22MB 유사 테스트 환경: Locust (Local)
부하 테스트는 로컬 Mac에서 Locust Python 스크립트를 실행하는 방식으로 진행했다.
로컬 환경 스펙
OS macOS (Darwin 24.3.0) Chip Apple Silicon 가용 스레드 2,560 Python 3.11+ Locust 2.x 실행 방법
환경변수 설정 후 실행
ACCESS_TOKEN="" AUTH_METHOD=cookie locust
-f tests/performance/test_ext_authz.py
--host=https://api.dev.growbin.app
--users=2000
--spawn-rate=200
--run-time=5m
--headless테스트 구성
항목 설정값 Target https://api.dev.growbin.appEndpoint GET /api/v1/character/ping인증 방식 Cookie ( s_access)wait_time 1-3초 (between) 로컬에서 클러스터로 요청을 보내는 구조로, 네트워크 레이턴시가 포함된다.
Locust Web UI(http://localhost:8089)에서 실시간 RPS, 응답시간, 실패율을 모니터링하며 Grafana 대시보드에서 ext-authz 내부 메트릭(JWT 검증, Redis 조회)을 병행 확인했다.스케일 한계
로컬 단일 프로세스로 동시 접속자 10,000+ 테스트 시 제약이 있다.
- File descriptor 한계:
ulimit -n 65536조정 필요 - CPU 포화: distributed mode 권장
Distributed mode (Master + Workers)
locust -f test_ext_authz.py --master --host=https://api.dev.growbin.app
별도 터미널에서 Worker 실행
locust -f test_ext_authz.py --worker --master-host=127.0.0.1
현재 테스트 규모의 요청 부하(2,000-2,500 users, wait_time: 3s)는 로컬 Mac으로 커버 가능했다.
이어지는 포스팅으론 Redis 풀 튜닝을 기록해두겠다. 해야할 피처들이 쌓이는 상황이라 우선순위를 정해야한다.
LLM과 함께한다지만 아이디어에서 적용, 구현까지의 주기가 짧아진만큼 기록, 처리해야할 정보량과 태스크(PR, 테스트)가 함께 폭증하기도 해서 살짝 지치기도 한다.. 주말엔 쉬는 게 맞다😴'이코에코(Eco²) > Auth Offloading (ext-authz)' 카테고리의 다른 글
이코에코(Eco) ext-authz 추가 테스트: Redis 병렬 연결 활성화 (vCPU 2, io-threads 2) (0) 2025.12.17 이코에코(Eco²) ext-authz 성능 튜닝: Redis PoolSize, HPA (0) 2025.12.15 이코에코(Eco²) Auth Offloading: 도메인 공통 모듈 제거 (0) 2025.12.13 - File descriptor 한계: