Skip to Content
Blog왜 트래픽은 받았지만 스케일링은 못 했을까?

왜 트래픽은 받았지만 스케일링은 못 했을까?

트래픽이 몰리는 날에는 보통 미리 Pod 수를 늘려둡니다. 몰리는 부하를 충분히 버티기 위한 단계, pre-warming입니다.

하루는 예열이 충분하지 않았고, 트래픽이 들어온 첫 순간 서비스가 요청을 받아내지 못했습니다.

여기까지는 운영 중 생길 수 있는 문제였습니다. 예측보다 큰 부하가 오면 한 번 흔들릴 수 있습니다.

문제는 그다음이었습니다. 트래픽은 계속 들어오고 있었는데, 오토스케일링은 Pod를 충분히 늘리지 않았습니다.

서비스는 자동으로 회복하지 못했고, 결국 사람이 직접 scale-up을 한 뒤에야 정상화됐습니다.


오토스케일링이 본 숫자

오토스케일링은 실제 상황을 직접 판단할 능력이 없습니다.

단순히 지정한 지표를 보고, 그 숫자에 맞게 Pod수를 조절합니다.

당시 기준으로 쓰던 지표는 요청 수였습니다. 겉으로 보면 트래픽 기반 스케일링이니 자연스러운 선택처럼 보였습니다.

문제는 그 요청 수를 어느 위치에서 센 값으로 보느냐였습니다.

우리는 요청을 받은 쪽 기준으로 세고 있었습니다. 즉 서비스로 들어오려는 전체 요청이 아니라,

실제로 Pod까지 도달해 처리되기 시작한 요청 수에 가까운 값이었습니다.

평소에는 들어온 트래픽과 거의 같습니다. 요청이 Service를 지나 Pod에 도달하고, Pod가 처리하기 때문입니다.

정상 상황은 대략 이렇습니다.

이때는 들어온 요청처리된 요청 사이에 큰 차이가 없습니다. 그래서 받은 쪽 기준 지표로 스케일링해도 문제가 드러나지 않습니다.


트래픽은 많이 받았는데, 스케일링은 되지 않은 이유

장애 상황에서는 두 숫자가 갈라집니다.

부하가 커져 Pod가 readiness check에 실패하면 Kubernetes는 그 Pod를 라우팅 대상에서 제외합니다.

이 동작 자체는 정상입니다. 준비되지 않은 Pod로 요청을 보내지 않기 위한 보호 장치입니다.

하지만 여러 Pod가 readiness에 실패하기 시작하면 남은 Pod로 요청이 더 몰립니다.

남은 Pod도 과부하가 되고, 다시 readiness에 실패합니다. 결국 라우팅 대상이 줄어들수록 실제로 처리되는 요청 수도 같이 줄어듭니다.

여기서 핵심은 트래픽이 줄어든 게 아니라는 점입니다. 들어온 요청은 계속 많았습니다. 다만 Service가 보기에 라우팅 대상이 되는 Pod는 점점 줄어들었고, 실제로 요청을 받을 수 있는 Pod로만 트래픽이 흘러갔습니다.

readiness 실패로 라우팅 대상이 아니게 된 Pod는 더 이상 요청을 받지 않습니다. 그러니 받은 쪽 기준의 요청 수 역시 남아 있는 Pod가 실제로 받아낸 요청만 세게 됩니다. 장애가 심해질수록 오토스케일링 지표도 같이 내려가는 구조였습니다.

결과적으로 시스템은 이런 루프에 빠졌습니다.

트래픽 증가 → Pod 과부하 → readiness 실패 → 라우팅 대상 감소 → 받은 쪽 기준 지표 하락 → scale-out 없음 → 남은 Pod 과부하

서비스를 회복시키려면 Pod를 늘려야 했지만, Pod를 늘리는 기준 지표는 오히려 낮아지고 있었습니다.


어떤 값을 기준으로 스케일링해야 했을까?

해결은 스케일링 기준을 처리된 요청이 아니라 들어온 요청에 맞추는 것이었습니다.

실제로는 Istio request metric의 reporter 조건을 바꾸는 일이었습니다. Istio metric에는 같은 요청이라도 reporter 관점이 있습니다. destination은 요청을 받은 workload 기준이고, source는 요청을 보낸 쪽 기준입니다. 이 상황에서 필요했던 값은 destination이 실제로 처리한 양이 아니라, 서비스로 유입되는 수요였습니다.

같은 요청 → Service → Pod 흐름에서, 우리가 봤어야 했던 두 값은 서로 다른 지점에 있습니다.

  • 실제 요청량 — 서비스로 들어오는 요청 전체입니다. probe 실패와 무관하게 그대로입니다.
  • 처리된 요청량 — 라우팅 대상으로 남아 있는 Pod가 실제로 받아낸 양입니다. probe 실패로 연결이 끊긴 Pod로 향하던 요청은 여기에 잡히지 않습니다.

probe 실패로 끊긴 Pod가 늘어날수록 처리된 요청량은 0에 가까워지지만, 실제 요청량은 줄지 않습니다. 우리가 스케일링 기준으로 봤어야 했던 건 실제 요청량이었습니다.

그래서 스케일링 쿼리의 reporter 조건을 바꿨습니다.

- reporter="destination" # 받은 쪽 기준: 실제 도달해 처리되기 시작한 요청 + reporter="source" # 보낸 쪽 기준: 서비스로 향한 요청 수요

이 변경의 의미는 단순합니다. Pod가 readiness 실패로 요청을 못 받는 순간에도, source 관점에서는 여전히 요청이 발생하고 있다는 사실을 볼 수 있습니다. 그러면 오토스케일링은 “처리된 양이 줄었으니 한가하다”가 아니라 “들어오는 요청이 많으니 Pod를 늘려야 한다”에 가깝게 판단합니다.

물론 이것만으로 모든 장애가 자동 복구되는 것은 아닙니다. readiness 실패의 원인, 애플리케이션 병목, 리소스 limit 같은 문제는 별도로 봐야 합니다. 하지만 적어도 이번 문제에서는 회복에 필요한 신호가 스케일러까지 도달하지 못한 것이 핵심이었고, reporter 관점을 바꾸는 것으로 그 신호를 되살릴 수 있었습니다.


정리

  • 예열이 부족해 첫 부하를 받아내지 못했고, 일부 Pod가 readiness 실패로 라우팅 대상에서 제외됐습니다.
  • 그 뒤에도 외부에서 들어오는 요청은 계속 많았지만, Pod가 실제로 처리한 요청은 줄었습니다.
  • 기존 오토스케일링은 받은 쪽 기준이라 처리된 요청 감소를 트래픽 감소로 해석했습니다.
  • 실제 Istio 쿼리에서는 reporter="destination"reporter="source"로 바꾸자 들어오는 수요가 지표에 남았고, 스케일링이 회복에 필요한 방향으로 동작할 수 있었습니다.

이번 문제는 “트래픽 기반 스케일링”이라는 말만으로는 충분하지 않다는 걸 보여줬습니다. 중요한 건 어떤 트래픽을 보느냐입니다. 장애 순간에는 처리량보다 수요를 봐야 할 때가 있습니다.