Skip to main content

Kubernetes 기초

컨테이너 하나를 실행하는 것은 쉽습니다. 하지만 수십, 수백 개의 컨테이너를 여러 서버에 걸쳐 관리하고, 장애 시 자동 복구하며, 트래픽에 따라 스케일링하려면 오케스트레이션이 필요합니다. Kubernetes(K8s)는 컨테이너 오케스트레이션의 사실상 표준(de facto standard)으로, Google이 내부 시스템(Borg)의 경험을 바탕으로 설계하고 CNCF에 기증한 오픈소스 프로젝트입니다.

학습 목표

  • Kubernetes 아키텍처(Control Plane, Worker Node)의 구성 요소와 역할을 이해한다
  • 핵심 오브젝트(Pod, Deployment, Service 등)를 목적에 맞게 선택할 수 있다
  • kubectl 명령어로 클러스터를 조회하고 애플리케이션을 배포할 수 있다
  • GPU 스케줄링과 리소스 관리 개념을 설명할 수 있다

왜 중요한가

LLMOps에서 Kubernetes는 모델 서빙의 핵심 인프라입니다. vLLM, TGI(Text Generation Inference), Triton Inference Server 같은 모델 서빙 프레임워크는 Kubernetes 위에서 운영되며, GPU 자원을 효율적으로 분배하고, 트래픽 급증 시 자동으로 Pod를 확장합니다. 학습 파이프라인에서도 Kubernetes는 중요합니다. Job 오브젝트로 학습 작업을 제출하고, 완료 시 자동 정리하며, GPU Node에 학습 Pod를 스케줄링합니다. KubeFlow, Argo Workflows 같은 ML 플랫폼은 모두 Kubernetes 위에서 동작합니다.

클러스터 아키텍처

┌─────────────────── Control Plane ────────────────────┐
│                                                       │
│  ┌─────────────┐  ┌────────┐  ┌─────────────────┐   │
│  │ API Server  │  │  etcd  │  │   Scheduler     │   │
│  │ (kube-api)  │  │ (상태DB)│  │ (Pod 배치 결정) │   │
│  └─────────────┘  └────────┘  └─────────────────┘   │
│  ┌──────────────────────────┐                        │
│  │   Controller Manager     │                        │
│  │ (Deployment, ReplicaSet) │                        │
│  └──────────────────────────┘                        │
└───────────────────────────────────────────────────────┘

          ┌─────────────┼──────────────┐
          ▼             ▼              ▼
┌─── Worker Node 1 ───┐┌── Worker Node 2 ──┐┌── Worker Node 3 ──┐
│ ┌───────┐ ┌───────┐ ││ ┌───────┐         ││ ┌───────┐         │
│ │ Pod A │ │ Pod B │ ││ │ Pod C │         ││ │ Pod D │         │
│ └───────┘ └───────┘ ││ └───────┘         ││ └───────┘         │
│ kubelet              ││ kubelet           ││ kubelet           │
│ kube-proxy           ││ kube-proxy        ││ kube-proxy        │
│ container runtime    ││ container runtime ││ container runtime │
└──────────────────────┘└───────────────────┘└───────────────────┘

Control Plane 구성 요소

구성 요소역할
API Server클러스터의 유일한 진입점. 모든 요청(kubectl, 내부 통신)이 여기를 통과
etcd클러스터 상태를 저장하는 분산 키-값 저장소. 유일한 상태 소스(source of truth)
Scheduler새 Pod를 어떤 Node에 배치할지 결정. 리소스, affinity, taint 등 고려
Controller ManagerDesired State와 Current State를 비교하고 일치시키는 제어 루프 실행

Worker Node 구성 요소

구성 요소역할
kubeletNode에서 Pod 라이프사이클 관리. API Server의 지시를 받아 컨테이너 실행/중지
kube-proxyService 네트워크 규칙 관리. 클러스터 내부 로드밸런싱 처리
Container Runtime실제 컨테이너 실행 (containerd, CRI-O 등)
Kubernetes의 핵심 설계 원칙은 선언적(Declarative) 관리입니다. “Pod 3개를 실행해라”가 아니라 “Pod 3개가 실행되고 있어야 한다”라고 선언하면, Controller가 현재 상태를 원하는 상태로 자동 수렴시킵니다.

핵심 오브젝트

오브젝트역할사용 사례
Pod하나 이상의 컨테이너 그룹, 최소 배포 단위단일 모델 서빙 프로세스
DeploymentPod의 선언적 업데이트, ReplicaSet 관리모델 서빙 API 배포/롤링 업데이트
Service (ClusterIP)클러스터 내부 고정 IP + DNS내부 서비스 간 통신
Service (NodePort)노드 포트로 외부 노출 (30000-32767)개발/테스트 환경 외부 접근
Service (LoadBalancer)클라우드 LB 프로비저닝프로덕션 외부 트래픽 수신
ConfigMap설정 데이터 (key-value, 파일)하이퍼파라미터, 모델 설정
Secret민감 데이터 (base64 인코딩)API 키, DB 비밀번호, 인증서
Namespace리소스 논리적 격리팀/환경별 분리 (dev, staging, prod)
IngressHTTP/HTTPS 라우팅 규칙도메인 기반 API 라우팅
Job한 번 실행 후 완료되는 작업모델 학습, 데이터 전처리 배치
CronJob주기적 Job 실행일일 재학습, 정기 데이터 수집
DaemonSet모든(또는 특정) Node에 Pod 1개씩로그 수집기, 모니터링 에이전트
StatefulSet상태 유지 + 고정 네트워크 ID분산 학습 워커, etcd, DB
HPACPU/메모리/커스텀 메트릭 기반 자동 스케일링추론 요청 증가 시 Pod 자동 확장
PVC영속 스토리지 요청모델 파일, 학습 데이터, 체크포인트

배포 흐름

1

컨테이너 이미지 빌드

docker build -t registry.example.com/model-server:v2.1.0 .
Dockerfile에서 모델 서빙 애플리케이션 이미지를 빌드합니다.
2

레지스트리에 이미지 푸시

docker push registry.example.com/model-server:v2.1.0
클러스터의 각 Node가 이미지를 pull할 수 있도록 레지스트리에 업로드합니다.
3

매니페스트 작성 및 적용

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: model-server
  namespace: ml-serving
spec:
  replicas: 3
  selector:
    matchLabels:
      app: model-server
  template:
    metadata:
      labels:
        app: model-server
    spec:
      containers:
      - name: server
        image: registry.example.com/model-server:v2.1.0
        ports:
        - containerPort: 8000
        resources:
          requests:
            cpu: "2"
            memory: "4Gi"
            nvidia.com/gpu: "1"
          limits:
            cpu: "4"
            memory: "8Gi"
            nvidia.com/gpu: "1"
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 5
kubectl apply -f deployment.yaml
4

롤링 업데이트 확인

kubectl rollout status deployment/model-server -n ml-serving
# 출력: deployment "model-server" successfully rolled out

kubectl get pods -n ml-serving -w
# Pod가 순차적으로 교체되는 과정 확인
5

헬스체크 점검

# Pod 상태 확인
kubectl get pods -n ml-serving -o wide

# 개별 Pod 상세 정보
kubectl describe pod <pod-name> -n ml-serving

# 로그 확인
kubectl logs <pod-name> -n ml-serving --tail=50

kubectl 명령어 레퍼런스

명령어설명예시
kubectl get리소스 목록 조회kubectl get pods -n ml-serving -o wide
kubectl describe리소스 상세 정보kubectl describe node gpu-node-01
kubectl logsPod 로그 확인kubectl logs -f <pod> -c <container>
kubectl execPod 내 명령 실행kubectl exec -it <pod> -- bash
kubectl apply매니페스트 적용 (선언적)kubectl apply -f deployment.yaml
kubectl delete리소스 삭제kubectl delete pod <pod>
kubectl rollout배포 상태/이력 관리kubectl rollout undo deployment/app
kubectl top리소스 사용량 조회kubectl top pods -n ml-serving
kubectl port-forward로컬 포트 포워딩kubectl port-forward svc/app 8080:80
kubectl scale레플리카 수 조정kubectl scale deploy/app --replicas=5
kubectl label레이블 추가/수정kubectl label node node1 gpu=true
kubectl taintTaint 추가kubectl taint nodes node1 gpu=true:NoSchedule
kubectl cordonNode 스케줄링 비활성화kubectl cordon node1
kubectl drainNode에서 Pod 퇴출kubectl drain node1 --ignore-daemonsets
kubectl config클러스터 컨텍스트 관리kubectl config use-context prod-cluster
kubectl create리소스 즉시 생성 (명령적)kubectl create ns ml-serving
kubectl edit리소스 라이브 수정kubectl edit deploy/app
kubectl patch리소스 부분 수정kubectl patch deploy/app -p '{"spec":{"replicas":5}}'
kubectl에 alias를 설정하면 생산성이 크게 향상됩니다:
alias k='kubectl'
alias kgp='kubectl get pods'
alias kgs='kubectl get svc'
alias kd='kubectl describe'
alias kl='kubectl logs -f'

GPU 스케줄링

NVIDIA Device Plugin

Kubernetes에서 GPU를 사용하려면 NVIDIA Device Plugin DaemonSet을 배포해야 합니다.
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.16.0/deployments/static/nvidia-device-plugin.yml

GPU 리소스 요청

resources:
  requests:
    nvidia.com/gpu: "1"    # 최소 1 GPU 필요
  limits:
    nvidia.com/gpu: "1"    # 최대 1 GPU 사용
GPU는 정수 단위로만 요청할 수 있습니다. nvidia.com/gpu: "0.5" 같은 분수 요청은 불가능합니다. GPU 공유가 필요하면 MIG(Multi-Instance GPU) 또는 GPU Time-Slicing을 사용해야 합니다.

Taint/Toleration으로 GPU 노드 격리

# GPU 노드에 Taint 설정 (GPU 워크로드만 스케줄링)
# kubectl taint nodes gpu-node-01 nvidia.com/gpu=present:NoSchedule

# Pod에 Toleration 추가
spec:
  tolerations:
  - key: "nvidia.com/gpu"
    operator: "Equal"
    value: "present"
    effect: "NoSchedule"
  nodeSelector:
    accelerator: nvidia-a100

MIG (Multi-Instance GPU)

A100/H100 GPU를 최대 7개의 독립적인 인스턴스로 분할하여 여러 Pod가 하나의 GPU를 공유할 수 있습니다.
MIG 프로파일GPU 메모리사용 사례
1g.5gb5 GB소형 모델 추론
2g.10gb10 GB중형 모델 추론
3g.20gb20 GB모델 파인튜닝
7g.40gb40 GB대형 모델 학습

헬스체크

Probe역할실패 시 동작
livenessProbe컨테이너가 살아있는지 확인컨테이너 재시작
readinessProbe트래픽을 받을 준비가 되었는지 확인Service에서 제외 (재시작 안 함)
startupProbe초기 시작 완료 확인시작 전까지 liveness/readiness 비활성화
# LLM 모델 서빙 — 모델 로딩에 시간이 걸리는 경우
startupProbe:
  httpGet:
    path: /health
    port: 8000
  failureThreshold: 30    # 최대 300초(30 x 10s) 대기
  periodSeconds: 10

livenessProbe:
  httpGet:
    path: /health
    port: 8000
  periodSeconds: 15
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /ready
    port: 8000
  periodSeconds: 5
  failureThreshold: 2
LLM 모델 서빙에서는 startupProbe가 특히 중요합니다. 대형 모델은 메모리 로딩에 수 분이 걸릴 수 있으므로, startupProbefailureThreshold를 충분히 높게 설정해야 합니다.

롤링 업데이트와 롤백

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 원하는 수 + 1개까지 초과 허용
      maxUnavailable: 0    # 항상 전체 레플리카 유지 (무중단)
배포 전략설명장점단점
Rolling UpdatePod를 순차적으로 교체무중단, K8s 기본값두 버전 공존 구간
Blue-Green새 환경 전체 준비 후 전환즉시 롤백 가능리소스 2배 필요
Canary소수 트래픽으로 먼저 검증리스크 최소화복잡한 라우팅 설정
# 롤백 명령어
kubectl rollout undo deployment/model-server -n ml-serving

# 특정 리비전으로 롤백
kubectl rollout history deployment/model-server -n ml-serving
kubectl rollout undo deployment/model-server --to-revision=3 -n ml-serving

리소스 관리

Requests vs Limits

구분requestslimits
의미스케줄링 보장량사용 상한선
CPU 초과 시-쓰로틀링(throttling)
메모리 초과 시-OOMKilled (프로세스 종료)
스케줄러이 값으로 Node 배치 결정런타임 제한 적용

QoS 클래스

QoS조건OOM 우선순위
Guaranteed모든 컨테이너의 requests = limits가장 마지막에 종료
Burstable일부 requests 설정중간
BestEffortrequests/limits 미설정가장 먼저 종료
GPU 워크로드에서 메모리 limits를 너무 낮게 설정하면 OOMKilled가 빈번하게 발생합니다. 모델 로딩 시 피크 메모리가 평상시보다 훨씬 높을 수 있으므로, 프로파일링 후 적절한 여유분(~20%)을 확보하세요.

스토리지

PV / PVC / StorageClass

# PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: model-storage
  namespace: ml-serving
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 100Gi
접근 모드약어설명사용 사례
ReadWriteOnceRWO단일 Node에서 읽기/쓰기학습 체크포인트
ReadOnlyManyROX다수 Node에서 읽기 전용공유 모델 파일
ReadWriteManyRWX다수 Node에서 읽기/쓰기분산 학습 공유 스토리지

Helm 기초

Helm은 Kubernetes의 패키지 매니저로, 복잡한 매니페스트를 Chart로 패키징하여 배포를 간소화합니다.

Chart 구조

my-chart/
  Chart.yaml        # 차트 메타데이터 (이름, 버전)
  values.yaml       # 기본 설정값
  templates/         # 템플릿 매니페스트
    deployment.yaml
    service.yaml
    ingress.yaml
    _helpers.tpl     # 재사용 가능한 템플릿 헬퍼

주요 명령어

# 차트 설치
helm install model-server ./my-chart -n ml-serving -f custom-values.yaml

# 업그레이드 (설정 변경 또는 차트 업데이트)
helm upgrade model-server ./my-chart -n ml-serving -f custom-values.yaml

# 롤백
helm rollback model-server 1 -n ml-serving

# 설치된 릴리스 목록
helm list -n ml-serving

# 차트 검색 (Artifact Hub)
helm search hub prometheus

values.yaml 예시

# values.yaml
replicaCount: 3
image:
  repository: registry.example.com/model-server
  tag: v2.1.0
  pullPolicy: IfNotPresent
resources:
  requests:
    cpu: "2"
    memory: "4Gi"
    nvidia.com/gpu: "1"
  limits:
    cpu: "4"
    memory: "8Gi"
    nvidia.com/gpu: "1"
service:
  type: ClusterIP
  port: 8000
autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

AI/ML에서 Kubernetes가 중요한 이유

ML 워크플로K8s 오브젝트역할
모델 학습 (단일)Job + GPU Node일회성 학습 작업 실행
모델 학습 (분산)StatefulSet + PVC멀티 GPU 분산 학습
모델 서빙Deployment + Service + HPA자동 스케일링 추론
정기 재학습CronJob일일/주간 모델 업데이트
실험 추적Deployment (MLflow) + PVCMLflow 서버 운영
데이터 파이프라인Argo Workflows / KubeflowDAG 기반 워크플로 오케스트레이션
  • minikube: 단일 노드 클러스터, 초보자에게 적합, VM 또는 Docker 드라이버 지원
  • kind (Kubernetes IN Docker): Docker 컨테이너를 Node로 사용, CI/CD에 적합, 빠른 생성/삭제
  • k3s: 경량 K8s 배포판, 프로덕션급, IoT/Edge 환경에 적합, GPU 지원 학습 목적이라면 minikube, CI/CD에는 kind, 소규모 프로덕션에는 k3s를 추천합니다.
  1. kubectl describe pod <name> → Events 섹션 확인
  2. Insufficient resources: Node에 요청된 CPU/메모리/GPU가 부족
  3. No matching node: nodeSelector나 affinity 조건을 만족하는 Node가 없음
  4. Taints not tolerated: GPU 노드의 Taint에 대한 Toleration 미설정
  5. PVC binding pending: 요청한 StorageClass의 PV가 없음
  6. kubectl get events --sort-by=.metadata.creationTimestamp로 전체 이벤트 시간순 확인
ConfigMap: 모델 하이퍼파라미터, 피처 목록, 서비스 설정을 분리하면 이미지 재빌드 없이 설정만 변경할 수 있습니다. Secret: API 키, DB 비밀번호는 반드시 Secret으로 관리하고, 가능하면 외부 시크릿 매니저(Vault, AWS Secrets Manager)와 연동하세요. 환경 변수 주입과 볼륨 마운트 두 가지 방식이 있으며, 파일 기반 설정은 볼륨 마운트가 적합합니다.
환경별 분리 (권장 시작점):
  • dev, staging, prod Namespace
  • ResourceQuota로 환경별 리소스 제한 팀별 분리 (조직 성장 시):
  • team-ml, team-data, team-platform
  • RBAC으로 팀별 접근 제어 실무에서는 {환경}-{팀} 조합(예: prod-ml-serving)을 많이 사용합니다.
기본 HPA는 CPU/메모리만 지원합니다. GPU 사용률 기반 스케일링이 필요하면:
  1. Prometheus Adapter 설치
  2. DCGM(Data Center GPU Manager) Exporter로 GPU 메트릭 수집
  3. HPA에 커스텀 메트릭 설정:
metrics:
- type: Pods
  pods:
    metric:
      name: gpu_utilization
    target:
      type: AverageValue
      averageValue: "80"
추론 서버는 초당 요청 수(RPS)나 큐 깊이 기반 스케일링이 더 효과적인 경우가 많습니다.
KEDA(Kubernetes Event-Driven Autoscaling)는 외부 이벤트 소스(Kafka, RabbitMQ, Prometheus 메트릭 등)를 기반으로 Pod를 스케일링합니다. 추론 요청 큐가 비어있으면 Pod를 0으로 축소(scale-to-zero)하여 GPU 비용을 절감할 수 있습니다. 비용 최적화가 중요한 LLM 서빙 환경에서 매우 유용합니다.

체크리스트

  • Control Plane과 Worker Node의 구성 요소 역할을 설명할 수 있는가
  • Pod, Deployment, Service, Job의 차이를 이해하고 있는가
  • kubectl get, kubectl describe, kubectl logs로 클러스터 상태를 조회할 수 있는가
  • YAML 매니페스트를 작성하여 kubectl apply로 배포할 수 있는가
  • GPU 리소스 요청과 taint/toleration 개념을 이해하고 있는가
  • livenessProbe, readinessProbe, startupProbe의 역할 차이를 설명할 수 있는가
  • 롤링 업데이트와 롤백 절차를 알고 있는가
  • requests와 limits의 차이, QoS 클래스를 이해하고 있는가
  • PVC를 사용하여 영속 스토리지를 Pod에 연결할 수 있는가
  • Helm으로 Chart를 설치하고 values.yaml로 커스터마이징할 수 있는가

다음 문서