kubelet evictionhard 옵션

작성자 박형춘 수정일 2024-05-24 08:50

#디스크풀, #evicted

들어가며

  • /var/lib/kubelet/config 에서 노드의 리소스 임계치를 설정하는 evictionHard 옵션이 있습니다.

  • 지정된 수치에 도달하면 kubelet은 스케줄된 파드와 도커이미지를 제거하며 리소스를 확보합니다.

  • 최근 테스트중 위의 기존 알고있던 내용과 다른 현상이 발견되어 정확한 인과관계를 파악하기 위해 아티클을 작성합니다.

  • 이번 아티클에서는 디스크 사용률(evictionhard.imagefs.availavle)이 임계치에 도달할 경우 도커 이미지가 제거되는 과정을 분석합니다.


kubelet 설정 확인 방법
** /var/lib/kubelet/config.yaml 파일에서 확인 가능하지만, kubelet 실행되며 config에 지정된 설정외 default 설정까지 확인하려면 아래 과정을 수행합니다.

# kubectl proxy &

# curl -sSL "http://localhost:8001/api/v1/nodes/<NODE_NAME>/proxy/configz"

아래는 결과의 예시입니다. (아래 예시는 jq 명령을 통해 json 파싱된 결과입니다.)
{
"kubeletconfig": {
"staticPodPath": "/etc/kubernetes/manifests",
"syncFrequency": "1m0s",
"fileCheckFrequency": "20s",
"httpCheckFrequency": "20s",
"address": "0.0.0.0",
"port": 10250,
"tlsCertFile": "/var/lib/kubelet/pki/kubelet.crt",
"tlsPrivateKeyFile": "/var/lib/kubelet/pki/kubelet.key",
"rotateCertificates": true,
"authentication": {
"x509": {
"clientCAFile": "/etc/kubernetes/pki/ca.crt"
},
"webhook": {
"enabled": true,
"cacheTTL": "2m0s"
},
"anonymous": {
"enabled": false
}
},
"authorization": {
"mode": "Webhook",
"webhook": {
"cacheAuthorizedTTL": "5m0s",
"cacheUnauthorizedTTL": "30s"
}
},
"registryPullQPS": 5,
"registryBurst": 10,
"eventRecordQPS": 5,
"eventBurst": 10,
"enableDebuggingHandlers": true,
"healthzPort": 10248,
"healthzBindAddress": "127.0.0.1",
"oomScoreAdj": -999,
"clusterDomain": "cluster.local",
"clusterDNS": [
"6.5.0.10"
],
"streamingConnectionIdleTimeout": "4h0m0s",
"nodeStatusUpdateFrequency": "10s",
"nodeStatusReportFrequency": "5m0s",
"nodeLeaseDurationSeconds": 40,
"imageMinimumGCAge": "2m0s",
"imageGCHighThresholdPercent": 85,
"imageGCLowThresholdPercent": 80,
"volumeStatsAggPeriod": "1m0s",
"cgroupsPerQOS": true,
"cgroupDriver": "systemd",
"cpuManagerPolicy": "none",
"cpuManagerReconcilePeriod": "10s",
"topologyManagerPolicy": "none",
"runtimeRequestTimeout": "2m0s",
"hairpinMode": "promiscuous-bridge",
"maxPods": 110,
"podPidsLimit": -1,
"resolvConf": "/etc/resolv.conf",
"cpuCFSQuota": true,
"cpuCFSQuotaPeriod": "100ms",
"maxOpenFiles": 1000000,
"contentType": "application/vnd.kubernetes.protobuf",
"kubeAPIQPS": 5,
"kubeAPIBurst": 10,
"serializeImagePulls": true,
"evictionHard": {
"imagefs.available": "15%",
"memory.available": "100Mi",
"nodefs.available": "10%",
"nodefs.inodesFree": "5%"
},
"evictionPressureTransitionPeriod": "5m0s",
"enableControllerAttachDetach": true,
"makeIPTablesUtilChains": true,
"iptablesMasqueradeBit": 14,
"iptablesDropBit": 15,
"failSwapOn": true,
"containerLogMaxSize": "10Mi",
"containerLogMaxFiles": 5,
"configMapAndSecretChangeDetectionStrategy": "Watch",
"enforceNodeAllocatable": [
"pods"
]
}
}



노드의 이미지가 제거되는 임계치 default 값은 "imagefs.available": "15%" 으로 확인되며, 이는 가용 공간이 15% 미만 즉 docker root_dir 디스크 영역이 85%에 도달하면 수행됨을 예상할 수 있습니다.



docker root_dir 디스크 확인 방법

# df -Th `docker info | grep -i root | awk -F':' '{print $2}'`

아래는 결과 예시입니다. 
Filesystem                    Type  Size  Used Avail Use% Mounted on 
/dev/mapper/centos-images--lv ext4  493G  198G  270G  90% /images 

* 예시 서버의 docker root_dir은 /images 영역에 위치하며 현재 볼륨의 90%를 사용중임을 확인할 수 있습니다.



내용

  • imagefs.available 값 변경 테스트
위 예시 서버에서 imagefs.available 옵션이 기본값으로 설정되어 있고, 현재 디스크 사용량이 90%를 초과했기 때문에 kubelet은 불필요 pod를 정리하고 사용하지 않는 도커이미지를 제거합니다.


- 테스트를 위해 imagefs.available 값을 3%(가용공간 97%가 임계치)로 변경.

sed -i 's/\(imagefs.available: \)[0-9]\+%/\1 3%/' /var/lib/kubelet/config.yaml && systemctl daemon-reload && systemctl restart kubelet.service



- 예상되는 결과

: 디스크 사용량이 97%가 되어야 도커이미지 제거 (현재 90%이므로 제거되지 않아야 함)


- 실제 결과

: 현재 디스크 사용률은 90% 이지만 여전히 이미지가 제거되고 있음.

Jan 07 20:46:13 ubuntu-test kubelet[4132594]: I0107 20:46:13.878063 4132594 image_gc_manager.go:304] [imageGCManager]: Disk usage on image filesystem is at 90% which is over the high threshold (85%). Trying to free 3163027865 bytes down to the low threshold (80%).
Jan 07 20:51:13 ubuntu-test kubelet[4132594]: I0107 20:51:13.905618 4132594 image_gc_manager.go:304] [imageGCManager]: Disk usage on image filesystem is at 90% which is over the high threshold (85%). Trying to free 3163150745 bytes down to the low threshold (80%).
로그에서 특이사항 -> kubelet이 image_gc_manager 라는 함수를 호출하여 image 파일시스템을 정리


  • 예상과 다른 원인 파악
    - 지정된 리소스 임계치 도달시 파드를 제거하는 로직과 도커이미지를 제거하는 로직은 상호 독립적으로 실행.


<기존 지식>
- evictionHard 옵션에 지정된 리소스 임계치 도달시 pod 축출(evicted) 및 도커 이미지 정리


<조사중 알게된 사실>
- evictionHard 옵션에 지정된 리소스 임계치 도달시 pod 축출(evicted)
- 도커 이미지 정리는 독립적으로 동작(image Garbage Collection 프로세스), 별개의 옵션 존재.
imageGCHighThresholdPercent (default : 85)
    imageGCLowThresholdPercent (defailt : 80)


- image Garbage Collection 프로세스

이미지 제거되는 과정은 다음 아티클에서 별도로 다룹니다.

kubelet image garbage collection 옵션



  • 정리
    - evictionHard 프로세스는 주기적(default : 5분)으로 실행
    - 지정된 임계치에 리소스 사용량이 도달시 kubelet은 해당 노드의 pod를 제거하여 리소스 확보를 시도.
    - 축출(evicted)된 파드는 클러스터 내 다른 노드에 스케줄.
    - kubelet의 image_gc_manager 프로세스 역시 독립적으로 실행. 주기적(default : 5분)으로 docker_root_dir 디스크 사용량을 polling
    - imageGCHighThresholdPercent 설정 값보다 docker_root_dir  디스크 사용량이 높은 경우 imageGC 프로세스 트리거
    - imageGC 프로세스는 imageGCLowThresholdPercent 설정 값보다 docker_root_dir 디스크 사용량이 낮아질때까지 미사용 도커이미지 제거 시도.

예시1 ) evictionHard 및 image_GC 설정이 기본값일 경우 

동일하게 Docker_Root_Dir 디스크 사용률이 85% 도달시 트리거.



예시 2 ) evictionHard 및 image_GC 설정이 다를 경우
    ex) imagefs.available : 90 / imageGCHighThresholdPercent : 85 / imageGCLowThreshold : 80
    
Docker_Root_Dir 디스크 사용률이 85% 도달시 imageGC 트리거 되며 미사용 이미지 제거 시도 (evicted는 수행되지 않았으므로 사용중이지 않은 이미지만 제거)

이후 imageGCLowThresholdPercent 에서 지정된 80% 이하로 내려올 때 까지 반복 수행.

*evictionHard는 수행되지 않으므로 파드는 제거되지 않고 5분마다 imageGC만 반복 수행.




kubelet이 eviction 수행시 파드 축출(evicted) 우선순위. (eviction 수행중 모든 파드가 내려가지 않음)

pod 스펙중 QOS 필드에 정의되며 기본적으로 4단계로 구분. (Mirror Pod , Guaranteed , BestEffort , Burstable )
    # kubectl describe po <POD_NAME>
   ... 생략 ...
    QoS Class:       BestEffort
    ... 생략 ...



제거되는 순서
1. Mirror Pod (최우선 제거되는 파드)
        : 데몬셋을 제외한 스태틱 파드가 Mirror Pod 레벨에 해당. 

2. BestEffort
        : 컨테이너가 cpu,mem 리소스에 대한 requests,limits 설정이 없는 경우 BestEffort 레벨에 해당.
          동일한 BestEffort 레벨의 파드가 있을 경우 메모리 사용량이 큰 파드를 제거.

3. Burstable
        : 하나 이상의 컨테이너가 requests,limits 설정된 경우 Burstable 레벨에 해당.
          동일한 Burstable 레벨의 QOS 파드가 있을 경우 오래된 파드가 제거.

4. Guaranteed (가장 마지막에 제거되는 파드)
        : 파드 내부 모든 컨테이너 cpu,memory의 requests와 limits을 동일하게 설정된 경우.
          동일한 Guaranteed 레벨의 QOS 파드가 있을 경우 오래된 파드가 제거.
          Guaranteed Pod는 Request와 limit이 동일하여 자원의 오버커밋이 없어 자원 사용을 보장.
          Guaranteed 클래스 내부에서 실행되는 프로세스들은 모두 기본 OOM 점수(oom_score_adj)가 -998로 설정되어 호스트 시스템이 메모리 리소스를 필요로 하는 경우에만 kill.

          * OOM(oom_score_adj)점수는 아래 아티클 참고
          서버 메모리 풀 확인 방법 (oom-killer)




마치며

  • 파드의 spec중 QOS 필드에 파드 우선순위가 명시되는데 PriorityClass와 priorityClassName 필드를 파드 spec에 추가하면 우선순위를 custom하게 수정할 수 있습니다. (동일한 priority 값을 가진 경우 QOS 비교)
    https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/

  • 이번 아티클에서는 노드의 리소스가 부족한 상황에 evictionHard, imageGarbageCollector가 상호작용하며 부족한 리소스 확보를 하는지 확인합니다.

  • Qos 와 관련된 설명은 아래 아티클을 참고합니다.
    쿠버네티스 리소스 관리 알아보기



아티클이 유용했나요?

훌륭합니다!

피드백을 제공해 주셔서 감사합니다.

도움이 되지 못해 죄송합니다!

피드백을 제공해 주셔서 감사합니다.

아티클을 개선할 수 있는 방법을 알려주세요!

최소 하나의 이유를 선택하세요
CAPTCHA 확인이 필요합니다.

피드백 전송

소중한 의견을 수렴하여 아티클을 개선하도록 노력하겠습니다.

02-558-8300