{"id":"IL-0230","org":"CircleCI","date":"2023-03-14","docs":[],"title":{"en":"kube-proxy sync failures during a Kubernetes upgrade corrupted node iptables, and CircleCI jobs stopped starting","ko":"쿠버네티스 업그레이드 도중 메인 운영 클러스터의 서비스 간 통신이 끊겨 모든 고객의 작업이 시작되지 않았다"},"domain":["kubernetes","networking"],"impact":{"scope":"Pipelines of all customers started late and then jobs did not run; Docker, Machine, macOS and Windows jobs, the CircleCI UI and Runner were affected. Two follow-on RabbitMQ incidents delayed all pipelines from 02:50 to 05:14 UTC and GitHub checks by up to 30 minutes from 09:50 to 13:15 UTC on 15 March.","users":"all customers","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"No single trigger caused the incident. Because networking underlies everything in the cluster, Kubernetes upgrades carry inherent risk, and CircleCI's risk mitigation did not hold up when several events interacted.","ko":"이번 장애를 촉발한 원인은 하나가 아니었다. 네트워크는 모든 것의 바탕이어서 쿠버네티스 업그레이드는 본래 위험하다. 여러 사건이 맞물려 터진 이번 경우에는 팀이 세워 둔 위험 완화 방법이 듣지 않았다."},"related":[],"sources":[{"url":"https://status.circleci.com/incidents/dcqb3fykhgvg","kind":"official-status","archived":"https://web.archive.org/web/20260718234454/https://status.circleci.com/incidents/dcqb3fykhgvg"}],"summary":{"en":"In the week before the incident the CircleCI infrastructure team was upgrading its main production Kubernetes cluster step by step. On 13 March, after a few components were updated, some services reported missing metrics; the team reverted the changes and the metrics came back. The team adjusted the plan to limit the blast radius and at about 17:40 UTC on 14 March tried a different way of upgrading those components. Between the two Kubernetes versions, the way kube-proxy handles and formats its iptables rulesets had changed. kube-proxy rewrites each node's iptables rules with iptables-save and iptables-restore in Proxier.syncProxyRules() whenever Service and Endpoints objects change. After the kube-proxy rollback on 13 March a small number of sync failures had already started. As the upgrade rolled out more pods and Endpoints changed, the failures grew, and on 14 March a spike of sync errors left iptables on many nodes badly corrupted, so traffic between services failed from about 18:00 UTC.\n\nWith all three suspected networking workloads looking healthy, the team restarted node-local-dns-cache and CoreDNS, which made DNS resolution fail completely. A single node restart showed that traffic through a freshly restarted node worked, so the team restarted every node in the cluster; each restart built a fresh iptables ruleset and the sync errors went away. By 23:00 UTC all nodes had been rolled and jobs recovered except those using contexts or the machine executor. After a separate pool of dependencies was restarted, all jobs ran correctly by 01:15 UTC on 15 March, and the incident is given as lasting until about 01:27 UTC.\n\nThe recovery caused two further incidents in the RabbitMQ queuing system. Logging for RabbitMQ was found missing, so the team rolled the rest of the infrastructure; at about 02:50 UTC RabbitMQ connection errors delayed jobs again, mitigated by 03:35 UTC and resolved by 05:14 UTC. The report ties a later restart of the RabbitMQ nodes, done to fix a few corrupted queues, to a further degradation from 09:50 UTC in which GitHub checks were delayed by up to 30 minutes; it was mitigated by 11:08 UTC and resolved at 13:15 UTC.","ko":"CircleCI 인프라 팀은 사고 전 일주일 동안 메인 운영 쿠버네티스 클러스터를 조금씩 업그레이드하고 있었다. 3월 13일 몇몇 구성 요소를 올린 뒤 일부 서비스의 지표가 빠져 변경을 되돌렸고 지표는 돌아왔다. 팀은 영향 범위를 줄이도록 계획을 고친 뒤 3월 14일 17:40 UTC 쯤 다른 방식으로 업그레이드를 다시 했다. 18:00 UTC 부터 서비스 간 통신이 실패해 모든 고객의 파이프라인이 늦어지다가 작업이 돌지 않았고, 장애는 3월 15일 01:27 UTC 쯤까지 이어졌다. 보고서는 진행 중이던 업그레이드가 kubelet 과 kube-proxy 의 버전을 어긋나게 만들어 넓은 네트워크 장애가 났다고 요약했다.\n\nkube-proxy 는 노드마다 돌면서 Service 와 Endpoints 객체를 지켜보고, 바뀐 내용을 노드의 iptables 규칙에 반영한다. 이 일은 Proxier.syncProxyRules() 가 맡는다. 이 함수는 iptables-save 로 상태를 읽고 새 규칙을 만든 뒤 iptables-restore 로 다시 쓴다. 팀이 다루던 두 쿠버네티스 버전 사이에 kube-proxy 규칙 집합의 처리 방식과 형식이 바뀌어 있었다. 3월 13일 kube-proxy 버전을 되돌린 뒤로 동기화 실패가 조금씩 나왔고, 업그레이드로 파드가 늘고 Endpoints 가 바뀔 때마다 실패가 늘었다. 3월 14일 업그레이드를 이어 가자 동기화 오류가 치솟아 여러 노드의 iptables 가 크게 망가졌다.\n\n팀은 node-local-dns-cache 와 core-dns 를 재시작했고, 그러자 DNS 조회가 완전히 실패했다. 노드 하나를 재시작하자 그 노드의 요청이 통과해, 모든 노드를 재시작하기로 했다. 노드를 재시작하면 iptables 규칙 집합이 새로 만들어졌다. 23:00 까지 모든 노드를 교체하자 contexts 나 machine executor 를 쓰는 작업을 빼고 모두 회복되었다. 팀은 별도의 의존성 풀을 재시작했고, 3월 15일 01:15 UTC 에 모든 작업이 정상으로 돌았다. 클러스터 전체를 재시작하자 동기화 오류가 사라졌다.\n\n복구 과정에서 장애 두 건이 더 났다. 팀은 RabbitMQ 로그가 빠져 있는 것을 알고 나머지 인프라를 모두 다시 띄웠다. 그 뒤 02:50 UTC 쯤 RabbitMQ 연결 오류로 모든 파이프라인이 다시 늦어졌다. 03:35 UTC 에 완화했고 05:14 UTC 에 해결했다. 이어서 팀은 망가진 큐 몇 개를 고치려고 RabbitMQ 노드를 재시작했다. 보고서는 09:50 UTC 부터 GitHub checks 가 최대 30분 늦어진 두 번째 성능 저하도 함께 적었다. 11:08 UTC 에 완화했고 지켜본 끝에 13:15 UTC 에 해결했다."},"trigger":{"en":"At about 17:40 UTC on 14 March the infrastructure team resumed the upgrade of the main production Kubernetes cluster with a changed approach for some components, after a first attempt on 13 March had been rolled back. The ongoing upgrade left kubelet and kube-proxy at mismatched versions.","ko":"3월 14일 17:40 UTC 쯤 인프라 팀이 전날 되돌렸던 구성 요소들을 다른 방식으로 다시 업그레이드했다. 전날 kube-proxy 를 되돌린 뒤로 iptables 동기화가 조금씩 실패하던 상태에서 업그레이드를 이어 가자 동기화 오류가 한꺼번에 치솟았다."},"severity":"unknown","symptoms":["service-unavailable","intermittent-connection-failures","dns-resolution-failures"],"timeline":{"detected_after":null,"resolved_after":"7h 27m"},"date_kind":"incident","detection":{"en":"At 18:00 UTC engineers saw communication errors rise on one service, then job processing delays climbing and job queues backing up across several services. The kube-proxy \"Sync failed\" errors were found only in the analysis after the incident.","ko":"팀은 3월 14일 18:00 UTC 에 한 서비스의 통신 오류가 늘어나는 것을 보고 장애를 알아챘다. 장애 통화에 사람이 더 들어오면서 영향이 여러 서비스로 퍼진 것을 확인했다. 3월 13일 롤백 뒤에 로그에 나온 소수의 Sync 실패 오류는 그때 알아채지 못했다."},"components":["kube-proxy","kubelet","coredns","dns-resolver"],"root_cause":"upgrade-breaking-change","remediation":[{"text":{"en":"Restarted every node in the cluster so each node built a fresh iptables ruleset","ko":"모든 노드를 차례로 재시작해 노드마다 iptables 규칙 집합을 새로 만들었고, 23:00 까지 모든 노드 교체를 마쳤다."}},{"text":{"en":"Restarted a separate pool of dependencies so jobs using contexts or the machine executor ran again","ko":"contexts 나 machine executor 를 쓰는 작업이 회복되지 않아 별도의 의존성 풀을 재시작했고, 3월 15일 01:15 UTC 에 모든 작업이 정상으로 돌았다."}},{"text":{"en":"Added capacity to work through the backlog of jobs","ko":"작업이 다시 돌기 시작하자 쌓인 작업을 처리하려고 용량을 늘렸다."}},{"text":{"en":"Restarted the RabbitMQ nodes to fix a few corrupted queues","ko":"망가진 큐 몇 개를 고치려고 RabbitMQ 노드를 재시작했다."}}],"action_items":[{"text":{"en":"Improve the Kubernetes upgrade process to limit the risk of future upgrades","ko":"앞으로의 업그레이드에서 이런 위험을 크게 줄이도록 쿠버네티스 업그레이드 절차를 고친다."}},{"text":{"en":"Add metrics and alerts for kube-proxy sync errors","ko":"이번 장애 때 kube-proxy 가 낸 동기화 오류를 잡는 지표와 알림을 더했다."}},{"text":{"en":"Evaluate ways to make the RabbitMQ infrastructure more resilient","ko":"RabbitMQ 인프라의 복원력을 높일 방법을 검토한다."}}],"contributing":["partial-rollout","revert-did-not-restore","monitoring-blind-spot","manual-action-worsened"],"recurrence_of":null,"symptoms_text":{"en":"On 14 March 2023 at about 18:00 UTC, communication errors rose on one CircleCI service, and pipelines for all customers began to start late until jobs did not run at all. Within an hour the errors had spread to many services and to every function of the application, including the CircleCI UI and API.\n\nJob processing delays kept climbing and job queues backed up across several services. A few services were restarted to ease some of the symptoms. The common thread turned out to be failed service-to-service communication inside the main production Kubernetes cluster. Engineers searched logs and metrics and tried to reach services by their internal Kubernetes domain names from inside pods, and suspected kube-proxy, CoreDNS or node-local-dns-cache, but all three workloads looked healthy.\n\nBetween 20:00 and 21:00 UTC the team restarted node-local-dns-cache and CoreDNS, and DNS resolution then failed completely. Restarting one node made requests through that node work again, which pointed at kube-proxy. After every node had been restarted by 23:00 UTC, jobs ran again except those using contexts or the machine executor.\n\nThe analysis afterwards found that kube-proxy, which runs on every node, had a spike of sync errors at the start of the incident, logged as \"Failed to execute iptables-restore\" err=\"exit status 1 (iptables-restore: line 8022 failed\\n)\" followed by \"Sync failed\" retryingTime=\"30s\". A small number of the same errors had already appeared on 13 March, after a first upgrade attempt had made some services report missing metrics and had been rolled back, but nobody noticed them then.","ko":"2023-03-14 18:00 UTC 쯤 한 서비스에서 통신 오류가 늘기 시작했다. 곧 모든 고객의 파이프라인이 늦게 시작하다가 끝내 작업이 돌지 않았고, 18:19 UTC 에 상태 페이지에 작업 시작 지연이 올라왔다.\n\n장애 통화에 엔지니어와 팀이 더 들어오면서 영향이 여러 서비스와 애플리케이션의 모든 기능에 퍼져 있다는 것이 드러났다. 작업 처리 지연이 계속 늘었고 여러 서비스의 작업 대기열이 쌓였다. 한 시간이 안 되어 처음의 성능 저하가 큰 장애가 되었다. 영향을 받은 것은 Docker, Machine, macOS, Windows 작업과 CircleCI UI, Runner 였다.\n\n사고 뒤 분석에서 보니, 장애가 시작될 때 모든 노드에서 도는 kube-proxy 의 로그에 \"Sync failed\" 오류가 치솟아 있었다. 로그 줄은 \"Failed to execute iptables-restore\" err=\"exit status 1 (iptables-restore: line 8022 failed\\n)\" 과 \"Sync failed\" retryingTime=\"30s\" 였다.\n\n전날인 3월 13일에도 조짐이 있었다. 몇몇 구성 요소를 올린 뒤 일부 서비스에서 지표가 빠졌다. 변경을 되돌리자 지표가 돌아와 회복된 것처럼 보였다. 하지만 이때 kube-proxy 를 되돌린 뒤부터 로그에 Sync 실패 오류가 아주 적게 나오기 시작했고, 팀은 이를 알아채지 못했다.\n\n팀은 먼저 증상을 줄이려고 서비스 몇 개를 재시작했다. 로그와 지표를 뒤지고 파드 안에서 내부 쿠버네티스 도메인 이름으로 서비스에 직접 붙어 본 끝에 kube-proxy, core-dns, node-local-dns-cache 중 하나를 의심했다. 하지만 세 워크로드 모두 정상으로 보여 확인하기 어려웠다. 20:00 에서 21:00 UTC 사이에 node-local-dns-cache 와 core-dns 를 재시작하자 DNS 조회가 완전히 실패했다.\n\n노드 하나를 재시작하자 그 노드와 관련된 요청은 통과하기 시작했다. 팀은 이것을 보고 kube-proxy 를 주된 원인으로 좁혔다."},"updated_at":"2026-11-14T05:53:44.819845Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0230","org_slug":"circleci"}