가용성/2026년 9월 25일 06시 사고
9월 25일 6시 2분부터 6시 6분까지 Route 53의 특수:빈문서 상태 검사가 다섯 번 연달아 실패했고, 그 5분 동안 페미위키 이용이 불가했습니다. php-fpm 워커가 모두 점유되어 요청이 처리되지 못했습니다. 컨테이너는 재시작되지 않았고 OOM도 없었으며, 이 시간대 가용 메모리는 가장 낮을 때도 373 MiB였습니다. 메모리가 아니라 워커가 말라붙은 사고입니다. 대기열은 6시 19분에 저절로 비었습니다. 7시 33분에 캐시 용량 문제를 고치고, 8시 8분에 사이트 전체 상한을 10분에 1,600건으로 내렸습니다.
| 시각 | 일어난 일 |
|---|---|
| 06:01 | 워커 대기열이 0에서 55로 오름. php-fpm 워커 16개가 전부 점유됨 |
| 06:02 | Route 53 상태 검사가 실패하기 시작 |
| 06:05 | 대기열이 512에 닿음. 512는 listen 백로그의 상한이라 그 위로는 오르지 않음 |
| 06:06 | Route 53 경보로 바뀌어 알림. Grafana의 워커 대기열 경보와 429 경보도 알림 |
| 06:13 | 대기열이 512에서 내려오기 시작 |
| 06:17 | 대기열 202 |
| 06:19 | 대기열 0. 저절로 회복 |
| 07:33 | mwcache의 용량 문제를 고친 이미지 배포.[1] |
| 08:08 | 사이트 전체 상한을 10분에 1,600건으로 내린 배포가 끝남.[2] |
원인
6시부터 6시 20분까지 20분 동안 요청이 10,684건 들어왔습니다. /index.php나 /api.php가 6,535건, 쿼리 없이 /w/ 문서를 읽은 것이 2,453건, 쿼리가 붙은 /w/가 54건이었고, 나머지 1,642건쯤은 정적 파일이었습니다.
이 가운데 비 로그인 검색, 차이, 옛 판, 역사, 정보 요청이 4,532건이었습니다. 속도 제한이 621건을 429로 거절했고 3,911건이 PHP까지 갔습니다. 분당 195건입니다. 전날 23시 40분부터 24시까지 같은 모양의 요청은 20분에 3,228건, 분당 161건이었고 아무 일도 없었습니다. 분당 161건은 버텼고 195건에서는 쓰러졌습니다.
당시 사이트 전체 상한은 10분에 2,000건, 곧 분당 200건이었습니다.[3] 9월 24일 16시 사고 직후에 3,000건에서 내린 값입니다.[4] 사고 구간에 PHP까지 간 양은 10분에 1,955건으로 상한에 거의 닿았지만, 서버는 그보다 적은 양에서 이미 쓰러졌습니다. 상한이 서버의 한계보다 위에 놓여 있어서, 한계를 넘는 양이 상한에 걸리지 않고 PHP까지 갔습니다.
요청을 보낸 쪽은 자신을 크롤러라고 밝히지 않았습니다. 사고 구간의 User-Agent 500건을 뽑아 보니 봇을 자처한 것은 하나도 없었고, 모두 그럴듯한 브라우저였습니다. iPhone은 18.2부터 18.6까지, Android는 14와 15였고 기기 모델도 제각각이었습니다. 주소도 넓게 퍼져 있어서, 20분 동안 한 주소가 보낸 요청은 많아야 43건이었습니다. 그래서 /24별 제한(10분에 300건과 100건)은 한 번도 발동하지 않았고, 이 트래픽을 볼 수 있던 것은 사이트 전체 상한뿐이었습니다. User-Agent로 거르는 규칙도 이런 트래픽에는 소용이 없습니다.
캐시도 받쳐 주지 못했습니다. 당시 mwcache는 ristretto의 내부 비용 계산 때문에 실제로는 항목을 17개밖에 담지 못했습니다.[5] 쿼리 없는 문서 읽기 2,453건 가운데 캐시에서 나간 것은 749건뿐이고, 1,704건이 PHP까지 갔습니다.
고친 것
- mwcache의 용량 문제를 고쳤습니다. docker-mediawiki#1048에서 고쳐 7시 33분에 infra#661로 배포했습니다. 배포 뒤 서로 다른 문서 25개를 한 번씩 읽어 데운 다음 다시 읽으니 25개 모두 캐시에서 나왔습니다.
- 사이트 전체 상한을 1,600건으로 내렸습니다.[6] 전날 밤 10분에 1,610건은 버텼고 이날 1,955건에서는 쓰러졌으니, 1,600건은 이 서버가 버티는 것을 실제로 본 가장 높은 값입니다. infra#687에서 고쳐 8시 8분에 배포했습니다.
남은 문제
- 상한을 내린 만큼 이용자가 거절당하는 일이 늘어납니다. 배포 전 7일 동안의 10분 구간 가운데 1,600건을 넘은 것은 21%로, 2,000건일 때의 10.6%보다 두 배 많습니다. 걸리는 것은 로그인하지 않은 상태의 검색, 차이, 옛 판 보기이고, 로그인한 사람은 제한을 받지 않습니다. 모두가 사이트를 열지 못하는 것보다 낫다고 보고 이 대가를 받아들였습니다. 로그인하지 않은 이용자가 크롤러 몫까지 429를 받는 문제는 infra#626에 적어 두었습니다.
- 캐시 용량을 바이트 예산으로 정하는 일은 femiwiki#452에 적어 두었습니다.
- 이번 요청은 봇을 자처하지 않았고 주소도 흩어져 있어서, User-Agent나 주소로 거르는 규칙으로는 막을 수 없었습니다. 손으로 쓴 차단 규칙을 접는 일은 femiwiki#569에서 다룹니다.
출처
- ↑ https://github.com/femiwiki/infra/pull/661
- ↑ https://github.com/femiwiki/infra/pull/687
- ↑ https://github.com/femiwiki/infra/blob/ec38a0d7973bc91c498c1ff1e71f62a71c2fae23/docker/container.tf#L33
- ↑ https://github.com/femiwiki/infra/pull/648
- ↑ https://github.com/femiwiki/docker-mediawiki/pull/1048
- ↑ https://github.com/femiwiki/infra/blob/6e5777da89b96211c24766cb8fc229e7b3a7fe2f/docker/container.tf#L35