{"id":"IL-0178","org":"Lotte E-commerce","date":"2022-10-21","docs":[],"title":{"en":"On EKS, HPA scale-in returned 502 errors because containers stopped before their pod IPs left the Service endpoints, and after blue/green switches conntrack kept sending established connections to deleted pods behind an instance-mode ALB","ko":"EKS 에서 HPA 가 파드를 줄일 때 엔드포인트 제거보다 컨테이너 종료가 먼저 끝나 502 가 났고, 블루/그린 전환 뒤에는 conntrack 에 남은 연결이 삭제된 파드로 가서 클라이언트가 타임아웃을 받았다."},"domain":["kubernetes","aws","networking"],"impact":{"scope":"LOTTE ON services on Amazon EKS during HPA scale-in and after blue/green deployments","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Removing a pod from Service endpoints and stopping its containers happen at the same time in Kubernetes, so a pod can stop while traffic is still routed to it unless shutdown is delayed with a preStop hook or in the application.\n\nWith an instance-mode ALB, traffic passes through node iptables and conntrack, and established connections can keep going to a pod IP after its iptables rules are gone. IP mode avoids that path, within the limit of 1000 targets per target group.","ko":"Kubernetes 에서 엔드포인트 제거와 컨테이너 종료는 동시에 진행되므로, 종료를 늦추지 않으면 스케일 인 때 지워진 컨테이너로 트래픽이 가서 502 가 날 수 있다. preStop 훅으로 기다릴 때는 terminationGracePeriodSeconds 로 최대 대기 시간을 함께 정해야 한다. ALB 를 instance 모드로 쓰면 트래픽이 노드의 iptables 와 conntrack 을 거치므로, 블루/그린 전환 뒤에도 established 연결이 지워진 파드로 갈 수 있다. IP 모드는 이 경로를 없애고 네트워크 홉도 줄이지만, 대상 그룹에 대상을 1000개까지만 넣을 수 있다."},"related":[],"sources":[{"url":"https://aws.amazon.com/ko/blogs/tech/case-study-lotteon-running-on-amazon-eks/","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"Client requests to LOTTE ON reach an Application Load Balancer created from a Kubernetes Ingress, which spreads traffic over the worker nodes in its target group and on to pods through Service iptables rules that kube-proxy maintains. The pods scale with HPA and the nodes with Cluster Autoscaler, and every service is released with a blue/green deployment.\n\nOn scale-in, deleting a pod starts two things at once: the kubelet stops the containers, and the endpoints controller removes the pod from the Service endpoints, after which kube-proxy updates iptables on each node through the API server. When the container stopped before its IP left the endpoints, incoming traffic still went to it and failed with 502. Lotte E-commerce's fix was to delay container shutdown long enough for the pod to be taken out of the endpoints first. The post gives two ways to do it: wait in the application's SIGTERM handler, or, more simply, add a preStop hook that sleeps. In both cases terminationGracePeriodSeconds (30 seconds by default) caps the wait, after which the kubelet sends SIGKILL.\n\nThe blue/green errors came from connection tracking. An ALB created by an Ingress uses instance mode by default: the Service is exposed as a NodePort on every worker node and the ALB sends traffic to the nodes. Once a pod starts terminating it accepts no new HTTP connections, but the TCP connection between node and pod stays in ip_conntrack for a while, and packets that match a tracked connection are forwarded along the stored path without checking iptables. So after the Blue pods were deleted, packets on existing connections still went to their IPs and clients timed out.\n\nThe fix was to use IP mode for the ALB on some workloads. In IP mode the AWS Load Balancer Controller registers pod IPs directly as ALB targets and updates the target group as soon as pods are added or removed, so traffic goes straight to pods without iptables or conntrack, with fewer network hops. A target group holds at most 1000 targets; Lotte E-commerce's services stayed under that, and it planned to move all workloads to IP mode gradually.","ko":"롯데이커머스는 마이크로서비스 40여 개로 된 롯데온을 Amazon EKS 에서 운영한다. 클라이언트 요청은 ingress 오브젝트로 만든 Application Load Balancer 를 거쳐 워커 노드로 가고, 노드의 iptables 규칙을 따라 파드로 전달된다. 파드 수는 HPA 가 조정하고, 노드 수는 Cluster Autoscaler 가 조정한다.\n\n첫 번째 문제는 스케일 인 때 났다. 파드를 지울 때 Endpoints controller 가 Service 엔드포인트에서 파드를 빼고 kube-proxy 가 각 노드의 iptables 규칙을 고치는 절차와, kubelet 이 컨테이너를 종료하는 절차가 동시에 진행된다. 엔드포인트에서 파드 IP 가 빠지기 전에 컨테이너가 먼저 사라지면 트래픽이 사라진 컨테이너로 가서 502 가 난다. 롯데이커머스는 컨테이너 종료를 늦춰 파드가 엔드포인트에서 빠질 때까지 기다리게 해서 이 문제를 풀었다.\n\n두 번째 문제는 블루/그린 배포 뒤에 났다. 파드가 종료 절차에 들어가면 새 HTTP 연결은 받지 않지만, 노드와 파드 사이의 TCP 연결은 ip_conntrack 이 한동안 기억한다. ip_conntrack 은 iptables 의 연결 상태를 추적하는 모듈이다. 노드에 패킷이 들어오면 저장된 연결과 같은지 보고, 같으면 iptables 규칙을 다시 보지 않고 저장된 목적지로 보낸다. 그래서 iptables 규칙이 갱신돼도 conntrack 에 남은 기록 때문에 지워진 파드로 패킷이 갔고, 클라이언트는 타임아웃을 받았다. ingress 로 만든 ALB 는 기본값이 instance 모드라 NodePort 와 노드 iptables 를 거친다. IP 모드에서는 AWS Load Balancer Controller 가 파드 IP 를 대상 그룹에 바로 등록하므로 iptables 와 ip_conntrack 을 거치지 않는다. 롯데이커머스는 일부 워크로드에 IP 모드를 적용해 문제를 풀었다."},"trigger":{"en":"HPA scale-in deleting pods, and the deletion of Blue pods after a blue/green switch of the Service label selector.","ko":"두 가지 작업이 각각 장애를 일으켰다. 하나는 HPA 가 파드를 줄이는 스케일 인이고, 다른 하나는 Service 레이블 셀렉터를 그린 디플로이먼트로 바꾼 뒤 블루 파드를 지우는 블루/그린 배포다."},"severity":"unknown","symptoms":["high-error-rate","intermittent-timeouts","stale-service-routing"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":null,"components":["hpa","pod-termination","kube-proxy","conntrack","cloud-load-balancer","ingress"],"root_cause":"dns-network","remediation":[{"text":{"en":"delayed container shutdown on pod deletion (a wait in the SIGTERM handler or a preStop sleep, capped by terminationGracePeriodSeconds) so containers stop only after the pod has left the Service endpoints","ko":"컨테이너 종료를 늦춰 Service 엔드포인트에서 파드가 빠질 때까지 기다리게 했다. 방법은 애플리케이션의 SIGTERM 처리 로직에서 기다리게 하거나 preStop 훅에 sleep 을 넣는 것이다. 무한정 기다리지 않도록 terminationGracePeriodSeconds 도 함께 설정한다."}},{"text":{"en":"switched some workloads' Ingress-created ALBs from instance mode to IP mode so traffic goes straight to pod IPs without iptables and conntrack","ko":"대상 그룹 한도인 1000개를 넘지 않는 일부 워크로드에서 ingress 로 만드는 ALB 를 instance 모드에서 IP 모드로 바꿨다."}}],"action_items":[{"text":{"en":"Move all workloads to ALB IP mode gradually","ko":"ALB IP 모드를 전체 워크로드에 점진적으로 적용한다."}}],"contributing":["default-config-unchanged"],"recurrence_of":null,"symptoms_text":{"en":"LOTTE ON, Lotte E-commerce's online shopping mall of about 40 microservices on Amazon EKS, returned errors for a period of time whenever the Horizontal Pod Autoscaler (HPA) scaled pods in after load dropped. Requests reached containers that had already been deleted and failed with 502 errors.\n\nA second problem appeared after blue/green deployments. The team created the Green Deployment, switched the Service label selector to it, confirmed it worked and started deleting the Blue pods. While the Blue pods' lifecycle hooks were running, the service still worked, but once the pods were fully removed, clients started getting timeout errors because part of the traffic was still being sent to the deleted pods.\n\nOn the nodes, the iptables rules no longer had any entry for the deleted pod's IP, but ip_conntrack still held the connection in the established state, and client requests kept flowing to the removed pod.","ko":"롯데온 서비스에서 HPA 가 파드 수를 줄이는 스케일 인 때마다 일정 시간 동안 요청이 오류로 끝났다. 엔드포인트에서 파드 IP 가 빠지기 전에 컨테이너가 먼저 지워지면, 들어오는 트래픽이 여전히 지워진 컨테이너로 가서 502 에러가 났다.\n\n블루/그린 배포 뒤에는 다른 증상이 나타났다. Service 의 레이블 셀렉터를 그린으로 바꾸고 정상 동작을 확인한 뒤 블루 파드를 지웠다. 파드의 수명 주기 훅이 처리되는 동안에는 문제가 없었지만, 파드가 완전히 사라진 뒤부터 클라이언트에 timeout 오류가 났다. 일부 트래픽이 일정 시간 동안 이미 지워진 파드로 계속 갔다.\n\n노드를 들여다보니 지워진 파드 IP 의 iptables 규칙은 모두 지워졌지만, ip_conntrack 에는 그 연결이 established 상태로 남아 있었다. 클라이언트 요청이 이 기록을 따라 제거된 파드로 계속 들어가고 있었다."},"updated_at":"2026-11-10T00:00:37.429490Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0178","org_slug":"lotte-e-commerce"}