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

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

트래픽에 한 번 무너진 서비스가, 자동으로 회복하지 못한 이유.

이벤트 같은 걸로 트래픽이 갑자기 평소의 몇 배로 몰리는 날이 있습니다. 그런 날을 대비해 보통은 서버를 미리 넉넉히 띄워둡니다. 갑자기 손님이 몰릴 걸 알면 미리 일손을 늘려두는 것과 같죠. 이걸 흔히 “예열(pre-warming)“이라고 부릅니다.

그날은 그 예열이 충분하지 않았습니다. 그래서 트래픽이 몰린 첫 순간에 서비스가 한 번 휘청하고 무너졌습니다.

사실 여기까지는 그럴 수 있는 일입니다. 트래픽을 항상 정확히 예측할 순 없으니까요. 진짜 이상했던 건 그다음이었습니다. 서비스가 한 번 쓰러진 뒤로, 다시 일어나질 못했습니다.


자동으로 회복될 거라고 생각했는데

요즘 시스템에는 트래픽이 많아지면 서버 수를 자동으로 늘려주는 장치가 있습니다. 이른바 오토스케일링이죠. 손님이 밀려들면 알바를 더 부르는 것처럼, “요청이 많네? 그럼 서버를 더 띄우자” 하고 알아서 대응합니다.

그러니 이번에도 그래야 했습니다. 트래픽이 몰려 서버가 버거워지면, 오토스케일링이 서버를 왕창 늘려서 받아내고, 서비스는 다시 일어나야 정상입니다.

그런데 오토스케일링은 끝내 서버를 늘리지 않았습니다. 오히려 “트래픽이 줄었으니 늘릴 필요 없다”고 판단했습니다. 요청은 분명히 쏟아지고 있는데도요.


트래픽 지표상은 줄었다고 보이던데

오토스케일링은 사람이 아니라 하나의 숫자를 보고 판단합니다. “지금 트래픽이 이만큼이다” 하는 숫자죠. 그런데 우리가 그 기준으로 쓰던 숫자가, 하필 서비스가 쓰러질 때 같이 곤두박질쳤습니다. 왜 그랬는지 한 단계씩 보면 이렇습니다.

  • 서버가 버거워지면, 시스템은 “이 서버는 지금 요청 받을 상태가 아니다”라고 판단해서 그 서버로 요청이 가지 않게 막습니다. (서버가 더 망가지지 않게 보호하는 장치예요.)
  • 그러면 그 서버에는 요청이 아예 도달하지 못합니다.
  • 그런데 우리가 보던 숫자는 하필 **“서버가 실제로 받아서 처리한 요청”**의 양이었습니다.
  • 서버들이 하나둘 요청을 못 받게 되니, 처리한 요청 수는 점점 0에 가까워졌고 — 그 숫자도 같이 바닥으로 떨어졌습니다.
  • 오토스케일링은 그 숫자만 보고 “어, 트래픽 없네? 서버 안 늘려도 되겠다”고 판단했습니다.

그림으로 보면, 평소와 무너졌을 때가 이렇게 다릅니다.

평소 (정상)

들어온 요청이 Pod들에 그대로 도달해 처리됩니다. “들어온 양”과 “처리한 양”이 같으니, 어느 쪽을 기준으로 삼아도 지표가 실제 트래픽을 잘 보여줍니다.

무너졌을 때 (readiness 실패)

readiness에 실패한 Pod가 하나둘 빠지면, 남은 Pod로 요청이 더 몰려 그마저 버티지 못하고 같이 쓰러집니다. 이렇게 용량이 쪼그라들수록 실제로 처리되는 양은 들어온 양보다 점점 더 적어집니다. 폭주하는 요청과는 정반대로, 우리가 보던 그 “처리한 양” 지표만 보면 “한가하네” 였던 거죠.

정리하면 이렇습니다.

요청은 들어오는데 못 받고 → 못 받으니 “처리한 양” 숫자가 떨어지고 → 숫자가 떨어지니 서버를 안 늘리고 → 서버가 그대로니 또 못 받고 …

한번 쓰러지고 나면, 정작 다시 일으켜줄 오토스케일링이 “괜찮네”라고 착각해버리는 겁니다. 그래서 그날도 결국 사람이 직접 서버를 왕창 늘려주고 나서야 회복됐습니다.


그럼 뭣이 중헌디

결국 중요한 건 하나였습니다. 원인은 한 줄로 좁혀집니다.

우리는 “서비스로 들어온 요청”이 아니라 “서비스가 처리한 요청”을 기준으로 서버를 늘리고 있었다.

평소엔 이 둘이 거의 같습니다. 들어온 요청은 대부분 그대로 처리되니까요. 그래서 수년간 아무 문제가 없었습니다. 둘이 크게 벌어지는 건 서비스가 쓰러져 요청을 못 받아낼 때뿐인데, 하필 그 순간이 서버를 가장 늘려야 할 순간이었던 거죠.

다행히 고치는 건 어렵지 않았습니다. 우리가 쓰던 Istio라는 도구는 같은 요청을 들어온 쪽처리한 쪽 양쪽에서 따로 세어줍니다. 우리는 그동안 “처리한 쪽”을 보고 있었고, 이걸 “들어온 쪽”으로 바꾼 게 전부였습니다.

- reporter="destination" # 서버가 받아서 처리한 요청 (쓰러지면 같이 줄어듦) + reporter="source" # 서비스로 들어온 요청 (쓰러져도 그대로 보임)

이렇게 바꾸면, 서버가 요청을 못 받는 상황에서도 “들어온 요청은 여전히 많다”는 게 그대로 보입니다. 그러면 오토스케일링이 제대로 서버를 늘려서, 서비스가 스스로 다시 일어날 수 있게 됩니다.


마무리

  • 트래픽이 몰려 서버가 한 번 쓰러지는 것 자체는 흔한 일이다.
  • 진짜 문제는, 그 뒤에 자동으로 다시 일어나지 못한 것이었다.
  • 일으켜줘야 할 오토스케일링이 “처리한 양”만 보고 있었던 탓에, 쓰러질수록 숫자가 더 떨어져 “괜찮다”고 착각했다.
  • 기준을 “처리한 요청”에서 “들어온 요청”으로 바꾸니, 쓰러진 순간에도 진짜 트래픽이 보여 다시 일어날 수 있었다.

결국 어떤 숫자를 보느냐가, 시스템이 스스로 회복할 수 있느냐 없느냐를 갈랐던 셈입니다.