본문으로 건너뛰기

5.2 리소스 관리

Kubernetes는 노드의 CPU·메모리를 여러 Pod가 나눠 쓴다. 이 자원을 어떻게 요청하고 어디까지 쓸 수 있는지 명시하지 않으면, 한 Pod가 자원을 독점하거나 노드가 메모리 부족에 빠졌을 때 PostgreSQL Pod가 먼저 종료되는 일이 벌어진다. CloudNativePG는 두 가지 값으로 이를 통제한다. requests는 스케줄러가 Pod를 배치할 때 확보하는 최소 자원이고, limits는 Pod가 넘을 수 없는 상한이다.

resources:
  requests:
    memory: "32Mi"
    cpu: "50m"
  limits:
    memory: "128Mi"
    cpu: "100m"

CPU 단위 50m은 1 코어의 1000분의 50, 즉 0.05 코어를 뜻한다. 메모리 Mi는 메비바이트다. requests만 있으면 상한이 없어 다른 Pod를 압박할 수 있고, limits만 있으면 스케줄러가 자원을 미리 확보하지 못한다. 둘을 함께 쓰는 것이 기본이다.

QoS class

Kubernetes는 Pod에 지정된 requests·limits 조합에 따라 Quality of Service(QoS) class를 자동으로 매긴다. 노드가 메모리 부족(OOM)에 빠지면 이 등급이 낮은 Pod부터 축출된다.

    flowchart TD
    START[requests·limits 확인] --> Q1{requests=limits<br/>메모리·CPU 모두}
    Q1 -->|예| G[Guaranteed]
    Q1 -->|아니오| Q2{requests 또는<br/>limits 존재}
    Q2 -->|예| B[Burstable]
    Q2 -->|아니오| BE[BestEffort]
  

세 등급의 우선순위는 다음과 같다.

QoS class조건OOM 시 축출 순위
Guaranteed메모리·CPU의 requests와 limits가 서로 같음가장 나중
Burstablerequests나 limits 중 일부만 지정중간
BestEffortrequests·limits 모두 없음가장 먼저

PostgreSQL 워크로드에는 Guaranteed가 권장된다. 메모리와 CPU 각각에서 requests와 limits를 동일한 값으로 맞추면 이 등급이 된다.

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: postgresql-resources
spec:
  instances: 3
  postgresql:
    parameters:
      shared_buffers: "256MB"
  resources:
    requests:
      memory: "1024Mi"
      cpu: 1
    limits:
      memory: "1024Mi"
      cpu: 1
  storage:
    size: 1Gi
QoS가 Guaranteed일 때 CNPG는 postmaster 프로세스의 PG_OOM_ADJUST_VALUE0으로 설정한다. 그러면 OOM 상황에서 postmaster보다 자식 프로세스가 먼저 종료되므로, 인스턴스 자체는 살아남아 깨끗하게 셧다운할 여지를 남긴다.

메모리 산정과 shared_buffers

PostgreSQL의 shared_buffers는 공유 버퍼 캐시 크기다. 관례적으로 Pod 전체 메모리의 25% 안팎으로 잡는다. 위 예시처럼 메모리 limits가 1024Mi라면 shared_buffers는 256MB가 적당하다. 나머지 메모리는 work_mem·maintenance_work_mem, 그리고 운영체제 페이지 캐시가 나눠 쓴다.

정리하면 다음을 지킨다.

  • 메모리·CPU의 requests와 limits를 같은 값으로 맞춰 Guaranteed QoS를 확보한다.
  • shared_buffers는 Pod 메모리의 약 25%로 시작해 워크로드에 맞게 조정한다.
  • requests/limits를 명시해 OOMKilled와 CPU throttling을 예방한다.
  • 전용 노드가 필요하면 nodeSelector로 Pod 배치를 제한한다(5.4에서 다룬다).

hugepages

hugepages는 리눅스가 메모리를 관리하는 페이지의 크기를 기본 4KB보다 크게(예: 2MB) 잡는 기능이다. PostgreSQL처럼 큰 공유 메모리를 다루는 프로세스는 페이지 테이블 오버헤드가 줄어 성능이 좋아진다. Kubernetes에서는 hugepages도 하나의 자원이므로 resources에 명시하고, 노드에 미리 hugepages가 예약되어 있어야 한다.

spec:
  instances: 3
  postgresql:
    parameters:
      shared_buffers: "256MB"
  resources:
    requests:
      memory: "1024Mi"
      cpu: 1
      hugepages-2Mi: "512Mi"
    limits:
      memory: "1024Mi"
      cpu: 1
      hugepages-2Mi: "512Mi"
hugepages는 노드 수준에서 미리 확보되어 있어야 한다. 노드에 예약된 hugepages가 부족하면 Pod가 스케줄되지 못하고 pending 상태에 머문다. PostgreSQL의 huge_pages 파라미터를 try로 두면 hugepages 확보에 실패해도 일반 페이지로 기동을 이어 간다.

오토스케일러 연동

  • Vertical Pod Autoscaler(VPA): CNPG는 권고 전용 모드(updateMode: Off)에서만 VPA와 함께 쓴다. VPA가 제안한 값은 DBA가 직접 Cluster 매니페스트에 반영한다.
  • Horizontal Pod Autoscaler(HPA): PostgreSQL 클러스터에는 권장하지 않는다. 인스턴스를 늘려도 standby replica만 추가될 뿐 primary의 쓰기 부하는 줄지 않기 때문이다.
VPA를 Auto·Recreate·Initial 모드로 CNPG 클러스터에 붙이지 않는다. Operator는 spec.resources를 유일한 기준(source of truth)으로 삼기 때문에, VPA가 Pod 스펙에 직접 써넣은 값을 받아들이지 않는다.