컨테이너 오케스트레이션과 Kubernetes 입문
단일 서버에서는 Compose로 충분했지만, 서버가 여러 대가 되자 배포·복구·스케일링이 모두 수동 작업이 됩니다. 장애가 나면 어느 노드에서 무엇이 죽었는지 찾는 데만 시간이 소모되고, 롤백도 일관되지 않습니다. 이 시점부터는 "컨테이너 실행"보다 "원하는 상태를 자동 유지"하는 오케스트레이션 사고가 필요합니다. 이 모듈은 Docker 경험을 Kubernetes 운영 개념으로 자연스럽게 연결합니다.
이 모듈의 위치: Docker Compose까지 배웠다면 "서버가 여러 대면 어떻게 하나?"라는 질문이 생깁니다. 그 답이 오케스트레이션입니다. 현업 표준은 Kubernetes(K8s)입니다. 이 모듈은 Docker 지식을 K8s 개념에 연결하는 다리 역할을 합니다.
Docker를 알면 Kubernetes의 절반은 이미 이해한 겁니다. 컨테이너 이미지, 포트 바인딩, 볼륨, 환경변수 — 모두 K8s에서 같은 개념이 다른 문법으로 존재합니다. 낯선 용어들을 Docker 용어에 대응시키면서 배우면 훨씬 빠릅니다.
- 1오케스트레이션이 왜 필요한가 — Compose 한계와 멀티 노드의 현실
- 2Docker 개념 → Kubernetes 개념 1:1 매핑 (run→Pod, Compose→Deployment)
- 3kubectl 기본 명령 — docker CLI와 비교해서 배우기
- 4첫 번째 Deployment 배포 — YAML 작성부터 외부 노출까지
- 5롤링 업데이트와 롤백 — Kubernetes가 무중단 배포를 처리하는 방식
minikube는 노트북에서 단일 노드 Kubernetes를 실행하는 도구입니다. 실제 클러스터 없이 이 모듈의 모든 실습을 진행할 수 있습니다.
minikube versionminikube startkubectl version --clientkubectl cluster-infoDocker Desktop의 Settings → Kubernetes → Enable Kubernetes로 로컬 클러스터 활성화 가능합니다
왜 Compose로는 부족한가 — 오케스트레이션의 필요성
Docker Compose로 운영 중인 서버가 다운됐습니다. 모든 컨테이너가 중단됩니다. 수동으로 서버를 복구하고 docker-compose up을 다시 실행해야 합니다. 트래픽이 갑자기 2배가 됐습니다. 컨테이너를 더 실행하려면 또 다른 서버에 SSH로 접속해서 수동으로 docker run을 해야 합니다. 배포할 때마다 잠깐 서비스가 중단됩니다. Compose는 단일 서버에서 컨테이너를 편하게 관리하는 도구지, 여러 서버를 자동으로 관리하는 도구가 아닙니다.

Compose의 한계
Docker Compose가 해결하는 것:
✓ 로컬 개발 환경 멀티 컨테이너 관리
✓ 단일 서버에서 서비스 간 네트워크
✓ 볼륨, 환경변수, 의존성 관리
Docker Compose가 해결하지 못하는 것:
✗ 여러 서버(노드)에 컨테이너 분산
✗ 노드 장애 시 자동 복구
✗ 트래픽에 따른 자동 스케일링
✗ 무중단 롤링 업데이트
✗ 서비스 간 부하분산
Kubernetes가 해결하는 것
Kubernetes(K8s)는 컨테이너 오케스트레이션 플랫폼입니다. 여러 서버를 하나의 클러스터로 추상화하고, 컨테이너의 배포·스케일링·복구를 자동화합니다.
[K8s 클러스터 구조]
Control Plane (마스터)
┌─────────────────────────────┐
│ API Server │ Scheduler │
│ etcd │ Controller │ ← 클러스터 상태 관리
└─────────────────────────────┘
↕ 명령/상태 동기화
Worker 노드들
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ [Pod A] │ │ [Pod B] │ │ [Pod C] │
│ [Pod D] │ │ [Pod E] │ │ │
└──────────┘ └──────────┘ └──────────┘
→ Node 2 장애 시: Pod B, E를 Node 1, 3으로 자동 재스케줄
→ 트래픽 급증 시: Pod 수를 자동으로 늘림 (HPA)
→ 배포 시: Pod를 순차적으로 교체 (무중단 롤링 업데이트)
Docker → Kubernetes 개념 매핑
K8s를 처음 배울 때 가장 힘든 것은 새로운 용어들입니다. Pod, Deployment, Service, ConfigMap, Ingress — 낯선 이름들이 쏟아집니다. 그런데 이 개념들은 Docker에서 이미 알고 있는 것들과 정확히 대응됩니다. 매핑을 알면 절반은 이미 이해한 겁니다.

핵심 개념 매핑
| Docker / Compose | Kubernetes | 역할 |
|---|---|---|
docker run | Pod | 컨테이너 실행 단위 |
docker-compose.yml service | Deployment | 컨테이너 수·이미지·설정 선언 |
--replicas: 3 | replicas: 3 | 동일 — 복제본 수 |
-p 8080:80 | Service (NodePort/LB) | 외부 포트 노출 |
docker network | Service (ClusterIP) | 컨테이너 간 통신 |
-e KEY=VALUE | env + ConfigMap | 환경변수 |
-e PASSWORD=... | Secret | 민감한 환경변수 |
-v /host:/container | PersistentVolumeClaim | 영구 볼륨 |
healthcheck: | livenessProbe / readinessProbe | 헬스체크 |
docker logs | kubectl logs | 컨테이너 로그 |
docker exec -it | kubectl exec -it | 컨테이너 접속 |
docker ps | kubectl get pods | 실행 중인 컨테이너 목록 |
Pod란?
Pod는 K8s에서 가장 작은 배포 단위입니다. docker run nginx가 컨테이너 하나를 실행하는 것처럼, K8s에서는 Pod 안에 컨테이너가 실행됩니다. 대부분의 경우 Pod 하나 = 컨테이너 하나입니다.
# docker run nginx와 동일한 K8s Pod
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
Deployment란?
Pod를 직접 만들지 않고 Deployment를 사용합니다. Deployment는 "nginx를 3개 실행하고 항상 3개 상태를 유지하라"는 선언입니다. Pod가 죽으면 자동으로 새 Pod를 만듭니다.
# docker-compose.yml의 service와 대응
# services:
# web:
# image: nginx:1.25-alpine
# deploy:
# replicas: 3
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # Compose의 replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
minikube 환경에서 nginx를 3개 복제본으로 배포하고, Service로 외부에 노출합니다.
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30080
# Deployment와 Service 동시 배포
kubectl apply -f nginx-deployment.yaml
# 배포 상태 확인
kubectl get pods
kubectl get deployment nginx-deployment
kubectl get service nginx-service
# minikube에서 외부 접근 URL 확인
minikube service nginx-service --url
kubectl apply -f nginx-deployment.yaml- kubectl get pods 에서 Pod 3개가 모두 Running 상태인지 확인
- kubectl get deployment nginx-deployment — READY 컬럼이 3/3인지 확인
- kubectl describe pod <pod-name> 에서 Events 섹션에 에러 없이 Started가 찍히는지
- minikube service nginx-service --url 로 받은 URL을 curl로 호출하면 nginx 응답이 오는지
- kubectl get replicaset — Deployment가 ReplicaSet을 통해 Pod를 관리하는 구조 확인
트러블슈팅
증상
Deployment를 배포했는데 Pod가 Running이 되지 않고 계속 Pending 상태입니다.
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# nginx-deployment-7d9c4bbfd-xk2p9 0/1 Pending 0 2m
kubectl describe pod nginx-deployment-7d9c4bbfd-xk2p9
# Events:
# Warning FailedScheduling 2m default-scheduler
# 0/1 nodes are available: 1 Insufficient memory.
원인 진단
# 노드 리소스 현황 확인
kubectl describe nodes | grep -A 5 "Allocated resources"
# Allocated resources:
# Resource Requests Limits
# cpu 950m (47%) 2 (100%)
# memory 1900Mi (95%) 2000Mi (99%)
# → 메모리가 거의 꽉 찬 상태
# Pod의 리소스 요청량 확인
kubectl get pod nginx-deployment-7d9c4bbfd-xk2p9 -o yaml | grep -A 10 resources
해결
# 방법 1: 다른 Pod 삭제하여 리소스 확보
kubectl get pods --all-namespaces | grep -v Running
# 불필요한 Pod 삭제
# 방법 2: Deployment에 resources.requests 조정
kubectl edit deployment nginx-deployment
# resources:
# requests:
# memory: "64Mi" ← 낮춤
# cpu: "100m"
# 방법 3: minikube 메모리 늘려서 재시작
minikube stop
minikube start --memory=4096
증상
kubectl apply 직후에는 Running이었다가 금방 Error → CrashLoopBackOff로 바뀝니다.
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# myapp-deployment-abc123-x7k2p 0/1 CrashLoopBackOff 5 3m
# 재시작 횟수가 계속 늘어남
kubectl get pods -w
# myapp-.. 0/1 Error 3 90s
# myapp-.. 0/1 CrashLoopBackOff 3 95s
# myapp-.. 0/1 Running 4 110s
# myapp-.. 0/1 Error 4 115s
원인 진단
# 컨테이너 로그 확인 — docker logs와 동일한 역할
kubectl logs myapp-deployment-abc123-x7k2p
# Error: Cannot find module '/app/server.js' ← 이런 에러 메시지 확인
# 이전 충돌 컨테이너의 로그 확인
kubectl logs myapp-deployment-abc123-x7k2p --previous
# Pod 상세 이벤트 확인
kubectl describe pod myapp-deployment-abc123-x7k2p
# Events:
# Warning BackOff 2m kubelet Back-off restarting failed container
주요 원인별 해결
# 원인 1: 이미지 내 실행 파일 경로 오류 → Dockerfile ENTRYPOINT/CMD 확인
# 원인 2: 환경변수 누락 → kubectl get pod -o yaml 로 env 확인
# 원인 3: ConfigMap/Secret 마운트 실패 → kubectl describe pod Events 확인
# 임시 디버깅: 같은 이미지로 셸 접속
kubectl run debug --image=myapp:latest --restart=Never -it -- /bin/sh
증상
단일 노드에서는 Pod 간 통신이 잘 됐는데, 멀티 노드 클러스터(또는 Swarm) 환경에서 서로 다른 노드에 뜬 Pod끼리 통신이 안 됩니다.
# Pod A (Node 1)에서 Pod B (Node 2)로 ping/curl 실패
kubectl exec -it pod-a -- curl http://pod-b-service
# curl: (7) Failed to connect to pod-b-service port 80: Connection timed out
원인 진단
# 노드 간 오버레이 네트워크 포트 확인 (방화벽 문제)
# Kubernetes(Flannel/Calico): UDP 8472, UDP 4789 필요
# Kubernetes API: TCP 6443
# Swarm: TCP 2377, TCP/UDP 7946, UDP 4789
# 방화벽 상태 확인
sudo firewall-cmd --list-all # CentOS/RHEL
sudo ufw status # Ubuntu
해결
# Ubuntu (ufw) — 오버레이 네트워크 포트 허용
sudo ufw allow 6443/tcp # K8s API Server
sudo ufw allow 8472/udp # Flannel VXLAN
sudo ufw allow 4789/udp # Overlay network
sudo ufw allow 7946/tcp # Calico BGP / Swarm gossip
sudo ufw allow 7946/udp
# 설정 후 Pod 간 통신 재확인
kubectl exec -it pod-a -- curl http://pod-b-service
"Compose로 충분한가" — 팀이 Kubernetes로 전환하는 실제 기준점
스타트업 초기에는 EC2 한 대에 docker-compose로 운영합니다. 빠르고 단순합니다. 그러다 어느 순간 이런 상황이 옵니다: 새벽 3시에 서버가 멈추고, 자동 복구가 없어서 수동으로 SSH 접속해서 docker-compose up을 칩니다. 다음 날 오전 트래픽 피크 때 컨테이너를 더 늘려야 하는데 새 서버에 일일이 접속해서 설정합니다. 이 지점이 오케스트레이션을 고민해야 할 때입니다.
팀 규모별 현실적 판단:
1~3명 팀, 단일 서버:
→ docker-compose + systemd restart=always 로 충분
→ K8s 도입하면 운영 오버헤드가 더 큼
4~10명 팀, 서버 3대 이상:
→ Kubernetes 도입 고려 시작
→ EKS/GKE 같은 매니지드 서비스가 현실적
10명+ 팀, MSA:
→ K8s는 선택이 아닌 필수
→ Helm, ArgoCD, Istio 등 생태계 전체 활용
실무에서 K8s를 배워야 하는 이유: 대부분의 중소기업 이상 회사의 인프라는 이미 K8s로 전환됐거나 전환 중입니다. 채용 공고에서 "K8s 운영 경험"이 필수 요건으로 등장하는 비율이 빠르게 늘고 있습니다. Docker를 잘 안다면 K8s 진입 장벽은 생각보다 낮습니다 — 이 모듈에서 배운 개념 매핑이 그 다리 역할을 합니다.
다음 모듈에서는 Kubernetes 트랙에서 실제 클러스터 운영, Helm 패키지 관리, 그리고 서비스 메시(Istio)를 심층적으로 다룹니다.