Mini Project 6 07

HPA와 RDS 연결 한계: 시스템 스케일링의 진짜 운영 규칙

HPA가 replica를 늘렸다면 성공처럼 보이는데, 왜 RDS 연결 한계 때문에 maxReplicas를 낮추는 판단이 필요했는지 헷갈린다.

근거 · 교안 확장성·부하 테스트 파트

HPA와 RDS 연결 한계: 시스템 스케일링의 진짜 운영 규칙 대표 이미지

1장: HPA 성공, 그런데 뭔가 이상하다?

솔라는 모니터 앞에 바싹 다가앉아 승리의 미소를 지었다. 대시보드 위 그래프가 솔라의 기대를 정확히 현실로 옮겨놓고 있었다.

“언니, 이것 봐! 드디어 성공했어.”

솔라의 목소리에 들뜬 기색이 가득했다. 언니인 루나는 조용히 다가와 솔라의 어깨너머로 화면을 들여다보았다. 화면에는 쿠버네티스 대시보드와 모니터링 툴이 나란히 떠 있었다.

솔라가 마우스 커서로 그래프의 한 부분을 가리켰다. CPU 사용량을 나타내는 선이 가파르게 치솟더니, 미리 설정해 둔 임계치인 50%를 가뿐하게 넘어섰다.

“내가 만든 load-generator 파드가 백엔드 서비스에 부하를 주기 시작한 시점이야. 그랬더니… 짜잔!”

솔라가 이번에는 다른 창을 클릭했다. 백엔드 배포(Deployment)의 상태를 보여주는 화면이었다. DESIRED: 3, CURRENT: 3, UP-TO-DATE: 3, AVAILABLE: 3. 원래 2개였던 레플리카(replica) 숫자가 3으로 늘어나 있었다. Horizontal Pod Autoscaler, HPA가 완벽하게 작동한 순간이었다.

“CPU 사용량이 50%를 넘으니까 HPA가 바로 감지하고 파드를 하나 더 늘려줬어. 이제 트래픽이 몰려도 이 시스템은 거뜬할 거야. 자동 확장으로 서비스 안정성을 확보한 거지.”

솔라는 자신이 수행한 이 테스트가 시스템의 견고함을 증명했다고 믿었다. CPU 부하에 맞춰 서비스의 처리 용량이 유연하게 늘어나는 것. 그것이 바로 오토스케일링의 교과서적인 성공 사례 아닌가. 솔라는 이 결과만으로도 프로젝트의 중요한 고비 하나를 넘었다고 생각했다.

루나는 잠시 동안 아무 말 없이 그래프와 숫자들을 응시했다. 솔라의 말대로 HPA는 맡은 임무를 충실히 수행했다. CPU 사용량이라는 명확한 신호에 반응하여, 정해진 규칙에 따라 파드의 수를 늘렸다. 그 자체만 놓고 보면 흠잡을 데 없는 성공이었다.

“정말 HPA는 자기가 할 일을 정확히 했네.”

루나의 차분한 목소리에 솔라는 어깨를 으쓱했다. 당연한 결과라는 듯한 표정이었다.

“그럼, 이제 부하 테스트는 성공적으로 끝난 거지.”

솔라가 의기양양하게 말하며 의자 등받이에 몸을 기댔다. 그때, 루나가 조용한 질문을 던졌다.

“솔라, 이 테스트로 우리가 확인한 건 ‘CPU 부하가 높을 때 파드가 늘어난다’는 사실이야. 그렇지?”

“응. 그게 핵심이지.”

“그렇다면, 이 시스템이 정말로 ‘안정적’이라고 말할 수 있는 근거가 그것만으로 충분할까?”

솔라는 순간 말문이 막혔다. 안정적이라는 근거가 아니면 뭐란 말인가.

“무슨 말이야? 파드가 늘어난다는 건, 그만큼 요청을 더 많이 처리할 수 있다는 뜻이잖아. 당연히 더 안정적인 거지.”

솔라의 목소리에는 약간의 당혹감이 섞였다. 성공의 기쁨을 누리기도 전에 찬물을 끼얹는 듯한 질문이었다.

루나는 대답 대신, 옆에 있던 화이트보드에 간단한 그림을 그리기 시작했다. 네모 상자 하나를 그리고 그 안에 ‘Backend’라고 적었다. 그리고 작은 동그라미 두 개를 그렸다.

“원래 우리 백엔드 서비스는 이렇게 파드 두 개가 요청을 처리하고 있었어.”

루나는 동그라미 하나를 더 그려 넣었다.

“그리고 방금 솔라 네가 한 것처럼 부하가 발생해서, HPA가 이렇게 파드를 하나 더 만들었지. 이제 파드는 총 세 개야.”

솔라는 말없이 고개를 끄덕였다. 방금 화면에서 본 것과 똑같은 그림이었다.

루나는 ‘Backend’ 상자 옆에 또 다른, 더 큰 네모 상자를 그렸다. 그리고 백엔드 파드를 나타내는 세 개의 동그라미 각각에서 화살표를 그어 새로 그린 상자로 연결했다.

“이 백엔드 파드들은 혼자 일하지 않아. 요청을 받으면 어딘가에 데이터를 요청하고, 또 데이터를 저장하기도 하지. 예를 들면… 데이터베이스 같은 곳에.”

루나는 새로 그린 상자에 ‘DB’라고 적었다.

솔라는 그제야 루나가 무엇을 말하려는지 어렴풋이 짐작했다. 늘어난 백엔드 파드들. 그 파드들이 모두 동시에 연결을 시도할 대상. 데이터베이스.

“HPA는 성공적으로 파드의 수를 2개에서 3개로 늘렸어. 이건 명백한 사실이야. 하지만 그 결과로, 데이터베이스의 문을 두드리는 손님이 동시에 2명에서 3명으로 늘어난 것도 사실이지.”

루나는 화살표 끝이 ‘DB’ 상자에 닿는 부분을 콕 짚었다.

“우리는 아직 이 문이 손님 세 명을 한꺼번에 맞이할 만큼 넓은지 확인하지 않았어.”

솔라는 화이트보드의 단순한 그림을 보다가, 다시 자신의 모니터 화면으로 눈을 돌렸다. 화면 속 replicas: 3이라는 숫자가 아까와는 전혀 다르게 보였다. 그것은 단순히 HPA의 성공을 증명하는 지표가 아니었다. 시스템의 또 다른 어딘가에 새로운 질문을 던지는 숫자였다.

‘HPA는 성공했다. 하지만 시스템 전체가 성공한 것일까?’

확신에 가득 찼던 솔라의 마음에 작은 의문과 불안감이 싹트기 시작했다. 부분의 성공이 전체의 성공을 보장하지 않을 수도 있다는, 당연하지만 놓치고 있던 감각이었다.

2장: 숨겨진 병목: HPA와 RDS의 연결고리

솔라는 화이트보드에 그려진 그림 앞에서 한참을 서성였다. ‘부분의 성공이 전체의 성공을 보장하지 않는다.’ 루나 언니의 말은 단순했지만, 솔라의 머릿속을 복잡하게 만들었다. 이전 테스트에서 백엔드 파드는 분명 2개에서 3개로 늘어났다. 그렇다면 4개, 5개로 늘어나도 괜찮을까? 데이터베이스의 문은 얼마나 넓은 걸까? 불확실성을 그냥 두고 볼 수만은 없었다. 솔라는 직접 확인해 보기로 마음먹었다.

자리에 돌아온 솔라는 망설임 없이 백엔드 서비스의 HPA 설정 파일을 열었다. maxReplicas 값이 3으로 되어 있었다. 솔라는 이 숫자를 5로 바꾸고 저장했다. ‘최대 5개까지 늘어나도록 해보자. 시스템이 부하를 얼마나 더 견딜 수 있는지, 그 한계를 직접 보는 거야.’ 이것은 단순히 호기심을 넘어, 시스템의 진짜 안정성을 확인하려는 엔지니어의 의지였다. 다시 한번 load-generator를 실행하자, CPU 사용량 그래프가 어김없이 치솟기 시작했다.

“좋아, 시작됐다.”

솔라는 숨을 죽이고 대시보드를 지켜봤다. replicas: 2… 잠시 후 replicas: 3으로 바뀌었다. 거기서 멈추지 않았다. 부하가 계속되자 replicas: 4가 되었고, 마침내 replicas: 5라는 숫자가 화면에 찍혔다. HPA는 설정된 최대치까지 파드를 성공적으로 늘려냈다. 솔라의 입가에 옅은 미소가 번졌다. 일단 HPA의 동작 자체는 완벽했다.

그때였다. 솔라가 함께 띄워놓은 백엔드 애플리케이션의 로그 창에서, 평소에는 볼 수 없던 붉은색 에러 메시지들이 폭포수처럼 쏟아지기 시작했다.

ERROR: (HY000): Too many connections

솔라의 미소가 순식간에 굳었다. ‘Too many connections’? 연결이 너무 많다고? 에러는 새로 생성된 4번째, 5번째 파드에서 집중적으로 발생하고 있었다. HPA가 파드를 늘리는 데 성공한 바로 그 순간, 시스템의 다른 한쪽에서는 비명을 지르기 시작한 것이다.

“언니! 이것 좀 봐, 이상해. 파드는 5개까지 잘 늘어났는데, 앱에서 갑자기 에러가 쏟아져.”

솔라의 다급한 목소리에 루나가 다가와 화면을 함께 들여다봤다. 루나는 요란한 에러 로그 대신, HPA 대시보드에서 파드 개수가 변하는 타임라인과 에러가 발생하기 시작한 로그의 타임스탬프를 차분히 비교해 보았다.

“솔라, 이 에러 메시지들, 언제부터 보이기 시작했어?”

“어… 파드가 4개로 늘어나고, 5개째가 막 뜨기 시작했을 때부터인 것 같아.”

“그렇구나. 그럼 ‘연결이 너무 많다’는 건 누구한테 하는 말일까? 누가 연결을 더는 받아주지 못하는 거지?”

루나의 질문은 솔라의 시선을 자연스럽게 화이트보드의 그림으로 이끌었다. 늘어난 백엔드 파드들이 화살표로 가리키고 있던 단 하나의 상자, ‘DB’.

“데이터베이스…?”

솔라는 설마 하는 마음으로 클라우드 콘솔에 접속해 RDS 서비스의 설정 페이지로 들어갔다. MySQL 데이터베이스의 수많은 파라미터들 속에서, 솔라는 검색 기능을 이용해 ‘connection’이라는 단어를 입력했다. 화면에 max_connections라는 파라미터와 그 값이 나타났다. 그리 높지 않은 숫자였다.

솔라는 잠시 머릿속으로 계산을 하기 시작했다. 백엔드 파드 하나는 시작할 때 데이터베이스와 연결을 맺기 위해 커넥션 풀을 생성한다. 만약 파드 하나가 10개의 커넥션을 사용하도록 설정되어 있다면?

  • 파드 2개: 2 * 10 = 20 커넥션
  • 파드 3개: 3 * 10 = 30 커넥션
  • 파드 4개: 4 * 10 = 40 커넥션
  • 파드 5개: 5 * 10 = 50 커넥션

솔라는 RDS에 설정된 max_connections 값과 자신의 계산 결과를 비교했다. 아찔한 순간이었다. 데이터베이스가 허용하는 최대 연결 수는 40을 약간 넘는 수준이었다. 파드가 4개로 늘어났을 때 이미 한계에 거의 도달했고, 5개가 되는 순간 그 한계를 넘어버린 것이다.

“아…!”

낮은 탄식이 터져 나왔다. HPA의 성공적인 확장이, 곧장 데이터베이스 장애의 원인이었다. 두 사건은 별개의 문제가 아니었다. 하나의 시스템 안에서 너무나도 명확하게 이어진, 하나의 인과 관계였다. HPA는 CPU만 보고 달렸지만, 그 결과는 시스템의 가장 약한 고리인 데이터베이스의 연결 한계를 강타했다.

솔라는 HPA 설정 파일의 maxReplicas: 5를 지우고 3으로 다시 고쳤다. 이전에는 불확실함 속에서 내렸던 결정이었지만, 지금은 달랐다. ‘Too many connections’라는 명백한 증거와 max_connections라는 구체적인 한계 값을 확인한 뒤 내리는, 데이터에 기반한 결정이었다.

“HPA의 maxReplicas를 정하는 건, 그냥 서버 용량을 몇 배로 늘릴지 정하는 게 아니었어. 이 숫자가 다운스트림에 있는 데이터베이스의 최대 연결 수를 넘지 않도록 하는, 일종의 안전장치 역할도 하는 거였네.”

솔라는 중얼거렸다. 이제 HPA 설정 값은 독립적인 지표가 아니라, 다른 시스템의 한계와 맞물려 돌아가는 톱니바퀴의 일부로 보였다. 한쪽의 스케일링이 다른 쪽의 한계를 어떻게 건드리는지 진단하는 것. 이것이 바로 ‘시스템 전체 보기’의 시작이었다.

하지만 새로운 의문이 고개를 들었다. 그렇다면 백엔드 서비스는 영원히 파드 3개 이상으로 확장할 수 없는 걸까? 트래픽이 지금보다 훨씬 많아지면 어떻게 대처해야 하지? maxReplicas를 3으로 낮춘 것은 당장의 장애를 막는 임시방편일 뿐, 근본적인 해결책은 아닌 것 같았다. 스케일링 정책이라는 것은, 과연 어떻게 만들어야 하는 걸까?

3장: 진정한 스케일링 규칙: HPA와 DB 연결 한계를 함께

솔라의 손가락이 키보드 위에서 멈칫했다. 모니터에는 두 개의 터미널 창이 나란히 열려 있었다. 왼쪽 창에는 백엔드 서비스의 HPA 설정이, 오른쪽 창에는 프론트엔드 서비스의 HPA 설정이 보였다. 최종적으로 정리된 값들이었다.

  • Backend HPA: minReplicas: 2, maxReplicas: 3, targetCPUUtilizationPercentage: 50
  • Frontend HPA: minReplicas: 2, maxReplicas: 4, targetCPUUtilizationPercentage: 60

백엔드의 maxReplicas3으로 고정된 것은 이제 명확히 이해했다. 데이터베이스의 max_connections 한계 때문에 어쩔 수 없는 선택이었다. 장애를 막기 위한 결정이었지만, 솔라의 마음 한구석에는 여전히 찝찝함이 남아있었다. 마치 보이지 않는 벽에 부딪혀 더 나아가지 못하는 기분이었다. ‘앞으로 트래픽이 훨씬 더 많아지면 어떡하지? 백엔드는 영원히 파드 3개가 한계인 걸까?’

이 고민은 또 다른 질문으로 이어졌다. 그렇다면 프론트엔드의 maxReplicas는 왜 4일까? 이 숫자는 괜찮은 걸까? 백엔드처럼 숨겨진 함정이 있는 건 아닐까? 이전처럼 단순히 CPU 사용량만 보고 결정한 것 같아 불안했다. 스케일링 정책이라는 것이, 컴포넌트마다 제각각의 규칙으로 움직이는 파편적인 조각들의 모음처럼 느껴졌다. 당장의 장애를 막는 임시방편을 넘어선, 시스템 전체를 아우르는 진짜 운영 규칙이 필요했다.

“언니, 이 숫자들 말이야.”

솔라가 화면을 가리키며 입을 열었다. 어느새 다가온 루나는 솔라가 무엇을 보고 있는지 조용히 살폈다.

“백엔드는 maxReplicas가 3, 프론트엔드는 4. 이 결정, 그냥 ‘테스트해보니 이 정도가 적당했다’에서 멈추면 안 될 것 같아. 백엔드 HPA가 RDS 연결 수에 발목이 잡혔던 것처럼, 이 숫자들 뒤에는 우리가 따라야 할 어떤 ‘규칙’이 숨어있는 게 아닐까?”

루나는 고개를 끄덕이며 솔라 옆의 화이트보드로 걸어갔다. 지난번에 그렸던 그림 위에, 루나는 새로운 질문을 던졌다.

“좋은 질문이야, 솔라. 그럼 우리, 백엔드의 maxReplicas가 5에서 3으로 바뀐 과정을 다시 한번 복기해볼까? 그건 ‘감’이 아니라 ‘계산’이었잖아.”

루나가 솔라에게 마커를 건넸다. 솔라는 잠시 생각에 잠기더니, 이내 화이트보드에 공식을 써 내려가기 시작했다.

백엔드 최대 파드 수 ≤ (RDS의 max_connections) / (파드당 필요 커넥션 수)

“RDS의 max_connections가 45였고, 우리 앱은 파드 하나당 커넥션 풀에 10개의 커넥션을 사용하니까… 45 나누기 10은 4.5. 그래서 안전하게 정수인 4까지 가능했지만, 다른 연결도 고려해서 3으로 정했지.”

솔라는 자신이 직접 겪었던 문제의 원인을 명확한 수식으로 재구성했다. 그때 루나가 수식을 손가락으로 가리키며 물었다.

“만약, 우리가 백엔드 파드를 꼭 5개까지 늘려야 하는 상황이라면 어떻게 해야 할까? 이 수식 안에서 바꿀 수 있는 건 뭘까?”

“어… maxReplicas를 5로 바꾸면… 등식이 성립하지 않으니까….”

솔라의 말이 흐려졌다. 그녀는 maxReplicas를 시스템이 따라야 할 결과값으로만 보고 있었다. 하지만 루나의 질문은 그것을 바꿀 수 있는 ‘변수’ 중 하나로 취급하고 있었다. 솔라는 잠시 멍하니 수식을 바라보다가, 아! 하는 작은 탄성을 내뱉었다.

“수식의 다른 쪽을 바꾸면 돼! RDS의 max_connections 값을 더 높은 사양으로 업그레이드해서 늘리거나, 아니면 애플리케이션 코드를 수정해서 파드당 사용하는 커넥션 수를 줄이면…!”

순간 눈앞의 벽이 사라지고 문이 나타나는 기분이었다. maxReplicas: 3은 절대적인 한계가 아니었다. 그것은 시스템의 다른 구성 요소들과의 관계 속에서 결정되는 ‘균형점’이었다. 스케일링 정책은 하나의 숫자를 정하는 것이 아니라, 연결된 시스템 전체의 균형을 맞추는 방정식 풀이와 같았다.

“맞아. 바로 그거야. HPA 설정은 독립적으로 존재하는 게 아니라, 자신이 의존하는 다른 시스템의 한계와 맞물려 있어. 이걸 ‘종속성’이라고 부르지. 스케일링 규칙은 바로 이 종속성을 계산에 넣는 거야.”

솔라는 이제 완전히 새로운 시각으로 모니터의 설정 값들을 바라보았다.

  • Backend HPA maxReplicas: 3: RDS max_connections에 의해 제한됨.
  • Frontend HPA maxReplicas: 4: 이 숫자는 어떤 종속성에 의해 결정될까? 프론트엔드는 데이터베이스에 직접 연결되지 않는다. 대신, 백엔드 서비스에 API 요청을 보낸다.

그렇다면 프론트엔드가 아무리 4개, 5개, 10개로 늘어나도, 그 요청을 받아줄 백엔드가 최대 3개밖에 없다면 병목은 결국 백엔드에서 발생할 것이다. 프론트엔드의 maxReplicas: 4는 백엔드의 처리 용량이라는 또 다른 한계를 고려한 결과였던 것이다.

솔라는 키보드를 당겨 새로운 문서를 열었다. 제목은 ‘신규 서비스 배포를 위한 스케일링 정책 수립 가이드’라고 적었다. 이것은 단순히 개인적인 메모가 아니었다. 오늘의 실패와 발견을 미래의 실수를 막아줄 시스템의 운영 규칙으로 만드는 작업이었다.


[운영 규칙] HPA 설정 전 필수 점검 사항

  1. 스케일링 대상 식별: HPA를 적용할 서비스(Deployment)를 정한다. (예: backend-api)
  2. 핵심 지표 선택: 스케일링의 기준이 될 지표를 선택한다. (예: CPU 사용량 50%)
  3. 다운스트림 종속성 분석 (가장 중요!):
    • 해당 서비스가 직접적으로 의존하는 모든 컴포넌트(DB, 외부 API, 내부 서비스 등) 목록을 작성한다.
    • 각 컴포넌트의 ‘처리 용량 한계’를 구체적인 수치로 파악한다. (예: RDS max_connections, 외부 API의 분당 요청 한도)
  4. maxReplicas 계산:
    • 최대 파드 수 = (다운스트림 컴포넌트의 한계치) / (파드 1개당 자원 사용량) 공식을 적용하여 각 종속성별 최대 파드 수를 계산한다.
    • 계산된 값 중 가장 작은 값maxReplicas의 최종 상한선으로 결정한다.
  5. 설정 및 근거 문서화:
    • HPA 설정값과 함께, maxReplicas가 왜 그 숫자로 결정되었는지 근거(제한 요소)를 반드시 명시한다.
    • 예시: backend-apimaxReplicas3으로 설정. (사유: RDS t3.small 인스턴스의 max_connections 한계 때문)

문서를 작성하는 솔라의 얼굴에 자신감이 비쳤다. 이제 스케일링은 단순히 CPU 부하 테스트를 통과하는 기술적인 문제가 아니었다. 시스템 전체의 연결고리를 파악하고, 가장 약한 고리의 한계를 인정하며, 그 안에서 최적의 균형점을 찾아내는 운영의 기술임을 이해하게 되었다. 단일 지표의 성공에 기뻐하는 대신, 시스템 전체의 안정성을 질문하는 것. 그것이 진짜 스케일링 규칙의 시작이었다.