쿠버네티스 소개
- 1.1 쿠버네티스 같은 시스템이 왜 필요한가
- 1.2 컨테이너 기술 소개
- 1.3 쿠버네티스 소개
쿠버네티스라는 도구가 왜 이렇게 생겼는지를 이해하려면, YAML이나 kubectl부터 들이밀면 안 돼. Marko Lukša가 책의 1장을 도입부로 잡은 이유도 그거야 — "요즘 앱은 왜 예전처럼 배포하면 안 되나" 부터 풀어줘야 그 다음이 자연스럽게 이어지거든. 모놀리스에서 마이크로서비스로, VM에서 컨테이너로, 수동 배포에서 오케스트레이션으로 넘어온 흐름이 이 장의 전부야.
옛날 앱은 거대한 모놀리스 하나였어. 프로세스 한 개, 서버 몇 대, 운영팀이 손으로 배포. 이 모델은 앱이 작을 땐 멀쩡한데, 커지기 시작하면 한 사람이 전체를 머릿속에 담을 수가 없게 돼. 작은 변경 하나에도 전체를 재배포해야 하는 것도 점점 부담이 되고. 그래서 사람들이 앱을 잘게 쪼개서 마이크로서비스로 가기 시작했지. 각 서비스는 독립적으로 배포되고, 어떤 건 Python으로 어떤 건 Go로 짜도 상관없게. 근데 이게 공짜가 아니야. 서비스가 10개에서 100개로 늘어나면 배포·설정·모니터링이 조합 폭발을 일으키거든. A 서비스는 Python 2.7이 필요한데 B는 3.9가 필요한데 같은 머신에 같이 돌려야 한다든가, 누가 누구한테 의존하는지 추적이 안 된다든가, 어느 서버가 한가한지 사람이 매번 판단해야 한다든가. 동시에 업계는 DevOps, 더 나아가 NoOps 쪽으로 가고 있어서, 개발자가 배포까지 책임지고 인프라 팀은 플랫폼만 제공하는 모델이 표준이 돼가고 있었어. 이게 작동하려면 개발자가 "노드 3번에 띄워주세요" 같은 말을 안 해도 배포가 돼야 해. 바로 이 지점에서 누군가 오케스트레이터가 필요하다고 결론 내리게 되는 거지.
오케스트레이션을 하려면 우선 격리 단위가 필요해. 예전엔 VM이 그 역할을 했지. 앱마다 VM을 하나씩 주면 환경이 깔끔히 분리되니까. 근데 VM은 무거워. 각각이 게스트 OS와 커널을 통째로 들고 있으니까, 마이크로서비스 수십 개에 VM을 하나씩 배정하면 리소스가 금방 바닥나. 그래서 리눅스 컨테이너가 등장했어. 컨테이너는 호스트 커널을 그대로 공유하면서도 안에서 도는 프로세스에게는 "내가 이 머신의 유일한 프로세스"인 것처럼 보이게 만들어. 비결은 두 가지 커널 기능이야. 첫째는 Linux Namespaces — 프로세스가 보는 파일시스템·PID·네트워크·호스트명 같은 걸 격리해주는 메커니즘이야. mnt, pid, net, ipc, uts, user 여섯 종류가 있고, 컨테이너는 자기만의 네트워크 네임스페이스를 가져서 자기 인터페이스만 보게 돼. 둘째는 cgroups — 프로세스가 쓸 수 있는 CPU, 메모리, 대역폭의 상한을 거는 거야. 이 둘을 합치면 VM 같은 격리감을 주면서도 부팅할 OS가 없어서 (호스트 커널 그대로 쓰니까) 즉시 실행되고 훨씬 가벼워.
여기서 Docker의 역할을 정리할 필요가 있어. Docker는 컨테이너를 발명한 게 아니야. Docker가 한 일은 컨테이너를 이식 가능한 패키지로 만든 거야. 세 가지 개념으로 풀리거든. 이미지는 앱과 환경(라이브러리, 파일시스템)을 통째로 묶은 것이고, 레이어로 구성돼서 공통 베이스를 재사용하니까 분산 효율이 좋고 디스크도 덜 먹어. 레지스트리는 이미지를 저장하고 공유하는 저장소(Docker Hub, Harbor 같은 거). 컨테이너는 그 이미지에서 만들어진 실행 인스턴스. 한 가지 주의할 게, 컨테이너가 호스트 커널을 공유한다는 건 곧 커널과 아키텍처 호환성 제약을 받는다는 뜻이야. x86용으로 빌드한 이미지는 ARM에서 못 돌아. VM처럼 완벽하게 추상화된 게 아니라는 거지. 책은 Docker를 주로 쓰지만 rkt 같은 다른 런타임도 언급하는데, 이건 OCI 표준이 생기면서 런타임이 교체 가능해졌고 쿠버네티스의 본질이 "Docker 오케스트레이터"가 아니라 "컨테이너를 여러 노드에 걸쳐 돌리는 시스템"이라는 점을 강조하기 위해서야.
이제 쿠버네티스 차례. 쿠버네티스의 뿌리는 구글 내부 시스템인 Borg와 Omega야. 구글은 수십만 대 서버에서 수백만 개 컨테이너를 굴리는 회사라 사람 손으로 관리할 방법이 없었거든. 10년 동안 내부에서 쓰던 노하우를 2014년에 오픈소스로 공개한 게 쿠버네티스야. 핵심 아이디어는 한 줄로 표현할 수 있어 — 데이터센터 전체를 하나의 거대한 컴퓨터처럼 추상화한다. 개발자는 "노드 3번에 배포해주세요"가 아니라 "이 앱이 다섯 개 떠 있어야 해" 라고 선언만 하면 돼. 어느 노드에 뜨는지는 쿠버네티스가 알아서 결정하고. 클러스터는 두 종류 노드로 구성돼. **마스터 노드(Control Plane)**는 클러스터를 제어하는 두뇌야. 여기엔 모든 컴포넌트가 통신하는 중앙 허브인 API Server, 각 파드를 어느 워커 노드에 배치할지 결정하는 Scheduler, 복제와 노드 장애 처리 같은 클러스터 수준 작업을 담당하는 Controller Manager, 그리고 클러스터 전체 상태를 저장하는 분산 KV 스토어이자 단일 진실의 원천인 etcd가 들어가. 워커 노드는 실제 앱 컨테이너가 도는 곳이고, 컨테이너를 실제로 돌리는 Container Runtime, API 서버와 대화하면서 자기 노드의 컨테이너를 관리하는 Kubelet, 그리고 서비스 트래픽을 로드밸런싱하는 kube-proxy가 들어가지.
앱을 돌리는 흐름은 단순해. 앱을 컨테이너 이미지로 빌드해서 레지스트리에 푸시하고, YAML 디스크립터로 "이 이미지를 N개 띄워줘, 서로 이렇게 묶어줘"를 선언해. 그러면 API 서버가 받아서 etcd에 저장하고, 스케줄러가 노드 배치를 결정하고, 해당 노드의 kubelet이 Container Runtime에게 이미지를 받아서 실행하라고 지시해. 이 모델에서 가장 중요한 단어는 선언적이라는 거야. "5개 떠 있어야 해" 라고 한 번 말해두면, 그 중 하나가 죽어도 쿠버네티스가 현재 상태를 원하는 상태와 계속 맞춰. 노드 자체가 죽으면 다른 노드로 옮겨서 재생성하고. 이게 사람들이 말하는 self-healing이지. 그래서 쿠버네티스가 결과적으로 주는 게 뭐냐면 — 개발자가 노드/서버를 몰라도 배포할 수 있는 단순함, 빈 공간을 사람보다 잘 찾아 쑤셔넣는 리소스 활용도, 새벽 3시에 안 일어나도 되는 자가 치유, CPU/메모리/커스텀 메트릭 기반 오토스케일링(클라우드면 클러스터 크기까지), 파드가 옮겨다녀도 안정적인 IP/DNS로 접근 가능한 서비스 디스커버리, 그리고 새 버전이 이상하면 롤아웃을 멈추거나 롤백할 수 있는 신뢰성 있는 배포야. 결국 CD 속도가 빨라지는 거지.
정리
1장 읽고 기억할 거 세 가지:
- 쿠버네티스는 데이터센터를 하나의 거대한 컴퓨터처럼 보이게 하는 추상화 레이어다. 개발자는 "어디"가 아니라 "무엇을 얼마나" 원하는지만 선언하고, 배치·재시작·스케일링은 시스템이 알아서 한다.
- 컨테이너 격리는 마법이 아니라 Linux Namespaces + cgroups라는 커널 기능의 조합이다. 그래서 가볍지만 커널·아키텍처 호환성 제약이 따라붙는다. Docker는 이 격리를 "이식 가능한 이미지"로 포장해서 대중화했을 뿐이다.
- 쿠버네티스 아키텍처는 **Control Plane(API Server, Scheduler, Controller Manager, etcd) + 워커 노드(kubelet, kube-proxy, 컨테이너 런타임)**의 두 층이다. 모든 상태는 etcd에 있고 모든 통신은 API Server를 거친다. 이 구조가 선언적 관리와 self-healing을 가능하게 한다.