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 | 클러스터의 유일한 진입점. 모든 요청(kubectl, 내부 통신)이 여기를 통과 |
| etcd | 클러스터 상태를 저장하는 분산 키-값 저장소. 유일한 상태 소스(source of truth) |
| Scheduler | 새 Pod를 어떤 Node에 배치할지 결정. 리소스, affinity, taint 등 고려 |
| Controller Manager | Desired State와 Current State를 비교하고 일치시키는 제어 루프 실행 |
Worker Node 구성 요소
| 구성 요소 | 역할 |
|---|---|
| kubelet | Node에서 Pod 라이프사이클 관리. API Server의 지시를 받아 컨테이너 실행/중지 |
| kube-proxy | Service 네트워크 규칙 관리. 클러스터 내부 로드밸런싱 처리 |
| Container Runtime | 실제 컨테이너 실행 (containerd, CRI-O 등) |
Kubernetes의 핵심 설계 원칙은 선언적(Declarative) 관리입니다.
“Pod 3개를 실행해라”가 아니라 “Pod 3개가 실행되고 있어야 한다”라고 선언하면, Controller가 현재 상태를 원하는 상태로 자동 수렴시킵니다.
핵심 오브젝트
| 오브젝트 | 역할 | 사용 사례 |
|---|---|---|
| Pod | 하나 이상의 컨테이너 그룹, 최소 배포 단위 | 단일 모델 서빙 프로세스 |
| Deployment | Pod의 선언적 업데이트, ReplicaSet 관리 | 모델 서빙 API 배포/롤링 업데이트 |
| Service (ClusterIP) | 클러스터 내부 고정 IP + DNS | 내부 서비스 간 통신 |
| Service (NodePort) | 노드 포트로 외부 노출 (30000-32767) | 개발/테스트 환경 외부 접근 |
| Service (LoadBalancer) | 클라우드 LB 프로비저닝 | 프로덕션 외부 트래픽 수신 |
| ConfigMap | 설정 데이터 (key-value, 파일) | 하이퍼파라미터, 모델 설정 |
| Secret | 민감 데이터 (base64 인코딩) | API 키, DB 비밀번호, 인증서 |
| Namespace | 리소스 논리적 격리 | 팀/환경별 분리 (dev, staging, prod) |
| Ingress | HTTP/HTTPS 라우팅 규칙 | 도메인 기반 API 라우팅 |
| Job | 한 번 실행 후 완료되는 작업 | 모델 학습, 데이터 전처리 배치 |
| CronJob | 주기적 Job 실행 | 일일 재학습, 정기 데이터 수집 |
| DaemonSet | 모든(또는 특정) Node에 Pod 1개씩 | 로그 수집기, 모니터링 에이전트 |
| StatefulSet | 상태 유지 + 고정 네트워크 ID | 분산 학습 워커, etcd, DB |
| HPA | CPU/메모리/커스텀 메트릭 기반 자동 스케일링 | 추론 요청 증가 시 Pod 자동 확장 |
| PVC | 영속 스토리지 요청 | 모델 파일, 학습 데이터, 체크포인트 |
배포 흐름
kubectl 명령어 레퍼런스
| 명령어 | 설명 | 예시 |
|---|---|---|
kubectl get | 리소스 목록 조회 | kubectl get pods -n ml-serving -o wide |
kubectl describe | 리소스 상세 정보 | kubectl describe node gpu-node-01 |
kubectl logs | Pod 로그 확인 | kubectl logs -f <pod> -c <container> |
kubectl exec | Pod 내 명령 실행 | 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 taint | Taint 추가 | kubectl taint nodes node1 gpu=true:NoSchedule |
kubectl cordon | Node 스케줄링 비활성화 | kubectl cordon node1 |
kubectl drain | Node에서 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}}' |
GPU 스케줄링
NVIDIA Device Plugin
Kubernetes에서 GPU를 사용하려면 NVIDIA Device Plugin DaemonSet을 배포해야 합니다.GPU 리소스 요청
Taint/Toleration으로 GPU 노드 격리
MIG (Multi-Instance GPU)
A100/H100 GPU를 최대 7개의 독립적인 인스턴스로 분할하여 여러 Pod가 하나의 GPU를 공유할 수 있습니다.| MIG 프로파일 | GPU 메모리 | 사용 사례 |
|---|---|---|
| 1g.5gb | 5 GB | 소형 모델 추론 |
| 2g.10gb | 10 GB | 중형 모델 추론 |
| 3g.20gb | 20 GB | 모델 파인튜닝 |
| 7g.40gb | 40 GB | 대형 모델 학습 |
헬스체크
| Probe | 역할 | 실패 시 동작 |
|---|---|---|
| livenessProbe | 컨테이너가 살아있는지 확인 | 컨테이너 재시작 |
| readinessProbe | 트래픽을 받을 준비가 되었는지 확인 | Service에서 제외 (재시작 안 함) |
| startupProbe | 초기 시작 완료 확인 | 시작 전까지 liveness/readiness 비활성화 |
LLM 모델 서빙에서는
startupProbe가 특히 중요합니다.
대형 모델은 메모리 로딩에 수 분이 걸릴 수 있으므로, startupProbe의 failureThreshold를 충분히 높게 설정해야 합니다.롤링 업데이트와 롤백
| 배포 전략 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Rolling Update | Pod를 순차적으로 교체 | 무중단, K8s 기본값 | 두 버전 공존 구간 |
| Blue-Green | 새 환경 전체 준비 후 전환 | 즉시 롤백 가능 | 리소스 2배 필요 |
| Canary | 소수 트래픽으로 먼저 검증 | 리스크 최소화 | 복잡한 라우팅 설정 |
리소스 관리
Requests vs Limits
| 구분 | requests | limits |
|---|---|---|
| 의미 | 스케줄링 보장량 | 사용 상한선 |
| CPU 초과 시 | - | 쓰로틀링(throttling) |
| 메모리 초과 시 | - | OOMKilled (프로세스 종료) |
| 스케줄러 | 이 값으로 Node 배치 결정 | 런타임 제한 적용 |
QoS 클래스
| QoS | 조건 | OOM 우선순위 |
|---|---|---|
| Guaranteed | 모든 컨테이너의 requests = limits | 가장 마지막에 종료 |
| Burstable | 일부 requests 설정 | 중간 |
| BestEffort | requests/limits 미설정 | 가장 먼저 종료 |
스토리지
PV / PVC / StorageClass
| 접근 모드 | 약어 | 설명 | 사용 사례 |
|---|---|---|---|
| ReadWriteOnce | RWO | 단일 Node에서 읽기/쓰기 | 학습 체크포인트 |
| ReadOnlyMany | ROX | 다수 Node에서 읽기 전용 | 공유 모델 파일 |
| ReadWriteMany | RWX | 다수 Node에서 읽기/쓰기 | 분산 학습 공유 스토리지 |
Helm 기초
Helm은 Kubernetes의 패키지 매니저로, 복잡한 매니페스트를 Chart로 패키징하여 배포를 간소화합니다.Chart 구조
주요 명령어
values.yaml 예시
AI/ML에서 Kubernetes가 중요한 이유
| ML 워크플로 | K8s 오브젝트 | 역할 |
|---|---|---|
| 모델 학습 (단일) | Job + GPU Node | 일회성 학습 작업 실행 |
| 모델 학습 (분산) | StatefulSet + PVC | 멀티 GPU 분산 학습 |
| 모델 서빙 | Deployment + Service + HPA | 자동 스케일링 추론 |
| 정기 재학습 | CronJob | 일일/주간 모델 업데이트 |
| 실험 추적 | Deployment (MLflow) + PVC | MLflow 서버 운영 |
| 데이터 파이프라인 | Argo Workflows / Kubeflow | DAG 기반 워크플로 오케스트레이션 |
minikube vs kind vs k3s: 로컬 환경 선택 가이드
minikube vs kind vs k3s: 로컬 환경 선택 가이드
- minikube: 단일 노드 클러스터, 초보자에게 적합, VM 또는 Docker 드라이버 지원
- kind (Kubernetes IN Docker): Docker 컨테이너를 Node로 사용, CI/CD에 적합, 빠른 생성/삭제
- k3s: 경량 K8s 배포판, 프로덕션급, IoT/Edge 환경에 적합, GPU 지원 학습 목적이라면 minikube, CI/CD에는 kind, 소규모 프로덕션에는 k3s를 추천합니다.
Pod가 Pending 상태에서 멈출 때 디버깅
Pod가 Pending 상태에서 멈출 때 디버깅
kubectl describe pod <name>→ Events 섹션 확인- Insufficient resources: Node에 요청된 CPU/메모리/GPU가 부족
- No matching node: nodeSelector나 affinity 조건을 만족하는 Node가 없음
- Taints not tolerated: GPU 노드의 Taint에 대한 Toleration 미설정
- PVC binding pending: 요청한 StorageClass의 PV가 없음
kubectl get events --sort-by=.metadata.creationTimestamp로 전체 이벤트 시간순 확인
ConfigMap과 Secret의 실무 활용 패턴
ConfigMap과 Secret의 실무 활용 패턴
ConfigMap: 모델 하이퍼파라미터, 피처 목록, 서비스 설정을 분리하면 이미지 재빌드 없이 설정만 변경할 수 있습니다.
Secret: API 키, DB 비밀번호는 반드시 Secret으로 관리하고, 가능하면 외부 시크릿 매니저(Vault, AWS Secrets Manager)와 연동하세요.
환경 변수 주입과 볼륨 마운트 두 가지 방식이 있으며, 파일 기반 설정은 볼륨 마운트가 적합합니다.
Namespace 전략: 팀별 vs 환경별
Namespace 전략: 팀별 vs 환경별
환경별 분리 (권장 시작점):
dev,staging,prodNamespace- ResourceQuota로 환경별 리소스 제한 팀별 분리 (조직 성장 시):
team-ml,team-data,team-platform- RBAC으로 팀별 접근 제어
실무에서는
{환경}-{팀}조합(예:prod-ml-serving)을 많이 사용합니다.
HPA 커스텀 메트릭으로 GPU 기반 오토스케일링
HPA 커스텀 메트릭으로 GPU 기반 오토스케일링
기본 HPA는 CPU/메모리만 지원합니다. GPU 사용률 기반 스케일링이 필요하면:추론 서버는 초당 요청 수(RPS)나 큐 깊이 기반 스케일링이 더 효과적인 경우가 많습니다.
- Prometheus Adapter 설치
- DCGM(Data Center GPU Manager) Exporter로 GPU 메트릭 수집
- HPA에 커스텀 메트릭 설정:
KEDA를 활용한 이벤트 기반 스케일링
KEDA를 활용한 이벤트 기반 스케일링
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로 커스터마이징할 수 있는가

