ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [k8s] watch/list 그리고 Thunder herd
    SW개발/Kubernetes 2025. 7. 20. 02:51

    대규모 트래픽 환경에서 k8s를 운영하다보면 많은 일이 일어나지만, 

    그 중에서 일정시간의 네트워크 이슈 발생 후 복구된 과정에서 kube-apiserver 가 OOM으로 여러차례 죽으면서 알게된 것들에 대해 기록하고자 한다.

     

    1. Thunder herd

    여러 프로세스나 스레드가 동시에 같은 이벤트(예: 소켓의 데이터 수신, 파일 디스크립터의 상태 변화)를 기다리다가, 이벤트가 발생했을 때 모든 대기자가 한꺼번에 깨어나는 현상으로 모든 대기자들이 동시에 깨서 자원(예: 네트워크 패킷, 파일 I/O)을 서로 차지하려고 경쟁하기 때문에, 불필요하게 많은 컨텍스트 스위칭과 CPU 오버헤드가 발생하고 성능이 저하 되는 것

     

    예를 들어,

    • 수 천개의 노드를 가진 클러스터가 있다고 가정할 때, 네트워크 등의 이슈로 kube-apiserver와 각 노드의 kubelet 통신이 끊기는 경우, kubelet이 노드에서 각자 kube-apiserver에 연결해 자신이 관리하는 Pod 상태를 watch 하다가 재연결하게 됨.
    • 이 과정에서 특정 리소스(예: ConfigMap, Node 상태)가 업데이트되면, 관련된 모든 클라이언트의 watch connection이 트리거되어 동시에 깨어나서 kube-apiserver에 요청을 보냄.
    • 이 때 kube-apiserver는 짧은 시간에 수천~수만 개의 watch 이벤트를 동시에 처리해야 하고, CPU와 메모리 부하가 급증하게 되는 현상

    그리고 관련 이슈는 kubernetes blog 에도 정리가 되어있다. 

    2. kube-apiserver <-> client 의 watch / list 통신 그리고 Bookmark

    Kube-apiserver API 클라이언트는 리소스의 상태를 확인할 때, list 와 watch 를 사용한다.

    방식 설명
    list 현재 시점의 전체 스냅샷을 한번 받아옴
    watch 특정 시점 이후에 생긴 변경사항들을 스트림으로 받아옴

     

    동작 방식

    클라이언트가 처음 연결하면 보통 list + watch를 같이 요청한다.

    ✅ 현재 상태를 알아야 하고 (list)
    ✅ 그리고 앞으로 생기는 변화도 전달 받고 싶기 때문 (watch)

     

    구체적으로는 아래와 같이 클라이언트가 kube-apiserver에 GET 요청을 보내면 아래와 같이 결과를 리턴해준다.

     

    클라이언트가 list 요청)

    GET /api/v1/pods
    응답결과)
    { 
      "resourceVersion": "12345", 
      "items": [ 
        {"name": "pod-A", "status": "Running"}, 
        {"name": "pod-B", "status": "Pending"} 
      ] 
    }

    여기서 중요한 점이 하나 있다. 응답에는 resourceVersion이라는 값이 같이 전달이 되는데,

    이 값은 Kube-apiserver가 ETCD로 부터 전달받은 resourceVersion을 의미하며,

    watch를 할 때, 이 resourceVersion을 인자로 같이 넘겨줘야합니다.

     

    만일, 요청한 resourceVersion의 버전이 최신 버전이 아니라면,

    API서버는 etcd로부터 resourceVersion is too old 에러를 전달받아 client에게 전달되게 됩니다.

    클라이언트가 watch 요청

    GET /api/v1/pods?watch=true&resourceVersion=12345
     

    client는 list 요청을 통해 resourceVersion을 알았으니, 이후에는 watch=true와 resourceVersion을 파라미터로 전달합니다.

    그리고 kube-apiserver는 새로운 이벤트(리소스의 변경사항)가 발생할 때마다 스트림으로 클라이언트에 푸시합니다.

    예를 들어 Pod가 하나 더 생기면 아래와 같이 JSON으로 변경점을 Client에게 알려주는 방식입니다.

    {
      "type": "ADDED",
      "object": {"name": "pod-C", "status": "Running"}
    }

     

    bookmark

    kube-apiserver와 client가 통신 중에 etcd에 저장된 리소스들이 얼마든지 바뀔 수 있습니다.

    그리고 그 때마다 resourceVersion이 변경됩니다. 이 때, 변경된 버전을 모르면 어떻게 될까요?

    이 때에는 위에서 언급한대로 resourceVersion is too old 에러를 전달받게 됩니다.

     

    결국, client는 kube-apiserver로 변경사항을 제대로 전달받기 위해서 resourceVersion을 최신 버전으로 가져올 수 있어야하는데,

    이 때 필요한게 BookMark입니다.

     

    kube-apiserver는 일정 주기로 client에게 최신 resourceVersion을 알려줍니다.

    물론, watch를 통해 객체 변경사항을 전달받으면 resourceVersion도 같이 전달이 됩니다만,

    BookMark의 경우 특별히 kube-apiserver와 client 간 통신 중에 네트워크 순단 등으로 일정시간 통신이 끊기게 되고,

    이 과정에서 리소스가 변경된다면, 일시적 장애가 복구되는 순간 kube-apiserver는 client 에게 BookMark를 전달하여

    현재 최신 리소스 버전이 몇인지 알려줍니다.

     

    kube-apiserver 의 Cache 구조

    kube-apiserver의 캐시 구조는 Kubernetes가 etcd를 과도하게 부하시키지 않고, 수많은 클라이언트의 watch를 효율적으로 처리하는 핵심 설계 중 하나입니다.

     

    etcd는 Kubernetes 클러스터 상태를 저장하는 중앙 저장소이지만:

    • 속도는 빠르지만, 부하에 취약합니다.
    • 수천~수만 개의 클라이언트가 직접 etcd에 watch를 붙이면 성능이 크게 저하됩니다.

    따라서, 이러한 성능저하 문제 해결을 위해 kube-apiserver가 etcd 앞단에 메모리 캐시를 두고,

    etcd와의 연결은 최소화하면서 클라이언트에게는 빠르게 응답하게 합니다.

     

    kube-apiserver의 캐시는 Delta FIFO 방식을 사용하는데 방식은 아래와 같습니다

    • etcd에서 들어온 이벤트(ADDED, MODIFIED, DELETED)를 큐에 쌓음.
    • 이 큐를 통해 Store를 갱신하고, 클라이언트에게 이벤트를 전달.

    동작방식은 아래와 같습니다

    1. kube-apiserver

    • 각 리소스별로 etcd에 하나의 watch를 생성.
    • etcd에서 받은 이벤트를 Watch Cache에 쌓음.

     2. 클라이언트가 요청을 보내면:

    • GET: 캐시에서 현재 상태를 반환.
    • LIST: 캐시에서 현재 전체 스냅샷을 반환.
    • WATCH: 클라이언트별 채널에 등록. Watch Cache에서 새로운 이벤트가 발생할 때마다 해당 채널에 전송.

    3. Store와 DeltaFIFO:

    • DeltaFIFO는 etcd로부터 받은 변경 사항을 큐에 쌓고, 순서대로 처리해 Store를 업데이트합니다.
    • 그리고 

    <Delta FIFO 구조도>

    <Delta FIFO flow chart>     

     

     

     

     

    'SW개발 > Kubernetes' 카테고리의 다른 글

    ETCD 이해하기  (3) 2025.08.10
    [번역] Kubernetes의 resource memory limit 이해하기  (0) 2021.04.26
Designed by Tistory.