IL-0591 Gitpod · 2022-04-11kubernetesgcp
Gitpod 워크스페이스가 새 클러스터 세대 배포 뒤 열흘 넘게 모든 리전에서 느려지고 시작 실패와 데이터 유실이 났다
ws39 는 워크스페이스 시작 시간을 줄이고 사용자에게 CPU 를 더 주려고 만든 새 세대 워크스페이스 클러스터다. Gitpod 은 ws39 를 4월 8일 20:08 UTC 에 XL 을 뺀 모든 리전 클러스터에, 4월 9일 17:45 UTC 에는 XL 클러스터에도 배포했다. 이 배포는 1TB 디스크를 작은 RAID0 디스크 여럿으로 나눴는데, 디스크가 작을수록 낼 수 있는 IOPS 가 줄었다. CPU 제한 기능의 개선에는 버그가 있어 제한 효과가 떨어졌고 노이지 네이버가 너무 많이 생겼다. 같은 비용으로 더 빠른 머신 유형으로 올리고 워크스페이스 네트워킹에서 slirp4netns 를 빼면서 워크스페이스가 쓸 수 있는 CPU 가 늘어, 노이지 네이버의 영향도 커졌다. 네트워킹 개선에는 네트워크 프로세스와 컨테이너 작업을 느리게 만드는 결함도 있었다. 디스크 IO 를 제한할 방법이 없었던 것이 이 모두를 키웠다. ws39 는 바로 앞 배포와 3.5주밖에 떨어지지 않은 큰 배포였다. 그런데도 Gitpod 은 4월 8일에 시작해 9일에 ws38 을 모두 ws39 로 바꿨고, 위험 평가도 모자랐다.
4월 11일 IO 병목을 확인한 Gitpod 은 워크스페이스 시작을 ws38 로 돌려 15:37 UTC 에 옮겼다. 이어서 t2d 대신 c2d 머신과 로컬 SSD 를 쓴 ws39a 를 만들어 21:29 UTC 에 트래픽을 옮겼다. c2d 머신이 평소 쓰던 영역에 없어 ws39a 는 새 리전에 배포해야 했고, GCP 할당량 승인도 늦었다. 또 한 고객이 gitpod.io 의 Let's Encrypt 한도를 써 버려 새 하위 도메인의 TLS 인증서를 받지 못했다. 4월 12일 IO 문제가 다시 나자 cgroups 기반 IO 제한기를 운영에 넣었다. 이 ws-daemon 배포가 ws-eu39xl 의 CPU 제한을 깨면서 노드가 응답하지 않게 됐고 사용자 데이터가 유실됐다. Gitpod 은 ws38 로 돌아가려 했지만, 컨트롤 플레인에 배포된 호환되지 않는 API 변경 때문에 ws38 을 쓸 수 없었다. 그래서 임시로 리전 EU 클러스터로 트래픽을 보낸 뒤 ws-eu39bxl 을 새로 만들었다. 4월 13일에는 Git CVE 수정에 필요한 설정 변경이 빠져 테스트가 실패하는 바람에 작업이 막혔다. 같은 날 워크스페이스 네트워킹의 결함도 찾았다. 4월 14일에는 1TB 부트 디스크로 돌아간 ws40 으로 트래픽을 옮겼지만 IO 제한기는 여전히 제대로 동작하지 않았다.
4월 17일과 18일에는 ws40 에서 암호화폐 채굴 방어가 깨져 채굴이 퍼졌다. 엔지니어들이 과도한 CPU 를 쓰는 워크스페이스를 지우고 부정 사용자를 막는 동안, 4월 18일 23:35 UTC 에 동적 CPU 제한의 주된 문제를 찾았다. ws38 에는 없던 ws-daemon 변경을 되돌린 ws34 클러스터를 4월 19일에 배포해 16:34 UTC 에 전환을 마치고 ws40 을 내렸다. 4월 20일에도 성능 문제가 이어지자 여러 실험 끝에 한 라이브러리로 IO 제한을 다시 만들었다. 이어서 워크스페이스 사이에 디스크 IO 를 공정하게 나누는 ws41xl 을 만들어 25% 부터 트래픽을 옮겼다. XL 클러스터의 트래픽 비율은 4월 21일 18:46 UTC 에 100% 가 됐고, 일반 리전 클러스터의 비율은 4월 22일 02:00 UTC 에 100% 가 됐다.
- 심각도
- –
- 영향 범위
- Gitpod workspaces: sustained performance degradation, failed workspace starts at times, and data loss for users on the ws-eu39xl cluster
- 영향 사용자
- Gitpod users in all regions, including unleashed customers and Gitpod staff on the EU XL cluster
- 지역
- all regions
- 감지까지
- –
- 복구까지
- –
증상
2022년 4월 11일 13:17 UTC 에 고객과 Gitpod 직원들이 워크스페이스가 간헐적으로 응답하지 않는다고 알렸다. 4월 8일과 9일에 새 세대 워크스페이스 클러스터 ws39 를 모든 리전에 배포한 직후였다.
노드와 파드의 CPU 사용량은 정상이었다. 한도에 닿은 것이 없었고, CPU 제한이 깨졌거나 노이지 네이버가 있다는 징후도 없었다. 13:58 UTC 에 엔지니어들은 워크스페이스가 CPU 바운드가 아니라 IO 바운드라는 것을 알아냈다.
4월 12일 11:53 UTC 에 워크스페이스가 다시 IO 바운드가 됐다. 같은 날 새 IO 제한기를 담은 ws-daemon 을 배포하자 ws-eu39xl 클러스터의 CPU 제한이 깨져 노드의 모든 코어가 소진됐다. 노드 위의 서비스가 멈췄고, ssh 로도 kubectl 로도 노드에 붙을 수 없었으며, 이 클러스터의 사용자는 데이터를 잃었다. 엔지니어들은 13:49 UTC 무렵 EU XL 트래픽을 ws-eu38xl 로 옮겼다. 그러나 이 클러스터가 트래픽을 받지 못한다는 것을 14:21 UTC 에야 알았고, 그동안 EU XL 사용자는 워크스페이스를 시작하지 못했다.
모든 리전의 워크스페이스에 걸친 성능 저하는 그 뒤에도 4월 22일까지 양상을 바꿔 가며 이어졌다. 4월 14일 ws40 으로 옮긴 뒤에도 노드가 IO 포화에 닿는 순간이 남았다. 4월 17일 05:18 UTC 와 4월 18일 12:36 UTC 에는 워크스페이스 노드의 CPU 부하가 올라갔다. ws40 에서 암호화폐 채굴 방어가 깨져 채굴이 퍼지면서 많은 노드를 쓸 수 없게 됐다. 과도한 CPU 를 쓴 사용자 대부분은 Gitpod 의 연산 자원만 노리고 자동으로 만든 GitHub 계정이었다. 4월 20일 13:45 UTC 에도 여러 고객이 4월 8일 배포 전에는 멀쩡하던 작업에서 성능 문제를 겪었다.
엔지니어들은 처음에 디스크 IO 대역폭이 모자란 것으로 보고 더 빠른 머신 유형과 로컬 SSD 를 쓴 ws39a 클러스터를 만들었지만, 다음 날 IO 문제가 다시 났다. 개발·테스트 환경에서 잘 돌던 cgroups 기반 IO 제한기는 운영에서 다르게 동작해 클러스터를 무너뜨렸다. 워크스페이스 네트워킹의 결함은 찾고 고치기 어려웠다. 개발 환경은 IP forwarding 을 켜 두었고 운영 환경은 꺼 두었기 때문이다.
원인
- 촉발
- 2022년 4월 8일과 9일에 새 세대 워크스페이스 클러스터 ws39 를 모든 리전에 배포한 것이 시작이었다.
감지
2022년 4월 11일 13:17 UTC 에 고객과 Gitpod 직원들의 신고로 알았고, incident.io 연동으로 Slack 에 장애를 열었다. 4월 12일에도 고객과 직원의 신고로 알았고, 4월 17일에는 엔지니어들이 워크스페이스 노드의 CPU 부하가 오른 것을 보고 내부 장애를 열었다.
조치
- IO 제한 기능이 없어 워크스페이스 시작 트래픽을 이전 세대 클러스터 ws38 로 돌렸고, 4월 11일 15:37 UTC 에 마쳤다.
- t2d 대신 더 빠른 c2d 머신 유형과 로컬 SSD 를 쓰는 새 클러스터 ws39a 를 만들어 트래픽을 옮겼다.
- Let's Encrypt 속도 제한 문제를 풀고, Let's Encrypt 에 한도를 올려 달라고 요청했다.
- ws38 클러스터를 쓸 수 없어 EU XL 트래픽을 임시로 리전 EU 클러스터로 보내 워크스페이스를 다시 시작할 수 있게 했다. 그리고 망가진 ws-eu39xl 을 대신할 ws-eu39bxl 클러스터를 새로 만들었다.
- stopping 상태에 걸린 워크스페이스를 정리해 사용자가 다시 시작할 수 있게 했다.
- 4월 8일 배포 전처럼 1TB 부트 디스크를 단 노드로 ws40 을 만들어 디스크 IO 대역폭을 늘렸다.
- 모니터링으로 노드의 작업을 직접 살펴 과도한 CPU 시간을 쓰는 워크스페이스를 찾아 시스템에서 지웠다.
- 노드를 손으로 뒤지고 채굴 탐지 시스템을 함께 써서 부정 사용자를 찾아 Gitpod 사용을 막았다.
- ws38 세대에는 없던 ws-daemon 변경을 되돌려 CPU 제한을 다시 안정시켰고, 이 수정을 ws34 클러스터에 담아 배포했다.
- 새 ws34 배포에서 워크스페이스 포트로 가는 ACME HTTP 요청을 막아 Let's Encrypt 한도에 다시 걸리지 않게 했다.
- 워크스페이스 사이에 디스크 IO 를 공정하게 나누는 ws41xl 클러스터와 일반 리전용 ws41 클러스터를 만들어 트래픽을 단계적으로 옮겼다.
후속 조치
- 테스트 결과가 진행하면 안 되는 배포를 미리 걸러 내도록 테스트 자동화에 계속 투자한다.
- 새 클러스터로 트래픽 비율을 옮기는 간격을 배포마다 다르게 하고, 큰 배포일수록 단계 사이를 길게 둔다.
- 워크스페이스 클러스터 배포에 위험 평가 단계를 넣어 변경 묶음의 크기와 배포 속도를 정하고, 이 단계를 자동화하는 것을 목표로 한다.
- 팀 사이 의존이 롤백을 막지 않도록 팀끼리 주는 영향을 계속 줄인다.
- PR 을 머지하기 전에 하위 호환이 깨지는 변경을 찾는 리뷰 기준을 더하고, 그런 PR 은 나눠서 배포를 롤백하기 어려운 상황을 피한다.
- 장애 완화의 첫 수단으로 롤백을 권하도록 절차를 고쳤고, 이 과정을 자동화하는 방법을 논의한다.
- 상태 페이지의 워크스페이스 서비스가 장애를 주황이나 빨강으로 제대로 표시하도록 고쳤고, 플레이북도 그에 맞게 고쳤다.
- 장애 중 내부 소통을 Slack 채널 하나와 Google Meet 하나로 모으고, 장애 절차를 그에 맞게 고쳤다.
- 당번 체계를 다시 짜서 5월 9일부터 엔지니어링 인력의 10% 가 늘 당번을 맡고, 그 시간으로 관측성과 알림과 시스템 안정성을 개선한다.
교훈
4월 11일에 롤백을 결정했다면 가장 좋았다. 롤백하지 않고 수정을 계속 만들어 배포하면서 고객은 필요 이상으로 오래 성능 저하를 겪었고 일부는 데이터를 잃었다. 규모가 크고 바로 앞 배포와 가까운 변경을 너무 빨리 내보냈고, 테스트 결과가 배포를 막았어야 했다. 배포 묶음을 작게 하고 배포를 자주 한다는 원칙을 ws39 에서 지키지 못했고, 팀 사이 의존이 롤백을 어렵게 만들었다. 기여 요인 가운데 일부는 막을 수 있었다.
출처
관련 사례
–
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0591
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0591/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.