Docker로 컨테이너를 다루는 데 어느 정도 익숙해진 뒤, 자연스럽게 드는 의문이 있었습니다.
컨테이너 하나 두 개는 docker run으로 충분한데, 이게 수십 개가 되면 어떻게 관리하지? 어떤 서버에 띄울지, 하나가 죽으면 누가 다시 살리는지, 트래픽이 몰리면 어떻게 늘리는지. 이런 문제들을 손으로 하나하나 해결하는 건 한계가 있다는 걸 느꼈고, 그래서 쿠버네티스(Kubernetes, 줄여서 k8s)를 본격적으로 공부하기 시작했습니다.
이 글에서는 쿠버네티스가 등장하게 된 배경과, 이 도구가 실제로 어떤 문제를 해결해주는지를 정리해보려고 합니다.
컨테이너는 어떻게 격리되는가
쿠버네티스 이야기로 들어가기 전에, 컨테이너가 왜 "격리된" 환경이라고 부르는지부터 짚고 가겠습니다.
컨테이너는 가상머신처럼 별도의 OS를 띄우는 게 아니라, 호스트 OS의 커널 하나를 공유하면서도 서로 영향을 주지 않는 프로세스입니다. 그게 어떻게 가능할까요?
리눅스는 두 가지 메커니즘으로 이 격리를 제공합니다.
| 매커니즘 | 역할 |
| cgroup (Control Group) | CPU, 메모리, 디스크 I/O 등 시스템 자원을 프로세스 단위로 분리해 할당하고 제한합니다 |
| namespace | PID, 네트워크, 마운트, UTS 등 실행 공간을 프로세스별로 격리해 관리합니다 |
cgroup이 자원을 나누고, namespace가 실행 공간(보이는 프로세스, 네트워크 등)을 나눈다고 생각하면 됩니다.
이 둘 덕분에 컨테이너 내부에서 실행되는 애플리케이션들은 서로 영향을 주지 않고 독립적으로 동작할 수 있습니다.
다만 이걸 직접 다루려면 파일 시스템 설정부터 자원 관리까지 복잡한 과정을 손수 처리해야 했습니다.
이 복잡한 과정을 명령어 몇 줄로 추상화해 누구나 쉽게 쓸 수 있게 만든 도구가 바로 Docker입니다.
컨테이너만으로는 부족했던 이유
Docker가 컨테이너 기술을 대중화한 건 맞지만, Docker 자체는 "하나의 호스트에서 컨테이너를 만들고 실행하는 도구"이기 때문에, 서비스가 커지면 문제는 금방 복잡해집니다.
예를 들어, 서버가 3대 있고 컨테이너가 20개라고 해봅시다. 어떤 컨테이너를 어느 서버에 올릴지 사람이 판단해야 하고, 서버 하나가 다운되면 그 위에 있던 컨테이너를 다른 서버로 수동으로 옮겨야 합니다. 트래픽이 갑자기 늘면 컨테이너를 더 띄워야 하는데, 그것도 사람이 모니터링하다가 직접 docker run을 치는 수밖에 없습니다.
여기에 한 가지 문제가 더 있습니다. 여러 사람이 수십 대의 서버를 관리하다 보면, 각자 패키지를 업데이트하거나 설정을 바꾸면서 서버마다 환경이 조금씩 달라지기 시작합니다. 이렇게 설정의 일관성이 깨진 서버를 SnowFlake 서버라고 부르는데(눈송이처럼 모양이 다 다르다는 의미), 시간이 지날수록 "왜 A 서버에선 되는데 B 서버에선 안 되지?" 같은 미스터리한 문제가 늘어납니다.
컨테이너로 모든 의존성을 이미지에 포함시키면 이 SnowFlake 문제는 상당 부분 해결됩니다. 하지만 서버 위에 띄운 컨테이너들 자체를 관리하는 문제는 여전히 남게 됩니다.
서버 3대에 컨테이너 20개면 어찌어찌 되겠지만, 실무에서는 수십~수백 대의 노드에 수천 개의 컨테이너가 돌아갑니다.
이걸 사람이 관리하는 건 사실상 불가능하고, 그래서 컨테이너 오케스트레이션(Container Orchestration) 이라는 개념이 나왔습니다.
여러 서버에 걸쳐 컨테이너의 배포, 스케일링, 네트워킹, 장애 복구를 자동화하는 시스템인 셈입니다.
컨테이너 인프라 환경의 3가지 구성 요소
컨테이너 기반 인프라를 운영한다고 하면 보통 세 가지 도구가 한 세트로 따라옵니다.
- 컨테이너 엔진
컨테이너의 빌드, 실행, 중지, 삭제 등 생명주기를 담당하는 소프트웨어입니다. 대표적으로 Docker, containerd, CRI-O가 있습니다.
참고로 쿠버네티스는 v1.20에서 Docker를 런타임으로 직접 사용하는 방식을 deprecated 처리했고, v1.24에서 완전히 제거했습니다. 지금은 대부분의 클러스터가 containerd를 씁니다. - 컨테이너 이미지
애플리케이션 실행에 필요한 라이브러리, 설정 파일, 실행 파일을 하나로 패키징한 것입니다.
어떤 환경에서 실행하더라도 동일한 결과를 보장하는 게 핵심이고, 레이어(Layer) 구조로 이루어져 있어서 변경된 부분만 새로 빌드하고 나머지는 캐시를 재사용합니다. - 오케스트레이션 도구
여러 컨테이너를 조정하고 관리하는 도구입니다.
Kubernetes, Docker Swarm, Apache Mesos 등이 있지만, 현재는 Kubernetes가 사실상 표준입니다.
이 글은 마지막 단계인 오케스트레이션, 그중에서도 쿠버네티스에 대한 이야기입니다.
쿠버네티스는 뭘 해결하는가
쿠버네티스는 Google이 내부에서 쓰던 Borg라는 컨테이너 관리 시스템의 경험을 바탕으로 만든 오픈소스 프로젝트입니다. 2014년에 공개됐고, 이름은 그리스어로 "조타수(키잡이)"를 의미합니다(로고의 방향타가 여기서 유래했습니다). 이후 CNCF(Cloud Native Computing Foundation)에 기증되면서 AWS, Azure, GCP 등 주요 클라우드 벤더가 모두 지원하는 표준으로 자리잡았습니다.
쿠버네티스가 해결하는 핵심 문제를 세 가지로 정리해봤습니다.
- 스케줄링
컨테이너를 어디에 띄울지 자동으로 결정합니다. 노드마다 남은 CPU, 메모리가 다를 텐데, 쿠버네티스가 각 노드의 리소스 상황을 보고 가장 적절한 곳에 컨테이너를 배치합니다.
사람이 "이 서버에 여유가 있으니까 여기에 올려야지" 하고 판단할 필요가 없는 거죠. - 셀프힐링
장애가 나면 알아서 복구합니다.. 컨테이너가 죽으면 자동으로 재시작하고, 노드 자체가 다운되면 그 위의 컨테이너들을 다른 노드로 옮깁니다. "3개의 Pod를 유지해라"라고 선언해두면 하나가 죽어도 쿠버네티스가 알아서 새 걸 하나 더 띄워줍니다.
이게 쿠버네티스의 핵심 철학인 선언적 관리(Desired State) 와 연결되는데, 잠시 뒤에 다시 다루겠습니다. - 오토스케일링
트래픽에 따라 컨테이너 수를 조절합니다.. CPU 사용률이 70%를 넘으면 Pod를 추가로 띄우고, 트래픽이 줄면 다시 줄입니다.
사람이 모니터링 화면을 쳐다보면서 kubectl scale을 칠 필요가 없어지게 됩니다.
이 세 가지만 놓고 봐도, 컨테이너를 대규모로 운영할 때 쿠버네티스가 왜 필요한지 느낌이 옵니다.
선언적 관리라는 철학
쿠버네티스를 공부하면서 가장 인상 깊었던 건 "선언적(Declarative)"이라는 접근 방식입니다.
Docker에서는 컨테이너를 직접 만들고, 멈추고, 지우는 명령형(Imperative) 방식이었습니다. docker run으로 띄우고, docker stop으로 멈추고, 뭔가 잘못되면 사람이 개입해서 고쳐야 했습니다.
쿠버네티스는 다릅니다. "나는 이 애플리케이션이 3개의 Pod로 돌아가길 원한다"라고 YAML 파일에 적어서 제출하면, 쿠버네티스가 현재 상태를 확인하고 원하는 상태에 맞추는 작업을 계속 반복합니다.
Pod가 2개밖에 없으면 하나를 더 만들고, 4개가 되어 있으면 하나를 지웁니다. 이 "원하는 상태를 선언하면 시스템이 맞춰간다"는 게 쿠버네티스의 핵심 동작 원리입니다.
처음에는 YAML 파일 작성이 번거롭게 느껴졌는데, 여러 사람들이 관리하는 프로덕션 환경에서 인프라를 코드로 관리(IaC)할 수 있는 큰 장점이 있는 것이 큰 장점인 것을 알게 되었습니다. Git에 넣어서 버전 관리도 되고, 팀원 간에 리뷰도 가능하기 때문에 운영 측면에서 특히 큰 장점이 되는 것 같습니다.
핵심 개념 빠르게 훑기
쿠버네티스를 공부하면서 다양한 용어들을 알아야 하기 때문에 복잡한데, 우선 아래의 다섯가지를 정리해보겠습니다.
각각의 상세한 내용은 이후 글에서 하나씩 정리할 예정입니다.
- 클러스터(Cluster)는 쿠버네티스가 관리하는 서버 묶음 전체를 말합니다.
크게 두 부분으로 나뉘는데, 클러스터의 두뇌 역할을 하는 컨트롤 플레인(Control Plane) 과 실제로 컨테이너가 돌아가는 워커 노드(Worker Node) 입니다. - Pod는 쿠버네티스에서 배포할 수 있는 가장 작은 단위입니다.
Docker에서는 컨테이너 하나가 기본 단위였다면, 쿠버네티스에서는 Pod가 그 역할을 합니다. 보통 하나의 Pod에 하나의 컨테이너가 들어가지만, 밀접하게 관련된 컨테이너를 같은 Pod에 넣을 수도 있습니다. 같은 Pod 안의 컨테이너들은 네트워크와 스토리지 볼륨을 공유합니다. - Deployment 는 "이 애플리케이션을 몇 개의 Pod로 실행해라"라고 선언하는 리소스입니다.
Pod를 직접 하나씩 만들지 않고 Deployment에 원하는 상태를 적어두면, 쿠버네티스가 그 상태를 유지해줍니다. 업데이트할 때도 Deployment를 수정하면 롤링 업데이트가 자동으로 진행됩니다. - Service 는 Pod들 앞에 놓이는 고정 주소입니다.
Pod는 생겼다 사라졌다 하면서 IP가 계속 바뀌는데, Service가 고정된 IP와 DNS를 제공해서 다른 Pod나 외부에서 안정적으로 접근할 수 있게 해줍니다. - Namespace 는 클러스터를 논리적으로 나누는 칸막이입니다.
같은 클러스터 안에서 팀별로, 환경별로(dev/staging/prod) 리소스를 격리할 수 있습니다.
쿠버네티스의 전체 구조
다음 글에서 각 컴포넌트를 자세히 다룰 건데, 여기서는 큰 그림만 잡아두겠습니다.

컨트롤 플레인(Control-plane)에는 클러스터 전체의 상태를 관리하고 의사결정을 내리는 컴포넌트들이 모여 있습니다.
모든 요청이 거치는 진입점인 API Server, 클러스터의 모든 상태를 저장하는 분산 키-값 데이터베이스 etcd, 새 Pod를 어느 노드에 배치할지 결정하는 Scheduler, 원하는 상태를 유지하도록 지속적으로 감시하는 Controller Manager가 여기에 속합니다.
워커 노드(Worker-Node)에는 실제 컨테이너를 실행하고 관리하는 컴포넌트들이 있습니다.
노드 위의 Pod를 관리하는 에이전트 kubelet, Service 기반의 네트워크 규칙을 관리하는 kube-proxy, 실제 컨테이너를 띄우는 Container Runtime(containerd 등)이 있습니다.
kubernetes에서 발생하는 모든 통신은 API 서버를 중심으로 이루어집니다.
사용자가 kubectl 명령을 치면 API 서버로 요청이 가고, API 서버가 etcd에 상태를 저장하고, 스케줄러와 컨트롤러가 그 상태를 보고 필요한 작업을 수행한다는 흐름으로 진행됩니다.
정리
쿠버네티스는 "컨테이너가 많아지면 사람이 관리할 수 없다"는 문제를 스케줄링, 셀프힐링, 오토스케일링으로 해결하는 도구입니다.
원하는 상태를 선언하면 시스템이 그 상태를 유지한다는 선언적 관리가 핵심 철학이고, 이 뒤의 글에서는 이 선언적 관리를 가능하게 하는 각 컴포넌트를 하나씩 정리해보려고 합니다.
'DevOps > kubernetes' 카테고리의 다른 글
| 6. ConfigMap과 Secret - 설정을 이미지에서 분리 (0) | 2026.06.13 |
|---|---|
| 5. Service와 Ingress - 네트워킹 정리 (0) | 2026.06.06 |
| 4. StatefulSet 부터 CronJob 까지 (0) | 2026.05.30 |
| 3. Pod에서 Deployment 까지 (0) | 2026.05.16 |
| 2. Kubernetes 클러스터 아키텍처 (0) | 2026.05.09 |