IL-0518 freedesktop.org · 2018-07-17kubernetesgcp
GKE 위 gitlab.freedesktop.org 의 디스크가 모자라 저장소를 늘리려던 30분 작업이 GitLab 전체의 4시간 장애가 됐다
gitlab.freedesktop.org 는 Google Cloud Platform 의 Google Kubernetes Engine 위에서 Helm 차트로 돌고, 클러스터는 Kubernetes 1.10.x 였다. 서비스는 gitlab.org 의 GitLab CE 이미지로 Kubernetes 파드 하나에서 돌았고, Git 저장소와 PostgreSQL 데이터는 PersistentVolumeClaim 에 있었다. 이 Kubernetes 버전에서는 PersistentVolume 의 크기를 바꿀 수 없었다. 매일 5am UTC 에 도는 백업은 데이터를 로컬 디렉터리에 모은 뒤 압축하지 않은 tarball 을 만들어 올리므로, 백업 크기의 두 배가 넘는 빈 공간이 필요했다.
7월 17일 9am UTC 조금 넘어 Mesa 포크가 많이 생기며 디스크가 위급해져 API 요청 일부와 백업 cron 작업이 실패했다. 관리자는 몇 시간 전에야 알린 작업 시간 2pm 부터 2:30pm UTC 에 볼륨을 늘리기로 했다. 방화벽으로 들어오는 트래픽을 막은 뒤 수동 백업과 디스크 스냅샷을 떴는데, 백업 크기가 크게 늘어 예상보다 훨씬 오래 걸렸다. 클러스터를 1.10.x 로 올리고 볼륨 크기를 바꾸려 했으나 고정 크기 디스크는 늘릴 수 없었고 GKE 는 이 알파 기능을 켤 수 없었다. 볼륨을 다시 만들고 데이터를 옮겨야 했다.
옮기는 과정에서 Helm 이 없는 자원을 끝없이 기다렸고, 관리자의 인터넷 연결이 크게 나빠졌다. GCE 스냅샷은 살아 있는 디스크에 바로 복원할 수 없었고, GCE 디스크에서 만든 PV/PVC 는 디스크 내용을 지웠다. 결국 Kubernetes 서비스를 모두 멈추고 새로 키운 PV 와 옛 디스크 스냅샷을 임시 GCE VM 에 함께 붙여 복사했다. 첫 복사에서 MySQL 하위 디렉터리가 빠져, 볼륨을 새로 만들고 올바른 디렉터리 구조로 다시 복사했다. 14:00 부터 18:00 UTC 까지 GitLab 전체가 내려갔고, 18:45 UTC 쯤 공개 접근을 되살렸다. 데이터 손실은 없었다.
- 심각도
- –
- 영향 범위
- The GitLab web UI, API and HTTPS git clones on gitlab.freedesktop.org were completely unavailable; anongit and cgit kept working
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- 4h 45m
증상
9am UTC 조금 넘어 gitlab.freedesktop.org 의 디스크 공간이 위급하다는 것이 분명해졌다. 디스크가 모자라 일부 API 요청이 실패했고, 매일 도는 백업 cron 작업도 같은 이유로 실패하기 시작했다. 관리자는 사람들이 IRC 에서 장애를 말하고 나서야 문제를 알았다.
디스크를 채운 것은 최근 많이 만들어진 Mesa 포크였다. Mesa 는 저장소 하나에 약 2.1GB 가 들고, 포크는 객체를 나누지 않고 git clone 을 그대로 하므로 포크 사이에 저장 공간이 중복 제거되지 않았다. Mesa 나 커널 포크가 하나만 더 생기면 디스크가 완전히 바닥날 상황이었다.
디스크를 늘리려고 잡은 2pm UTC 부터 2:30pm UTC 까지의 작업 시간 동안 GitLab 웹 UI, API, https://gitlab.freedesktop.org 를 통한 HTTPS git clone 이 모두 연결 타임아웃을 냈다. 관리자가 방화벽 규칙으로 자기 IP 에서 오는 것을 빼고 들어오는 트래픽을 모두 버렸기 때문이다. anongit 과 cgit 은 계속 정상으로 돌았다.
백업과 디스크 스냅샷이 예상보다 훨씬 오래 걸려 작업 시간을 3pm UTC 까지 늘렸다. 그 뒤 클러스터를 Kubernetes 1.10.x 로 올리고 PersistentVolume 의 크기를 바꾸려 했으나 곧바로 실패했다. 크기가 고정된 디스크를 그 자리에서 늘릴 수 없었고, GKE 에서는 디스크 크기 변경이 켤 수 없는 알파 기능이었다.
복구하는 동안 여러 방법이 막혔다. Helm 은 오타로 없는 자원을 기다리며 바로 실패하지 않고 끝없이 멈췄다. GCE 디스크에서 새 Kubernetes PV/PVC 를 만들자 디스크의 내용이 모두 지워졌다. 새 VM 에서 스냅샷을 새 볼륨으로 복사한 뒤에는 Git 데이터는 보였다. 그러나 Kubernetes 는 '$disk/mysql/' 를 MySQL 데이터 볼륨의 루트 마운트 지점으로 쓰는데, 복사가 이 하위 디렉터리 구조를 지키지 못해 MySQL 이 빈 데이터베이스를 봤다.
원인
- 촉발
- 최근 Mesa 포크가 많이 만들어지면서 GitLab 클러스터의 디스크가 거의 찼다. 관리자가 디스크 볼륨을 늘리려고 몇 시간 전에 알린 작업 시간에 GitLab 을 내리고 저장소 업그레이드를 시작했다.
감지
사람들이 IRC 에서 장애를 말하고 나서야 문제를 알았다. 디스크 사용량이나 API 오류율을 지켜보고 알리는 모니터링은 없었다.
조치
- 관리자가 일하던 IP 를 빼고 GitLab 으로 들어오는 트래픽을 모두 버리도록 방화벽 규칙을 고쳤다. 그래서 다른 사람들은 잠깐씩 나는 실패 대신 연결 타임아웃만 봤고, 관리자는 그 시점 뒤의 변경을 잃을 걱정 없이 한 시점의 데이터를 스냅샷으로 뜰 수 있었다.
- GCS 에 있는 것을 뺀 GitLab 데이터 전부를 손으로 백업했다. 평소의 백업 Rake 작업은 데이터 모으기까지만 돌렸다. tar 를 돌리면 디스크가 넘치므로, tar 출력을 SSH 로 로컬의 안전한 기계에 있는 암호화된 파티션에 흘려보냈다.
- 모든 데이터 디스크의 스냅샷을 떴다. Kubernetes PersistentVolume 마다 Google Compute Engine 디스크가 있어 손으로 스냅샷을 뜨고 태그를 붙였다.
- Kubernetes 서비스를 모두 멈추고, 새로 키운 PV 와 옛 디스크 스냅샷을 새 임시 GCE VM 에 함께 붙여 직접 복사한 뒤 VM 을 멈추고 Kubernetes 를 다시 켰다.
- 첫 복사가 MySQL 하위 디렉터리를 지키지 못해 MySQL 이 빈 데이터베이스를 봤다. 그래서 어긋남을 막으려고 자원을 모두 지우고, 비어 있는 새 볼륨과 새 GCE VM 을 만들어 올바른 디렉터리 구조로 다시 복사했다. 저장소와 사용자 목록이 전과 같은지, UI 일부와 흔한 작업 흐름이 되는지 손으로 확인했다. 다음 날 아침 백업 cron 작업이 또 다른 장애 없이 성공하도록 전체 백업을 백그라운드로 돌렸다.
- 18:45 UTC 쯤 공개 접근을 되살리고 장애가 끝났다고 알렸다.
후속 조치
- 모니터링, 로깅, 알림을 켠다. 이 일은 gitlab.freedesktop.org 의 freedesktop 이슈 8번으로 오래전에 열어 두었고, 이 분야를 아는 사람의 도움을 구한다.
- 소통을 개선한다. 이번 장애는 급하게 잡혀 IRC 에만 알렸고 메일링 리스트에는 알리지 않았으며, 장애가 길어질 때도 리스트에 알리지 않았다. 이런 일을 훨씬 잘 알리는 방법을 찾는다.
- Cloud Console 웹 UI 는 페이지 사이 로딩이 길고 이동이 많아 대화형 작업에 거의 쓸 수 없다. 그 대신 콘솔 클라이언트를 실제 상황에서 쓰는 데 익숙해지고, 되도록 콘솔 클라이언트를 쓴다.
- GitLab 관리자가 한 명뿐인 상태는 지속할 수 없으므로 관리자를 늘린다. 특히 Kubernetes 배포에 익숙한 사람이 있으면 큰 도움이 된다.
- Omnibus 컨테이너에서 벗어난다. 지금은 gitlab.fd.o 뒤의 모든 서비스가 파드 하나에 있고, 시작할 때 Chef 가 2~3분 동안 파일 시스템을 설정해 변경마다 최소 4~5분 동안 GitLab 전체가 내려간다. GitLab 이 만드는 cloud native 배포로 옮기려면 몇 시간의 장애가 필요하므로 충분히 앞서 알린다.
- 저장소를 크기를 바꿀 수 있는 볼륨이나 GCS 로 옮긴다. 할 수 있는 만큼 클라우드 스토리지로 옮기고, GCP 에서 Kubernetes 1.11.x 를 쓸 수 있게 되면 디스크를 필요에 따라 커지는 볼륨으로 다시 만든다.
교훈
잘못된 것이 많았다. 디스크 사용량이나 API 오류율을 재고 알리는 장치가 없어서 IRC 에서 사람들이 말하고 나서야 알았다. GitLab 관리자가 한 명뿐이라 그 사람이 없으면 다른 사람이 Kubernetes 계정을 만들고 배포를 익히는 데서 시작해야 했고, 그 관리자도 Kubernetes 운영 경험이 비교적 적었다. 백업 크기가 크게 늘었으니 백업이 전보다 오래 걸린다는 것을 미리 알았어야 했고, 그렇지 않더라도 작업 시간을 알리기 전에 시험 작업으로 걸리는 시간을 잴 수 있었다. 오전에 일부 변경을 시험했지만 실제와 똑같은 변경은 시험하지 않았고, 디스크 스냅샷 같은 것은 시험을 더 철저하고 실제에 가깝게 할 수 있었다.
잘된 것도 있었다. 파괴적인 작업을 시작한 뒤로 어느 시점에나 백업이 적어도 세 벌 있어서 데이터를 영영 잃을 위험은 없었다. 다만 그중 일부는 복원 원본으로 쓰려면 받아들일 수 없을 만큼 긴 장애가 필요했다. 데이터의 많은 부분이 GCS 에 있었기 때문에 이번 같은 작업이 필요해지는 시점이 오래 늦춰졌다.
출처
참고 문서
관련 사례
IL-0067Redis 마스터의 50Mi 영구 볼륨이 가득 차 AOF 가 깨졌고, Redis 파드가 크래시 루프에 빠져 Klamm 운영 서비스가 멈췄다BC Government · 2025 · sev1같은 원인 · 공통 영구 볼륨과 스테이트풀셋
IL-0085Hetzner Cloud 의 k3s 컨트롤 플레인 노드가 메모리 부족으로 꺼진 뒤 클러스터가 Hetzner API 호출 한도를 다 써 버려 모든 고객 사이트가 약 7.5시간 멈췄다.Magic Pages · 2025같은 원인 · 공통 영구 볼륨과 스테이트풀셋
IL-0232codfw WikiKube 클러스터에서 Calico 구성 요소가 자원 한도에 걸려 calico-node 파드가 하나씩 죽고 노드가 차례로 빠지면서 MediaWiki 지연과 오류가 났다Wikimedia Foundation · 2024같은 원인 · 공통 Helm
같은 원인 · 공통 클라우드 블록 스토리지
같은 원인 · 공통 영구 볼륨과 스테이트풀셋
같은 원인 · 공통 클라우드 블록 스토리지
IL-0401Kubernetes 프로젝트의 Prow 빌드 클러스터 노드가 GCP 디스크 IOPS 스로틀링에 걸려 테스트가 이상하게 실패하고 PR 머지가 몇 시간 멈췄다Kubernetes project · 2019같은 원인 · 공통 클라우드 블록 스토리지
IL-0040비운영 쿠버네티스 클러스터에 Helm 차트를 한꺼번에 설치하자 작은 gp2 루트 볼륨의 IOPS 가 바닥나, 워커 노드 세 대에서 kubelet 이나 Docker 가 내려가 세 대 모두 NotReady 가 됐다Saltside · 2017같은 원인 · 공통 Helm
같은 원인 · 공통 클라우드 블록 스토리지
같은 원인 · 공통 클라우드 블록 스토리지
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0518
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0518/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.