containerd 를 런타임으로 쓰는 클러스터에는 docker 명령이 없다. 노드에서 이미지를 미리 받아 두거나 레지스트리 인증을 확인할 때는 crictl 을 쓴다. 그리고 이미지가 쌓여 노드에 node.kubernetes.io/disk-pressure taint 가 걸리면 파드가 Pending 으로 남는데, df -h 상으로는 여유가 충분해 보이는 경우가 있다. 두 가지는 같은 노드 작업에서 이어지므로 한 문서로 묶는다.
crictl images
crictl pull nginx:1.27
crictl rmi <image-id>
crictl ps -a
crictl logs <container-id>
프라이빗 레지스트리는 --creds 로 자격 증명을 준다. Harbor 로봇 계정처럼 이름에 $ 가 들어가면 셸이 변수로 해석하지 않도록 작은따옴표로 감싼다.
crictl pull --creds '${REGISTRY_USER}:${REGISTRY_PASSWORD}' \
harbor.example.local/project/nginx:1.27
crictl 은 자체 설정 없이 containerd 의 설정을 그대로 따른다. 레지스트리 인증이나 TLS 를 항상 쓰려면 --creds 가 아니라 containerd 쪽에 넣는다.
cat /etc/crictl.yaml
cat /etc/containerd/config.toml
ls /etc/containerd/certs.d/
certs.d/<registry-host>/hosts.toml 에 레지스트리별 설정을 두는 방식이 최근 containerd 의 표준이다. 설정을 고쳤으면 systemctl restart containerd 로 반영한다.
파드가 뜨지 않을 때 먼저 근거를 확정한다.
kubectl -n ${NS} describe pod ${POD} | grep -A5 Events
# → "had taint {node.kubernetes.io/disk-pressure: }, that the pod didn't tolerate"
kubectl describe node ${NODE} | egrep -A3 'DiskPressure|Taints'
taint 가 원인이 맞다면, kubelet 이 어떤 기준으로 그렇게 판단했는지를 본다. kubelet 은 루트 파일 시스템 하나만 보지 않는다.
| 신호 | 대상 | 기본 임계치 |
|---|---|---|
nodefs.available |
kubelet 루트(/var/lib/kubelet)가 있는 파일 시스템 |
10% |
nodefs.inodesFree |
같은 파일 시스템의 inode | 5% |
imagefs.available |
이미지 저장소(/var/lib/containerd)가 있는 파일 시스템 |
15% |
imagefs.inodesFree |
같은 파일 시스템의 inode | 5% |
containerd 를 별도 디스크로 분리해 둔 환경이라면 df -h / 는 멀쩡해도 imagefs 쪽이 임계치를 넘었을 수 있다. 반대로 용량은 남아도 작은 파일이 많아 inode 가 먼저 마르는 경우도 흔하다.
# kubelet 이 실제로 무엇을 보고 판단했는지
journalctl -u kubelet --since '-2h' \
| egrep -i 'DiskPressure|eviction|imagefs|nodefs|ephemeral-storage|garbage'
# 각 파일 시스템의 용량과 inode
df -h /var/lib/kubelet /var/lib/containerd /var/log
df -i /var/lib/kubelet /var/lib/containerd /var/log
image garbage collection failed 나 failed to garbage collect 가 보이면 kubelet 이 정리를 시도했지만 실패한 것이다. 이 경우 공간을 비워도 조건이 풀리지 않을 수 있으므로 containerd 쪽 오류를 함께 본다.
# 사용하지 않는 이미지 정리
crictl rmi --prune
# 종료된 컨테이너 정리
crictl rm $(crictl ps -a -q --state Exited)
# 로그
du -sh /var/log/pods /var/log/containers
journalctl --vacuum-size=500M
조건이 풀리면 kubelet 이 taint 를 자동으로 제거한다. 다만 상태 갱신에 지연이 있다. kubelet 은 evictionPressureTransitionPeriod(기본 5분) 동안 조건이 안정적으로 유지돼야 해제하므로, 정리 직후 곧바로 풀리지 않는다고 설정을 더 건드리지 않는다.
watch -n 10 "kubectl describe node ${NODE} | egrep 'DiskPressure|Taints' -A2"
5~10분이 지나도 DiskPressure: True 가 유지되면 kubelet 재시작을 고려한다. 그전에 LastTransitionTime 을 확인해 조건이 언제 True 가 됐는지, 정리 이후에 갱신이 있었는지를 본다.
latest 로 두지 않는다. 태그 없는 레이어가 계속 쌓인다./var/lib/containerd 를 루트와 분리된 디스크에 둔다. 차더라도 노드 전체가 멈추지 않는다.