Gom3rye

Kubernetes- Volume API, Requests, Limits 본문

현대 오토에버 클라우드 스쿨

Kubernetes- Volume API, Requests, Limits

Gom3rye 2025. 7. 11. 17:52
728x90
반응형

영구 볼륨 클레임

  • 영구 볼륨을 요청하는 리소스
  • 영구 볼륨 클레임에서 지정된 조건(용량, 레이블)을 기반으로 영구 볼륨에 대한 요청이 들어오면 스케쥴러는 현재 가지고 있는 영구 볼륨에서 적당한 볼륨을 할당한다.

영구 볼륨 클레임 설정

  • 설정 가능한 항목
    • 레이블 셀렉터
    • 용량
    • 접근 모드
    • 스토리지 클래스
  • 위의 설정 값들은 영구 볼륨에서 먼저 설정해야 하는 항목들로 영구 볼륨 클레임 요청에 일치하는 영구 볼륨이 할당된다.
  • 영구 볼륨 클레임에서 요청하는 용량이 볼륨 용량보다 작거나 같아야 한다.
  • nfs(network file syste: 원격지의 파일 시스템을 로컬 시스템처럼 사용하는 것) 플러그인에서는 용량 제한 구조를 지원하지 않는다.

영구 볼륨 클레임을 위한 리소스 매니페스트

# sample-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: sample-pv # 쿠버네티스가 기억하려고 하는 이름
  labels: # 다른 애들이 가져다 쓰려고 하는 이름(pvc가 pv의 label을 찾아서 가져다 쓴다.)
    type: gce-pv
    environment: stg
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce # 하나씩 순차적으로 읽고 쓰겠다.
  persistentVolumeReclaimPolicy: Retain
  storageClassName: manual
  # 플러그인별 설정
  gcePersistentDisk:
    pdName: sample-gce-pv
    fsType: ext4

# 리소스 생성    
kubectl apply -f sample-pv.yaml
kubectl get persistentvolume
##
NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
sample-pv   10Gi       RWO            Retain           Available           manual         <unset>                          32s

----------------------------
# sample-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pvc # 얘는 이제 레이블이 필요 없다.
spec:
  selector:
    matchLabels:
      type: gce-pv # sample-pv의 type과 맞춰주기
    matchExpressions:
    - key: environment
      operator: In
      values:
        - stg
  resources:
    requests:
      storage: 3Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: manual

# 리소스 생성
kubectl apply -f sample-pvc.yaml
# 리소스 확인
kubectl get persistentvolumeclaim # sample-pvc가 sample-pv를 볼륨으로 쓰고 있다는게 나온다.
# Status가 Bound면 볼륨이 할당된 것이고 Pending이면 할당 실패
# Retain 정책을 사용하고 있는 경우 pvc를 삭제하면 pc status가 Bound에서 Released로 변경되고 재할당 가능하다.
kubectl delete persistentvolumeclaim sample-pvc

----------------------------
# 파드에서 사용
# spec.volumes에 persistentVolumeClaim.claimName을 지정하면 된다.
# sample-pvc-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample-pvc-pod
spec:
  containers:
  - name: nginx-container
    image: nginx:1.16
    volumeMounts:
    - mountPath: "/usr/share/nginx/html" # gce-pv와 연결이 된다.
      name: nginx-pvc
  volumes:
  - name: nginx-pvc
    persistentVolumeClaim:
      claimName: sample-pvc
      readOnly: true

동적 프로비저닝

  • pvc를 사용하기 위해서는 pv를 먼저 만들어야 한다. (정적 프로비저닝)
  • pv를 먼저 만들어야 하는데 pvc가 요청하는 용량 이상으로 pv가 할당이 되어 낭비가 발생한다. (정적 프로비저닝의 단점)
  • 동적 프로비저닝은 이런 문제를 해결하기 위해서 pvc를 생성하는 시점에 pv를 생성하는 것으로 용량 낭비를 줄이거나 없앤다.
  • 쿠버네티스는 BestFit 정책을 이용해서 pv 할당
    • OS에서는 WorstFit 정책을 써서 사용 (조각화 줄이려고 남는 공간이 가장 많은 곳에 할당)
  • 동적 프로비저닝을 사용하려면 어떤 pv를 생성할지 정의한 스토리지 클래스를 생성해야 한다.
    • 스토리지 클래스를 이용해서 영구 볼륨을 생성하는데 이때 pvc-로 시작하는 pv가 생성된다.
# sample-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: sample-storageclass
parameters:
  type: pd-ssd
provisioner: kubernetes.io/gce-pd
reclaimPolicy: Delete

# sample-pvc-dynamic.yaml (동적 프로비저닝을 위한 리소스 매니페스트)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pvc-dynamic
spec:
  storageClassName: sample-storageclass
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

StatefulSet에서 영구 볼륨 클레임

  • StatefulSet의 워크로드에서는 영구 데이터 영역을 사용하는 경우가 많기 때문에 spec.volumeClaimTemplate 항목이 존재한다.
  • volumeClaimTemplate 항목을 이용하면 별도로 정의하지 않아도 자동으로 pvc를 생성해준다.
  • 컨테이너 내부의 volumeMounts에 volumeClaimTemplate 이름을 지정하는 것만으로 완료된다.
# StatefulSet을 생성하기 위한 리소스 매니페스트 (sample-statefulset-with-pvc.yaml)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sample-statefulset-with-pvc
spec:
  serviceName: stateful-with-pvc
  replicas: 2
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: sample-pvc
        image: nginx:1.16
        volumeMounts:
        - name: pvc-template-volume
          mountPath: /tmp
  volumeClaimTemplates:
  - metadata:
      name: pvc-template-volume
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi
      storageClassName: sample-storageclass
   
# 리소스 생성
kubectl apply -f sample-statefulset-with-pvc.yaml

누가, 언제, 어떤 볼륨을 쓰는지를 컨트롤하는 건 StatefulSet이 담당

→ PV는 저장만 해주고, StatefulSet이 그걸 "누구에게 어떻게 지속적으로 연결할지"를 보해주는 것이다.

클러스터 API와 메타데이터 API

  • 클러스터 API
    • 보안 관련 설정이나 쿼터 설정 등 클러스터 동작을 제어하기 위한 리소스
    • 사용자가 직접 이용할 수 있는 리소스
      • 노드
      • 네임스페이스
      • 영구 볼륨
      • 리소스 쿼터
      • 서비스 어카운트
      • 클러스터롤
      • 롤바인딩
      • 클러스터롤 바인딩
      • 네트워크 정책
  • 메타데이터 API
    • 클러스터에 컨테이너를 기동하는데 사용하는 리소스
    • 사용자가 이용 가능한 리소스
      • LimitRange
      • HorizontalPodAutoScaler
      • PodDisruptionBudget
      • CustomResourceDefinition

Node

  • 기본적으로 사용자가 생성하거나 삭제하는 리소스는 아니지만 쿠버네티스 리소스에 등록되어 있다.
  • 노드의 상세 정보 표시: kubectl get nodes -o wide
  • 특정 노드의 정보를 yaml 형식으로 출력: kubectl get nodes <노드이름> -o yaml

[Control Plane Node] ├── kube-apiserver <--- kubectl 요청 도착 ├── etcd <--- 클러스터 상태 저장소 ├── scheduler <--- 어디서 실행할지 결정 ├── controller-mgr <--- 복제, 상태관리 └── (kube-proxy) (일부 환경)

[Worker Node 1] ├── kubelet <--- Pod 실행/상태 관리 ├── kube-proxy <--- 네트워크 라우팅 └── container runtime (containerd, etc)

Namespace

  • 가상의 쿠버네티스 클러스터 분리 기능
  • 초기 상태에는 default/kube-system/kube-public/kube-node-lease 가 자동으로 생성된다.
  • 리소스의 쿼터를 설정하는 리소스 쿼터나 인증을 수행하는 RBAC(Role Based Access Control)에서도 설정 범위를 지정할 때 사용 가능하다.
# Cli로 namespace 생성
kubectl create namespace 네임스페이스이름
kubectl create namespace sample-namespace
  • 배포할 땐 대부분 default에 넣는 것이 아니라 namespace를 만들어서 배포한다.

네임스페이스를 지정한 리소스 획득

(프로메테우스 파드가 만들어졌는지 안 만들어졌는지 알아보려고 default에다 아무리 get pods로 물어봐도 안 나옴)

# 네임스페이스를 지정해서 리소스 획득
kubectl get 리소스 -n 네임스페이스이름

# 모든 네임스페이스의 리소스를 획득
kubectl get 리소스 --all-namespaces

# 클러스터를 식별하는 방법
# kube-system이라는 네임스페이스에는 중요한 시스템 구성 요소가 기동하고 있어서 클러스터에서 지우는 일이 없기 때문에 이 네임스페이스의 UID를 사용해서 클러스터를 식별할 수 있다.
kubectl get namespace kube-system -o jsonpath="{.metadata.uid}" # 78b0e6e4-c51d-4700-aad5-79f9a63b2afc

리소스 관리와 오토스케일링

  • 리소스 제한
    • CPU/메모리 리소스 제한
      • CPU는 클럭 수로 지정하지 않고 1vCPU(가상 CPU)를 1,000m(millicores) 단위로 지정한다.
      • 3GHz CPU라고 해도 1Core 정도를 지정하게 되면 3,000m 가 아니고 1,000m 이다.
      • 파드 정의 내의 각 컨테이너 정의 부분에 포함하고 있는 spec.containers[].rescource의 requests.cpu/requests.memory 또는 limits.cpu/limits.memory를 지정하는 형태로 제한된다.
      • requests는 리소스 최솟값을 지정하는 것으로 노드에 requests로 지정한 양의 리소스가 존재하지 않으면 스케쥴링 되지 않는다.
      • limits는 사용할 리소스의 최댓값을 설정한다.
      • 리소스 제한을 설정한 디플로이먼트를 생성한 후 리소스 사용 현황을 알고자 할 때는 kubectl get nodes -o yaml로 확인할 수 있는건 노드의 총 리소스 양(capacity)과 할당 가능한 리소스양(allocatable)뿐 이므로 현재 리소스 사용량을 확인할 때는 kubectl describe node 명령을 사용해야 한다.
      • requests만 설정하고 limits를 설정하지 않으면 호스트 측의 부하가 상승할 때까지 리소스를 계속 소비하려고 하고 파드가 많이 기동하는 노드에서 리소스뺏기가 발생하고 메모리의 경우는 OOM(Out Of Memory)으로 인해 프로세스 정지로 이어진다.
      • limits만 설정하고 requests를 설정하지 않으면 requests에 limits 값이 설정된다.
# requests만 설정한 경우(sample-resource-only-requests.yaml)
apiVersion: v1
kind: Pod
metadata:
  name: sample-resource-only-requests
spec:
  containers:
  - name: nginx-container
    image: nginx:1.16
    resources:
      requests:
        memory: 256Mi
        cpu: 200m
        
#
kubectl apply -f sample-resource-only-requests.yaml
kubectl describe pod sample-resource-only-requests # cpu, memory 잘 할당되었다.
# requests 대신에 limits만 적은 경우
# 리소스 자원 같은 것들은 수정이 안되므로 delete 한 후에 다시 apply 해야 한다.
# limits만 설정하는 것은 위험, 양쪽 다 설정해주는 것이 제일 좋다.

시스템에 할당된 리소스와 Eviction 매니저

  • CPU/Memory/Storage의 일반적인 리소스는 완전히 고갈되면 쿠버네티스 자체가 동작하지 않거나 그 노드 전체에 영향을 줄 수 있다.
  • 각 노드에서는 kube-reserved와 system-reserved라는 두 가지 리소스가 시스템용으로 확보되어 있다.
  • 실제 파드에 할당 가능한 리소스는 쿠버네티스 노드에 존재하는 리소스 총량에서 kube-reserved와 system-reserved를 제외한 양이다.
  • Eviction 매니저는 쿠버네티스 내부에서 동작하는 구성 요소로 시스템 전체가 과부하되지 않도록 관리해주는 도구이다.
  • Eviction 매니저는 Allocatable, system-reserved, kube-reserved 에서 실제로 사용되는 리소스 합계가 Eviction Threshold를 초과하지 않는지 정기적으로 확인하고 초과할 경우 파드를 Evict한다.
    • 이를 이용해서 노드의 리소스를 100% 다 사용하지 않도록 한다.
  • 특히 메모리의 경우는 100% 다 사용하게 되면 OOM(OutOfMemory)가 많이 발생하므로 Eviction Threshold에 의해 제어된다.
  • Eviction Threshold는 soft, hard 2가지가 존재한다.
  • soft 제한이 걸리면 SIGTERM 신호를 보내 파드를 정지하려고 하는데 이때 terminationGracePeriodSeconds는 파드에 설정되어 있는 값 또는 kubelet에 지정되어 있는 --eviction-soft-grace-period 옵션 값 중 짧은 값만큼 대기한 후 파드를 정지시킨다.
  • hard 제한이 걸리면 SIGKILL을 보내 파드가 바로 정지된다.
  • kube-reserved, system-reserved나 Eviction Threshold 설정 값은 사용하는 쿠버네티스 환경이나 노드 인스턴스 유형에 따라 다르다.
  • Eviction 매니저가 Evict하는 파드는 우선순위에 따라 결정한다.
    • 일반적으로는 Requests에 할당된 양보다 초과하여 리소스를 소비하고 있는 것
    • PodPriority가 더 낮은 것
    • Requests에 할당된 양보다 초과하여 소비하고 있는 리소스 양이 더 많은 것
  • GPU 등의 리소스 제한
    • Kubernetes 1.8 이후 Device Plugins 기능이 지원되어 GPU나 그 외 리소스도 CPU나 메모리와 마찬가지로 Requests/Limits 제한을 둘 수 있다.
    • FPGA/VPU(Vision Processing Unit)/QAT(Quick Assist Technology - 암호 관련 워크로드 엑셀러레이터)/TPU(Tensor Processing Unit) 등의 장치도 지원한다.
    • 이런 장치를 사용하는 파드를 만드려면 드라이버를 같이 설치해줘야 한다.
  • 오버커밋과 리소스 부족
    • 파드를 생성할 때 노드 컴퓨터에서 리소스가 부족하면 파드는 스케쥴링 되지 않고 Pending 상태에 머무르게 된다.
    • 이미 구동 중인 파드의 CPU requests가 500m 이고 CPU limits가 1000m인 경우 requests 리소스양 < limits 리소스양으로 설정되었다면 파드에 부하가 증가해서 노드 리소스 사용량이 100%를 초과하더라도 오버커밋해서 실행한다. ⇒ 웬만하면 requests, limits 둘 다 잡고 limits를 더 크게 잡자.
  • 여러 컨테이너 사용 시 리소스 할당
    • 파드에는 여러 컨테이너와 여러 초기화 컨테이너가 포함되어 있다.
    • 스케쥴링 될 때는 파드 단위로 수행되어 필요한 리소스는 이러한 여러 컨테이너를 기반으로 계산이 이뤄진다.
    • 필요한 리소스의 양은 모든 컨테이너의 리소스 합계 또는 모든 초기화 컨테이너의 리소스 최댓값 중 큰 쪽을 사용한다.
      • max(sum(containers[]), max(initContainers[]) 중 큰 값

Cluster AutoScaler와 리소스 부족

  • 쿠버네티스에는 Cluster AutoScaler가 구현된 환경이 많다.
  • Cluster AutoScaler는 쿠버네티스 클러스터 자체의 Auto Scaling으로 수요에 따라 쿠버네티스 노드를 자동으로 추가하는 기능이다.
  • CSP에서 제공하는 관리형 쿠버네티스(GKE나 EKS 등)는 대부분 이 환경을 제공한다.
  • 노드 풀 단위로 기능을 활성화하거나 비활성화 할 수 있다.
  • Cluster AutoScaler는 클러스터 전체나 각 노드의 부하 평균이 높아졌을 때 확장하는 것처럼 보이지만 Pending 상태의 파드가 생기는 타이밍에 Cluster AutoScaler가 동작한다.
    • 부하 평균이 높을 때 동작하는게 아니라 파드가 잘 안 만들어졌을 때 동작
  • Requests와 Limits를 적절하게 설정하지 않은 상태에서는 실제 노드의 부하 평균이 낮은 상황에서도 스케일 아웃이 되거나 부하 평균이 높은 상황인데도 스케일 아웃이 되지 않는 상황이 발생할 수 있다. → 서비스가 죽어버릴 확률이 높다.
    • ⇒ 이런 부분 때문에 클라우드가 어려운 것!!!
  • Kubernetes는 스케줄링을 requests 기준으로만 판단한다.
  • Requests를 초과하여 할당한 경우에는 최소 리소스 요청만으로 리소스가 꽉 차서 신규 노드를 추가해야만 한다.
  • Requests와 Limits의 차이가 크면 각 컨테이너는 Limits로 할당된 리소스를 최대로 사용하는데 이렇게 되면 실제 리소스 사용량이 높아졌더라도 Requests 합계로 보면 아직 스케쥴링이 가능하기 때문에 Cluster가 스케일 아웃 하지 않는 상황이 발생한다.
    • 계속 만들려고만 하고 그러다가 무한 루프 걸려버린다.
  • Requests와 Limits는 차이를 크게 두면 안되고 Requests를 너무 크게 설정하는 것도 좋지 않다.

리소스 설정 효과

requests 스케줄러가 얼마나 리소스를 예약해야 하는지 판단할 기준
limits 실제 컨테이너가 사용할 수 있는 최대치 (오버하면 OOM 등 발생)

LimitRange를 사용한 리소스 제한

  • 파드에 대해 CPU나 메모리 리소스의 최솟값과 최댓값, 기본값 등을 설정할 수 있다.
  • LimitRange가 네임스페이스에 제한을 주려면 네임스페이스마다 설정이 필요하다.
  • 신규로 파드를 생성할 때 사용되기 때문에 기본 파드에는 영향을 주지 않는다.
  • 설정 가능한 제한 종목

설정 항목 의미

default 기본 Limits
defaultRequest 기본 Requests
max 최대 리소스
min 최소 리소스
maxLimitRequestRatio Limits/Requests의 비율
  • 설정 가능한 리소스는 파드/컨테이너/영구 볼륨 클레임이다.
    • 파드의 경우 max/min/maxLimitRequestRatio가 가능하고 pvc는 max/mi 그리고 컨테이너는 전부 설정 가능하다.
  • 기본으로 생성되는 LimitRange
    • 파드나 컨테이너에 대해 리소스 제한을 전혀 하지 않을 경우 실제 노드 부하와 상관없이 계속 파드를 스케쥴링한다.
    • 스케쥴러는 Requests에서 지정한 리소스를 확보할 수 있을 지에 따라 스케쥴링 여부를 판단한다.
    • 리소스 소비가 과도하게 많아지면 메모리는 OOM(OutOfMemory)Killer에 의해 프로세스가 중단되지만 CPU는 전체 동작이 느려져 최악의 경우 운영체제가 동작하지 않는다.
# 컨테이너에 대한 limit range
# sample-limitrange-container.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: sample-limitrange-container
  namespace: default
spec:
  limits:
  - type: Container
    default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 256Mi
      cpu: 250m
    max:
      memory: 1024Mi
      cpu: 1000m
    min:
      memory: 128Mi
      cpu: 125m
    maxLimitRequestRatio:
      memory: 2
      cpu: 2
      
# 리소스 제한 설정이 없는 리소스를 생성
kubectl apply -f sample-pod.yaml
# 리소스 확인
kubectl get pod sample-pod -o json

"현재 사용량(CPU/메모리 등)을 파드 단위로 정확히 알 수 있으면", requests/limits를 현실에 맞게 조정할 수 있어요.

문제는 쿠버네티스 기본 기능만으로는 현재 리소스 사용량을 잘 안 보여준다는 점이에요. 하지만 몇 가지 방법과 도구를 통해 충분히 파악할 수 있습니다.


✅ 현재 리소스 사용량을 보는 방법

1. 🔧 kubectl top 명령 (Metrics Server 필요)

kubectl top pod
kubectl top node

예:

kubectl top pod -n my-namespace

출력 예:

NAME                            CPU(cores)   MEMORY(bytes)
my-app-1234567890-abcde         100m         180Mi

👉 Metrics Server가 설치되어 있어야 작동합니다.

설치 안 됐다면 GKE나 EKS는 기본 탑재돼 있거나 다음과 같이 설치 가능:

kubectl apply -f <https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml>

2. 📊 HPA로 평균 사용량 모니터링

HPA 설정 시 CPU/메모리의 사용률 평균을 자동으로 수집해서 기준 넘으면 스케일 아웃합니다.

예:

metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60

→ HPA 상태를 보면 현재 사용량도 확인 가능:

kubectl get hpa

3. 📦 Prometheus + Grafana (실무용)

  • Prometheus: 리소스 사용량 수집
  • Grafana: 대시보드로 시각화
  • → Pod별, 컨테이너별, Node별 사용량을 실시간 그래프로 확인 가능

이게 실무에서는 가장 강력한 도구입니다.

예: 특정 Deployment가 10개 파드로 운영되는데 각 파드 CPU 평균이 80m 정도면 requests.cpu: 100m 정도로 조정 가능하겠죠?


💡 그래서 결국...

"현재 사용량을 보여주는 것"은 단순한 문제가 아니라, 클러스터 전체 효율성을 결정하는 핵심입니다.

왜냐하면:

  • 사용량을 알면 → 적절한 requests/limits 설정 가능
  • 설정이 정확하면 → 스케줄러도 정확하게 작동
  • 정확한 스케줄링이 되면 → Autoscaler도 제대로 작동
  • → 결과적으로 안정적이면서도 비용 효율적인 클러스터 운영 가능
728x90
반응형

'현대 오토에버 클라우드 스쿨' 카테고리의 다른 글

Git  (0) 2025.07.15
CI/CD  (1) 2025.07.14
Kubernetes- Volume API  (1) 2025.07.10
Kubernetes- Config & Storage API  (4) 2025.07.09
Kubernetes- Session Affinity, Headless, ExternalName, None-Selector, Ingress  (2) 2025.07.08