cgroup v1 vs v2 — PSI와 메모리 압력
쿠버네티스 노드도 결국 리눅스 머신이라 노드 이미지를 새로 올리면 커널과 런타임이 같이 올라갑니다. 그래서 올릴 때마다 릴리스 노트를 훑어 워크로드에 영향이 갈 만한 변경이 있는지 봅니다.
이번 노트에 cgroup 이 v2 로 바뀐다는 줄이 있었고, 커널 쪽 얘기라 워크로드와는 상관없겠거니 하고 넘길 뻔했습니다.
매니페스트에 적는 requests 와 limits 는 스케줄러가 참고하는 숫자로만 보이지만 실제로 그
제한을 거는 건 cgroup 이고, 파드가 메모리 한계에 부딪혀 죽을 때 그 판정을 내리는 것도 이
층입니다. 그러니까 cgroup 세대가 바뀐다는 건 제한을 거는 인터페이스와 압력을 읽는 값이 통째로
바뀐다는 뜻이라, 같은 매니페스트를 올려도 밑에 깔린 게 다르면 동작이 달라집니다.
v1 — 리소스별로 갈라진 트리
cgroup v1 은 컨트롤러(메모리·CPU·blkio 등)마다 계층 트리가 하나씩 있어서, 파일 시스템으로 보면 이렇게 갈라져 있습니다.
/sys/fs/cgroup/
├── memory/kubepods/pod<uid>/<container>/
│ ├── memory.limit_in_bytes # 이 컨테이너의 메모리 상한
│ └── memory.usage_in_bytes # 지금 쓰는 양
├── cpu/kubepods/pod<uid>/<container>/
│ ├── cpu.cfs_quota_us # CPU 상한
│ └── cpu.stat
└── blkio/kubepods/pod<uid>/<container>/
같은 컨테이너인데도 메모리는 memory/ 아래, CPU 는 cpu/ 아래에 따로 있습니다. 한 컨테이너가
지금 어떤 상태인지 보려면 트리를 옮겨 다니면서 같은 경로를 다시 찾아야 하고, 컨테이너 런타임마다
이 트리를 만드는 방식이 달라서 경로가 조금씩 다릅니다.
v2 — 트리 통합과 PSI
v2 는 컨트롤러를 트리 하나로 합쳐서, 한 컨테이너의 것이 한 디렉토리에 모입니다.
/sys/fs/cgroup/kubepods.slice/.../<container>/
├── memory.max # 메모리 상한 (v1 의 memory.limit_in_bytes)
├── memory.current # 지금 쓰는 양
├── cpu.max # CPU 상한
├── cpu.stat
└── memory.pressure # v2 에서 새로 생긴 것
경로가 하나로 모인 것도 편하지만, 실무에서 체감되는 건 마지막 줄입니다.
PSI(Pressure Stall Information) 는 자원이 모자라서 작업이 실제로 얼마나 멈춰 있었는지를 재는
값이고, memory.pressure 를 열어 보면 이렇게 나옵니다.
some avg10=12.35 avg60=8.21 avg300=2.14 total=48213904
full avg10=3.02 avg60=1.87 avg300=0.44 total=11302847
some 은 일부 프로세스가 메모리를 못 구해 멈춰 있던 시간 비율이고, full 은 그 cgroup 안의
프로세스가 전부 멈춰 있던 비율입니다. avg10 은 최근 10초 평균이라 some avg10=12.35 는 지난
10초 중 12.35% 는 누군가 메모리를 기다리고 있었다는 뜻입니다. full 이 올라가면 그 컨테이너는
사실상 일을 못 하고 있는 상태입니다.
여기서 중요한 건 이 값이 아직 아무도 안 죽었는데도 올라간다는 점입니다.
PSI가 바꾸는 것 — OOM 이전에 압력 읽기
v1 에는 이런 값이 없어서, 볼 수 있는 건 memory.usage_in_bytes 가 상한에 얼마나 가까운지
정도입니다. 이건 지금 얼마나 쓰고 있느냐지 부족해서 멈췄느냐가 아닙니다. 캐시가 많이 잡혀
사용량이 상한 근처인 컨테이너가 멀쩡히 도는가 하면, 사용량은 여유가 있어도 특정 순간에 할당을 못
받아 멈추는 컨테이너도 있어서, 사용량 하나로는 이 둘이 갈라지지 않습니다.
그래서 v1 에서 메모리 부족은 대개 OOM Kill 로 드러납니다. 커널이 프로세스를 죽이고 나서야 모자랐다는 게 보이니, 확인 시점이 항상 사후입니다.
PSI 가 있으면 memory.pressure 를 읽어서 압력이 올라가는 걸 OOM 전에 볼 수 있습니다. 사용량이
상한에 얼마나 가까운지가 아니라 메모리를 확보하느라 실제로 얼마나 멈춰 있었는지가 지표로 나오니,
알람을 걸든 파드를 미리 옮기든 손쓸 시간이 생깁니다.
다만 kubelet 의 eviction 은 여전히 남은 메모리 양으로 판정하니, PSI 는 그 판정이 내려지기 전에 압력을 미리 보여 주는 지표로 씁니다.
노드 OS 업그레이드가 패키지 교체 이상인 이유
새 노드 OS, 예를 들어 Amazon Linux 2023 으로 올릴 때 릴리스 노트에서 눈에 띄는 건 yum 이 dnf
로 바뀌었다거나 커널 버전이 올랐다는 정도입니다. 워크로드 입장에서 더 큰 변화는 그 밑에 있어서,
cgroup v2 가 기본이 되면 kubelet 이 자원 압력을 판단하는 근거가 v2 와 PSI 로 바뀝니다.
v1의 사후 OOM, v2의 사전 압력(PSI)
cgroup 은 쿠버네티스 리소스 제한의 실제 구현체입니다. v1 은 리소스별로 트리가 쪼개져 있고 메모리
부족을 OOM 으로 사후에 알게 되는 반면, v2 는 트리를 통합하고 PSI 로 압력을 미리 보여줘서 죽기
전에 손쓸 시간을 줍니다. 노드 이미지를 올릴 일이 생기면 커널 버전만 보지 말고 그 이미지가 cgroup
을 어느 세대로 쓰는지도 같이 확인하면 됩니다. stat -fc %T /sys/fs/cgroup/ 이 cgroup2fs 로
나오면 v2 입니다.
댓글 0