cgroup이란?
cgroup(Control Group)은 리눅스 커널에서 제공하는 프로세스 그룹의 리소스 사용을 제한하고 격리하는 기능입니다. Docker, Kubernetes 같은 컨테이너 기술의 핵심 기반이며, CPU, 메모리, 디스크 I/O 등의 리소스를 프로세스 단위가 아닌 그룹 단위로 관리할 수 있게 합니다.
cgroup이 해결하는 문제
cgroup이 없다면:
- 한 프로세스가 시스템 리소스를 독점할 수 있음
- 멀티 테넌트 환경에서 공정한 리소스 배분 불가
- 컨테이너 간 격리가 불완전함
cgroup을 사용하면:
- 프로세스 그룹별 리소스 할당량 설정
- 리소스 사용량 추적 및 통계
- 우선순위 기반 리소스 스케줄링
cgroup 계층 구조
cgroup은 트리 구조로 구성되며, 각 노드는 리소스 제한을 상속받습니다.
계층별 역할:
| 계층 | 경로 예시 | 설명 |
|---|---|---|
| Root | /sys/fs/cgroup |
시스템 전체 cgroup 루트 |
| Slice | /kubepods.slice |
Kubernetes Pod들을 위한 최상위 그룹 |
| QoS Class | /kubepods.slice/guaranteed |
Pod의 QoS(서비스 품질)별 분류 |
| Pod | /kubepods.slice/guaranteed/pod-abc123 |
개별 Pod의 cgroup |
| Container | /kubepods.slice/guaranteed/pod-abc123/container-1 |
Pod 내 개별 컨테이너 |
cgroup v1 vs v2
리눅스는 cgroup v1과 v2 두 가지 버전을 제공합니다.
주요 차이점
| 항목 | cgroup v1 | cgroup v2 |
|---|---|---|
| 계층 구조 | 컨트롤러별로 독립적인 트리 | 단일 통합 계층 |
| 마운트 위치 | /sys/fs/cgroup/cpu, /sys/fs/cgroup/memory 등 |
/sys/fs/cgroup (단일) |
| 프로세스 배치 | 트리의 모든 노드에 배치 가능 | 리프 노드에만 배치 가능 |
| 메모리 관리 | memory.limit_in_bytes | memory.max, memory.high |
| CPU 관리 | cpu.cfs_quota_us, cpu.cfs_period_us | cpu.max |
| IO 관리 | blkio.* | io.max, io.weight |
| Pressure Stall Information | 미지원 | PSI 메트릭 제공 |
| Kubernetes 지원 | 1.25 이전 기본값 | 1.25+ 기본값 (systemd 기반) |
cgroup 버전 확인
# cgroup v2가 활성화되어 있는지 확인
$ stat -fc %T /sys/fs/cgroup/
cgroup2fs # v2 사용 중
tmpfs # v1 사용 중
# 또는
$ cat /proc/filesystems | grep cgroup
nodev cgroup
nodev cgroup2
# Kubernetes 노드에서 확인
$ kubectl get nodes -o wide
$ kubectl debug node/<node-name> -it --image=ubuntu
$ mount | grep cgroupKubernetes에서의 cgroup 활용
Kubernetes는 cgroup을 사용하여 Pod와 컨테이너의 리소스를 관리합니다.
Pod QoS Class와 cgroup 매핑
Kubernetes는 Pod의 리소스 요청/제한에 따라 자동으로 QoS Class를 할당하고, 이를 cgroup 계층에 반영합니다.
QoS Class별 특징:
| QoS Class | 조건 | OOM Kill 우선순위 | cgroup 경로 |
|---|---|---|---|
| Guaranteed | 모든 컨테이너의 requests == limits | 가장 나중 (보호) | /kubepods.slice/kubepods-guaranteed.slice |
| Burstable | requests < limits (일부라도) | 중간 | /kubepods.slice/kubepods-burstable.slice |
| BestEffort | requests/limits 미설정 | 가장 먼저 (희생) | /kubepods.slice/kubepods-besteffort.slice |
Pod 리소스 설정 예시
1. Guaranteed Pod
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "512Mi" # requests와 동일
cpu: "500m" # requests와 동일생성되는 cgroup 구조:
/sys/fs/cgroup/kubepods.slice/
└── kubepods-guaranteed.slice/
└── kubepods-guaranteed-pod<uid>.slice/
└── crio-<container-id>.scope/
├── cpu.max # 50000 100000 (500m = 0.5 CPU)
├── memory.max # 536870912 (512Mi)
└── memory.high # 5368709122. Burstable Pod
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi" # requests보다 큼
cpu: "1000m" # requests보다 큼생성되는 cgroup:
/sys/fs/cgroup/kubepods.slice/
└── kubepods-burstable.slice/
└── kubepods-burstable-pod<uid>.slice/
└── crio-<container-id>.scope/
├── cpu.max # 100000 100000 (1 CPU limit)
├── cpu.weight # 10 (250m request 기반)
├── memory.max # 536870912 (512Mi limit)
└── memory.high # 268435456 (256Mi request)3. BestEffort Pod
apiVersion: v1
kind: Pod
metadata:
name: besteffort-pod
spec:
containers:
- name: app
image: nginx
# resources 설정 없음생성되는 cgroup:
/sys/fs/cgroup/kubepods.slice/
└── kubepods-besteffort.slice/
└── kubepods-besteffort-pod<uid>.slice/
└── crio-<container-id>.scope/
├── cpu.max # max (무제한)
└── memory.max # max (무제한, 노드 메모리까지 가능)CPU cgroup 상세
CPU 제한 방식
Kubernetes는 두 가지 방식으로 CPU를 제어합니다.
cgroup v2 CPU 설정
1. CPU Weight (requests 기반)
# Pod의 cpu.weight 확인
$ cat /sys/fs/cgroup/kubepods.slice/.../cpu.weight
10 # 250m request → weight 10 (250m / 1024m * 1024 / 25)
# 계산 공식
cpu.weight = (cpu_request / 1_core) * 1024 / 25
# 예: 500m → (0.5 * 1024) / 25 = 20.48 ≈ 202. CPU Max (limits 기반)
# Pod의 cpu.max 확인
$ cat /sys/fs/cgroup/kubepods.slice/.../cpu.max
50000 100000 # 500m = 50ms per 100ms (50%)
# 형식: <quota> <period>
# quota: period 동안 사용 가능한 CPU 시간 (마이크로초)
# period: 스케줄링 주기 (기본 100ms = 100000μs)
# 1 CPU = 100000 100000
# 500m = 50000 100000 (50%)
# 2 CPU = 200000 100000 (200%)CPU Throttling 이해하기
CPU limit을 초과하면 프로세스가 throttling됩니다.
Throttling 모니터링:
# cgroup v2
$ cat /sys/fs/cgroup/kubepods.slice/.../cpu.stat
usage_usec 12850352
user_usec 11234521
system_usec 1615831
nr_periods 128503 # 총 period 수
nr_throttled 1247 # throttle된 횟수
throttled_usec 543211 # throttle된 총 시간
# throttle 비율 계산
throttle_ratio = nr_throttled / nr_periods
# 1247 / 128503 = 0.97% (낮음, 정상)
# 10% 이상이면 CPU limit 증가 고려Memory cgroup 상세
Memory 제한 구조
cgroup v2 Memory 설정
# 1. Hard Limit (resources.limits.memory)
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.max
536870912 # 512Mi
# 2. Soft Limit (resources.requests.memory, 참고용)
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.high
268435456 # 256Mi
# 3. 현재 사용량
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.current
134217728 # 128Mi (현재 사용 중)
# 4. 메모리 상세 통계
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.stat
anon 104857600 # 익명 메모리 (Heap, Stack)
file 29360128 # 파일 캐시
kernel 4194304 # 커널 메모리
slab 2097152 # Slab 캐시
...OOM (Out of Memory) Kill
메모리가 limit을 초과하면 OOM Killer가 작동합니다.
OOM 우선순위:
OOM 이벤트 확인:
# Kubernetes 이벤트
$ kubectl describe pod <pod-name>
Events:
Type Reason Message
---- ------ -------
Warning OOMKilled Container 'app' exceeded memory limit
# 노드 dmesg 로그
$ dmesg | grep -i oom
[12345.678] Memory cgroup out of memory: Killed process 1234 (java) total-vm:2097152kB, anon-rss:524288kB
# cgroup OOM 카운터
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.events
low 0
high 12 # memory.high 초과 횟수
max 3 # memory.max 도달 횟수 (OOM)
oom 2 # OOM Kill 횟수
oom_kill 2실무 시나리오: 리소스 튜닝
시나리오 1: CPU Throttling이 심한 경우
증상:
# Pod에서 높은 throttle 비율 관찰
$ kubectl exec -it <pod> -- cat /sys/fs/cgroup/cpu.stat
nr_periods 50000
nr_throttled 12500 # 25% throttled!
throttled_usec 31250000원인 분석:
# 현재 설정
resources:
requests:
cpu: "500m"
limits:
cpu: "1000m" # 1 CPU로 제한애플리케이션이 버스트 트래픽 시 1 CPU 이상을 사용하려 하지만, limit에 의해 throttle됨.
해결 방법:
# 방법 1: limit 증가
resources:
requests:
cpu: "500m"
limits:
cpu: "2000m" # 2 CPU로 확대
# 방법 2: limit 제거 (Burstable로 변경)
resources:
requests:
cpu: "500m"
# limits 없음 → 필요시 노드 CPU까지 사용 가능검증:
# 1시간 후 throttle 재확인
$ kubectl exec -it <pod> -- cat /sys/fs/cgroup/cpu.stat
nr_periods 60000
nr_throttled 600 # 1%로 감소 (정상)시나리오 2: 메모리 OOM이 반복되는 경우
증상:
$ kubectl get pods
NAME READY STATUS RESTARTS
app-pod 0/1 OOMKilled 5
$ kubectl describe pod app-pod
Last State: Terminated
Reason: OOMKilled
Exit Code: 137원인 분석:
# 현재 설정
resources:
requests:
memory: "512Mi"
limits:
memory: "1Gi" # 1GB로 제한
# 실제 사용량 확인
$ kubectl exec -it <pod> -- cat /sys/fs/cgroup/memory.current
1073741824 # 1GB (거의 limit에 도달)해결 방법 1: Memory Limit 증가
resources:
requests:
memory: "512Mi"
limits:
memory: "2Gi" # 2GB로 확대해결 방법 2: 애플리케이션 튜닝 (Java 예시)
spec:
containers:
- name: app
image: openjdk:17
env:
- name: JAVA_OPTS
value: "-Xmx768m -Xms512m" # Heap을 limit의 75% 이하로 설정
resources:
requests:
memory: "512Mi"
limits:
memory: "1Gi"추가 모니터링:
# PSI (Pressure Stall Information) - cgroup v2만 지원
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.pressure
some avg10=12.50 avg60=8.32 avg300=4.21 total=12850000
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
# avg10 > 10: 최근 10초 동안 메모리 압박 발생
# full > 0: 완전한 메모리 정지 (심각)PSI (Pressure Stall Information)
PSI는 cgroup v2에서 도입된 리소스 압박(pressure) 측정 메커니즘입니다. CPU, Memory, IO 자원이 부족할 때 프로세스가 얼마나 지연되는지를 정량적으로 측정합니다.
PSI가 필요한 이유
기존 메트릭(CPU 사용률, 메모리 사용량)은 얼마나 사용하는지는 알려주지만, 리소스 부족으로 인한 성능 저하는 감지할 수 없습니다.
Some vs Full 메트릭
PSI는 두 가지 수준의 압박을 측정합니다.
차이점:
| 메트릭 | 의미 | 예시 | 심각도 |
|---|---|---|---|
| some | 일부 태스크가 대기 중 | 4개 프로세스 중 1개가 메모리 대기 | 보통 |
| full | 모든 태스크가 대기 중 | 4개 프로세스 모두 메모리 대기 (CPU 유휴) | 심각 |
시간 윈도우: avg10, avg60, avg300
PSI는 세 가지 시간 윈도우로 압박을 측정합니다.
$ cat /sys/fs/cgroup/kubepods.slice/.../memory.pressure
some avg10=15.23 avg60=8.45 avg300=3.21 total=12850000
full avg10=2.10 avg60=0.85 avg300=0.12 total=542000각 필드 의미:
| 필드 | 시간 윈도우 | 용도 |
|---|---|---|
| avg10 | 최근 10초 | 즉각적인 스파이크 감지 (알림) |
| avg60 | 최근 60초 | 단기 트렌드 파악 |
| avg300 | 최근 5분 | 장기 패턴 분석 |
| total | 누적 시간 (마이크로초) | 전체 압박 시간 |
해석 예시:
리소스별 PSI
1. CPU Pressure
$ cat /sys/fs/cgroup/cpu.pressure
some avg10=8.50 avg60=5.20 avg300=2.10 total=8520000
# full은 CPU에서 정의되지 않음 (CPU는 항상 일부 태스크 실행 가능)특징:
full메트릭 없음 (CPU는 time-slicing 가능)some > 10%: CPU가 수요를 따라가지 못함 → CPU 추가 고려
2. Memory Pressure
$ cat /sys/fs/cgroup/memory.pressure
some avg10=12.50 avg60=8.32 avg300=4.21 total=12850000
full avg10=0.00 avg60=0.00 avg300=0.00 total=0특징:
some > 10%: Page reclaim이 빈번 (성능 저하)full > 0: 모든 태스크가 메모리 대기 (매우 심각)full이 발생하면 곧 OOM Kill 가능성 높음
실무 기준:
| avg10 값 | 상태 | 조치 |
|---|---|---|
| < 5% | 정상 | 모니터링 |
| 5-10% | 경고 | 메모리 사용 패턴 확인 |
| 10-30% | 주의 | 메모리 증설 고려 |
| > 30% | 위험 | 즉시 스케일업 필요 |
3. IO Pressure
$ cat /sys/fs/cgroup/io.pressure
some avg10=5.20 avg60=3.10 avg300=1.50 total=5200000
full avg10=0.10 avg60=0.05 avg300=0.02 total=10000특징:
- 디스크 I/O 대기 시간 측정
- Database, 로그 집약적 애플리케이션에서 중요
full > 0: I/O 병목으로 인한 완전 정지
Kubernetes에서 PSI 활용
1. Pod별 PSI 확인
# Pod의 cgroup 경로 찾기
$ POD_UID=$(kubectl get pod app-pod -o jsonpath='{.metadata.uid}')
$ CGROUP_PATH="/sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod${POD_UID}.slice"
# Memory pressure 확인
$ kubectl debug node/<node-name> -it --image=ubuntu
$ cat ${CGROUP_PATH}/memory.pressure
some avg10=15.23 avg60=8.45 avg300=3.21 total=12850000
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
# CPU pressure 확인
$ cat ${CGROUP_PATH}/cpu.pressure
some avg10=8.50 avg60=5.20 avg300=2.10 total=85200002. PSI 기반 알림 설정
Prometheus Exporter를 통한 수집:
# node-exporter DaemonSet에 PSI 활성화
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
spec:
template:
spec:
containers:
- name: node-exporter
image: prom/node-exporter:latest
args:
- --collector.pressure
- --path.sysfs=/host/sys
volumeMounts:
- name: sys
mountPath: /host/sys
readOnly: true
volumes:
- name: sys
hostPath:
path: /sysPrometheus Alert 규칙:
groups:
- name: psi-alerts
rules:
# Memory pressure 경고
- alert: HighMemoryPressure
expr: |
node_pressure_memory_waiting_seconds_total{type="some"}
> 10
for: 5m
labels:
severity: warning
annotations:
summary: "High memory pressure on {{ $labels.instance }}"
description: "Memory some pressure > 10% for 5 minutes"
# Memory full pressure (심각)
- alert: CriticalMemoryPressure
expr: |
node_pressure_memory_waiting_seconds_total{type="full"}
> 0
for: 1m
labels:
severity: critical
annotations:
summary: "Critical memory pressure on {{ $labels.instance }}"
description: "Memory full pressure detected - OOM imminent"
# CPU pressure 경고
- alert: HighCPUPressure
expr: |
node_pressure_cpu_waiting_seconds_total{type="some"}
> 15
for: 10m
labels:
severity: warning
annotations:
summary: "High CPU pressure on {{ $labels.instance }}"3. PSI 기반 오토스케일링 (실험적)
일부 클러스터 오토스케일러는 PSI를 활용합니다.
# Custom Metrics Adapter 설정 예시
apiVersion: v1
kind: ConfigMap
metadata:
name: adapter-config
data:
config.yaml: |
rules:
- seriesQuery: 'node_pressure_memory_waiting_seconds_total{type="some"}'
resources:
overrides:
node: {resource: "node"}
name:
as: "memory_pressure_some"
metricsQuery: 'avg_over_time(<<.Series>>{<<.LabelMatchers>>}[1m])'실무 PSI 모니터링 대시보드
#!/bin/bash
# psi-monitor.sh - 실시간 PSI 모니터링 스크립트
CGROUP_PATH="/sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod<uid>.slice"
while true; do
clear
echo "=== PSI Dashboard ($(date)) ==="
echo ""
echo "📊 CPU Pressure:"
cat ${CGROUP_PATH}/cpu.pressure
echo ""
echo "💾 Memory Pressure:"
cat ${CGROUP_PATH}/memory.pressure
echo ""
echo "💿 IO Pressure:"
cat ${CGROUP_PATH}/io.pressure
echo ""
# 간단한 상태 표시
MEM_SOME=$(cat ${CGROUP_PATH}/memory.pressure | grep some | awk '{print $2}' | cut -d= -f2)
if (( $(echo "$MEM_SOME > 10" | bc -l) )); then
echo "⚠️ WARNING: High memory pressure detected!"
fi
sleep 5
donePSI vs 기존 메트릭 비교
비교표:
| 상황 | CPU 사용률 | Memory 사용률 | PSI Memory Some | 실제 문제 |
|---|---|---|---|---|
| 정상 | 80% | 3.5GB/4GB | 2% | 없음 - 효율적 사용 |
| 압박 | 60% | 3GB/4GB | 25% | 있음 - Page reclaim 빈번 |
| 심각 | 40% | 3.8GB/4GB | 45% (full: 5%) | 있음 - 거의 정지 상태 |
핵심 인사이트:
- CPU/Memory 사용률이 낮아도 PSI가 높으면 성능 문제 발생
- PSI는 리소스 부족의 영향을 직접 측정
- 사용률은 얼마나 쓰는지, PSI는 얼마나 기다리는지 측정
cgroup 모니터링 및 디버깅
Kubernetes Metrics Server 활용
# Pod별 리소스 사용량
$ kubectl top pods
NAME CPU(cores) MEMORY(bytes)
app-pod 450m 768Mi
# 노드별 리소스 사용량
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
worker-1 1850m 46% 3072Mi 38%cAdvisor를 통한 상세 모니터링
cAdvisor는 각 노드에서 실행되며 cgroup 메트릭을 수집합니다.
# kubelet에 내장된 cAdvisor 접근
$ kubectl proxy
$ curl http://localhost:8001/api/v1/nodes/<node-name>/proxy/stats/summary주요 메트릭:
{
"pods": [
{
"podRef": {
"name": "app-pod",
"namespace": "default"
},
"cpu": {
"time": "2026-01-20T12:00:00Z",
"usageNanoCores": 450000000,
"usageCoreNanoSeconds": 1234567890
},
"memory": {
"time": "2026-01-20T12:00:00Z",
"usageBytes": 805306368,
"workingSetBytes": 536870912,
"rssBytes": 402653184,
"pageFaults": 12345,
"majorPageFaults": 45
}
}
]
}노드에서 직접 cgroup 확인
# 1. 노드에 접속
$ kubectl debug node/<node-name> -it --image=ubuntu
# 2. cgroup 트리 확인
$ apt-get update && apt-get install -y tree
$ tree /sys/fs/cgroup/kubepods.slice/ -L 3
# 3. 특정 Pod의 cgroup 찾기
$ POD_UID=$(kubectl get pod app-pod -o jsonpath='{.metadata.uid}')
$ find /sys/fs/cgroup -name "*${POD_UID}*"
# 4. CPU 사용량 실시간 모니터링
$ watch -n 1 cat /sys/fs/cgroup/kubepods.slice/.../cpu.stat
# 5. Memory 사용량 실시간 모니터링
$ watch -n 1 cat /sys/fs/cgroup/kubepods.slice/.../memory.currentBest Practices
1. 모든 컨테이너에 리소스 설정
# ❌ 나쁜 예: 리소스 미설정 (BestEffort)
spec:
containers:
- name: app
image: myapp:latest
# resources 없음 → OOM 시 가장 먼저 Kill
# ✅ 좋은 예: requests와 limits 설정
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"2. QoS Class 선택 가이드
추천:
| 워크로드 유형 | QoS Class | 설정 예시 |
|---|---|---|
| Database | Guaranteed | requests: 2Gi/1CPU, limits: 2Gi/1CPU |
| Web API | Burstable | requests: 512Mi/250m, limits: 1Gi/1CPU |
| Batch Job | Burstable | requests: 256Mi/100m, limits: 2Gi/2CPU |
| Background Worker | BestEffort | 설정 없음 (여유 리소스만 사용) |
3. CPU Limit 설정 주의
CPU limit은 throttling을 유발할 수 있으므로 신중히 설정해야 합니다.
# 일반적인 권장사항
spec:
containers:
- name: app
resources:
requests:
cpu: "500m" # 기본 보장량
limits:
cpu: "2000m" # requests의 2-4배 (버스트 허용)
# 또는 limits 제거 (노드 CPU까지 사용 가능)CPU Throttling 모니터링:
# Prometheus 쿼리 예시
rate(container_cpu_cfs_throttled_seconds_total[5m]) > 0.1 # 10% 이상 throttle 시 알림4. Memory는 항상 Limit 설정
메모리는 압축 불가능한(non-compressible) 리소스이므로 반드시 limit을 설정해야 합니다.
# ❌ 나쁜 예: Memory limit 없음
resources:
requests:
memory: "512Mi"
# limits.memory 없음 → 노드 전체 메모리 사용 가능 (위험)
# ✅ 좋은 예: Memory limit 설정
resources:
requests:
memory: "512Mi"
limits:
memory: "1Gi" # requests의 1.5-2배5. LimitRange로 기본값 설정
Namespace에 기본 리소스 정책을 설정하여 실수를 방지합니다.
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: production
spec:
limits:
- type: Container
default: # 기본 limits
memory: "512Mi"
cpu: "500m"
defaultRequest: # 기본 requests
memory: "256Mi"
cpu: "250m"
max: # 최대값
memory: "4Gi"
cpu: "2000m"
min: # 최소값
memory: "128Mi"
cpu: "100m"트러블슈팅 체크리스트
CPU 관련
-
kubectl top pods로 CPU 사용량이 limit에 근접하는지 확인 - Pod 내에서
cpu.stat의nr_throttled비율 확인 (10% 이하 권장) - CPU intensive 워크로드는 Guaranteed QoS 고려
- 불필요한 CPU limit 제거 검토 (throttle이 심한 경우)
Memory 관련
- OOM Kill 이벤트 확인 (
kubectl describe pod) -
memory.current가memory.max에 근접하는지 확인 -
memory.pressurePSI 메트릭 확인 (cgroup v2) - 애플리케이션 힙 크기 조정 (Java, Node.js 등)
- Memory leak 여부 확인 (heap dump 분석)
cgroup 버전 관련
- 노드가 cgroup v1인지 v2인지 확인
- Kubernetes 1.25+ 사용 시 cgroup v2 전환 고려
- systemd 기반 cgroup driver 사용 확인 (
kubelet --cgroup-driver=systemd)