Kubernetes와 cgroup - 리소스 격리의 핵심 메커니즘

2026년 01월 20일

devops

# Kubernetes# cgroup# Linux# Resource Management# Container

cgroup이란?

cgroup(Control Group)은 리눅스 커널에서 제공하는 프로세스 그룹의 리소스 사용을 제한하고 격리하는 기능입니다. Docker, Kubernetes 같은 컨테이너 기술의 핵심 기반이며, CPU, 메모리, 디스크 I/O 등의 리소스를 프로세스 단위가 아닌 그룹 단위로 관리할 수 있게 합니다.

cgroup이 해결하는 문제

cgroup 사용

Process A

30% 제한

CPU/Memory

Process B

40% 제한

Process C

30% 제한

cgroup 없이무제한 사용대기대기

Process A

리소스 독점

CPU/Memory

Process B

리소스 부족

Process C

리소스 부족

cgroup이 없다면:

  • 한 프로세스가 시스템 리소스를 독점할 수 있음
  • 멀티 테넌트 환경에서 공정한 리소스 배분 불가
  • 컨테이너 간 격리가 불완전함

cgroup을 사용하면:

  • 프로세스 그룹별 리소스 할당량 설정
  • 리소스 사용량 추적 및 통계
  • 우선순위 기반 리소스 스케줄링

cgroup 계층 구조

cgroup은 트리 구조로 구성되며, 각 노드는 리소스 제한을 상속받습니다.

/sys/fs/cgroup

(Root)

system.slice

user.slice

kubepods.slice

besteffort

burstable

guaranteed

pod-abc123

pod-def456

container-1

container-2

container-3

계층별 역할:

계층 경로 예시 설명
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 v2

통합된 계층 구조

CPU + Memory + IO

Process

단일 계층에서

모든 리소스 관리

cgroup v1

CPU Controller

Process

Memory Controller

Blkio Controller

각 컨트롤러가

독립적인 계층

주요 차이점

항목 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 cgroup

Kubernetes에서의 cgroup 활용

Kubernetes는 cgroup을 사용하여 Pod와 컨테이너의 리소스를 관리합니다.

Pod QoS Class와 cgroup 매핑

Kubernetes는 Pod의 리소스 요청/제한에 따라 자동으로 QoS Class를 할당하고, 이를 cgroup 계층에 반영합니다.

requests == limits(모든 컨테이너)requests &lt; limits또는 일부만 설정requests/limits 없음

Pod 정의

리소스 설정

Guaranteed

최고 우선순위

Burstable

중간 우선순위

BestEffort

최하 우선순위

/kubepods.slice/guaranteed

/kubepods.slice/burstable

/kubepods.slice/besteffort

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    # 536870912

2. 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를 제어합니다.

CPU 제어 메커니즘

CPU Shares

(상대적 가중치)

resources.requests.cpu

CPU Quota

(절대적 제한)

resources.limits.cpu

cpu.weight (v2)

cpu.shares (v1)

cpu.max (v2)

cpu.cfs_quota_us (v1)

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 ≈ 20

2. 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됩니다.

cgroup리눅스 커널애플리케이션cgroup리눅스 커널애플리케이션다음 period까지 대기alt[CPU quota 남음][CPU quota 초과]CPU 사용 시작현재 사용량 확인허용실행 계속제한 (throttle)실행 일시중지period 갱신 후 재개

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 제한 구조

동작

사용량 < memory.high

정상 동작

memory.high < 사용량 < memory.max

Page Reclaim 시작

(성능 저하)

사용량 >= memory.max

OOM Kill

(Pod 종료)

메모리 계층

memory.max

(Hard Limit)

resources.limits.memory

memory.high

(Soft Limit)

resources.requests.memory

memory.current

(실제 사용량)

실시간 모니터링

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 Kill 순서
1. BestEffort Pods

(리소스 설정 없음)
2. Burstable Pods

(limit > usage > request)
3. Burstable Pods

(usage ≈ request)
4. Guaranteed Pods

(마지막 희생)

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 사용률, 메모리 사용량)은 얼마나 사용하는지는 알려주지만, 리소스 부족으로 인한 성능 저하는 감지할 수 없습니다.

PSI 메트릭

CPU some: 15%

Memory full: 2%

✅ 15% 시간 동안 CPU 대기

✅ 2% 시간 동안 완전 정지

기존 메트릭

CPU: 80% 사용

Memory: 3GB/4GB

❓ 성능은 괜찮은가?

성능 문제?

명확한 병목 지점 파악

Some vs Full 메트릭

PSI는 두 가지 수준의 압박을 측정합니다.

Full Pressure

모든 비유휴 태스크가

동시에 대기

실제 CPU 낭비

(stall)

Some Pressure

최소 1개 태스크가

리소스 대기 중

다른 태스크는

계속 실행 가능

경미한 압박

심각한 압박

차이점:

메트릭 의미 예시 심각도
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 누적 시간 (마이크로초) 전체 압박 시간

해석 예시:

some avg10=25.5 avg60=12.3 avg300=5.1

avg10이 높음

→ 최근 급격한 부하

avg60도 높음

→ 단기 지속

avg300은 낮음

→ 일시적 현상

즉시 확인 필요

스케일링 고려

일시적 - 모니터링

리소스별 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=8520000

2. 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: /sys

Prometheus 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
done

PSI vs 기존 메트릭 비교

PSI 메트릭

Memory some: 25%

Memory full: 3%

✅ 메모리 부족으로

25% 시간 동안 대기

→ 메모리 증설 필요

기존 메트릭

CPU: 60%

Memory: 2GB/4GB

❓ 왜 느린가?

시나리오: Pod가 느려짐

사용자 불만

비교표:

상황 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.current

Best 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 선택 가이드

미션 크리티컬DB, 결제버스트 트래픽Web API배치 작업우선순위 낮음

애플리케이션 특성

Guaranteed

requests == limits

Burstable

requests < limits

BestEffort

설정 없음

안정적이지만

리소스 비효율적

유연하고

비용 효율적

노드 리소스 활용

하지만 불안정

추천:

워크로드 유형 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.statnr_throttled 비율 확인 (10% 이하 권장)
  • CPU intensive 워크로드는 Guaranteed QoS 고려
  • 불필요한 CPU limit 제거 검토 (throttle이 심한 경우)

Memory 관련

  • OOM Kill 이벤트 확인 (kubectl describe pod)
  • memory.currentmemory.max에 근접하는지 확인
  • memory.pressure PSI 메트릭 확인 (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)

참고 자료

© 2026, 미나리와 함께 만들었음