고급 설정
- 11.1 자원 제한: Limit과 Request
- 11.2 QoS 클래스
- 11.3 ResourceQuota와 LimitRange
- 11.4 스케줄링: NodeSelector, Affinity, Taints/Tolerations
- 11.5 롤링 업데이트 전략
- 11.6 HPA (Horizontal Pod Autoscaler)
프로덕션에서 안정적으로 운영하려면 자원 제한, QoS, 스케줄링 제어, 롤링 업데이트 세부 설정, 오토스케일링까지 알아야 해. 쿠버네티스에 앱을 올리는 건 기본이고, 진짜 실력은 여기서 갈려.
파드의 컨테이너에 CPU와 메모리 사용량을 제한할 수 있어. 두 가지 값이 있지:
- requests: "최소한 이만큼은 보장해줘." 스케줄러가 파드를 배치할 때 이 값을 기준으로 노드를 선택해.
- limits: "최대 이만큼만 써." 이 값을 초과하면 CPU는 쓰로틀링(속도 제한)되고, 메모리는 OOMKilled(강제 종료)돼.
spec:
containers:
- name: app
image: my-app
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
CPU는 밀리코어(m) 단위로 지정해. 1000m = 1 CPU 코어니까 250m은 코어 하나의 25%야. 메모리는 Mi(Mebibyte) 또는 Gi(Gibibyte) 단위를 써. 256Mi는 약 268MB지.
requests를 설정하지 않으면 스케줄러가 자원이 부족한 노드에 파드를 배치할 수 있고, limits를 설정하지 않으면 하나의 파드가 노드의 자원을 독점할 수 있어. 프로덕션에서는 반드시 둘 다 설정해야 해.
쿠버네티스는 파드의 requests/limits 설정에 따라 QoS(Quality of Service) 클래스를 자동으로 부여해. 노드의 자원이 부족해지면 QoS 클래스에 따라 어떤 파드를 먼저 죽일지(eviction) 결정하지.
세 가지 클래스가 있어. Guaranteed는 모든 컨테이너에 requests와 limits가 설정되어 있고, 둘이 같은 경우야. 가장 마지막에 퇴거(eviction)되니까 가장 안전한 등급이지. Burstable은 requests와 limits가 설정되어 있지만 둘이 다른 경우, 또는 일부 컨테이너에만 설정된 경우야. Guaranteed와 BestEffort 사이지. BestEffort는 requests와 limits가 아예 설정되지 않은 경우인데, 가장 먼저 퇴거돼. 자원이 부족하면 이 파드가 첫 번째 희생양이야.
중요한 서비스는 Guaranteed 클래스가 되도록 requests와 limits를 같은 값으로 설정하는 게 좋아. 배치 작업처럼 중요도가 낮은 건 BestEffort로 둬도 돼.
ResourceQuota는 네임스페이스 단위로 자원 사용량을 제한해:
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: dev
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"
이 네임스페이스에서는 전체 파드의 CPU requests 합이 4코어를 넘을 수 없고, 파드 수도 20개로 제한돼. 팀별 또는 환경별 자원을 공평하게 분배할 때 유용하지. ResourceQuota가 설정된 네임스페이스에서는 모든 파드에 requests/limits를 명시해야 해. 안 하면 파드 생성이 거부돼.
LimitRange는 개별 파드/컨테이너 단위로 기본값과 최대/최소값을 설정해:
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: dev
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: "256Mi"
defaultRequest:
cpu: "250m"
memory: "128Mi"
max:
cpu: "2"
memory: "1Gi"
min:
cpu: "100m"
memory: "64Mi"
리소스를 명시하지 않은 컨테이너에 자동으로 default 값이 적용돼. max/min을 벗어나는 값을 설정하면 파드 생성이 거부되지.
기본적으로 쿠버네티스 스케줄러가 자동으로 파드를 적절한 노드에 배치하지만, 특정 노드에 파드를 배치하고 싶거나 반대로 특정 노드를 피하고 싶을 때 스케줄링을 제어할 수 있어.
NodeSelector가 가장 간단한 방법이야. 노드에 라벨을 붙이고, 파드에서 해당 라벨을 선택하는 거지:
kubectl label nodes node1 disk=ssd
spec:
nodeSelector:
disk: ssd
이 파드는 disk=ssd 라벨이 있는 노드에만 배치돼. 단순하지만 유연하지 않아.
Node Affinity는 NodeSelector의 확장판이야. 더 복잡한 조건을 표현할 수 있지:
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disk
operator: In
values:
- ssd
- nvme
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: zone
operator: In
values:
- ap-northeast-2a
required...: 반드시 충족해야 하는 조건 (hard)preferred...: 가능하면 충족하는 조건 (soft). weight으로 우선순위를 설정해.
Pod Affinity / Anti-Affinity는 파드가 다른 특정 파드와 같은 노드(또는 다른 노드)에 배치되도록 제어하는 거야:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: web
topologyKey: kubernetes.io/hostname
이 설정은 app=web 라벨을 가진 파드가 같은 호스트에 두 개 이상 배치되지 않도록 해. 고가용성을 위해 레플리카를 서로 다른 노드에 분산시킬 때 유용하지.
Affinity가 "이 노드에 가고 싶다"라면, Taint는 "이 노드에 오지 마"야. 노드에 Taint를 설정하면 해당 Taint를 Toleration하는 파드만 배치될 수 있어:
kubectl taint nodes node1 gpu=true:NoSchedule
이 노드에 파드를 배치하려면:
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"
Taint effect 종류는 세 가지야:
- NoSchedule: 새 파드를 배치하지 않아 (기존 파드는 유지)
- PreferNoSchedule: 가능하면 배치하지 않아 (soft)
- NoExecute: 새 파드를 배치하지 않고, 기존 파드도 퇴거시켜
GPU 노드, 전용 노드 같은 특수한 노드를 관리할 때 Taint/Toleration이 유용해.
디플로이먼트의 업데이트 전략을 세밀하게 제어할 수 있어:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
- maxSurge: 업데이트 중 원하는 레플리카 수보다 몇 개를 더 만들 수 있는지.
1이면 replicas + 1개까지 허용. - maxUnavailable: 업데이트 중 몇 개의 파드가 사용 불가능할 수 있는지.
0이면 모든 파드가 항상 동작 중이어야 해 (무중단 보장).
maxSurge: 1, maxUnavailable: 0은 가장 안전한 설정이야. 새 파드가 먼저 뜨고, 정상 확인 후 기존 파드를 내리는 방식이지. 대신 업데이트 속도가 느려.
다른 전략으로 Recreate가 있어. 기존 파드를 모두 삭제한 뒤 새 파드를 만드는 방식인데, 다운타임이 발생하지만 구버전과 신버전이 동시에 실행되면 안 되는 경우에 사용해.
HPA는 CPU 사용률이나 메모리 사용량 등의 메트릭에 따라 파드 수를 자동으로 늘리거나 줄이는 오브젝트야:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-deploy
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
이 HPA는 my-deploy의 평균 CPU 사용률이 70%를 넘으면 파드를 늘리고, 낮아지면 줄여. 최소 2개, 최대 10개 사이에서 조절하지.
HPA가 동작하려면 metrics-server가 설치되어 있어야 해. metrics-server가 파드의 CPU/메모리 사용량을 수집해서 HPA에 제공하거든.
kubectl autoscale deployment my-deploy --cpu-percent=70 --min=2 --max=10
kubectl get hpa
HPA의 스케일링 알고리즘은 원하는 레플리카 수 = ceil(현재 레플리카 수 * (현재 메트릭 / 목표 메트릭))이야. 현재 CPU가 140%이고 목표가 70%면, 현재의 2배로 스케일 아웃하지.
커스텀 메트릭(요청 수, 큐 길이 등)을 기반으로 스케일링하려면 Prometheus Adapter 같은 커스텀 메트릭 서버를 설치하면 돼.
정리
11장 읽고 기억할 거 네 가지:
- requests는 보장 자원, limits는 최대 자원이다. 프로덕션에서는 반드시 둘 다 설정하고, QoS 클래스를 고려해야 한다.
- ResourceQuota는 네임스페이스 전체, LimitRange는 개별 컨테이너 수준의 자원 제한이다.
- NodeSelector, Affinity, Taints/Tolerations로 스케줄링을 제어한다. 단순한 건 NodeSelector, 복잡한 조건은 Affinity, 노드를 보호하려면 Taint.
- HPA는 메트릭 기반으로 파드 수를 자동 조절한다. metrics-server가 필수이고, 커스텀 메트릭도 지원 가능.