{"id":"IL-0062","org":"Ravelin","date":"2019-07-26","docs":["https://golang.org/pkg/net/http/#Server.Shutdown"],"title":{"en":"Intermittent 502 errors behind a GKE Ingress because pods shut down before the load balancer stopped sending them traffic","ko":"GKE Ingress 뒤의 파드가 SIGTERM 을 받자마자 새 연결을 받지 않고 정상 종료해서 API 에 가끔 502 오류가 났다"},"domain":["kubernetes","gcp","networking"],"impact":{"scope":"occasional 502 errors on the API behind the GKE Ingress, seen in integration tests","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Removing a pod's endpoint and signalling the pod happen at the same time, and an ingress can take a long time to catch up. A pod behind an ingress should keep serving after SIGTERM until its grace period ends, and the grace period should be longer than the load balancer's reprogramming time.","ko":"쿠버네티스에서 엔드포인트 제거와 SIGTERM 은 동시에 일어나므로, SIGTERM 을 받았다고 바로 새 연결을 거부하면 안 된다. Ingress 는 특히 엔드포인트 변경을 늦게 반영한다. 파드는 종료 유예 시간이 끝날 때까지 계속 요청을 처리해야 하고, 유예 시간은 로드 밸런서에 변경이 반영되는 시간보다 길어야 한다."},"related":[],"sources":[{"url":"https://philpearl.github.io/post/k8s_ingress/","kind":"blog","license":"all-rights-reserved","archived":null}],"summary":{"en":"Ravelin had moved its workloads to Kubernetes on GKE and was moving its API layer off the old VMs, exposing it to the outside world through an Ingress. Integration tests then started to get occasional 502 errors.\n\nThe common picture of pod removal is that the pod's endpoint leaves the Service or load balancer first, then the pod gets its preStop hook or SIGTERM, stops listening and exits once its open connections finish. In fact the endpoint removal and the SIGTERM happen at the same time. For a normal Service the endpoint change is so fast that the gap goes unnoticed, but an ingress load balancer is reprogrammed much more slowly, so a pod can receive SIGTERM well before the ingress stops sending it new connections. A pod that does a conventional graceful shutdown refuses those connections, and clients get 5xx errors.\n\nThe author's conclusion is that the pod should mostly ignore SIGTERM: keep running and keep serving new connections, optionally hint clients to go elsewhere (for HTTP, a \"Connection: close\" response header), and exit only when the termination grace period ends and it is killed with SIGKILL. The grace period must be longer than the time the load balancer takes to reprogram. For third-party code whose shutdown cannot be changed, a preStop hook that sleeps for the length of the grace period keeps the pod serving.","ko":"Ravelin 은 GKE 의 쿠버네티스로 옮기는 작업의 마지막 단계로 API 계층을 기존 VM 에서 클러스터로 옮기고, 외부에서 API 에 접근할 수 있도록 Ingress 를 설정했다. 설정은 순조로웠지만 통합 테스트가 가끔 502 오류를 받았다. 글쓴이 Phil Pearl 은 원인을 찾아간 과정은 건너뛰고 결론만 정리했다.\n\n흔히 파드 제거는 이런 순서로 일어난다고 생각한다. 레플리케이션 컨트롤러가 파드를 지우기로 하고, 파드의 엔드포인트가 서비스나 로드 밸런서에서 빠져 새 트래픽이 끊긴다. 그다음 파드가 preStop 훅이나 SIGTERM 을 받아 새 연결을 받지 않고, 기존 연결이 모두 끝나면 종료한다. 실제로는 엔드포인트 제거와 SIGTERM 이 순서 없이 동시에 일어난다. 일반 서비스는 엔드포인트가 아주 빨리 빠져 문제가 드러나지 않지만, Ingress 는 반응이 꽤 느려서 파드가 SIGTERM 을 받고 한참 뒤에야 엔드포인트 변경이 반영될 수 있다. 그동안 파드가 새 연결을 처리하지 않으면 클라이언트는 오류를 받는다.\n\n글쓴이는 쿠버네티스에서는 흔히 배운 방식의 정상 종료를 하지 말라고 권한다. 파드는 SIGTERM 을 거의 무시하고 계속 새 연결을 처리해야 한다. 가능하면 클라이언트에게 다른 곳으로 연결을 옮기라고 알리고, HTTP 라면 응답 헤더에 \"Connection\": \"close\" 를 넣을 수 있다. 파드는 종료 유예 시간이 끝나 SIGKILL 을 받을 때 비로소 종료하고, 유예 시간은 로드 밸런서에 변경이 반영되는 시간보다 길게 잡는다. 동작을 바꿀 수 없는 외부 코드라면 유예 시간만큼 잠드는 preStop 훅을 넣어 파드가 아무 일 없다는 듯 계속 요청을 처리하게 한다. 글은 Ravelin 이 실제로 무엇을 바꿨는지는 적지 않았다."},"trigger":{"en":"Pods behind the Ingress being removed by their controller, as happens during rolling deploys and scaling.","ko":"파드가 종료될 때 SIGTERM 을 받자 새 연결을 받지 않는 정상 종료를 시작했지만, GKE Ingress 는 엔드포인트 변경을 늦게 반영해 그 파드로 계속 새 트래픽을 보냈다."},"severity":"unknown","symptoms":["intermittent-connection-failures","stale-service-routing"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"Integration tests occasionally received 502 errors.","ko":"Ingress 를 설정한 뒤 통합 테스트가 가끔 502 오류를 받는 것을 보고 알았다."},"components":["ingress","pod-termination"],"root_cause":"software-bug","remediation":[],"action_items":[],"contributing":["assumption-unverified"],"recurrence_of":null,"symptoms_text":{"en":"After Ravelin moved its API layer from VMs into its GKE cluster behind a Kubernetes Ingress, the integration tests occasionally received 502 errors.\n\nThe errors came from pods being removed. The ingress kept sending new connections to pods that had already received SIGTERM and stopped accepting connections as part of a graceful shutdown.","ko":"Ravelin 이 API 계층을 기존 VM 에서 GKE 의 쿠버네티스 클러스터로 옮기고 Ingress 를 설정한 뒤, 통합 테스트가 가끔 502 오류를 받기 시작했다.\n\n오류는 파드가 내려갈 때 생겼다. 파드가 SIGTERM 을 받은 뒤에도 Ingress 는 한동안 그 파드로 새 연결을 보냈다. 그런데 파드는 흔히 배우는 방식대로 정상 종료를 시작해 새 연결을 더 받지 않았다."},"updated_at":"2026-11-08T19:10:31.395418Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0062","org_slug":"ravelin"}