{"id":"IL-0039","org":"FREE NOW","date":"2019-09-19","docs":["https://github.com/kubernetes/kops/issues/7608"],"title":{"en":"New Kubernetes worker nodes could not join any cluster because a package URL hardcoded in kops nodeup broke","ko":"CentOS 저장소 재구성으로 kops nodeup 에 하드코딩된 container-selinux 패키지 주소가 깨져 새 워커 노드가 클러스터에 합류하지 못했다"},"domain":["kubernetes","aws"],"impact":{"scope":"all Kubernetes clusters at risk of losing capacity; on clusters without enough capacity pods stayed Pending and developers could not deploy","users":"developers deploying to the clusters","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Applying cluster changes through kops and Jenkins worked well, and troubleshooting quickly showed that the failing deployments came from missing cluster capacity.\n\nWhat went wrong was that nobody noticed, over a long period, that some autoscaling groups held more worker instances than Kubernetes had nodes. The team was lucky that no large number of production spot instances was terminated during the problem and that it was noticed during office hours.","ko":"kops 와 Jenkins 로 클러스터 변경을 적용하는 흐름은 잘 동작했다. 조사를 시작하자 배포 실패의 원인이 클러스터 용량 부족이라는 것이 빠르게 드러났다.\n\n일부 오토스케일링 그룹의 워커 인스턴스 수와 쿠버네티스에 등록된 워커 노드 수가 오랫동안 달랐는데도 아무도 알아채지 못한 것은 잘못이었다. 문제가 이어지는 동안 운영 환경의 스팟 인스턴스가 대량으로 종료되지 않은 것과 업무 시간에 발견한 것은 운이 좋았다."},"related":[],"sources":[{"url":"https://github.com/freenowtech/postmortems/blob/master/2019-09-19%20-%20New%20K8s%20workers%20unable%20to%20join%20cluster.pdf","kind":"github","license":"unknown","archived":null}],"summary":{"en":"FREE NOW runs its Kubernetes clusters on AWS with kops, and the worker autoscaling groups in every environment use spot instances. Nodes are expected to disappear at any time, and the clusters cope as long as replacements eventually join. When a new worker boots, kops nodeup installs docker-ce, which depends on the container-selinux package; nodeup downloaded that package from a hardcoded URL on a CentOS mirror (kops issue 7608). Two or three days before the incident the package was moved inside the mirror. From then on every new worker failed to bootstrap and never joined, and as spot nodes were terminated the clusters slowly ran short of capacity.\n\nOn 2019-09-19 the Prelive cluster ran out of room: pods stayed Pending and deployments failed. By 16:20 the team had matched it to the kops issue and realised that capacity could vanish from every cluster without being replaced. At 16:40 the on-call team was warned that production could become badly impaired at any moment, and at 16:55 developers were asked not to deploy to production. Downloading the package by hand on Prelive nodes let them finish bootstrapping and join.\n\nThere was no fixed kops release. The first workaround uploaded the package to a public S3 bucket and added a kops startup hook to fetch it during bootstrap. It failed first on the syntax of the systemd unit and then, at 18:20, because startup hooks run too late in the boot. The second workaround switched the cluster AMI from Amazon Linux 2 (CentOS) to Debian. It worked on Prelive at 18:55, was applied to all clusters at 19:00, and the incident was declared resolved at 19:08.","ko":"FREE NOW 의 모든 환경에서 쿠버네티스 워커 오토스케일링 그룹은 스팟 인스턴스를 쓴다. 노드는 언제든 종료될 수 있지만, 시간이 지나 용량이 충분히 다시 채워지기만 하면 클러스터는 이를 견딘다.\n\n장애 2~3일 전쯤, 정확히 알 수 없는 시점에 노드가 패키지를 내려받던 저장소 미러 안에서 CentOS 패키지 container-selinux-2.68-1.el7.noarch.rpm 이 다른 위치로 옮겨졌다. 새 워커 노드의 부트스트랩 과정은 이 패키지의 주소를 하드코딩하고 있었다. 패키지를 내려받아 설치하지 못하자 부트스트랩이 끝나지 않았고, 종료된 스팟 노드의 자리를 새 노드가 채우지 못했다. 용량이 모자란 클러스터에서는 파드가 Pending 에 머물렀고 개발자들은 배포하지 못했다.\n\n고친 kops 버전이 없었으므로 팀은 우회책을 찾았다. AMI 를 바꾸는 방법은 해 본 적이 없었다. 그래서 노드가 시작할 때 올바른 패키지를 S3 버킷에서 받아 오는 방법이 더 안전하다고 보고 먼저 골랐다. Prelive 노드에서 패키지를 손으로 내려받자 부트스트랩이 끝나고 노드가 합류했다. 그사이 온콜 팀에 운영 환경이 언제든 크게 손상될 수 있다고 알렸고, 16:55 에는 개발자들에게 운영 환경 배포를 멈춰 달라고 요청했다.\n\n팀은 패키지를 공개 S3 버킷에 올리고 kops 클러스터 설정에 부트스트랩 중 패키지를 내려받는 시작 훅을 넣었다. 첫 적용은 systemd 유닛 문법 문제로 실패했다. 문법을 고친 뒤에는 시작 훅이 너무 늦게 실행됐고, 팀은 18:20 에 이 방법으로는 해결할 수 없다는 것을 알았다. 팀은 두 번째 방안으로 클러스터 AMI 를 Amazon Linux 2 에서 Debian 으로 바꿨다. Prelive 에서 새 노드가 합류하는 것을 확인한 뒤 19:00 에 모든 클러스터에 적용했고 19:08 에 장애 종료를 선언했다."},"trigger":{"en":"Roughly two to three days earlier, the CentOS repository mirror was restructured and container-selinux-2.68-1.el7.noarch.rpm moved, which made the static download URL hardcoded in kops nodeup invalid.","ko":"CentOS 저장소가 재구성되면서 container-selinux 패키지의 고정 다운로드 주소를 더는 쓸 수 없게 됐다. kops 는 nodeup 에 이 주소를 하드코딩해 두었고, docker-ce 를 실행하려면 이 패키지가 필요했다."},"severity":"unknown","symptoms":["pod-pending","autoscaler-scale-up-failed"],"timeline":{"detected_after":null,"resolved_after":"3h8m"},"date_kind":"incident","detection":{"en":"Developers asked for help in the #ask-platform Slack channel because their deployments were failing.","ko":"팀은 개발자들이 배포가 실패한다며 #ask-platform 슬랙 채널에 도움을 요청하면서 장애를 알게 됐다. 조사를 시작하자 배포 실패의 원인이 클러스터 용량 부족이라는 것이 빠르게 드러났다."},"components":["node-image","node-pool"],"root_cause":"dependency-failure","remediation":[{"text":{"en":"downloaded the package by hand on the Prelive nodes so their bootstrap could finish","ko":"Prelive 노드에서 패키지를 손으로 내려받아 부트스트랩을 끝내고 노드를 클러스터에 합류시켰다."}},{"text":{"en":"changed the cluster AMI from Amazon Linux 2 to Debian and applied it to all clusters","ko":"클러스터 AMI 를 Amazon Linux 2 에서 Debian 으로 바꿔 Prelive 에서 확인한 뒤 모든 클러스터에 적용했다."}}],"action_items":[{"text":{"en":"Create an alert that shows when nodes cannot join the cluster, possibly from information the cluster autoscaler already has","ko":"노드가 클러스터에 합류하지 못하는 상황을 알려 주는 알림을 만든다. cluster-autoscaler 가 이 정보를 갖고 있을 수 있다."}},{"text":{"en":"Document the external dependencies of node startup and look for ways to reduce them","ko":"노드 시작에 필요한 외부 의존성을 문서로 정리하고 줄일 방법을 찾는다."}},{"text":{"en":"Change the AMI back to Amazon Linux 2 Minimal once kops fixes the issue","ko":"kops 가 이 문제를 고치면 AMI 를 Amazon Linux 2 Minimal 로 되돌린다."}},{"text":{"en":"Document node troubleshooting better: what happens during bootstrap and where the logs are","ko":"클러스터 노드 문제 해결 문서를 보강해, 부트스트랩 중에 무슨 일이 일어나고 로그가 어디에 남는지 적는다."}}],"contributing":["monitoring-blind-spot"],"recurrence_of":null,"symptoms_text":{"en":"At 16:00 CEST a developer reported in the #ask-platform Slack channel that a deployment to the Prelive cluster was failing, and a second report followed at 16:06.\n\nPods could not be scheduled because the cluster had no free capacity, so they stayed Pending and deployments failed. The Prelive autoscaling group held four fairly new instances that had never joined the cluster. Logging in to one of them over SSH showed that the container-selinux package could not be installed because of a checksum mismatch, so the node's bootstrap never finished.\n\nThe mismatch had been building for a while: on some clusters the number of worker nodes in the autoscaling groups had differed from the number of nodes registered in Kubernetes for a long period without anyone noticing. Because the workers are spot instances that can be terminated at any moment, every cluster could lose capacity that would not come back.","ko":"Prelive 클러스터에 배포하던 개발자들이 #ask-platform 슬랙 채널에 배포가 실패한다고 알렸다. 16:00 에 첫 제보가 왔고 16:06 에 두 번째 제보가 왔다.\n\n조사해 보니 클러스터에 남은 용량이 없어서 컨테이너가 스케줄되지 못하고 있었다. 파드는 Pending 상태에 머물렀다. Prelive 의 오토스케일링 그룹에는 새로 뜬 인스턴스 4대가 있었지만 이 인스턴스들은 클러스터에 합류하지 않았다.\n\n그 노드에 SSH 로 들어가 보니 container-selinux 패키지가 체크섬 불일치로 설치되지 못했고, 그래서 부트스트랩이 끝나지 않았다. 팀은 16:20 에 이 현상이 kops 이슈 7608 과 같다는 것을 확인했다. 워커 노드는 스팟 인스턴스여서 언제든 종료될 수 있었다. 그러면 모든 클러스터에서 사라진 용량이 다시 채워지지 않을 수 있었다.\n\n오토스케일링 그룹의 워커 노드 수와 쿠버네티스에 등록된 워커 노드 수가 오랫동안 달랐던 클러스터도 있었지만 아무도 알아채지 못했다."},"updated_at":"2026-11-10T00:00:38.242672Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0039","org_slug":"free-now"}