- Published on
EKS DiskPressure로 Pod Evicted 폭주 해결 10가지
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 멀쩡해 보이는데도 EKS에서 갑자기 Pod가 Evicted로 쏟아지면, 대부분 노드의 ephemeral storage(로컬 디스크) 가 임계치를 넘어 kubelet이 DiskPressure를 선언한 상황입니다. 문제는 한 번 압박이 시작되면 이미지/로그/emptyDir/컨테이너 writable layer가 연쇄적으로 디스크를 잠식하면서, 스케줄링은 계속되고(eviction으로 자리 생김) 다시 쓰레기가 쌓여 Evicted 폭주(storm) 로 이어진다는 점입니다.
이 글은 “지금 당장 폭주를 멈추는 응급처치”부터 “재발 방지 설계”까지, 현장에서 가장 효과가 컸던 10가지 해결책을 체크리스트 형태로 정리합니다.
> 참고로 노드 상태 이상이 동반되면 원인이 겹칠 수 있습니다. kubelet이 NotReady로 흔들리는 케이스는 EKS kubelet NotReady - PLEG is not healthy 7가지도 같이 확인해 보세요.
0) 증상 확인: DiskPressure Evicted인지 먼저 확정
먼저 “정말 DiskPressure 때문에 Evicted 되었는지”를 빠르게 확정합니다.
# Evicted Pod 목록
kubectl get pod -A --field-selector=status.phase=Failed | grep -i evicted
# 특정 Pod가 왜 죽었는지
kubectl describe pod -n <ns> <pod>
# 노드 컨디션
kubectl get nodes
kubectl describe node <node-name> | sed -n '/Conditions:/,/Addresses:/p'
describe pod 이벤트에 아래 같은 문구가 있으면 DiskPressure 계열입니다.
The node was low on resource: ephemeral-storage.Evicted: The node was low on resource: ephemeral-storage.
또한 노드 컨디션에 DiskPressure=True가 켜져 있는지 확인합니다.
1) 즉시 폭주 멈추기: 문제 노드 Cordon/Drain + 워크로드 확산
Evicted가 폭주 중이면 “원인 분석”보다 먼저 확산을 멈추는 것이 중요합니다.
# 문제 노드 스케줄링 차단
kubectl cordon <node-name>
# 안전하게 비우기(daemonset 제외)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --grace-period=60
--delete-emptydir-data는 emptyDir가 큰 워크로드에 필요할 수 있으나, 데이터 유실 영향이 있으니 앱 특성에 맞게 판단하세요.- 노드가 여러 대 동시에 DiskPressure면, 한 대씩 처리하며 서비스 가용성을 유지합니다.
2) “무엇이 디스크를 먹는지” 5분 안에 분해해서 보기
DiskPressure는 원인이 다양합니다. 크게 4가지 축으로 쪼개서 보면 빨라집니다.
- 컨테이너 로그(stdout/stderr)
- 이미지/레이어/컨테이너 런타임 캐시
- emptyDir / hostPath / writable layer
- kubelet/OS 레벨 로그, core dump, 임시파일
노드에 접속할 수 있다면(SSM/SSH) 우선순위대로 확인합니다.
# 전체 사용량
sudo df -h
sudo df -h /var/lib/kubelet /var/lib/containerd /var/log
# 큰 디렉터리 Top
sudo du -xh /var/lib | sort -h | tail -n 30
sudo du -xh /var/log | sort -h | tail -n 30
# containerd 기준: 스냅샷/콘텐츠/이미지
sudo du -xh /var/lib/containerd | sort -h | tail -n 30
EKS AMI/런타임에 따라 경로가 다를 수 있지만 보통 다음이 핵심입니다.
/var/lib/kubelet(Pod 볼륨, emptyDir, 플러그인 데이터)/var/lib/containerd또는/var/lib/docker(이미지/레이어)/var/log/containers,/var/log/pods,/var/log/journal(로그)
3) 컨테이너 로그 폭주 차단: 로그 로테이션/드라이버 설정
DiskPressure의 가장 흔한 원인은 애플리케이션 로그 폭주입니다. stdout로 무한정 찍히면 /var/log/containers/*.log가 기하급수로 커집니다.
(1) Docker 사용 시(구형 노드) log-opts 강제
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}
sudo systemctl restart docker
(2) containerd 사용 시: kubelet 로그 로테이션 파라미터 확인
kubelet이 컨테이너 로그 로테이션을 수행하도록 설정되어야 합니다.
--container-log-max-size--container-log-max-files
EKS Managed Node Group는 런치 템플릿/부트스트랩 인자로 제어하는 경우가 많습니다.
ps -ef | grep kubelet | grep -E "container-log-max|container-log"
팁: 로그 수집이 필요하면 stdout를 줄이는 대신 Fluent Bit 등으로 전송하되, 노드 로컬 파일이 무한정 커지지 않도록 로테이션은 반드시 켜세요.
4) 이미지/레이어 정리: Pull 폭주 + 캐시 누적 방지
새 버전을 자주 배포하거나 이미지 태그를 매번 바꾸면 노드에 이미지가 계속 쌓입니다. 특히 큰 베이스 이미지(ML, GPU, JDK)가 많으면 치명적입니다.
(1) containerd 이미지 정리
# 설치되어 있다면
sudo crictl images
sudo crictl rmi --prune
# nerdctl 사용 가능 시
sudo nerdctl images
sudo nerdctl image prune -a
(2) 이미지 풀 정책 점검
imagePullPolicy: Always는 개발 환경에선 편하지만, 운영에서 태그 전략이 나쁘면 디스크/레지스트리 모두를 압박합니다.
- 운영:
IfNotPresent+ immutable tag(예: git sha) - 레지스트리 rate limit/429 문제까지 동반되면 EKS ImagePullBackOff 429 Too Many Requests 해결도 같이 점검하세요.
5) emptyDir/writable layer 상한 설정: ephemeral-storage requests/limits
DiskPressure Eviction은 “노드 전체 디스크” 기준이지만, 쿠버네티스는 Pod별 ephemeral-storage request/limit을 통해 스케줄링과 eviction 우선순위를 더 똑똑하게 만들 수 있습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
template:
spec:
containers:
- name: api
image: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/api:sha-abc
resources:
requests:
cpu: "200m"
memory: "512Mi"
ephemeral-storage: "1Gi"
limits:
cpu: "1"
memory: "1Gi"
ephemeral-storage: "2Gi"
emptyDir에 대용량 쓰기(압축, 캐시, 임시 업로드)가 있으면 반드시 설정하세요.- requests를 넣으면 스케줄러가 “디스크 여유가 있는 노드”로 더 잘 분산합니다.
6) emptyDir 자체에 sizeLimit 걸기 + 캐시를 PV/S3로 이동
Pod 내부 캐시/임시파일이 원인이라면 emptyDir에 sizeLimit을 걸어 “한 Pod가 노드를 먹어치우는” 상황을 막습니다.
spec:
volumes:
- name: cache
emptyDir:
sizeLimit: 2Gi
containers:
- name: worker
volumeMounts:
- name: cache
mountPath: /cache
근본적으로는 다음 중 하나로 이동하는 게 좋습니다.
- EBS PVC: 노드 루트 디스크와 분리
- EFS: 여러 Pod 공유 캐시(성능/비용 고려)
- S3: 임시 업로드/아카이브 성격이면 최적
7) 노드 루트 볼륨(EBS) 용량/타입/IOPS 재설계
EKS Managed Node Group의 기본 루트 볼륨이 20GiB인 경우가 아직도 많습니다. 컨테이너 이미지 몇 개 + 로그만으로도 쉽게 터집니다.
권장 방향
- 루트 볼륨을 최소 50~100GiB로 상향(워크로드에 따라 더)
- gp3로 전환해 비용 대비 성능 확보
- 이미지가 큰 워크로드는 별도 노드그룹으로 격리
Terraform을 쓴다면 Launch Template에서 block_device_mappings로 조정합니다.
resource "aws_launch_template" "eks" {
name_prefix = "eks-ng-"
block_device_mappings {
device_name = "/dev/xvda"
ebs {
volume_size = 100
volume_type = "gp3"
iops = 3000
throughput = 125
delete_on_termination = true
}
}
}
8) kubelet eviction threshold 튜닝(마지막 수단)
kubelet은 evictionHard/evictionSoft 임계치에 따라 Pod를 내보냅니다. 이를 무작정 늦추면 “더 늦게 터지되 더 크게 터지는” 부작용이 있어 마지막 수단으로만 고려하세요.
확인:
ps -ef | grep kubelet | grep eviction
튜닝 시 원칙:
- 임계치를 완화하기보다, 디스크를 더 주고(볼륨 확장)
- 로그/캐시 상한을 걸어 생성량을 줄이는 쪽이 우선
9) DaemonSet/노드 에이전트가 디스크를 먹는지 점검
운영 환경에서는 앱보다 에이전트(로그/보안/모니터링) 가 더 많이 쓰는 경우가 흔합니다.
체크 포인트:
- Fluent Bit/Fluentd 버퍼가 노드에 쌓임
- 보안 에이전트가
/var/log에 대용량 파일 생성 - 노드 문제 발생 시 core dump가 누적
노드에서 “어떤 프로세스가 파일을 크게 잡고 있는지”를 보면 빠릅니다.
# 큰 파일 Top
sudo find /var -xdev -type f -size +200M -printf '%s %p\n' | sort -n | tail -n 30
# 삭제해도 공간이 안 돌아오면: 열린 파일 핸들(삭제된 파일)
sudo lsof | grep deleted | head
10) 재발 방지: 모니터링/알람 + 스케일링/분리 전략
DiskPressure는 “터지고 나서” 알면 이미 늦습니다. 아래 3가지만 해도 재발률이 크게 내려갑니다.
(1) 노드 파일시스템 사용률 알람
- CloudWatch Agent / node-exporter로
node_filesystem_avail_bytes감시 - 70% 경고, 85% 치명 알람(환경에 맞게)
(2) 이벤트 기반 감지
쿠버네티스 이벤트에서 Evicted, DiskPressure를 수집해 슬랙/페이지로 알립니다.
kubectl get events -A --sort-by=.lastTimestamp | tail -n 50
(3) 워크로드 분리
- 이미지 큰 서비스, 로그 많은 서비스, 배치/ETL은 별도 노드그룹
nodeSelector/taints/tolerations로 격리
spec:
tolerations:
- key: "workload"
operator: "Equal"
value: "batch"
effect: "NoSchedule"
nodeSelector:
workload: batch
보너스: 현장에서 가장 많이 맞닥뜨린 “원인-대응” 매핑
- Evicted가 배포 직후 폭주: 새 이미지가 너무 큼 → 이미지 최적화(멀티스테이지), 노드 루트 볼륨 상향, 이미지 prune
- 특정 서비스만 반복 Evicted: emptyDir/캐시 폭주 →
ephemeral-storagelimit + emptyDirsizeLimit - 새벽 배치 시간에만 터짐: 임시 압축/다운로드 → PVC/S3로 이동, 배치 노드그룹 분리
- 로그 수집 도입 후 터짐: 에이전트 버퍼/로컬 파일 → 로테이션/버퍼 설정 재점검
마무리 체크리스트(우선순위)
- 문제 노드
cordon/drain으로 확산 차단 /var/log,/var/lib/kubelet,/var/lib/containerd사용량 Top 확인- 컨테이너 로그 로테이션 강제
- 이미지/레이어 prune + 태그/풀 정책 개선
ephemeral-storagerequests/limits 적용- emptyDir
sizeLimit적용 및 캐시 외부화(PV/S3) - 노드 루트 볼륨 확장(gp3, 100GiB 등)
- 에이전트/DaemonSet 디스크 사용 점검
- (필요 시) kubelet eviction threshold는 신중히
- 모니터링/알람으로 사전 감지
DiskPressure는 단순히 “디스크가 부족”한 문제가 아니라, 로그/이미지/캐시/배치/에이전트의 합이 노드 로컬 스토리지를 잠식하는 구조적 문제인 경우가 대부분입니다. 위 10가지를 순서대로 적용하면 Evicted 폭주는 빠르게 진정되고, 재발도 크게 줄일 수 있습니다.