{"id":"IL-0591","org":"Gitpod","date":"2022-04-11","docs":[],"title":{"en":"Gitpod workspaces in all regions became IO bound and unresponsive for days after the ws39 workspace cluster release","ko":"Gitpod 워크스페이스가 새 클러스터 세대 배포 뒤 열흘 넘게 모든 리전에서 느려지고 시작 실패와 데이터 유실이 났다"},"domain":["kubernetes","gcp"],"impact":{"scope":"Gitpod workspaces: sustained performance degradation, failed workspace starts at times, and data loss for users on the ws-eu39xl cluster","users":"Gitpod users in all regions, including unleashed customers and Gitpod staff on the EU XL cluster","region":"all regions"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Gitpod concluded that it should have committed to rolling back on April 11th instead of building and shipping fixes, which kept customers on degraded workspaces longer than necessary. A large release so soon after the previous one needed a risk assessment and slower traffic shifts, and backwards incompatible changes that block a rollback need to be found before pull requests are merged. Failure-free operations require experience with failure.","ko":"4월 11일에 롤백을 결정했다면 가장 좋았다. 롤백하지 않고 수정을 계속 만들어 배포하면서 고객은 필요 이상으로 오래 성능 저하를 겪었고 일부는 데이터를 잃었다. 규모가 크고 바로 앞 배포와 가까운 변경을 너무 빨리 내보냈고, 테스트 결과가 배포를 막았어야 했다. 배포 묶음을 작게 하고 배포를 자주 한다는 원칙을 ws39 에서 지키지 못했고, 팀 사이 의존이 롤백을 어렵게 만들었다. 기여 요인 가운데 일부는 막을 수 있었다."},"related":[],"sources":[{"url":"https://www.gitpod.io/blog/sustained-performance-degradation","kind":"blog","archived":"https://web.archive.org/web/20250618174733/https://www.gitpod.io/blog/sustained-performance-degradation"}],"summary":{"en":"Gitpod released ws39, a new generation of its workspace clusters, on April 8th and 9th, 2022, to cut workspace startup times and give users more CPU. The release came 3.5 weeks after the previous one, and ws39 replaced ws38 within about a day. Several of its changes combined. The 1TB disk was split into many smaller RAID0 disks to separate IO for image pulls and workspaces, but the IOPS a disk can reach decrease with disk size, so disk IO was slower than expected. An enhancement to CPU limiting had a bug that let too many noisy neighbours through, and their effect grew because faster machine types and the removal of slirp4netns from workspace networking made more CPU available to workspaces. The workspace networking changes also had defects that slowed network processes, including containerized workloads. Gitpod had no IO limiting at the time.\n\nOn April 11th workspaces became IO bound. Gitpod moved workspace starts back to ws38 by 15:37 UTC and then to new ws39a clusters with c2d machines and local SSDs by 21:29 UTC. Building ws39a meant deploying to a new region because c2d was not available in the usual zones, waiting for a slow GCP quota approval, and getting past a Let's Encrypt rate limit that a customer's certificate requests had used up for gitpod.io. On April 12th workspaces were IO bound again, and the new IO limiter rolled out with ws-daemon broke the CPU limiter on ws-eu39xl, made its nodes unresponsive and lost users' data. Because a breaking control plane API change had made ws38 incompatible, EU XL traffic went to the regional EU cluster while the replacement cluster ws-eu39bxl was built. ws40, with 1TB boot disks and a rewritten IO limiter, took all traffic on April 14th, but nodes still saturated their IO.\n\nOn April 17th and 18th the broken CPU limiting let crypto-currency miners, most of them on auto-generated GitHub accounts, take over many workspace nodes, and the team removed their workspaces and blocked them by hand. At 23:35 UTC on April 18th the team found the main issue in the dynamic CPU limiting and fixed it by rolling back a ws-daemon change that ws38 did not have; the fix shipped in clusters named ws34, an old name used because of the Let's Encrypt limit, and was fully rolled out by 16:34 UTC on April 19th. IO limiting built on a library chosen after many experiments went out with ws41xl from 23:48 UTC on April 20th, and traffic moved gradually until the XL clusters were at 100% at 18:46 UTC on April 21st and the standard regional clusters at 02:00 UTC on April 22nd. Gitpod's review says the April 8th changeset lacked risk assessment and was released too quickly, test results should have stopped it, and committing to a rollback on April 11th would have been best.","ko":"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 로 바꿨고, 위험 평가도 모자랐다.\n\n4월 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 제한기는 여전히 제대로 동작하지 않았다.\n\n4월 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% 가 됐다."},"trigger":{"en":"The ws39 generation of workspace clusters, released to all regional clusters on April 8th at 20:08 UTC and to the XL clusters on April 9th at 17:45 UTC, split the 1TB disk into smaller RAID0 disks, changed CPU limiting and workspace networking, and moved to faster machine types.","ko":"2022년 4월 8일과 9일에 새 세대 워크스페이스 클러스터 ws39 를 모든 리전에 배포한 것이 시작이었다."},"severity":"unknown","symptoms":["throughput-degraded","node-high-cpu","node-not-ready","data-loss"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"Customers and Gitpod staff reported on April 11th at 13:17 UTC that workspaces were intermittently unresponsive, and the team opened an incident in Slack through its incident.io integration. On April 17th the team noticed increased CPU load on the workspace nodes and opened an internal incident.","ko":"2022년 4월 11일 13:17 UTC 에 고객과 Gitpod 직원들의 신고로 알았고, incident.io 연동으로 Slack 에 장애를 열었다. 4월 12일에도 고객과 직원의 신고로 알았고, 4월 17일에는 엔지니어들이 워크스페이스 노드의 CPU 부하가 오른 것을 보고 내부 장애를 열었다."},"components":["node-disk","cpu-limit"],"root_cause":"deploy-rollout","remediation":[{"text":{"en":"Shifted workspace starts back to the ws38 clusters on April 11th","ko":"IO 제한 기능이 없어 워크스페이스 시작 트래픽을 이전 세대 클러스터 ws38 로 돌렸고, 4월 11일 15:37 UTC 에 마쳤다."}},{"text":{"en":"Built ws39a clusters with c2d machines and local SSDs and moved traffic to them","ko":"t2d 대신 더 빠른 c2d 머신 유형과 로컬 SSD 를 쓰는 새 클러스터 ws39a 를 만들어 트래픽을 옮겼다."}},{"text":{"en":"Requested a limit increase from Let's Encrypt","ko":"Let's Encrypt 속도 제한 문제를 풀고, Let's Encrypt 에 한도를 올려 달라고 요청했다."}},{"text":{"en":"Routed EU XL traffic to the regional EU cluster and built ws-eu39bxl to replace the crashed ws-eu39xl","ko":"ws38 클러스터를 쓸 수 없어 EU XL 트래픽을 임시로 리전 EU 클러스터로 보내 워크스페이스를 다시 시작할 수 있게 했다. 그리고 망가진 ws-eu39xl 을 대신할 ws-eu39bxl 클러스터를 새로 만들었다."}},{"text":{"en":"Cleaned up workspaces stuck in a stopping state so users could start them again","ko":"stopping 상태에 걸린 워크스페이스를 정리해 사용자가 다시 시작할 수 있게 했다."}},{"text":{"en":"Released ws40 with 1TB boot disks to get more IO bandwidth","ko":"4월 8일 배포 전처럼 1TB 부트 디스크를 단 노드로 ws40 을 만들어 디스크 IO 대역폭을 늘렸다."}},{"text":{"en":"Removed the workspaces that consumed excessive CPU time for crypto-mining","ko":"모니터링으로 노드의 작업을 직접 살펴 과도한 CPU 시간을 쓰는 워크스페이스를 찾아 시스템에서 지웠다."}},{"text":{"en":"Blocked the fraudulent users behind the crypto-mining from using Gitpod","ko":"노드를 손으로 뒤지고 채굴 탐지 시스템을 함께 써서 부정 사용자를 찾아 Gitpod 사용을 막았다."}},{"text":{"en":"Rolled back the ws-daemon change that broke CPU limiting and shipped the fix in the ws34 clusters","ko":"ws38 세대에는 없던 ws-daemon 변경을 되돌려 CPU 제한을 다시 안정시켰고, 이 수정을 ws34 클러스터에 담아 배포했다."}},{"text":{"en":"Blocked ACME HTTP requests to workspace ports so Let's Encrypt rate limits would not be hit again","ko":"새 ws34 배포에서 워크스페이스 포트로 가는 ACME HTTP 요청을 막아 Let's Encrypt 한도에 다시 걸리지 않게 했다."}},{"text":{"en":"Rolled out IO limiting with the ws41xl and ws41 clusters and moved traffic to them step by step","ko":"워크스페이스 사이에 디스크 IO 를 공정하게 나누는 ws41xl 클러스터와 일반 리전용 ws41 클러스터를 만들어 트래픽을 단계적으로 옮겼다."}}],"action_items":[{"text":{"en":"Invest in test automation so that test results stop releases like this one","ko":"테스트 결과가 진행하면 안 되는 배포를 미리 걸러 내도록 테스트 자동화에 계속 투자한다."}},{"text":{"en":"Vary the interval between traffic shifts to new clusters, with more time for larger releases","ko":"새 클러스터로 트래픽 비율을 옮기는 간격을 배포마다 다르게 하고, 큰 배포일수록 단계 사이를 길게 둔다."}},{"text":{"en":"Add a risk assessment stage, to be automated, that governs changeset size and rollout speed of workspace cluster rollouts","ko":"워크스페이스 클러스터 배포에 위험 평가 단계를 넣어 변경 묶음의 크기와 배포 속도를 정하고, 이 단계를 자동화하는 것을 목표로 한다."}},{"text":{"en":"Reduce the impact teams have on each other","ko":"팀 사이 의존이 롤백을 막지 않도록 팀끼리 주는 영향을 계속 줄인다."}},{"text":{"en":"Add review criteria that find backwards incompatible changes before pull requests are merged, and split such pull requests","ko":"PR 을 머지하기 전에 하위 호환이 깨지는 변경을 찾는 리뷰 기준을 더하고, 그런 PR 은 나눠서 배포를 롤백하기 어려운 상황을 피한다."}},{"text":{"en":"Recommend rollback as the primary means of mitigation in the incident procedures, and look at automating it","ko":"장애 완화의 첫 수단으로 롤백을 권하도록 절차를 고쳤고, 이 과정을 자동화하는 방법을 논의한다."}},{"text":{"en":"Make the status page show the workspace service as down when it is, and update the playbooks","ko":"상태 페이지의 워크스페이스 서비스가 장애를 주황이나 빨강으로 제대로 표시하도록 고쳤고, 플레이북도 그에 맞게 고쳤다."}},{"text":{"en":"Keep incident communication in a single Slack channel and Google Meet","ko":"장애 중 내부 소통을 Slack 채널 하나와 Google Meet 하나로 모으고, 장애 절차를 그에 맞게 고쳤다."}},{"text":{"en":"Put 10% of the engineering staff on the on-call rotation from May 9th and use that time on observability, alerting and stability","ko":"당번 체계를 다시 짜서 5월 9일부터 엔지니어링 인력의 10% 가 늘 당번을 맡고, 그 시간으로 관측성과 알림과 시스템 안정성을 개선한다."}}],"contributing":["no-staged-rollout","test-coverage-gap","staging-not-representative","limit-unknown","overlapping-failures","revert-did-not-restore","manual-action-worsened","limited-debug-access"],"recurrence_of":null,"symptoms_text":{"en":"From April 11, 2022 at 13:17 UTC customers and Gitpod staff reported that Gitpod workspaces were intermittently becoming unresponsive. CPU usage of nodes and pods looked normal, with nothing maxed out, and at 13:58 UTC the team found that the workspaces were IO bound rather than CPU bound.\n\nThe degradation, which affected workspaces in all regions, came back again and again between April 11th and 22nd. After traffic moved to new ws39a clusters with faster machines and local SSDs, workspaces were IO bound again on April 12th. On April 14th, after traffic moved to ws40, nodes still had spikes of IO saturation. On April 17th and 18th CPU load rose on the workspace nodes because the defence against crypto-currency miners was broken in ws40, and mining made many workspace nodes unusable. On April 20th several customers still had unreliable workspaces for workloads that had behaved normally before the April 8th release.\n\nAt times users could not start workspaces, and some lost data. On April 12th the ws-daemon rollout of a new cgroups-based IO limiter broke the CPU limiter on the ws-eu39xl cluster: all cores of each node were consumed, the services on the nodes stopped working, and the team could not reach the nodes with ssh or kubectl. Users of that XL cluster, unleashed customers and Gitpod staff, lost data. EU XL traffic was moved back to ws-eu38xl at about 13:49 UTC, but that cluster was not receiving traffic, which the team noticed at 14:21 UTC, so EU XL users could not start workspaces.\n\nGitpod had no way to limit IO. On April 11th it moved workspace starts back to the previous ws38 clusters, but then went forward with new clusters instead of staying there, and a breaking API change in the control plane soon made ws38 incompatible. The fixes that followed did not end the problem: the IO limiter worked in development and test but not in production, and a rewritten one still did not work as expected. A defect in the workspace networking changes of the April 8th release also made workspaces sluggish, and it was hard to find because production has IP forwarding disabled while development has it enabled.","ko":"2022년 4월 11일 13:17 UTC 에 고객과 Gitpod 직원들이 워크스페이스가 간헐적으로 응답하지 않는다고 알렸다. 4월 8일과 9일에 새 세대 워크스페이스 클러스터 ws39 를 모든 리전에 배포한 직후였다.\n\n노드와 파드의 CPU 사용량은 정상이었다. 한도에 닿은 것이 없었고, CPU 제한이 깨졌거나 노이지 네이버가 있다는 징후도 없었다. 13:58 UTC 에 엔지니어들은 워크스페이스가 CPU 바운드가 아니라 IO 바운드라는 것을 알아냈다.\n\n4월 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 사용자는 워크스페이스를 시작하지 못했다.\n\n모든 리전의 워크스페이스에 걸친 성능 저하는 그 뒤에도 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일 배포 전에는 멀쩡하던 작업에서 성능 문제를 겪었다.\n\n엔지니어들은 처음에 디스크 IO 대역폭이 모자란 것으로 보고 더 빠른 머신 유형과 로컬 SSD 를 쓴 ws39a 클러스터를 만들었지만, 다음 날 IO 문제가 다시 났다. 개발·테스트 환경에서 잘 돌던 cgroups 기반 IO 제한기는 운영에서 다르게 동작해 클러스터를 무너뜨렸다. 워크스페이스 네트워킹의 결함은 찾고 고치기 어려웠다. 개발 환경은 IP forwarding 을 켜 두었고 운영 환경은 꺼 두었기 때문이다."},"updated_at":"2026-11-14T12:34:23.746795Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0591","org_slug":"gitpod"}