리소스 관리
리소스 관리 (Resource management)
전형적인 쿠버네티스 클러스터에서 Pod는 무제한 리소스로 실행돼요. 기본적으로 필요한 만큼의 CPU와 RAM을 사용하도록 허용될 수 있죠.
CloudNativePG는 관리자가 매니페스트의 resources 섹션을 통해 클러스터의 Pod들이 사용하는 리소스를 제어하고 관리할 수 있게 해줘요. 여기에는 두 가지 조절 장치(knob)가 있어요:
requests: 초기 요구량limits: 리소스 필요량이 동적으로 늘어날 경우의 최대 사용량
예를 들어 초기 RAM 32MiB(최대 128MiB로 확장 가능)와 CPU 50m(최대 100m로 확장 가능)를 요청하려면 다음과 같이 해요:
resources:
requests:
memory: "32Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
메모리 requests와 limits는 컨테이너와 연관되지만, Pod에 메모리 request와 limit이 있다고 생각하는 것이 유용해요. Pod의 메모리 request는 Pod 안의 모든 컨테이너의 메모리 request의 합이에요.
Pod 스케줄링은 limits가 아니라 requests를 기반으로 해요. Pod는 노드가 Pod의 메모리 request를 충족할 충분한 가용 메모리를 가질 때만 그 노드에서 실행되도록 스케줄링돼요.
각 리소스에 대해 컨테이너를 우선순위가 높은 순서부터 3개의 Quality of Service(QoS) 클래스로 나눠요:
- Guaranteed
- Burstable
- Best-Effort
자세한 내용은 쿠버네티스 문서의 "Configure Quality of Service for Pods" 섹션을 참조하세요.
PostgreSQL 워크로드에는 "Guaranteed" QoS를 설정하는 것을 권장해요.
:::info
Quality of service가 "Guaranteed"로 설정되면, CloudNativePG는 postmaster 프로세스에 대해 PG_OOM_ADJUST_VALUE를 0으로 설정해요. 이렇게 하면 postmaster는 낮은 Out-Of-Memory(OOM) 점수인 -997을 유지하고, 자식 프로세스는 0의 OOM 점수 조정으로 실행돼요. 결과적으로 OOM 킬러가 발동하면 postmaster보다 먼저 자식 프로세스를 종료해요. 이 동작은 PostgreSQL 인스턴스를 가능한 한 오래 살려두고, 축출(eviction) 발생 시 깨끗한 종료 절차를 가능하게 해요.
:::
쿠버네티스에서 리소스 관련 문제를 피하려면 클러스터를 만들 때 "out of resource(리소스 부족)" 처리에 대한 모범 사례를 따를 수 있어요:
- 매니페스트 파일의 resources 섹션에 메모리와 CPU의 필요한 값을 지정해요. 이렇게 하면 실행 중인 인스턴스에서
OOM Killed와CPU throttle또는 다른 리소스 관련 문제를 피할 수 있어요. - 클러스터의 Pod가 "Guaranteed" QoS 클래스에 배정되도록 하려면 메모리와 CPU 둘 다에 대해 limits와 requests를 같은 값으로 설정해야 해요.
- 필요한 PostgreSQL 메모리 파라미터를 Pod 리소스와 일관되게 지정해요 (VM이나 물리 머신 시나리오에서 하듯이 — 아래 참조).
- nodeSelector를 사용해 데이터베이스 서버 Pod를 전용 노드에 배치해요. API 레퍼런스 페이지의 "affinityconfiguration" 리소스의 "nodeSelector"와 "tolerations" 필드를 참조하세요.
다음 예시 매니페스트를 참조할 수 있어요:
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
위 예시에서 shared_buffers 파라미터를 256MB 값으로 지정했어요 — 즉 PostgreSQL 서버가 데이터 캐싱에 얼마나 많은 메모리를 전용하는지 정한 거예요(정의되지 않으면 이 파라미터의 기본값은 128MB예요).
shared_buffers의 합리적인 시작 값은 시스템 메모리의 25%예요. 예를 들어 shared_buffers가 256 MB라면 컨테이너 메모리 크기의 권장 값은 1 GB인데, 이는 Pod 안의 모든 컨테이너가 총 1 GB 메모리를 가지며 쿠버네티스가 항상 보존해서 컨테이너가 예상대로 동작하게 한다는 뜻이에요. 자세한 내용은 PostgreSQL 문서의 "Resource Consumption" 섹션을 참조하세요.
:::note[Managing Compute Resources for Containers] 리소스 관리에 대한 더 자세한 내용은 쿠버네티스 문서의 "Managing Compute Resources for Containers" 페이지를 참조하세요. :::
출처: 문서
본문
Vertical Pod Autoscaler (VPA) 통합
Cluster CRD는 인스턴스 Pod에 대한 레이블 셀렉터와 함께 scale 하위리소스를 노출해요. 이렇게 하면 Cluster가 Vertical Pod Autoscaler(VPA)의 유효한 targetRef가 되어, VPA가 인스턴스 Pod를 관찰하고 CPU와 메모리 추천값을 생성할 수 있어요.
VPA를 추천 전용(recommendation-only) 모드(updatePolicy.updateMode: Off)로 실행하는 것을 권장해요. 이 모드에서 VPA는 추천값만 보고하고, 오퍼레이터가 일반 매니페스트 업데이트를 통해 이를 Cluster의 spec.resources에 적용할 수 있어요. CloudNativePG는 일반적인 스위치오버 인지 절차를 사용해 인스턴스의 롤링 업데이트를 수행해요. VPA는 컨테이너별 추천값을 만들어요: spec.resources가 구성하는 대상이 바로 postgres 컨테이너이므로 그것을 사용하세요.
Cluster를 대상으로 하는 예시:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: cluster-example-vpa
spec:
targetRef:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
name: cluster-example
updatePolicy:
updateMode: "Off"
:::warning
CloudNativePG가 관리하는 Cluster에 updateMode: Auto, Recreate, Initial을 사용하지 마세요. 오퍼레이터는 Pod 사양을 소유하며 Cluster의 spec.resources를 진실 원천(source of truth)으로 취급해요: 실행 중인 Pod에 VPA가 쓴 리소스를 채택하지 않으므로, 라이브 Pod와 선언된 spec.resources는 조용히 어긋나요. 선언된 리소스에 맞춰 조정한 튜닝(예: 메모리 request에 대해 오퍼레이터가 검증한 shared_buffers 값)은 더 이상 Pod의 실제 limits와 일치하지 않아요. VPA는 또한 쿠버네티스 축출(eviction) API를 통해 Pod를 축출해서 오퍼레이터의 스위치오버 인지 시퀀싱을 우회해요. 이때 기본 프라이머리 PodDisruptionBudget이 프라이머리 축출을 막으므로 VPA는 완료되지 않고 멈춰요. VPA 추천값을 대신 Cluster 매니페스트에 수동으로 적용하세요.
:::
Horizontal Pod Autoscaler (HPA) 통합
scale 하위리소스는 spec.instances도 노출하므로, Horizontal Pod Autoscaler(HPA)는 기술적으로 Cluster의 인스턴스 수를 바꿀 수 있어요. 하지만 실제로는 여러 이유로 PostgreSQL 클러스터에서는 권장되지 않아요.
Cluster를 스케일링하면 스탠바이 레플리카를 추가하거나 제거할 뿐이며, 프라이머리의 쓰기 부하를 절대 완화하지 못해요. scale 하위리소스를 통해 노출된 셀렉터는 프라이머리와 모든 레플리카를 매칭하는데, 이들은 반대되는 워크로드 프로필(쓰기 중심의 프라이머리, 읽기 전용 레플리카)을 가지므로, HPA가 CPU나 메모리 메트릭에서 계산하는 Pod당 평균은 그 어느 것에도 의미 있는 신호가 아니에요. 레플리카를 추가하면 뜨거운 프라이머리를 해결하지 못한 채 그 평균을 더 희석할 뿐이에요.
HPA는 또한 spec.instances에 대한 CloudNativePG 자체의 제약을 인지하지 못해요. 동기식 레플리카를 구성했다면 maxSyncReplicas + 1 미만으로의 스케일다운은 validating webhook에 의해 거부되고, HPA는 충족할 수 없는 값을 계속 재시도해요. 그래도 HPA에서 spec.instances를 구동한다면, 실제로 읽기 레플리카 부하를 반영하는 커스텀 메트릭을 기반으로 하고 minReplicas를 동기식 레플리카 하한보다 높게 설정하세요. 먼저 복제와 쿼럼에 미치는 영향을 신중히 검토하세요.