IL-0312 Gadget · 2024-12-13database
max_connections 를 올리려고 재시작한 주 PostgreSQL 데이터베이스가 다시 올라오지 않아 Gadget 의 모든 앱 요청이 멈췄다
Gadget 은 모든 앱 데이터를 PostgreSQL 데이터베이스 하나에 두고, API 서버의 Node.js 프로세스들은 PgBouncer 로 커넥션 풀을 나눠 쓴다. 이 데이터베이스 업체는 PgBouncer 가 연결할 때마다 auth_query 로 사용자 비밀번호를 가져오는 인증 방식만 지원한다. auth_query 도 연결 하나를 쓰기 때문에, 새 gadget_api 연결 하나가 잠깐 Postgres 연결 둘이 된다. Gadget 은 Black Friday 와 Cyber Monday 에 트래픽이 두 배로 늘 것으로 보고 계획했지만, 실제 부하는 거의 5배로 늘었다. 트래픽이 몰릴 때마다 max_connections 한도에 빠르게 닿아 아주 짧은 장애가 났다. max_connections 는 Postgres 를 재시작해야만 바뀐다. Gadget 은 짧은 장애를 오래 겪기보다 한 번 짧게 멈추고 한도를 크게 올리기로 했다. 그래서 12월 13일 11:00 AM EST 에 긴급 재시작을 결정했다.
업체가 지원하는 재시작은 두 가지였다. 하나는 Postgres 프로세스를 SIGTERM 으로 내리고 같은 호스트에서 다시 띄우는 것이고, 다른 하나는 새 설정으로 replica 를 만들어 leader 를 따라잡게 한 뒤 장애 조치하는 것이다. 데이터베이스가 수 테라바이트라 replica 를 다시 만드는 데 오래 걸리므로, Gadget 은 중단 시간이 더 짧을 것으로 보고 그 자리에서 재시작하는 방법을 골랐다. 11:25 AM 에 재시작하자 Postgres 가 내려간 뒤 다시 올라오지 않았다. Postgres 는 huge_pages=on 설정에 따라 수 기가바이트의 연속 메모리를 요청했다. 그런데 몇 달 동안 쓴 서버의 커널 메모리가 조각나 있어, 전체 여유 메모리는 충분해도 연속 메모리가 모자랐다. 11:45 AM 에 메모리 조각화가 원인이라는 것을 찾았다. 데이터베이스 서버 자체를 하드웨어 리셋해 메모리를 비우자 11:50 AM 에 Postgres 가 올라왔다.
데이터베이스가 멈춘 동안 오토스케일러가 컴퓨트를 줄였기 때문에 서비스가 돌아올 때 거의 4배의 컴퓨트를 다시 띄워야 했다. 이 때문에 12:10 PM EST 까지 지연이 높은 상태가 20분 더 이어졌다. 전체 서비스 저하는 약 45분이었다. Shopify 웹훅은 잃지 않았고 늦게 처리됐을 뿐이다. Shopify 가 웹훅을 재전송했고, Gadget 의 야간 대조 작업이 빠진 전송을 다시 처리했다.
- 심각도
- –
- 영향 범위
- All requests to Gadget applications: fully unavailable for 35 minutes after the restart, then elevated latency until 12:10 PM EST; Shopify webhook data delayed but not lost
- 영향 사용자
- All Gadget applications
- 지역
- –
- 감지까지
- 1m
- 복구까지
- 45m
증상
2024-12-13 11:25 AM EST 에 Gadget 은 커넥션 한도를 올리려고 주 Postgres 데이터베이스를 그 자리에서 재시작했는데, 서버가 내려간 뒤 다시 올라오지 않았다. 그동안 Gadget 앱으로 오는 모든 요청이 실패했다.
재시작 직후 35분 동안 프런트엔드 요청, GraphQL API 요청, 웹훅 처리, 동기화가 모두 멈췄다. 11:50 AM 에 데이터베이스가 올라온 뒤에도 요청은 지연이 높은 채로 처리됐고, 12:10 PM EST 에 모든 지표가 돌아왔다.
Postgres 는 huge_pages=on 설정으로 시작할 때 shared_buffer 용으로 수 기가바이트의 연속 메모리를 커널에 요청한다. 몇 달 동안 계속 돌던 서버는 커널 메모리가 조각나 있었다. 전체 여유 메모리는 충분했지만 huge_pages 할당을 채울 연속 메모리가 없어, 새 Postgres 프로세스가 시작하지 못했다. Gadget 도 데이터베이스 업체도 이런 일이 생길 수 있다는 것을 몰라 원인을 찾는 데 시간이 걸렸다. 11:45 AM 에 메모리 조각화가 원인이라는 것을 찾았다.
데이터베이스가 멈춘 동안 오토스케일러가 부하가 줄었다고 보고 컴퓨트를 줄였다. 서비스가 돌아왔을 때는 그때 떠 있던 컴퓨트의 거의 4배를 띄워야 했다. 이 확장이 프로비저닝 시스템에 부담을 주면서 일부 앱의 요청이 느리게 처리되는 시간이 20분 더 이어졌다.
이 재시작은 플랫폼이 빠르게 커지면서 커넥션 풀 압박으로 생긴 아주 짧은 장애들에 대응하는 작업이었다. 트래픽이 크게 몰리면 PgBouncer 가 auth_query 로 비밀번호를 가져오는 연결까지 만들어 새 gadget_api 연결 하나가 Postgres 연결 둘이 됐고, 그래서 max_connections 한도에 빠르게 닿았다. PgBouncer 는 연결을 만들다 오류가 나면 아주 느리게 재시도했고, 프런트엔드 API 프로세스의 쿼리가 타임아웃됐다. API 서버의 헬스 체크인 SELECT 1 이 제때 돌지 못하면 그 서버는 로드 밸런서에서 빠졌다.
원인
- 촉발
- Gadget 은 플랫폼이 커지며 Postgres 의 max_connections 한도에 닿아 생기던 짧은 장애를 끝내려고, 이 값을 올리는 긴급 설정 변경을 결정했다. max_connections 는 서버를 재시작해야만 바뀌므로 11:25 AM EST 에 데이터베이스를 그 자리에서 재시작했다.
감지
Gadget 은 11:25 AM EST 에 재시작한 데이터베이스가 기대대로 다시 올라오지 않는 것을 11:26 AM 에 확인했다.
조치
- 데이터베이스 서버를 하드웨어 리셋해 메모리 조각화를 없앴고, 11:50 AM EST 에 데이터베이스가 다시 올라왔다.
- 데이터베이스가 멈춘 동안 오토스케일러가 줄인 컴퓨트를 당시의 거의 4배로 다시 띄웠고, 이 확장은 성공했지만 서비스 저하가 20분 더 이어졌다.
후속 조치
- 이번처럼 중단을 일으키는 데이터베이스 작업은 앞으로 replica 를 만들어 장애 조치하는 방법으로만 한다. 새 replica 를 먼저 띄우면 넘어가기 전에 운영 인프라 안에서 동작을 확인할 수 있다.
- Black Friday 와 Cyber Monday 전의 용량 계획을 훨씬 공격적으로 세워 부하 6배 증가에 대비하고, 충분한 시간을 두고 종합 부하 시험을 해서 준비가 됐는지 확인한다.
- 계획된 중단은 상태 페이지와 커뮤니티 Discord 로 미리 알리고, 계획된 점검과 서비스 장애의 차이를 분명히 알리도록 표준 운영 절차를 다듬는다.
- 계획해 온 데이터베이스 샤딩 프로젝트를 끝내 Gadget 앱마다 다른 데이터베이스를 쓰게 한다. 그러면 데이터베이스 설정 변경에 카나리 배포와 블루/그린 구성을 써서 고객 앱에 닿기 전에 충분히 시험하고, 장애 범위도 좁힐 수 있다.
- 이미 진행 중인 무중단 데이터베이스 장애 조치 시스템을 완성해 어떤 앱도 멈추지 않고 데이터베이스 크기와 설정을 바꿀 수 있게 한다.
- 오토스케일러의 최소 규모를 크게 높였다. 앞으로 장애가 나도 컴퓨트가 훨씬 덜 줄어 복구가 빨라진다.
교훈
Gadget 은 그 자리에서 재시작하는 방법을 고른 것을 판단 착오로 보았다. 확신할 수 없고 겪어 본 적 없는 상태로 시스템을 몰아넣은 위험한 선택이었다는 것이다. Gadget 은 모든 실패 상태를 미리 알 수는 없어도 처음 보는 실패 상태로 들어가는 일은 대개 피할 수 있다고 했다. Black Friday 와 Cyber Monday 의 용량 계획이 두 배로 잡혀 거의 5배의 부하를 감당하지 못했다. 제대로 계획했다면 점검 시간을 미리 잡고 스테이징에서 연습할 수 있었다. 새 데이터베이스 설정을 사용자에게 영향 없이 운영과 비슷한 환경에서 시험할 방법도 없었다. 통합 테스트는 합성 Postgres 데이터베이스와 쿠버네티스 인프라에서 돌기 때문에, 몇 달 동안의 운영 부하가 만드는 메모리 조각화나 메모리 사용 문제를 재현하지 못한다.
출처
관련 사례
같은 원인 · 공통 데이터베이스, 데이터베이스 프록시와 커넥션 풀러
IL-0064CPU 를 많이 쓰는 파드가 Google Cloud 노드 하나에 몰려 노드가 커널 패닉으로 죽었고, 다시 배치된 파드가 다음 노드를 또 죽이며 웹사이트와 API 가 멈췄다.Moonlight · 2019같은 원인 · 공통 리눅스 커널, 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0288CDN77 의 ClickHouse 데이터 파이프라인에서 이중화된 인스턴스 세 곳의 ZooKeeperWait 가 수천 초로 치솟아 복제가 한 시간 넘게 느려졌다CDN77 · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0305Intercom 의 PlanetScale 샤드 하나에서 복제본이 CPU 100% 로 주 데이터베이스를 따라가지 못해 받은편지함에 최대 2.2시간 전의 데이터가 보였다Intercom · 2026같은 원인 · 공통 데이터베이스
IL-0390PostHog US Cloud 의 Persons Postgres 데이터베이스 쓰기가 크게 느려져 이벤트와 persons 수집이 최대 2일 지연됐다PostHog · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0287Atlas 와 Cloud Manager 의 모니터링을 받치는 내부 데이터베이스 클러스터의 샤드 하나가 과부하되어 지표 수집과 클러스터 작업이 늦어졌다MongoDB · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0298Wikimedia sessionstore 의 Cassandra 노드가 디스크가 가득 차 잇따라 죽고 쿼럼을 잃어 편집이 실패했다Wikimedia Foundation · 2025같은 원인 · 공통 데이터베이스
IL-0440파드가 보낸 DNS 쿼리 패킷이 노드에서 버려져 클러스터 내부 서비스의 이름 조회가 no such host 와 i/o timeout 으로 자주 실패했다Cybozu · 2024같은 원인 · 공통 리눅스 커널
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0110Grafana Agent 파드가 메모리를 많이 쓰던 노드에 Hangfire 파드가 함께 올라가 시스템 OOM 으로 CrashLoopBackOff 를 반복했고, 노드가 응답을 멈추고 NotReady 가 됐다.DFDS · 2024같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0202갑자기 몰린 쿼리와 데이터베이스 쓰기로 Honeycomb 수집 호스트가 메모리 부족으로 죽었고, 쿠버네티스 CrashLoopBackOff 가 용량 회복을 막아 US1 이벤트 수집이 한 시간 넘게 불안정했다Honeycomb · 2023 · sev1같은 원인 · 공통 데이터베이스
IL-0386Google Cloud 로 옮긴 뒤 GKE 노드에서 Postgres 연결 타임아웃과 memcached i/o timeout 이 간헐적으로 튀었다incident.io · 2023같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0206GKE 파드 안의 Consul 에이전트가 메모리 급증으로 거듭 OOM kill 되어 Rails 파드가 데이터베이스 복제본을 찾지 못했고, 모든 쿼리가 primary 로 몰려 GitLab.com 이 느려졌다.GitLab · 2022같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0176시계열 급증으로 호스트 메모리가 바닥나자 oom_score_adj 999 를 받은 Prometheus 컨테이너가 커널 OOM Killer 에게 먼저 죽어 CrashLoopBackOff 에 빠졌다.LINE · 2020같은 원인 · 공통 리눅스 커널
같은 원인 · 공통 리눅스 커널
IL-0496전역 복제 Cassandra 클러스터에서 모든 리전의 인스턴스 CPU 가 100% 로 치솟아 쿼리가 타임아웃됐고, 같은 부하가 이틀 저녁 더 찾아왔다Ably · 2020같은 원인 · 공통 데이터베이스
IL-0443Aurora MySQL reader 가 이유 없이 재시작한 뒤 writer 의 insert latency 가 7배쯤 오르고 replication lag 가 하루 넘게 이어졌다Repro · 2019같은 원인 · 공통 데이터베이스
IL-0037imagePullPolicy가 Always인 DaemonSet이 크래시 루프를 돌며 이미지를 계속 받아, NAT 세 대를 같이 쓰던 리전 전체가 이미지 레지스트리에서 429로 막혔다Datadog · 2019같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0211Mandrill 의 PostgreSQL 샤드 하나가 트랜잭션 ID 랩어라운드를 막으려고 쓰기를 멈춰 약 40시간 동안 메일 발송이 줄었다Mailchimp · 2019같은 원인 · 공통 데이터베이스
같은 원인 · 공통 리눅스 커널
같은 원인 · 공통 데이터베이스
IL-0334RDS MySQL 의 bookmarks 테이블이 2TB 파일 크기 한도에 닿아 INSERT 가 실패하고 Instapaper 가 31시간 동안 멈췄다Instapaper · 2017같은 원인 · 공통 데이터베이스
IL-0333사양을 낮춘 RDS PostgreSQL 인스턴스가 피크 부하를 못 버텨 헬스 체크가 실패하고 Buildkite 대시보드가 몇 시간 동안 내려갔다Buildkite · 2016같은 원인 · 공통 데이터베이스
IL-0282US-East 리전의 DynamoDB 스토리지 서버들이 멤버십 정보를 받지 못해 스스로 요청 처리에서 빠지면서 고객 요청의 약 55% 가 실패했다Amazon Web Services · 2015같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0312
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0312/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.