IL-0194 Palark · 2023-03-23kubernetes
고객사 운영 클러스터에서 kube-apiserver 를 재시작한 뒤 etcd 메모리가 거의 세 배로 늘었고 모든 마스터 노드가 연쇄적으로 재시작했다. 감사 로그로 찾은 원인은 로그에 파드 메타데이터를 덧붙이려고 파드 정보를 읽는 Vector 로그 수집기 DaemonSet 이었다. 이 파드들은 client-go 인포머의 기본 동작처럼 kube-apiserver 가 재시작하면 캐시를 다시 채우려고 list 요청을 보낸다. 파드 하나가 시작하면서 list 요청을 60번 보냈고 노드는 약 80개였다.
요청은 /api/v1/pods?fieldSelector=spec.nodeName=$NODE_NAME 모양으로, 자기 노드에 있는 파드만 달라는 것이었다. 하지만 etcd 는 /registry/<kind>/<namespace>/<name> 꼴의 키로 값을 저장하는 단순한 키-값 저장소라서 필드 셀렉터나 레이블 셀렉터를 모른다. 그래서 kube-apiserver 는 파드 110개로 응답하려고 etcd 에서 파드 전부, 이 클러스터에서는 7130개를 읽어야 했다. resourceVersion 을 지정하지 않은 요청은 기본값인 MostRecent 로 처리돼 매번 etcd 까지 간다.
요청에 resourceVersion=0 과 resourceVersionMatch=NotOlderThan 을 넣으면 kube-apiserver 가 자기 캐시에서 답해 응답 시간이 337 밀리초에서 117 밀리초로 줄었다. 하지만 이 애플리케이션은 Rust 로 짜여 있었고 쿠버네티스 Rust 라이브러리에는 resourceVersion 설정이 없었다. 대신 API Priority and Fairness 로 이 DaemonSet 서비스 어카운트의 파드 list 와 get 요청을 대기열에 줄 세웠다. 그 뒤 etcd 메모리 그래프는 스파이크가 일부 남았지만 대체로 정상이었고, 팀은 장애 등급을 낮췄다.
일상적인 kube-apiserver 재시작 직후 etcd 메모리 사용량이 거의 세 배로 치솟았고, 그 여파로 모든 마스터 노드가 연쇄적으로 재시작했다. 고객사의 운영 클러스터였다.
etcd 로그에는 "apply request took too long" 경고가 찍혔다. took 는 130.768157ms, expected-duration 은 100ms 였고, 요청은 key:"/registry/pods/" range_end:"/registry/pods0" 인 read-only range 였다. 응답은 range_response_count:7130 size:13313287 이었다. 노드 하나의 파드만 묻는 요청이라 110개를 넘지 않아야 했는데 etcd 는 파드 7130개를 읽고 있었다.
감사 로그를 보니 DaemonSet 하나가 원인이었다. kube-apiserver 가 재시작하면 이 DaemonSet 의 파드들이 캐시를 채우려고 list 요청을 새로 보냈다. 파드 하나가 시작하면서 list 요청을 60번 보냈고 클러스터 노드는 약 80개였다. 파드를 하나씩 차례로 재배포할 때는 메모리 그래프에 변화가 없었고, 한꺼번에 재배포할 때만 메모리가 치솟았다.
원인 ? 용량 한계
kube-apiserver 재시작 직후 마스터 노드 전체 메모리와 etcd 메모리 그래프가 치솟고 마스터 노드가 연쇄적으로 재시작하면서 알았다. 원인이 된 DaemonSet 은 감사 로그를 보고 찾았다.
etcd 는 필드 셀렉터와 레이블 셀렉터를 모르기 때문에, kube-apiserver 에는 가벼워 보이는, 필터를 건 list 요청도 etcd 에서는 그 종류의 객체 전체를 읽는 일이 된다. 데이터가 꼭 최신이어야 하는 컨트롤러가 아니라면 resourceVersion=0 으로 kube-apiserver 캐시에서 읽게 할 수 있다. 다만 kube-apiserver 가 여러 대면 인스턴스마다 다른 데이터를 받을 수 있다. 여러 파드가 한꺼번에 보내는 요청은 Priority and Fairness 로 대기열에 넣어 kube-apiserver 와 etcd 를 지킬 수 있다. 위험한 것은 파드 요청만이 아니어서 Cilium CNI 에서도 비슷한 문제가 나왔다.
같은 원인 · 공통 etcd, 텔레메트리 에이전트, 쿠버네티스 API 서버
같은 원인 · 공통 etcd, 쿠버네티스 API 서버
같은 원인 · 공통 etcd, 쿠버네티스 API 서버
같은 원인 · 공통 텔레메트리 에이전트
같은 원인 · 공통 텔레메트리 에이전트
같은 원인 · 공통 쿠버네티스 API 서버
같은 원인 · 공통 텔레메트리 에이전트
같은 원인 · 공통 쿠버네티스 API 서버
같은 원인 · 공통 쿠버네티스 API 서버
같은 원인 · 공통 텔레메트리 에이전트
같은 원인 · 공통 쿠버네티스 API 서버
같은 원인 · 공통 쿠버네티스 API 서버