목차
1 장

쿠버네티스란

왜 컨테이너 오케스트레이터가 필요한가. Docker / docker-compose까지 써 본 독자를 출발선으로 두고, 단일 컨테이너 도구의 한계 다섯 가지, 선언형 desired state + reconcile loop 모델, control plane / worker node의 큰 그림, 책의 범위까지 정리합니다.

이 책의 시작 챕터입니다. 다음 2장부터 본격적으로 손에 잡히는 명령을 다룹니다. 본 챕터에서는 코드를 쓰기 전에 왜 쿠버네티스인지, 이 책이 다른 입문서와 어떻게 다른지, 그리고 1장부터 31장까지의 흐름을 정리합니다.

이어 나올 4부 EKS 실전, 5부 운영·디버깅·비용, 6부 풀스택 캡스톤까지도 모두 본 챕터의 마지막 절 “이 책이 다룰 범위” 안에 들어 있습니다. 그래서 본 챕터는 단순한 “1부 1장"을 넘는 길잡이 역할입니다. 천천히 따라와 주세요.

Docker까지 와 본 독자에게 #

이 책은 Docker / docker-compose까지 손에 익은 독자를 출발선으로 잡습니다. 단일 컨테이너를 빌드하고 띄우고 데이터를 보존하는 흐름이 익숙하시다면, 이번 챕터는 자연스럽게 다음 질문에서 시작합니다.

컨테이너 한 대는 띄울 줄 안다. 그렇다면 컨테이너 100대를, 그것도 죽으면 자동으로 다시 살리고 트래픽에 따라 늘렸다 줄였다 하면서 어떻게 운영할까?

이 질문에 대한 사실상 표준 해답이 **쿠버네티스 (Kubernetes, K8s)**입니다. 본 챕터는 그 질문이 왜 자연스러운지, 그리고 K8s가 어떤 모델로 그 질문에 답하는지를 정리합니다.

Docker만 알고 들어오신 분이라면 본 챕터를 다 읽은 뒤 부록 A docker-compose에서 k8s로를 먼저 펼쳐 보시면 도움이 됩니다. 부록 A는 docker-compose.yml의 각 키가 k8s의 어떤 리소스에 대응되는지를 매핑 표로 정리해 둔 챕터입니다. 본문이 진행되면서 자주 다시 보게 되실 겁니다.

docker run 만으로 부족해지는 순간 #

Docker 마지막 단계의 한 컨테이너 운영 명령은 이런 모양입니다.

한 컨테이너 — Docker 단계의 끝
docker run -d --name myapp \
  --restart unless-stopped \
  -p 127.0.0.1:8000:8000 \
  -v myapp-data:/app/data \
  ghcr.io/curtis/myapp:1.0.0

호스트 한 대에 컨테이너 하나, 또는 Compose로 같은 호스트에 web + db + cache를 묶는 정도까지는 이걸로 충분합니다. 그러나 트래픽이 늘고, 무중단 배포가 요구되고, 서버 한 대가 죽어도 서비스는 살아 있어야 하는 단계가 오면 같은 도구로는 해결되지 않는 문제들이 생깁니다.

1) 노드 한 대가 죽으면 서비스도 같이 죽습니다. docker run으로 띄운 컨테이너는 그 호스트에 묶여 있습니다. 호스트가 재부팅되거나 디스크가 망가지면, 컨테이너를 다른 머신으로 옮길 표준 방법이 없습니다. 사람이 새 머신에 SSH로 들어가 같은 명령을 다시 쳐야 합니다.

2) 컨테이너가 죽었을 때의 자동 복구가 약합니다. --restart unless-stopped는 같은 호스트 안에서만 통합니다. 호스트 자체가 살아 있다면 docker 데몬이 컨테이너를 다시 띄우지만, 호스트가 죽으면 거기까지입니다. 그리고 “프로세스는 살아 있는데 응답을 못 한다” 같은 부분 장애는 잡지 못합니다.

3) 인스턴스 수를 트래픽에 맞춰 조절하는 일이 전부 수동입니다. 평소 2대로 충분한 API 서버를 캠페인 시간 동안 10대로 늘리려면, 누군가 새 머신을 마련하고, 같은 이미지를 받아 같은 명령을 10번 실행한 다음, 앞단의 로드밸런서 설정까지 손으로 바꿔야 합니다. 끝나면 다시 줄입니다. 수작업이 많이 필요합니다.

4) 무중단 배포 (롤링 업데이트)를 직접 짜야 합니다. v1.0.0에서 v1.1.0으로 옮길 때, 10대 중 한 대씩 새 버전으로 교체하면서 헬스체크가 통과한 컨테이너로만 트래픽을 보내는 흐름은 손으로 짜기 까다롭습니다. 실패 시 자동 롤백까지 포함하면 더 복잡합니다.

5) 설정·비밀 값·서비스 디스커버리에 표준이 없습니다. DB 접속 정보를 환경변수로 줘야 하는데, 환경 (staging / prod) 마다 다른 값을 안전하게 주입하는 표준적인 방법이 docker 기본 도구에는 없습니다. 컨테이너 A가 컨테이너 B를 어떤 이름으로 찾을지 (서비스 디스커버리)도 직접 정해야 합니다.

이 다섯 가지가 한 호스트, 한 컨테이너 단위 도구의 한계입니다. 그리고 이 한계를 푸는 것이 컨테이너 오케스트레이터의 역할입니다.

컨테이너 오케스트레이터가 푸는 문제 #

위의 다섯 항목을 한 줄로 추상화하면 이렇습니다.

여러 머신을 한 덩어리 (클러스터)로 묶고, “어떤 모양으로 돌아야 하는가"만 선언하면, 시스템이 알아서 그 모양을 유지해 줍니다.

이를 부르는 이름은 선언형 desired state와 **reconcile loop (맞추기 루프)**입니다.

  • 선언형 (declarative) — “어떻게 만들지 (how)“가 아니라 **“무엇이어야 하는지 (what)”**만 적습니다. 예: “myapp 컨테이너 3개가 항상 떠 있어야 합니다.”
  • reconcile loop — 시스템이 끊임없이 현재 상태 (actual state)와 원하는 상태 (desired state)를 비교하고, 차이가 있으면 그 차이를 줄이는 방향으로 움직입니다. 컨테이너 한 개가 죽어 2개가 됐다면, 시스템이 알아서 한 개를 더 띄워 다시 3개로 맞춥니다.
reconcile loop의 그림
   ┌──────────────────┐         ┌──────────────────┐
   │  desired state   │         │   actual state   │
   │  "myapp 3 replicas"│ ◀───▶  │  현재 떠 있는 2개 │
   └──────────────────┘ compare └──────────────────┘
                  │                       │
                  └────────┬──────────────┘
                    ┌─────────────┐
                    │  controller │  → 차이 → "한 개 더 띄워라"
                    └─────────────┘

이 모델이 K8s의 핵심 발상입니다. 사람은 “이렇게 돼 있어야 한다"만 선언하고, 그 선언을 계속 지키는 일은 시스템이 합니다. 노드 하나가 죽어도, 새 버전을 점진적으로 굴려도, 트래픽 따라 5개를 8개로 늘려도 — 모두 같은 모델 안에서 풀립니다.

이 책에서는 4장 Deployment와 ReplicaSet에서 reconcile loop가 실제 리소스 위에서 어떻게 동작하는지 직접 보고, 18장 CRD와 Operator 패턴 에서 같은 모델을 사용자 정의 리소스로 확장하는 방법을 다룹니다.

쿠버네티스가 어디서 왔나 #

이 모델은 K8s가 처음 만든 것이 아닙니다. 구글이 사내에서 10년 이상 운영하던 Borg라는 클러스터 관리 시스템이 그 뿌리입니다. 구글의 검색·지메일·맵 같은 서비스는 Borg 위에서 운영되었고, 그 경험을 바탕으로 다음 세대 시스템인 Omega가 나왔습니다. 이후 그 발상을 오픈소스로 다시 구현한 것이 Kubernetes입니다.

대략의 흐름은 다음과 같습니다.

  • 2014년 6월 — 구글이 Kubernetes의 초기 버전을 오픈소스로 공개했습니다.
  • 2015년 7월 — Kubernetes 1.0이 출시되었고, 같은 시점에 **CNCF (Cloud Native Computing Foundation)**가 발족되며 K8s가 첫 프로젝트로 기증되었습니다.
  • 이후 — 주요 클라우드 사업자 (AWS EKS, GCP GKE, Azure AKS)가 모두 매니지드 K8s를 제공하면서 사실상 컨테이너 오케스트레이션의 표준이 되었습니다.

“Kubernetes"는 그리스어로 키잡이 (helmsman)를 뜻합니다. 약자 K8s는 K와 s 사이의 8글자를 줄여 쓴 것입니다. 두 표기는 자주 섞여 쓰이며, 같은 대상을 가리킵니다.

본 책은 집필 시점 기준 Kubernetes 1.32를 표준으로 둡니다. 일부 deprecated API와 신규 GA 기능이 등장할 때마다 해당 챕터에서 명시합니다.

K8s 클러스터의 큰 그림 #

K8s 클러스터는 두 종류의 노드로 구성됩니다 — control planeworker node.

K8s 클러스터 — 컴포넌트 한눈에
                ┌───────────────────────────────────┐
                │           Control Plane            │
                │                                    │
   kubectl ───▶ │   kube-apiserver  ◀──────┐         │
                │        │                 │         │
                │        ▼                 │         │
                │       etcd               │         │
                │                          │         │
                │   kube-scheduler  ───────┤         │
                │   kube-controller-manager ─┤       │
                │   cloud-controller-manager ┘       │
                └────────────┬───────────────────────┘
        ┌────────────────────┼────────────────────┐
        ▼                    ▼                    ▼
 ┌────────────────┐  ┌────────────────┐  ┌────────────────┐
 │  Worker Node 1 │  │  Worker Node 2 │  │  Worker Node 3 │
 │                │  │                │  │                │
 │   kubelet      │  │   kubelet      │  │   kubelet      │
 │   kube-proxy   │  │   kube-proxy   │  │   kube-proxy   │
 │   containerd   │  │   containerd   │  │   containerd   │
 │                │  │                │  │                │
 │   ┌──────┐     │  │   ┌──────┐     │  │   ┌──────┐     │
 │   │ Pod  │ ... │  │   │ Pod  │ ... │  │   │ Pod  │ ... │
 │   └──────┘     │  │   └──────┘     │  │   └──────┘     │
 └────────────────┘  └────────────────┘  └────────────────┘

Control plane — 클러스터의 뇌 #

control plane은 클러스터의 desired state를 받아 보관하고, 그 상태가 실제로 유지되도록 결정을 내리는 부분입니다.

  • kube-apiserver — 클러스터의 정문. kubectl을 비롯한 모든 도구는 이 API 서버에 REST로 요청을 보냅니다. 인증·인가·검증을 거친 뒤 결과를 etcd에 기록합니다.
  • etcd — 클러스터의 모든 상태 (어떤 Pod가 어디에 있어야 하는지, 어떤 Service가 정의돼 있는지 등)를 보관하는 분산 키-값 저장소. **클러스터의 단일 진실 공급원 (source of truth)**입니다.
  • kube-scheduler — 새로 만들어진 Pod에게 “어느 worker node에 갈지"를 정해 줍니다. 노드의 가용 자원, 라벨, 제약 조건을 보고 결정합니다.
  • kube-controller-manager — 여러 컨트롤러를 한 프로세스로 묶어 돌립니다. 위에서 본 reconcile loop를 실제로 수행하는 주체입니다. 예: ReplicaSet 컨트롤러는 “Pod 3개가 떠 있어야 한다"는 선언과 실제 떠 있는 수를 끊임없이 비교합니다.
  • cloud-controller-manager — 클라우드 (AWS / GCP / Azure 등) 위에 올라간 클러스터에서, 그 클라우드의 로드밸런서·디스크·노드 같은 자원을 K8s 리소스와 연결해 줍니다. 자체 데이터센터에 올린 클러스터에는 없을 수 있습니다.

Worker node — 일을 하는 곳 #

worker node는 실제로 컨테이너가 실행되는 머신입니다. 보통 노드 한 대에 다음 세 가지가 설치되어 있습니다.

  • kubelet — 노드의 에이전트. apiserver로부터 “이 노드에 이 Pod를 띄우라"는 지시를 받아, 컨테이너 런타임에 실제 실행을 요청합니다. 컨테이너의 헬스를 살피고 상태를 다시 apiserver에 보고하는 일도 합니다.
  • kube-proxy — 노드의 네트워크 규칙 (iptables / IPVS 등)을 관리해 Service (5장)의 가상 IP가 실제 Pod로 전달되도록 합니다.
  • 컨테이너 런타임 — 실제로 컨테이너를 실행하는 계층. 현재 표준은 containerd이고, CRI-O도 자주 쓰입니다. 과거에 K8s가 docker 데몬을 직접 쓰던 어댑터 (dockershim)는 Kubernetes 1.24 (2022) 부터 제거되었습니다. 그래서 “K8s가 docker를 쓴다"는 말은 더 이상 정확하지 않습니다. K8s는 OCI 호환 런타임을 쓰고, docker가 만든 이미지 (OCI 이미지)는 그대로 동작합니다.

흐름 한 번에 #

kubectl apply -f deployment.yaml을 친 순간 일어나는 일을 줄여 보면 이렇습니다.

  1. kubectl이 apiserver에 “이 Deployment가 있어야 한다"는 요청을 보냅니다.
  2. apiserver가 인증·검증 후 그 사실을 etcd에 기록합니다.
  3. controller-manager 안의 Deployment 컨트롤러가 “Pod 3개가 더 필요하다” 고 판단해 Pod 객체 3개를 만듭니다.
  4. scheduler가 그 Pod 각각에 노드를 할당합니다.
  5. 해당 노드의 kubelet이 그 정보를 보고 containerd에게 컨테이너 실행을 지시합니다.
  6. kubelet이 결과를 다시 apiserver에 보고하고, 사용자는 kubectl get pods로 그 상태를 확인할 수 있습니다.

전체가 apiserver를 거쳐 etcd에 적힌 desired state를, 각 컴포넌트가 자기 영역에서 reconcile 하는 한 가지 패턴으로 흐릅니다. 이 책에서는 3장 kubectl과 첫 Pod에서 위 흐름을 직접 손으로 따라가 봅니다.

핵심 리소스 한 표 #

K8s에서 사람이 다루는 단위는 **컨테이너가 아니라 리소스 (resource, object)**입니다. Pod, Deployment, Service 같은 이름들입니다. 다음 챕터부터 하나씩 깊게 다루지만, 미리 한 표로 모양을 잡아 두겠습니다.

리소스한 줄 정의다루는 챕터
Pod컨테이너 1개 또는 같이 동작하는 몇 개를 묶은 K8s의 최소 실행 단위. IP를 공유함3장
ReplicaSet“이 Pod 템플릿이 N 개 떠 있어야 한다"를 보장4장
DeploymentReplicaSet 위에서 롤링 업데이트·롤백을 다루는 상위 리소스. 사람이 가장 자주 만지는 단위4장
Service여러 Pod 앞단에 안정적인 가상 IP / DNS 이름을 붙여 주는 네트워크 추상화5장
ConfigMap설정값 (환경변수, 설정 파일)을 코드와 분리해 주입6장
Secret비밀 값 (비밀번호, 토큰, 인증서)을 분리해 주입. base64 인코딩 + 접근 제어6장
Namespace클러스터 내부의 논리 구획. 권한·리소스 한도·이름 충돌을 분리7장

이 일곱 가지를 손에 익히면 일상적으로 다루는 일의 상당 부분을 처리할 수 있습니다. 더 깊은 리소스 (StatefulSet, PV / PVC, Ingress, RBAC, CRD 등)는 2부~3부에서 이어 다룹니다.

Docker Compose와 무엇이 다른가 #

Docker까지 와 본 독자에게 가장 먼저 떠오르는 비교 대상은 Docker Compose입니다. Compose도 YAML 한 파일에 여러 컨테이너를 선언적으로 정의한다는 점에서 K8s와 닮았기 때문입니다. 그러나 둘은 푸는 문제의 범위가 다릅니다.

Docker ComposeKubernetes
다루는 범위한 호스트 안의 멀티 컨테이너여러 노드 (클러스터) 위의 컨테이너 묶음
자가 복구같은 호스트에서 재시작 정도노드가 죽어도 다른 노드로 자동 이전
스케일링--scale로 수동desired state로 선언 → 자동 유지, 오토스케일링 가능
무중단 배포직접 짜야 함Deployment의 롤링 업데이트가 기본
설정 / 비밀.env, secrets 정도ConfigMap / Secret이 1급 리소스
적합한 곳로컬 개발, 단일 서버 운영여러 머신 위 운영 / 본격 SLA 환경

Compose는 한 호스트의 개발·운영을 단순하게 만드는 도구이고, K8s는 그 한계를 넘어서는 운영 환경에서 사용됩니다. 둘은 경쟁 관계라기보다 서로 다른 단계의 문제를 풉니다. 로컬 개발은 여전히 Compose가 가볍고, 본 운영은 K8s 또는 그 매니지드 서비스가 자연스럽습니다.

이 표의 각 행이 k8s 리소스로 어떻게 옮겨지는지에 대한 자세한 매핑은 부록 A docker-compose에서 k8s로에 정리돼 있습니다. Docker / docker-compose의 멘탈 모델에 익숙하신 분은 본문을 읽으시면서 부록 A를 곁에 두시면, 매 챕터의 새 리소스가 Compose의 어느 키에 대응되는지 빠르게 잡으실 수 있습니다.

이 책이 다룰 범위 #

K8s는 넓은 주제입니다. 본 책의 32장에서 어디까지 다룰지 미리 정해 두겠습니다.

다루는 내용챕터
1부쿠버네티스 시작 — Pod · Deployment · Service · ConfigMap / Secret · Namespace1~7
2부워크로드와 운영 — StatefulSet · PV / PVC · Ingress · resources · 헬스체크 · 오토스케일링 · RBAC8~14
3부깊이 — CNI · RBAC 깊이 · Admission Controller · CRD / Operator · 옵저버빌리티 · GitOps15~20
4부EKS 실전 — 클러스터 셋업 · 앱 배포 골격 · RDS 연동 · CI / CD · 모니터링 · 운영 체크리스트21~26
5부운영 · 디버깅 · 비용 — kubectl 디버깅 · 비용 최적화 · 시크릿 운영 · 업그레이드 전략27~30
6부종합 실습 — 풀스택 앱 EKS 배포 (Next.js + FastAPI + RDS + GitOps + 옵저버빌리티)31
부록docker-compose에서 k8s로 — 매핑 표 + 흔히 막히는 7가지 차이A

이 책을 모두 마치고 나면 다음이 손에 잡힙니다.

  • Pod / Deployment / Service / ConfigMap / Secret / Namespace로 작은 앱을 K8s 방식으로 배포할 수 있습니다.
  • StatefulSet · PV / PVC · Ingress · 헬스체크 · 오토스케일링 · RBAC까지 워크로드 운영의 한 사이클을 익힐 수 있습니다.
  • Prometheus / Grafana / Loki / OpenTelemetry로 옵저버빌리티를, ArgoCD / Flux로 GitOps를 도입할 수 있습니다.
  • AWS EKS 위에 production 워크로드 한 사이클 (클러스터 셋업 · 앱 배포 · DB 연동 · CI / CD · 모니터링 · 운영 체크리스트)을 운영할 수 있습니다.
  • CrashLoopBackOff · OOMKilled · ImagePullBackOff · Pending 같은 흔한 장애를 진단할 수 있습니다.
  • Karpenter + Spot + bin packing으로 클러스터 비용을 최적화할 수 있습니다.
  • 시크릿을 sealed-secrets · external-secrets · IRSA 패턴으로 운영할 수 있습니다.
  • 컨트롤 플레인과 노드 업그레이드 사이클을 안전하게 따라갈 수 있습니다.
  • modern-react의 Next.js 앱과 modern-python의 FastAPI 앱을 한 EKS 클러스터에 배포할 수 있습니다.

다루지 않는 부분은 다음과 같이 정리해 둡니다.

주제자리
Helm 차트 작성법 깊이 · ArgoCD ApplicationSet 패턴 · Cilium eBPF 데이터플레인 깊이후속 K8s 심화 책
멀티 클러스터 페더레이션 · 서비스 메시 (Istio / Linkerd)후속 K8s 심화 책
EKS 외 다른 매니지드 (GKE / AKS) 별 차이별도 부록 또는 별책
쿠버네티스 컨트리뷰션 · kubelet 내부 · etcd 운영 깊이별도 책

본 책은 “쓰는 법” 까지의 범위입니다. “만드는 법"의 영역은 후속 K8s 심화 책에서 다룹니다.

연습문제 #

  1. 본인이 현재 운영하거나 알고 있는 Docker / docker-compose 환경에서 §"docker run 만으로 부족해지는 순간"의 다섯 한계 중 어느 항목이 가장 먼저 부딪힐 가능성이 높은지 한 단락으로 적어 두세요. 책을 다 읽은 뒤 다시 봤을 때, 그 항목이 K8s의 어느 리소스나 패턴으로 해소되는지를 확인하는 데 씁니다.
  2. kubectl apply -f deployment.yaml 흐름의 6단계를 보지 않고 적어 보세요. 막히는 단계가 있다면, control plane 5개 컴포넌트 (apiserver · etcd · scheduler · controller-manager · cloud-controller-manager) 중 누가 그 단계를 담당하는지를 함께 메모해 두세요. 4장 Deployment와 ReplicaSet에서 같은 흐름을 직접 손으로 따라가게 됩니다.
  3. 본인이 이미 알고 있는 docker-compose 구성 (작은 web + db 같은) 하나를 고르고, 본 챕터 §“Docker Compose와 무엇이 다른가” 표의 각 행이 그 구성에 어떻게 적용될지 한 줄씩 메모해 두세요. 부록 A docker-compose에서 k8s로를 읽으실 때 출발점이 됩니다.

한 줄 요약: 쿠버네티스는 여러 머신을 한 클러스터로 묶고 desired state + reconcile loop 모델로 컨테이너를 운영하는 사실상 표준 도구다. 클러스터는 control plane + worker node로 나뉘고, 사람이 다루는 단위는 Pod / Deployment / Service / ConfigMap / Secret / Namespace 같은 리소스다. Docker Compose는 한 호스트, 쿠버네티스는 여러 노드를 다룬다.

다음 챕터 #

다음 2장 로컬 환경에서는 minikube / kind / Docker Desktop k8s 세 가지 로컬 클러스터 옵션 중 어느 것을 어떤 경우에 고를지, kubectl 설치와 첫 컨텍스트 전환, 그리고 kubectl get nodes로 첫 클러스터를 확인하는 절차까지 다룹니다. 로컬 클러스터를 준비한 뒤 3장 kubectl과 첫 Pod로 넘어가겠습니다.

X