Incident Library

IL-0572 Mercari · 2026-06-16 (게시일)gcpdatabase

Merpay 회계 이벤트 처리 시스템에서 Cloud Pub/Sub 메시지 1000만 건이 밀려 새 회계 데이터가 하루 넘게 Cloud Spanner에 들어가지 못했다

Merpay 회계 시스템은 사내 마이크로서비스가 보내는 회계 이벤트를 Cloud Pub/Sub에서 push 구독으로 Cloud Run에 넘기고 Cloud Spanner에 기록한다. 기록된 데이터는 보낸 서비스와 대조하는 리컨사일을 거쳐 가맹점 정산과 매달 하는 회계 마감에 쓰인다. 모든 처리가 비동기라 Cloud Run과 Spanner의 처리 능력이 파이프라인 전체의 처리량 상한이 되고, 월말과 월초에는 평소의 수십 배 부하가 몰린다. Pub/Sub 메시지는 별도의 pull 구독으로 Dataflow가 읽어 Cloud Storage에도 저장한다.

장애 한 달여 전 Cloud Functions를 2nd gen으로 옮길 때 --max-instances 를 명시하지 않아 상한이 100으로 내려갔지만, 평소에는 100 인스턴스로 충분해 드러나지 않았다. 이전 월말도 maxInstances 100, eventMaxAge 21분, 3시간 트래픽 총량 약 17~19GB로 조건이 거의 같았지만, 부하 사이사이에 쉬는 구간이 있어 Spanner CPU 부하와 ack되지 않은 메시지의 적체가 그때마다 풀렸다. 장애 당일에는 쉬는 구간 없이 피크가 약 40분 이어졌다. 상한에 걸린 Cloud Run은 메시지를 다 처리하지 못했다. 인스턴스가 한꺼번에 쓰는 탓에 Spanner CPU가 치솟아 처리가 더 느려졌고, eventMaxAge 를 넘긴 메시지는 버려졌다. Spanner autoscaler는 High priority CPU만 봐서 Total CPU가 100%인데도 스케일업하지 않았다.

데이터 손실은 Cloud Storage의 데이터를 다시 넣어 피했다. 그러나 Spanner에 등록되지 않은 데이터는 리컨사일이 끝나지 않은 채 남았고, 마감일과 겹쳐 업무 전반에 영향을 줬다. 팀은 Cloud Run 스케일 파라미터를 배포 정의에 모두 명시하고 값을 올렸으며, mercari/spanner-autoscaler에 Total CPU 기준 스케일링을 넣고, 상한 접근과 메시지 적체를 알리는 모니터링을 더했다.

심각도
–
영향 범위
Merpay accounting event pipeline: new accounting data was not registered in Cloud Spanner and stayed unreconciled, so it could not go on to merchant settlement and monthly closing; falling on a closing day, this caused wide business impact
영향 사용자
–
지역
–
감지까지
–
복구까지
–

증상

월말 마지막 영업일 아침 6시쯤부터 Cloud Pub/Sub 메시지가 밀리기 시작했고, Cloud Spanner에 새 회계 데이터를 등록하지 못하는 상태가 간헐적으로 이어졌다. 밀린 메시지는 1000만 건 규모에 이르렀다. 복구와 재발을 거듭하며 수습하기까지 하루가 넘게 걸렸다.

Cloud Run 인스턴스 수는 maxInstances=100 에 걸려 더 늘지 않았고, 상한까지 찬 인스턴스가 한꺼번에 쓰면서 Spanner CPU가 치솟았다. 한 건을 쓰는 시간이 길어지자 처리량이 더 떨어지고 밀린 메시지가 더 쌓였다. 밀린 시간이 eventMaxAge 를 넘은 메시지는 Spanner에 등록되지 못한 채 버려졌다.

Spanner의 Total CPU는 100%에서 내려오지 않았지만, autoscaler는 Spanner의 계산 용량 단위인 PU, 곧 Processing Unit을 전혀 늘리지 않았다. autoscaler가 보는 High priority CPU는 임계값 60%를 계속 넘지 않아 스케일업 조건을 채우지 못했다.

Cloud Run 인스턴스가 스케일 상한에 닿았을 때 알려 주는 알림이 없었다. 팀에게는 어느 월말에 갑자기 장애가 난 것처럼 보였지만, 나중에 보니 상한에 가까워진 달도 이미 있었다.

증상 처리량 저하데이터베이스 과부하오토스케일러 오동작

원인

촉발
장애 한 달여 전 Cloud Functions를 1st gen에서 Cloud Run 기반 2nd gen으로 옮기면서 --max-instances 를 지정하지 않아, 스케일 상한이 1st gen 기본값인 사실상 무제한에서 2nd gen 기본값인 100으로 내려갔다. 월말 아침 부하가 쉬는 구간 없이 약 40분 이어지자 이 상한에 걸렸다.

감지

–

조치

후속 조치

교훈

관리형 서비스의 세대를 옮길 때는 직접 바꾸지 않은 기본값이 부하가 걸릴 때 문제로 드러난다. 스케일 상한, 동시 처리 수, 타임아웃처럼 평소에는 효과가 보이지 않는 파라미터는 옮길 때마다 명시해야 한다. 장애 원인은 하나가 아닐 수 있으므로 촉발, 증폭, 실제 피해로 이어진 요인을 각각 봐야 한다. 설정 최적화와 그 한계에 가까워지는 것을 알아채는 모니터링은 늘 함께 갖춰야 하고, 물려받아 운영하는 시스템일수록 설정과 전제를 정기적으로 다시 살펴야 한다.

출처

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: 서버리스 함수 플랫폼, 메시지 브로커, 데이터베이스. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.