**도커(Docker)**는 애플리케이션을 이미지로 패키징해 컨테이너로 실행하는 컨테이너 플랫폼/런타임
**쿠버네티스(Kubernetes, K8s)**는 여러 서버(노드)에 흩어진 컨테이너를 자동으로 배치·복구·확장·분산하는 컨테이너 오케스트레이션 플랫폼
둘은 경쟁 관계가 아니라 “컨테이너를 만드는 도구 vs 컨테이너 무리를 운영하는 도구”라는 역할 분담
L4 로드밸런서는 IP + TCP/UDP 포트만 보고 트래픽을 여러 서버로 나눠주는 전송 계층(4계층) 부하 분산 장치
쿠버네티스에서 L4가 구체화된 형태가 Service(ClusterIP/NodePort/LoadBalancer)
해당 개념이 필요한 이유
도커 이미지 하나를 “빠르게 올리는 것”까지는 도커로 되지만, 운영 환경에서는 컨테이너가 죽고, 트래픽이 몰리고, 여러 서버에 나눠 배치해야 하는 문제가 남음
이걸 사람이 수동으로 하면 새벽에 컨테이너 죽을 때 직접 재시작해야 하는 무중단·자동화의 한계
쿠버네티스는 “원하는 상태(desired state)“만 선언하면 알아서 맞춰주는 선언적 자동 운영을 제공
L4는 그 자동 운영 안에서 **“들어온 트래픽을 살아있는 컨테이너들에게 어떻게 나눠줄까”**를 담당
AS-IS (도커 단독 운영)
sequenceDiagram
autonumber
participant Dev as 운영자(사람)
participant S1 as 서버 A
participant S2 as 서버 B
Dev->>S1: docker run (수동 배치)
Dev->>S2: docker run (수동 배치)
Note over S1: 컨테이너 다운!
S1--xDev: 사람이 직접 알아채야 함
Dev->>S1: docker run (수동 재시작)
Note over Dev: 트래픽 분산·스케일·롤백도 전부 수동
TO-BE (쿠버네티스 선언적 운영)
sequenceDiagram
autonumber
participant Dev as 운영자(사람)
participant CP as Control Plane
participant N1 as Node A
participant N2 as Node B
Dev->>CP: "이 이미지로 Pod 3개 유지해줘" (선언)
CP->>N1: Pod 배치
CP->>N2: Pod 배치
Note over N1: Pod 다운!
N1--xCP: 컨트롤 플레인이 상태 감지
CP->>N2: 새 Pod 자동 생성 (self-healing)
Note over CP: 스케일·트래픽 분산·롤백도 자동
도커와 쿠버네티스는 경쟁 관계가 아니다 (역할 구분)
도커 = 컨테이너를 만들고(build) 실행(run) 하는 계층. 클라이언트 개발자가 이미 아는 docker build / docker run이 여기에 해당
쿠버네티스 = 이미 만들어진 컨테이너들을 여러 서버에 걸쳐 오케스트레이션(배치·복구·확장·분산) 하는 계층
비유: 도커가 “레고 블록(컨테이너)을 찍어내는 틀”이라면, 쿠버네티스는 “그 블록 수백 개를 어느 판에 꽂고, 부서지면 다시 꽂고, 손님 몰리면 더 꽂는 관리자”
쿠버네티스는 하드웨어가 아니라 컨테이너 레벨에서 동작하며, 애플리케이션이 이미 컨테이너화되어 있다고 전제함
”쿠버네티스가 도커를 버렸다”는 오해 정리
2020년 K8s v1.20에서 “Docker deprecated” 발표 → 가장 많이 오해받은 뉴스
사실은 “컨테이너 런타임으로서의 도커”만 제거된 것 (v1.24, 2022년 완전 제거)
핵심은 “도커”가 하나가 아니라 여러 겹의 스택이라는 점:
Docker (제품 전체)
├── CLI / 사람 친화적 UX 도구 ← 개발자가 쓰는 부분
├── containerd (고수준 런타임) ← 실제로 컨테이너를 굴리는 엔진
└── runc (저수준 OCI 런타임)
런타임 스택 용어 (CRI · containerd · runc · OCI)
컨테이너 런타임(container runtime): 노드에서 이미지를 pull하고 컨테이너를 실제로 띄우고 내리는 저수준 소프트웨어
CRI (Container Runtime Interface): kubelet(각 노드의 K8s 에이전트)이 런타임과 대화하기 위한 표준 gRPC API. 이 표준만 지키면 어떤 런타임이든 K8s에 꽂을 수 있음. 도커는 CRI가 생기기 전에 만들어져서 이 표준을 네이티브로 구현하지 않음
dockershim: 그래서 K8s가 도커를 쓰려고 사이에 끼워 넣던 어댑터(번역기). containerd에 닿기 위한 군더더기라서 제거됨
containerd: CRI를 구현하는 고수준 런타임. 이미지 관리와 컨테이너 생명주기를 담당. 도커 안에도 이미 들어있고 단독으로도 동작 → K8s가 이걸 직접 씀
runc: containerd가 실제 리눅스 커널 기능(namespace·cgroup)으로 컨테이너를 띄우는 저수준 OCI 런타임
OCI (Open Container Initiative): 이미지 포맷과 런타임의 표준 스펙. docker build가 만드는 이미지가 OCI 표준이라, containerd·CRI-O 등 어떤 런타임에서도 동일하게 실행
flowchart LR
subgraph 과거["과거 (dockershim 경유)"]
K1[kubelet] --> DS[dockershim] --> DD[Docker 데몬] --> C1[containerd] --> R1[runc]
end
subgraph 현재["현재 (CRI 직접 연결)"]
K2[kubelet] --> CRI[CRI 표준] --> C2[containerd] --> R2[runc]
end
클라이언트 개발자에게 중요한 결론: docker build로 만든 이미지는 OCI 표준이라 그대로 잘 돌아감. “도커 지원 중단”이 아니라 “노드에서 도커 데몬을 안 쓸 뿐”
쿠버네티스 핵심 구성요소
Node: 컨테이너가 실제로 도는 서버(물리/가상). 노드마다 kubelet이 컨테이너를, kube-proxy가 네트워크를 관리
Cluster: 노드들의 집합 전체
Control Plane: 클러스터의 두뇌. “원하는 상태”와 “현재 상태”를 계속 비교하며 맞춰줌(self-healing의 근원)
Pod — 최소 배포 단위
쿠버네티스가 다루는 가장 작은 단위. 컨테이너 1개(또는 밀접하게 붙어 다니는 여러 개)를 감싼 껍데기
같은 Pod 안의 컨테이너는 네트워크(IP)와 저장소를 공유
Pod는 일회용(cattle, not pet) — 언제든 죽고 새 IP로 다시 뜰 수 있음. 그래서 “Pod에 직접 붙기”가 아니라 앞단의 고정 진입점(Service)이 필요
Deployment — 원하는 상태를 선언하는 객체
“이 이미지로 Pod 3개를 항상 유지해줘” 같은 desired state를 선언하는 상위 객체
Pod를 직접 만들기보다 Deployment로 관리하는 게 일반적 — 롤링 업데이트/롤백/스케일을 자동 처리
내부적으로 ReplicaSet이 “Pod 개수 유지”를 담당하고, Deployment는 그 위에서 버전 전환을 관리
즉 앞의 TO-BE 다이어그램에서 “Pod 3개 유지해줘”가 바로 Deployment 선언
L4 로드밸런서란
로드밸런서 = 들어온 트래픽을 여러 서버로 나눠 부하를 분산하는 장치/소프트웨어
L4(4계층, 전송 계층) 로드밸런서는 IP 주소 + TCP/UDP 포트만 보고 분산 결정
패킷 안의 내용(HTTP 헤더, URL, 쿠키)은 들여다보지 않음 → 그래서 빠르고 단순
분산 방식은 라운드 로빈, 최소 연결(least busy) 등 단순 알고리즘 위주
구분
L4 로드밸런서
L7 로드밸런서
동작 계층
전송 계층 (TCP/UDP)
응용 계층 (HTTP/HTTPS)
판단 근거
IP + 포트
URL, 헤더, 쿠키 등 내용
특징
빠름·단순, 프로토콜 무관
느리지만 내용 기반 정교한 라우팅
예시
DB 접속, 내부 API, TCP 서비스
/api는 A로, /img는 B로 경로 라우팅
쿠버네티스에서 L4가 등장하는 곳 (Service)
Service는 L4(TCP/UDP)에서 동작하고, 경로 기반 라우팅이 필요한 **Ingress는 L7(HTTP)**에서 동작
Service 3가지 타입:
ClusterIP (기본): 클러스터 내부 전용 고정 IP. 외부 노출 없음
NodePort: 모든 노드의 특정 포트(30000~32767)로 외부 노출
LoadBalancer: 클라우드의 외부 L4 로드밸런서를 붙여 외부 IP 제공 (운영 표준)
kube-proxy — L4 분산이 실제로 일어나는 지점
kube-proxy는 각 노드에서 도는 네트워크 프록시. Service(ClusterIP)로 온 트래픽을 실제 살아있는 Pod로 전달하는 규칙을 커널에 설치
Pod가 죽고 새로 뜨면 EndpointSlice가 갱신되고, kube-proxy가 규칙을 다시 그려서 항상 살아있는 Pod로만 트래픽을 보냄
동작 모드 두 가지:
iptables 모드: 리눅스 방화벽 규칙(iptables)으로 ClusterIP → Pod 라우팅. 확률 기반으로 Pod 선택
IPVS 모드: 리눅스 커널에 내장된 L4 로드밸런서(IP Virtual Server) 사용. 규모가 커질수록 iptables보다 성능이 좋고 라운드로빈·최소연결 등 알고리즘 선택 가능
즉 “K8s에서 L4 로드밸런싱”의 실체는 상당 부분 kube-proxy가 커널에 심어 놓은 규칙