{"id":"IL-0008","org":"Jetstack","date":"2019-09-05","docs":["https://github.com/kubernetes/kubernetes/blob/e09f5c40b55c91f681a46ee17f9bc447eeacee57/pkg/master/client_ca_hook.go#L43","https://github.com/kubernetes/kubernetes/blob/e09f5c40b55c91f681a46ee17f9bc447eeacee57/pkg/master/client_ca_hook.go#L121","https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair","https://github.com/open-policy-agent/opa/pull/1435","https://github.com/open-policy-agent/opa/pull/1605","https://github.com/helm/charts/tree/master/stable/opa","https://github.com/kubernetes/kubernetes/issues/54522"],"title":{"en":"An unresponsive OPA validating webhook crash-looped the API server during a GKE master upgrade, and node auto-repair then recreated every node","ko":"응답하지 않는 OPA 검증 웹훅 때문에 GKE 마스터 업그레이드가 멈추고 API 서버가 간헐적으로 끊기면서 노드가 끝없이 재생성됐다"},"domain":["kubernetes","gcp"],"impact":{"scope":"one regional GKE development cluster shared by many teams; all tenants lost service","users":"unknown","region":"europe-west1"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"With alerting on API server response times, the team might have seen CREATE and UPDATE requests slow down across the board as soon as the OPA-backed webhook was first deployed. Every workload needs probes: OPA looked so simple to deploy that the team skipped the Helm chart, which adds a livenessProbe for the admission controller containers among other adjustments to the tutorial configuration. Others had hit the same problem, and an upstream issue to improve it was still open.","ko":"API 서버 응답 시간에 알림을 걸어 두었다면, OPA 웹훅을 처음 배포한 뒤 모든 CREATE 와 UPDATE 요청의 응답 시간이 늘어난 것을 알아챘을 수도 있다.\n\n모든 워크로드에 프로브를 설정하는 것이 중요하다. 팀은 OPA 배포가 너무 간단해 보여서 Helm 차트를 쓰지 않았는데, 그 차트에는 어드미션 컨트롤러 컨테이너의 livenessProbe 를 포함해 튜토리얼 기본 설정에 없는 조정이 여럿 들어 있었다.\n\n노드 자동 복구를 끄는 것도 검토했지만 끄지 않기로 했다. 같은 문제를 겪은 것은 Jetstack 이 처음이 아니며, 관련 업스트림 이슈는 아직 열려 있다."},"related":[],"sources":[{"url":"https://www.jetstack.io/blog/gke-webhook-outage/","kind":"blog","license":"unknown","archived":"https://web.archive.org/web/20211202164703/https://www.jetstack.io/blog/gke-webhook-outage/"}],"summary":{"en":"Jetstack used the Open Policy Agent (OPA) project as an admission controller to enforce custom policies on multi-tenant clusters, through a ValidatingAdmissionWebhook. The webhook covered more namespaces and resources than the policy needed, including ConfigMaps in kube-system, and its OPA backend was not responding. OPA had no liveness probe, because it had been installed from the basic tutorial configuration rather than the Helm chart, which includes one.\n\nWith Google Support, the team reconstructed what happened. GKE finished upgrading the first master instance, which then received all API server traffic while the others were upgraded. During the upgrade of the second master, its API server had to run the PostStartHook for ca-registration, which updates the ConfigMap extension-apiserver-authentication in kube-system. That update went through the OPA webhook and timed out. The hook must complete for a master to pass its health check, so the second master went into a crash loop and the upgrade halted.\n\nThe resulting intermittent API downtime kept kubelets from reporting node health. GKE node auto-repair treats a node that reports no status for about 10 minutes as unhealthy and recreates it, so nodes were recreated again and again. Once the webhook was identified, the team used a window of API server access to delete the ValidatingAdmissionWebhook resource, and the cluster recovered. Afterwards the webhook was limited to the namespaces where the policy applies and development teams have access, and to Ingress and Service resources only, and OPA got a liveness probe. Disabling GKE node auto-repair was considered and rejected.","ko":"Jetstack 은 고객의 멀티테넌트 쿠버네티스 플랫폼을 구축하면서, 기본 쿠버네티스 설정으로 다룰 수 없는 요구를 처리하려고 Open Policy Agent, 줄여서 OPA 를 어드미션 컨트롤러로 쓰기 시작했다. 이 장애는 그 연동 설정이 잘못되어 생겼다.\n\nJetstack 은 europe-west1 의 GKE 리전 개발 클러스터에서 마스터를 업그레이드하고 있었다. 팀들에게 업그레이드를 미리 알렸고 API 중단은 예상하지 않았다. 같은 날 앞서 다른 사전 운영 환경에서 같은 업그레이드를 마쳤다. GKE 는 첫 번째 마스터 인스턴스 업그레이드를 끝냈고, 나머지 마스터를 업그레이드하는 동안 그 인스턴스가 모든 API 서버 트래픽을 받았다.\n\n두 번째 마스터의 API 서버는 ca-registration PostStartHook 을 실행하지 못했다. 이 훅은 kube-system 의 extension-apiserver-authentication ConfigMap 을 갱신하는데, 설정해 둔 OPA 검증 웹훅의 백엔드가 응답하지 않아 요청이 타임아웃 났다. 이 작업이 끝나야 마스터가 헬스 체크를 통과하므로, 두 번째 마스터는 계속 실패하며 재시작을 반복했고 업그레이드가 멈췄다.\n\n그 결과 API 가 간헐적으로 끊겼고 kubelet 은 노드 상태를 보고하지 못했다. GKE 노드 자동 복구는 약 10분 동안 상태를 보고하지 않는 노드를 비정상으로 보고 다시 만든다. 그래서 모든 노드가 끝없이 재생성됐고 모든 테넌트가 서비스를 쓸 수 없게 됐다. 팀은 웹훅이 원인이라는 것을 찾은 뒤, API 서버에 간헐적으로 접근될 때를 이용해 ValidatingAdmissionWebhook 리소스를 지워 클러스터를 복구했다."},"trigger":{"en":"A planned master upgrade of the shared development cluster. The same upgrade had been done on another pre-production environment earlier that day, and no API downtime was expected.","ko":"마스터 업그레이드 중 두 번째 마스터 인스턴스의 API 서버가 ca-registration PostStartHook 을 실행하면서 kube-system 의 extension-apiserver-authentication ConfigMap 을 갱신하려 했다. 이 요청은 설정해 둔 OPA 검증 웹훅의 백엔드가 응답하지 않아 타임아웃 났다."},"severity":"unknown","symptoms":["upgrade-stuck","control-plane-unresponsive","nodes-recreated-in-loop","service-unavailable"],"timeline":{"detected_after":"20m","resolved_after":null},"date_kind":"published","detection":{"en":"The first sign was the master upgrade missing the 20-minute Terraform timeout; re-running the pipeline returned the kube-apiserver unhealthy error. The cause was identified with the help of Google Support.","ko":"GKE Terraform 파이프라인으로 시작한 마스터 업그레이드가 20분으로 잡아 둔 Terraform 타임아웃 안에 끝나지 않은 것이 첫 신호였다. 파이프라인을 다시 돌리자 kube-apiserver 가 unhealthy 라는 오류가 났다. 원인은 Google Support 의 도움을 받아 찾았다."},"components":["admission-webhook","gke-control-plane","kube-apiserver","kubelet"],"root_cause":"config-change","remediation":[{"text":{"en":"Deleted the OPA ValidatingAdmissionWebhook resource during a window of API server access","ko":"API 서버에 간헐적으로 접근될 때를 이용해 OPA 의 ValidatingAdmissionWebhook 리소스를 지워 클러스터를 복구했다."}}],"action_items":[{"text":{"en":"Limit the OPA webhook to the namespaces where the policy applies and development teams have access","ko":"OPA 의 ValidatingAdmissionWebhook 이 정책이 적용되고 개발 팀이 접근하는 네임스페이스만 보도록 범위를 좁혔다."}},{"text":{"en":"Enable the webhook only for Ingress and Service resources, the only resources the policy validates","ko":"웹훅을 정책이 실제로 검증하는 Ingress 와 Service 리소스에만 켰다."}},{"text":{"en":"Add a liveness probe so OPA is restarted when it stops responding, and update the OPA documentation","ko":"OPA 가 응답하지 않으면 재시작되도록 liveness probe 를 더하고 문서를 고쳤다."}},{"text":{"en":"Follow the open upstream Kubernetes issue about this behaviour","ko":"이 동작을 개선하자는 쿠버네티스 업스트림 이슈가 아직 열려 있어, 그 진행을 지켜본다."}}],"contributing":["blast-radius-too-wide","no-health-probe","monitoring-blind-spot","automation-amplified"],"recurrence_of":null,"symptoms_text":{"en":"Jetstack was upgrading the master of a development cluster that many teams use to test their apps during the working day, a regional Google Kubernetes Engine (GKE) cluster in europe-west1. The upgrade ran through Jetstack's GKE Terraform pipeline and did not finish before the 20-minute Terraform timeout, while the GKE console still showed the cluster as upgrading.\n\nRe-running the pipeline failed with this error: google_container_cluster.cluster: Error waiting for updating GKE master version: All cluster resources were brought up, but the cluster API is reporting that: component \"kube-apiserver\" from endpoint \"gke-...\" is unhealthy\n\nDuring this time the API server timed out intermittently and teams could not deploy their applications. Then, while the investigation was going on, all nodes started being destroyed and recreated in an endless loop, and every tenant of the cluster lost service.","ko":"Jetstack 은 europe-west1 에 있는 GKE 리전 클러스터의 마스터를 업그레이드하고 있었다. 이 개발 클러스터는 여러 팀이 낮 동안 앱을 시험하는 데 쓴다. GKE Terraform 파이프라인으로 업그레이드를 시작했는데, 마스터 업그레이드가 20분으로 잡아 둔 Terraform 타임아웃 안에 끝나지 않았다. GKE 콘솔에는 클러스터가 여전히 업그레이드 중으로 나왔다.\n\n파이프라인을 다시 돌리자 다음 오류가 났다: google_container_cluster.cluster: Error waiting for updating GKE master version: All cluster resources were brought up, but the cluster API is reporting that: component \"kube-apiserver\" from endpoint \"gke-...\" is unhealthy\n\n그동안 API 서버가 간헐적으로 타임아웃 났고 팀들은 앱을 배포할 수 없었다. 조사를 시작하자 모든 노드가 끝없이 삭제되고 다시 만들어지기 시작했다. 테넌트를 가리지 않고 모두 서비스를 쓸 수 없게 됐다.\n\nGoogle Support 와 함께 확인해 보니 두 번째 마스터가 계속 실패하며 재시작을 반복해 업그레이드가 멈춰 있었다. API 가 간헐적으로 끊기자 kubelet 이 노드 상태를 보고하지 못했고, GKE 노드 자동 복구가 이 노드들을 고치려고 다시 만들고 있었다."},"updated_at":"2026-11-10T00:00:31.612215Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0008","org_slug":"jetstack"}