Kubernetes 입문
목표: “k8s에 올려 두었어요”, “Pod가 CrashLoopBackOff예요”, “매니페스트 apply하세요” 같은 말을 들었을 때 누가 무엇을 어디에 띄우고 계속 지켜보는지 그림으로 떠올리고,
kubectl로 직접 확인해 볼 수 있기.
이 노트는 Docker 입문의 다음 편이다. 이미지·컨테이너·포트가 낯설면 그 노트를, 서버와 네트워크가 낯설면 컴퓨터 기본 도구와 개발 환경 입문과 네트워크 기초 입문을 먼저 읽는 편이 좋다.
한 줄로 먼저
Kubernetes는 “이 컨테이너를 항상 3개 띄워 두고, 이 이름으로 접속되게 해 줘”라고 원하는 상태를 적어 두면, 여러 서버에 걸쳐 그 상태를 계속 맞춰 주는 시스템이다.
이름은 그리스어로 “조타수”라는 뜻이다. k와 s 사이에 글자가 8개라서 k8s라고 줄여 쓴다.
| 질문 | Docker 혼자 | Kubernetes |
|---|---|---|
| 서버 몇 대? | 한 대 | 여러 대(클러스터) |
| 컨테이너가 죽으면? | 같은 서버에서만 재시작 가능 | 알아서 다시 띄움. 서버가 죽으면 다른 서버에 |
| 요청이 몰리면? | 내가 더 띄움 | 개수 숫자만 바꿈. 자동 확장도 가능 |
| 새 버전 배포는? | 끄고 새로 켬 | 하나씩 교체(롤링 업데이트), 문제 시 롤백 |
| 일하는 방식 | ”이걸 실행해" | "이런 상태여야 해” |
0. 왜 필요한가
Docker로 컨테이너 하나를 띄우기는 쉽다. 문제는 서비스가 커질 때다.
- 같은 웹 서버를 10개 띄워야 하는데, 서버 한 대로는 모자라 세 대에 나눠야 한다.
- 새벽 3시에 컨테이너 하나가 죽었다. 서버 한 대가 통째로 꺼졌다.
- 새 버전을 배포하는 동안에도 서비스가 끊기면 안 된다.
- 컨테이너는 다시 뜰 때마다 IP가 바뀐다. 다른 컨테이너는 어디로 접속해야 할까?
이 일을 대신해 주는 프로그램을 컨테이너 오케스트레이터(orchestrator) 라고 한다. 여러 컨테이너의 배치·재시작·확장·연결을 자동으로 지휘한다는 뜻이다. Kubernetes는 Google의 내부 경험을 바탕으로 2014년에 공개되었고, 지금은 CNCF(Cloud Native Computing Foundation) 재단이 관리한다.
1. 선언적 방식과 조정 루프
Kubernetes의 핵심은 하나다. 명령하지 않고 원하는 상태를 적는다.
| 방식 | 말하는 법 | 예 |
|---|---|---|
| 명령형 (imperative) | “하나 더 띄워” | docker run을 세 번 친다 |
| 선언형 (declarative) | “3개 떠 있어야 해” | YAML에 replicas: 3을 적고 제출 |
Kubernetes 안에는 컨트롤러(controller) 라는 프로그램이 여럿 돈다. 컨트롤러는 같은 일을 끝없이 반복한다. 이것을 reconciliation loop(조정 루프) 라고 한다.
1. 원하는 상태를 읽는다 → "Pod 3개"
2. 지금 상태를 관찰한다 → "Pod 2개" (하나가 죽음)
3. 차이를 없앤다 → Pod 1개 새로 만듦
4. 1번으로에어컨 온도 조절기와 같다. 24도로 맞춰 두면 방이 더워질 때마다 스스로 켜진다.
이 구조에서 나오는 성질이 있다.
- Pod를 손으로 지워도 다시 생긴다. 원하는 상태가 “3개”인 한 그렇다. 정말 없애려면 원하는 상태 자체를 바꾼다.
- 같은 YAML을 여러 번 적용해도 결과가 같다. 이미 그 상태면 아무것도 안 한다.
- YAML 파일이 곧 기록이다. Git에 넣어 두면 누가 언제 무엇을 바꿨는지 남는다(Git 입문).
2. 클러스터 구조: 두뇌와 일꾼
클러스터(cluster) 는 서버 여러 대를 묶은 것이고, 서버 한 대를 노드(node) 라고 부른다.
┌──────────── Control plane (두뇌) ────────────┐
│ kube-apiserver 모든 요청이 지나는 정문 │
│ etcd 클러스터 상태 저장소 │
│ kube-scheduler 새 Pod를 둘 노드 결정 │
│ kube-controller-manager 조정 루프 묶음 │
└──────────────────────────────────────────────┘
▲ kubectl (내 노트북) │
│ ▼
┌── Worker node 1 ──┐ ┌── Worker node 2 ──┐
│ kubelet │ │ kubelet │
│ container runtime │ │ container runtime │
│ kube-proxy │ │ kube-proxy │
│ [Pod] [Pod] │ │ [Pod] [Pod] [Pod] │
└───────────────────┘ └───────────────────┘| 부품 | 위치 | 하는 일 |
|---|---|---|
| kube-apiserver | control plane | 클러스터의 유일한 창구. kubectl도 컨트롤러도 kubelet도 여기를 통해서만 상태를 읽고 쓴다 |
| etcd | control plane | 모든 상태를 저장하는 키-값 데이터베이스. 날아가면 클러스터의 기억이 사라진다 |
| kube-scheduler | control plane | 자원과 조건을 따져 Pod를 놓을 노드를 정한다. 실행은 하지 않는다 |
| kube-controller-manager | control plane | Deployment·ReplicaSet·Job 등의 컨트롤러를 한 프로세스로 돌린다 |
| kubelet | 각 노드 | 그 노드에 배정된 Pod를 실제로 띄우고 상태를 보고한다 |
| container runtime | 각 노드 | 컨테이너를 실행하는 프로그램. 주로 containerd, CRI-O |
| kube-proxy | 각 노드 | Service로 온 트래픽이 알맞은 Pod로 가도록 네트워크 규칙을 설정한다 |
Docker는 이제 안 쓰나?
- 예전 Kubernetes는 dockershim이라는 연결 부품으로 Docker Engine을 런타임으로 썼다.
- Kubernetes는 런타임과 대화하는 표준 CRI(Container Runtime Interface) 를 만들었는데, Docker Engine은 이를 직접 지원하지 않았다.
- 그래서 v1.24(2022년)에서 dockershim이 제거되었다. 지금 노드에서는 보통 containerd나 CRI-O가 컨테이너를 돌린다.
- 그래도 Docker로 빌드한 이미지는 그대로 돌아간다. 이미지가 OCI라는 공개 표준 형식이기 때문이다. 바뀐 것은 노드 안의 실행 부품이다.
즉 개발자는 여전히 docker build로 이미지를 만들어 레지스트리에 올리고, Kubernetes가 그것을 받아 실행한다.
3. 핵심 오브젝트 한눈에
Kubernetes에 등록하는 모든 것을 오브젝트(object) 라고 부른다. 각각 YAML 한 덩어리로 적는다.
| 오브젝트 | 한 줄 정의 |
|---|---|
| Pod | 컨테이너 1개 이상을 묶은 가장 작은 실행 단위 |
| ReplicaSet | 같은 Pod를 N개 유지 |
| Deployment | ReplicaSet을 관리하며 버전 교체·롤백 담당 |
| Service | Pod 묶음에 고정 이름과 주소를 붙임 |
| Ingress | 바깥 HTTP 요청을 URL·도메인별로 Service에 연결 |
| ConfigMap / Secret | 설정 값 / 민감한 값 묶음 |
| PV / PVC | Pod가 죽어도 남는 저장 공간 / 그 요청서 |
| Namespace | 클러스터 안의 칸막이 |
| Job / CronJob | 끝나는 작업 한 번 / 정해진 시각마다 |
| StatefulSet | 이름과 저장 공간이 고정된 Pod 묶음 (DB 등) |
| DaemonSet | 노드마다 Pod를 하나씩 (로그 수집기, GPU plugin 등) |
4. Pod와 Deployment
Pod는 Kubernetes가 다루는 최소 단위다. 컨테이너를 직접 다루지 않고 Pod로 감싸서 다룬다.
- 한 Pod 안의 컨테이너들은 IP와 네트워크를 공유한다. 서로
localhost로 통신한다. - 같은 볼륨을 함께 쓸 수 있고, 항상 같은 노드에 놓인다.
- 대부분은 컨테이너가 하나다. 주 컨테이너를 돕는 보조 컨테이너를 사이드카(sidecar) 라고 한다.
Pod는 일회용이다. 죽으면 되살아나는 게 아니라 새 이름, 새 IP의 다른 Pod가 생긴다. 그래서 Pod를 직접 만들지 않고 Deployment에게 맡긴다.
Deployment web ← 내가 만드는 것 (버전 관리)
└─ ReplicaSet web-7d9f8c ← 자동 생성 (개수 유지)
├─ Pod web-7d9f8c-abcde
├─ Pod web-7d9f8c-fghij
└─ Pod web-7d9f8c-klmnoReplicaSet을 직접 만들 일은 거의 없다. 웹 서버, API 서버, 추론 서버처럼 계속 떠 있어야 하는 것은 Deployment로 만든다.
5. 라벨, 셀렉터, Service, Ingress
라벨과 셀렉터
라벨(label) 은 오브젝트에 붙이는 키: 값 이름표다(예: app: web). 셀렉터(selector) 는 “이 라벨을 가진 것들”을 고르는 조건이다.
Kubernetes 오브젝트들은 이름이 아니라 라벨로 서로를 찾는다. ReplicaSet은 app: web 라벨의 Pod를 세고, Service는 그 Pod들에게 트래픽을 보낸다. 라벨에 오타가 나면 “Service는 있는데 연결이 안 된다”가 된다. 명령줄에서도 kubectl get pods -l app=web처럼 고른다.
Service
Service는 라벨로 고른 Pod 묶음 앞에 고정된 이름과 IP를 세우고, 요청을 살아 있는 Pod들에 나눠 준다.
| 종류 | 어디서 접속되나 | 언제 |
|---|---|---|
| ClusterIP (기본) | 클러스터 안에서만 | 앱 → DB, 내부 API |
| NodePort | 모든 노드의 한 포트(기본 30000–32767) | 테스트, 간단한 외부 노출 |
| LoadBalancer | 클라우드가 만든 외부 IP | 클라우드에서 외부 공개 |
클러스터 안에는 DNS가 있다. 같은 Namespace면 http://web, 다른 Namespace에서는 http://web.<namespace>로 접속한다. Docker Compose에서 서비스 이름으로 접속하던 것과 비슷하다. LoadBalancer는 클라우드에서는 자동이지만, 직접 구축한 클러스터에서는 MetalLB 같은 구성 요소가 없으면 외부 IP가 <pending>에 머문다.
Ingress
Ingress는 입구 하나에서 URL 경로나 도메인을 보고 알맞은 Service로 나눠 보낸다. 예: example.com/api는 api로, 나머지는 web으로.
Ingress 오브젝트는 규칙일 뿐이다. 규칙을 실제로 수행할 Ingress controller를 따로 설치해야 동작한다. Kubernetes 문서는 Ingress API의 기능 추가를 멈췄고(frozen), 후속으로 Gateway API를 권장한다.
6. YAML 매니페스트 예: Deployment + Service
매니페스트(manifest) 는 원하는 상태를 적은 YAML 파일이다. 아래는 nginx 3개와 그 앞의 Service다. ---로 오브젝트를 구분한다.
# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3 # Pod 3개 유지
selector:
matchLabels:
app: web # 이 라벨의 Pod를 관리
template: # 만들 Pod의 틀
metadata:
labels:
app: web # 위 selector와 반드시 일치
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
memory: "256Mi"
readinessProbe:
httpGet:
path: /
port: 80
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web # 이 라벨의 Pod로 전달
ports:
- port: 80 # Service가 받는 포트
targetPort: 80 # 컨테이너 포트| 키 | 뜻 |
|---|---|
apiVersion | 규격 버전. Deployment는 apps/v1, Service·Pod·ConfigMap은 v1, Job은 batch/v1 |
kind | 오브젝트 종류 |
metadata | 이름, 라벨, Namespace |
spec | 원하는 상태. 내가 쓰는 부분 |
클러스터는 여기에 status(지금 상태)를 채워 넣는다. kubectl get deployment web -o yaml로 둘을 나란히 볼 수 있다. 조정 루프는 status를 spec에 맞추는 일이다. YAML은 들여쓰기가 문법이다. 탭 말고 공백을 쓴다.
7. kubectl 기본 명령
kubectl은 API server와 대화하는 명령줄 도구다. 어느 클러스터에 붙을지는 ~/.kube/config(kubeconfig)에 적혀 있다.
kubectl apply -f app.yaml # 원하는 상태 제출 (생성 또는 갱신)
kubectl delete -f app.yaml # 파일에 적힌 오브젝트 삭제
kubectl get pods -o wide # Pod 목록 + 노드·IP
kubectl get deploy,svc,pods # 여러 종류 한 번에
kubectl describe pod <pod> # 자세한 정보 + 맨 아래 Events (원인이 주로 여기)
kubectl logs -f <pod> # 로그 따라 보기
kubectl logs <pod> --previous # 직전에 죽은 컨테이너의 로그
kubectl exec -it <pod> -- bash # 컨테이너 안 셸 (bash가 없으면 sh)
kubectl port-forward svc/web 8080:80 # 내 localhost:8080 → Service web의 80
kubectl scale deployment web --replicas=5
kubectl rollout status deployment/web
kubectl rollout undo deployment/web # 직전 버전으로 롤백
kubectl get pods -n kube-system # 다른 Namespace
kubectl config current-context # 지금 붙어 있는 클러스터exec ... -- bash의 --는 “여기부터는 컨테이너 안에서 실행할 명령”이라는 구분선이다. port-forward는 디버깅용 임시 터널이고, Docker의 -p처럼 왼쪽이 내 컴퓨터다.
8. ConfigMap과 Secret
설정을 이미지에 박으면 바꿀 때마다 다시 빌드해야 한다. 그래서 설정은 이미지 밖에 둔다. ConfigMap은 일반 설정, Secret은 API 키·비밀번호 같은 민감한 값이다. 둘 다 환경 변수나 파일로 Pod에 넣는다.
kubectl create configmap app-config --from-literal=log_level=info
kubectl create secret generic hf-secret --from-literal=token=hf_xxx# Deployment의 containers 항목 안에서
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: log_level
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-secret
key: tokenSecret은 기본적으로 암호화가 아니다
Secret 값은 base64로 인코딩되어 있을 뿐이다. base64는 글자를 다른 글자로 바꿔 적는 방식이지 잠금이 아니다. echo 'aGZfeHh4' | base64 -d면 원래 값이 나온다. 기본 설정에서는 etcd에도 암호화 없이 저장된다.
실제 보호는 다른 데서 나온다. RBAC(역할별 권한 관리)로 읽을 수 있는 사람을 제한하고, 관리자가 etcd 저장 시 암호화(encryption at rest) 를 켜고, 필요하면 외부 비밀 관리 도구와 연동한다. Secret을 적은 YAML은 Git에 올리지 않는다.
9. 저장 공간과 Namespace
Volume, PV, PVC
컨테이너 안의 파일은 컨테이너와 함께 사라진다. 남길 데이터는 바깥 저장 공간에 둔다.
- Volume: Pod에 붙이는 저장 공간.
emptyDir처럼 Pod와 수명이 같은 것도 있다. - PersistentVolume (PV): 실제 디스크 한 조각(클라우드 디스크, NFS 등).
- PersistentVolumeClaim (PVC): 사용자가 내는 “50Gi 주세요” 요청서.
사용자는 PVC를 만들고 Pod에서 마운트한다. StorageClass가 있는 클러스터에서는 PVC만 만들면 PV가 자동으로 생긴다.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: model-cache
spec:
accessModes:
- ReadWriteOnce # 한 노드에서만 읽기·쓰기
resources:
requests:
storage: 50Gi여러 노드의 Pod가 같은 데이터를 함께 쓰려면 ReadWriteMany를 지원하는 저장소(NFS 등)가 필요하다.
Namespace
Namespace는 한 클러스터를 팀·프로젝트·환경별로 나누는 칸막이다. 이름은 같은 Namespace 안에서만 겹치면 안 되고, 권한과 자원 할당량(ResourceQuota)을 Namespace 단위로 건다. 다만 보안 격리를 완전히 보장하지는 않는다. 기본 설정에서는 다른 Namespace의 Pod끼리도 통신된다(막으려면 NetworkPolicy).
10. Job, CronJob, StatefulSet, DaemonSet
Deployment는 프로그램이 끝나면 다시 띄운다. 학습·평가·전처리처럼 한 번 돌고 끝나야 하는 작업은 Job으로 만든다.
apiVersion: batch/v1
kind: Job
metadata:
name: eval-run
spec:
backoffLimit: 2 # 실패 시 재시도 횟수
template:
spec:
restartPolicy: Never # Job은 Never 또는 OnFailure
containers:
- name: eval
image: myregistry/eval:0.3
command: ["python", "eval.py", "--split", "test"]- CronJob: cron 형식(
"0 3 * * *"= 매일 새벽 3시) 일정마다 Job을 만든다. - StatefulSet: Pod에
db-0,db-1같은 고정 이름과 각자의 저장 공간을 준다. - DaemonSet: 노드마다 Pod를 하나씩 둔다.
11. 자원 요청과 제한, 그리고 GPU
requests와 limits
| 항목 | 뜻 | 쓰이는 곳 |
|---|---|---|
| requests | ”최소 이만큼 확보해 줘” | 스케줄러가 노드를 고를 때 |
| limits | ”이 이상은 쓰지 마” | 실행 중 강제 |
CPU는 1이 코어 1개, 500m이 0.5코어다. 메모리는 512Mi, 4Gi처럼 쓴다. 넘었을 때 결과가 다르다.
- CPU limits 초과: 느려진다(throttling). 죽지는 않는다.
- 메모리 limits 초과: 강제 종료된다. OOMKilled가 찍힌다.
requests가 너무 크면 들어갈 노드가 없어 Pending에 머문다.
GPU 쓰기
Kubernetes 자체는 GPU를 모른다. device plugin이 노드의 GPU를 찾아 “이 노드에 nvidia.com/gpu가 4개”라고 알린다. NVIDIA는 이 plugin과, 드라이버·Container Toolkit·plugin을 함께 설치하는 GPU Operator를 제공한다. 그다음은 숫자로 요청한다.
# containers 항목 안에서
resources:
limits:
nvidia.com/gpu: 1- GPU는 limits에 적는다. requests를 쓰면 limits와 같아야 한다.
- 정수 단위다. 한 장을 나눠 쓰려면 MIG나 time-slicing 같은 별도 설정이 필요하다.
- Docker 때처럼 드라이버는 노드에, CUDA·PyTorch는 이미지에 있다(GPU와 CUDA 입문).
12. 롤링 업데이트, 롤백, 헬스체크
롤링 업데이트와 롤백
Deployment의 이미지를 바꾸면 기본 전략(RollingUpdate)대로 Pod를 조금씩 바꾼다.
v1 v1 v1 → v1 v1 v1 v2 → v1 v1 v2 → … → v2 v2 v2
(새 Pod 준비 대기) (옛 Pod 하나 제거)한 번에 더 만들 수(maxSurge)와 줄일 수(maxUnavailable)는 기본 25%씩이다. 문제가 생기면 kubectl rollout undo deployment/web으로 되돌린다. Deployment가 옛 ReplicaSet을 개수 0으로 남겨 두기 때문에 가능하다. 되돌린 뒤에는 YAML 파일도 맞게 고쳐 둔다.
헬스체크: probe
“프로세스가 살아 있다”와 “제대로 일한다”는 다르다. probe는 HTTP 요청, TCP 연결, 명령 실행으로 컨테이너를 주기적으로 검사한다.
| probe | 질문 | 실패하면 |
|---|---|---|
| livenessProbe | 멈춰 버리지 않았나? | 컨테이너 재시작 |
| readinessProbe | 요청 받을 준비가 됐나? | Service 트래픽에서 제외 (재시작 안 함) |
| startupProbe | 시작이 끝났나? | 성공할 때까지 위 두 검사를 미룸 |
LLM 추론 서버는 가중치를 읽느라 시작에 몇 분이 걸릴 수 있다. liveness가 너무 일찍 돌면 계속 재시작되어 영원히 못 뜬다. 이럴 때 startupProbe를 넉넉히 건다. 롤링 업데이트도 readiness를 보고 옛 Pod를 줄이므로, readinessProbe가 있어야 무중단 배포가 제대로 된다.
13. 흔한 상태와 원인
먼저 kubectl get pods의 STATUS와 RESTARTS를 본다. 원인은 대부분 kubectl describe pod의 Events나 kubectl logs에 있다.
| 상태 | 뜻 | 흔한 원인 |
|---|---|---|
| Pending | 아직 노드에 못 놓임(또는 시작 전) | 자원 부족(특히 GPU), 노드 조건 불일치, PVC 미연결 |
| ImagePullBackOff | 이미지를 못 받음 | 이름·태그 오타, 비공개 레지스트리 인증 없음 |
| CrashLoopBackOff | 시작하자마자 죽기를 반복 | 코드 오류, 환경 변수·설정 누락, 잘못된 command, liveness 실패 |
| OOMKilled | 메모리 limits 초과로 강제 종료 (종료 코드 137) | limits가 작음, 배치 크기, 메모리 누수 |
CrashLoopBackOff의 “BackOff”는 재시도 간격이 10초, 20초, 40초처럼 늘어나 최대 5분까지 간다는 뜻이다. 원인은 kubectl logs <pod> --previous로 직전 실행의 로그를 보면 대개 나온다.
Apple Silicon(arm64)에서 빌드한 이미지를 amd64 노드에 올리면 실행 직후 exec format error로 죽어 CrashLoopBackOff가 된다. Docker 입문의 --platform linux/amd64 문제와 같다.
14. 로컬 실습, 관리형 서비스, Helm
| 도구 | 특징 |
|---|---|
| Docker Desktop 내장 | 설정에서 Kubernetes를 켜면 끝 |
| OrbStack | Mac용 가벼운 Docker 대안, Kubernetes 옵션 내장 |
| kind | ”Kubernetes IN Docker”. 컨테이너를 노드처럼 써서 다중 노드도 흉내 |
| minikube | 오래된 학습용 도구, 여러 드라이버와 애드온 |
kind create cluster --name dev
kubectl apply -f app.yaml
kubectl port-forward svc/web 8080:80 # 브라우저에서 http://localhost:8080
kind delete cluster --name devMac의 로컬 클러스터도 보이지 않는 Linux VM 안에서 돈다. 그래서 NVIDIA GPU는 쓸 수 없다. 로컬에서는 YAML과 동작 원리를 익히고, GPU 작업은 실제 클러스터에서 한다.
- 관리형 Kubernetes: 클라우드가 control plane을 대신 운영한다. EKS(AWS), GKE(Google Cloud), AKS(Azure).
- Helm: Kubernetes용 패키지 매니저. YAML 묶음 템플릿인 차트(chart) 를
helm install로 한 번에 설치한다.
15. 연구 맥락: Kubernetes와 Slurm
연구실 GPU 클러스터에서는 Kubernetes보다 Slurm을 더 자주 만난다. Slurm은 HPC(고성능 컴퓨팅)용 작업 스케줄러다. “GPU 4장으로 이 스크립트를 돌려 줘”라는 작업을 줄 세우고, 자원이 비면 실행한다. 서버 사용법은 원격 GPU 서버 사용 입문 참고.
| 구분 | Slurm | Kubernetes |
|---|---|---|
| 기본 단위 | 돌고 끝나는 작업(job) | 대개 계속 떠 있는 서비스 |
| 주 용도 | 배치 학습·실험 | 웹·API·추론 서버 |
| 사용 방식 | sbatch train.sh, squeue | YAML + kubectl apply |
| GPU 요청 | --gres=gpu:4 | nvidia.com/gpu: 4 |
| 환경 | 서버의 conda·모듈, 또는 Apptainer | 항상 컨테이너 이미지 |
| 대기열·공정 분배 | 기본 기능 | 기본은 약함. Kueue 같은 도구로 보완 |
| 다중 노드 학습 | 여러 노드를 한꺼번에 잡기 자연스러움 | 모든 Pod 동시 시작(gang scheduling)에 추가 도구 필요 |
| 흔한 곳 | 대학 연구실, 슈퍼컴퓨터 | 기업 서비스, 추론 서빙, 대규모 ML 플랫폼 |
학습 작업을 돌리고 결과를 받는 것이 중심이면 Slurm이 편하다. 모델을 계속 띄워 두고 요청을 받거나, 여러 팀이 GPU 풀을 서비스와 학습에 함께 쓰면 Kubernetes가 맞다.
Kubernetes 위의 ML 도구로는 Kubeflow(노트북·파이프라인·분산 학습을 묶은 ML 플랫폼)와 KubeRay(Ray 클러스터를 띄우고 관리하는 operator)가 있다. operator는 특정 소프트웨어의 운영 지식을 담은 사용자 정의 컨트롤러다. 코드 push부터 이미지 빌드·배포까지 자동화하는 흐름은 CI-CD와 GitHub Actions 입문과 이어진다.
16. 언제 Kubernetes가 필요 없는가
Kubernetes는 배우고 운영하는 비용이 크다. 다음이라면 대개 필요 없다.
- 서버 한 대로 충분하다.
docker compose와 재시작 정책이면 된다. - 혼자 하는 연구 실험이다.
tmux안에서 돌리거나 Slurm에sbatch로 내는 편이 빠르다. - 데모나 작은 웹앱이다. 관리형 플랫폼(PaaS, 서버리스)이 간단하다.
- 운영할 사람이 없다. 설치보다 유지(업그레이드, 인증서, 모니터링, 보안)가 더 어렵다.
여러 서버, 여러 서비스, 무중단 배포, 트래픽 따른 확장, 여러 팀의 자원 공유가 함께 필요해질 때 고려한다. 연구자에게 현실적인 목표는 클러스터 운영이 아니라 “남이 운영하는 클러스터에 내 작업을 띄우고, 안 뜨면 원인을 찾는 것” 이다. §6, §7, §11, §13이 그 최소한이다.
17. 아주 짧은 암기
- Docker는 컨테이너 하나, Kubernetes는 여러 서버의 컨테이너 함대.
- 원하는 상태를 적으면 컨트롤러가 지금 상태를 계속 맞춘다.
- Control plane = API server + etcd + scheduler + controller manager. 모든 길은 API server로.
- 노드마다 kubelet이 Pod를 띄운다. 런타임은 주로 containerd, Docker 이미지는 그대로 돈다.
- Pod는 일회용, 계속 떠 있을 것은 Deployment로.
- 오브젝트는 라벨로 서로를 찾는다. selector와 template 라벨을 맞춘다.
- Service = Pod 묶음의 고정 주소. ClusterIP는 안에서만.
- Secret은 base64일 뿐 암호화가 아니다.
- 메모리 초과는 OOMKilled, CPU 초과는 느려짐.
- GPU는
limits에nvidia.com/gpu: 1, 정수 단위. - liveness 실패 = 재시작, readiness 실패 = 트래픽 제외.
- 안 뜨면
describe의 Events, 죽으면logs --previous. - 배치 학습은 Slurm, 서비스·추론 서버는 Kubernetes.
입문자가 조심할 점
- 지금 어느 클러스터, 어느 Namespace인지 확인한다. kubeconfig에 여러 클러스터가 있으면 연습용인 줄 알고 운영 클러스터에서
delete를 칠 수 있다. kubectl delete는 되돌릴 수 없다. PVC를 지우면 설정에 따라 실제 디스크 데이터까지 지워진다. Namespace를 지우면 그 안의 모든 것이 지워진다.- 손으로 고친 것은 YAML에 반영한다.
kubectl edit나scale로 바꾸고 파일을 안 고치면, 다음apply때 옛 값으로 돌아간다. latest태그를 쓰지 않는다. 무엇이 돌고 있는지 모르게 되고 롤백도 의미가 없어진다.- requests·limits를 비워 두지 않는다. 공용 클러스터에서는 할당량 규칙 때문에 생성이 거부될 수도 있다.
- GPU를 잡고 놀리지 않는다. GPU를 받은 Pod는 계산을 안 해도 그 GPU를 점유한다. 디버깅용 GPU Pod는 끝나면 지운다.
- 토큰과 키를 이미지·로그·Git에 남기지 않는다. Secret으로 주입하고, 노출되면 새로 발급받는다.
출처
- Kubernetes Documentation, Overview, Cluster Architecture, Kubernetes Components, Controllers. https://kubernetes.io/docs/concepts/
- Kubernetes Documentation, Workloads (Pods, Deployments, StatefulSets, DaemonSet, Jobs, CronJob). https://kubernetes.io/docs/concepts/workloads/
- Kubernetes Documentation, Services, Load Balancing, and Networking (Service, Ingress, Gateway API, DNS). https://kubernetes.io/docs/concepts/services-networking/
- Kubernetes Documentation, Configuration (ConfigMaps, Secrets, Resource Management for Pods and Containers). https://kubernetes.io/docs/concepts/configuration/
- Kubernetes Documentation, Encrypting Confidential Data at Rest. https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
- Kubernetes Documentation, Storage (Volumes, Persistent Volumes, Storage Classes). https://kubernetes.io/docs/concepts/storage/
- Kubernetes Documentation, Configure Liveness, Readiness and Startup Probes; Pod Lifecycle. https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
- Kubernetes Documentation, Schedule GPUs. https://kubernetes.io/docs/tasks/manage-gpus/scheduling-gpus/
- Kubernetes Blog, Dockershim Removal FAQ (2022). https://kubernetes.io/blog/2022/02/17/dockershim-faq/
- kubectl Reference. https://kubernetes.io/docs/reference/kubectl/
- NVIDIA, GPU Operator. https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/
- kind. https://kind.sigs.k8s.io/ · minikube. https://minikube.sigs.k8s.io/ · Helm. https://helm.sh/docs/
- Slurm Workload Manager Documentation. https://slurm.schedmd.com/documentation.html
- Kubeflow. https://www.kubeflow.org/docs/ · Ray, Ray on Kubernetes (KubeRay). https://docs.ray.io/en/latest/cluster/kubernetes/index.html · Kueue. https://kueue.sigs.k8s.io/