"내 컴퓨터에서는 잘 되는데, 서버에서는 왜 안 되지?"라는 개발자들의 오랜 악몽은 docker의 등장으로 해결된 듯 보였습니다. 하지만 트래픽이 폭주하던 어느 날, 수동으로 띄운 수십 개의 컨테이너 중 하나가 메모리 누수로 조용히 죽어버렸을 때 우리는 깨달았습니다. 단순히 '실행(Run)'하는 것과 '운영(Operate)'하는 것은 완전히 다른 차원의 문제라는 것을 말이죠. 이 글은 망치(Docker)만 들고 집을 지으려다 실패한 경험을 바탕으로, 언제 전동 드릴(Kubernetes)을 꺼내 들어야 하는지에 대한 기술적 회고록입니다.
Deep Dive: 컨테이너 런타임 vs 오케스트레이션
많은 주니어 엔지니어들이 Kubernetes(이하 k8s)를 Docker의 대체재로 오해하곤 합니다. 하지만 기술 스택의 관점에서 볼 때, 이 둘은 경쟁 관계가 아닌 상호 의존적 관계에 가깝습니다.
Docker는 '프로세스 격리'에 집중합니다. 리눅스 커널의 cgroups와 namespaces를 활용해 애플리케이션을 독립된 환경에 가두는 역할을 완벽하게 수행합니다. 하지만 프로덕션 환경에서는 다음과 같은 문제에 직면하게 됩니다.
- 스케일링의 고통: 트래픽 급증 시
docker run명령어를 수동으로 입력하거나 스크립트로 제어하는 것은 한계가 명확합니다. - 자가 치유(Self-healing) 부재: 컨테이너가 OOM(Out of Memory)으로 종료되었을 때, 이를 감지하고 재시작해 줄 관리자가 없습니다.
- 서비스 디스커버리: 수십 개의 컨테이너가 동적으로 생성되고 삭제될 때, 서로의 IP를 어떻게 추적할 것인가요?
docker-compose로 운영 중인 서비스는 호스트 머신 자체의 장애(SPOF)에 매우 취약합니다. k8s는 이러한 노드 장애 시 자동으로 파드(Pod)를 다른 노드로 스케줄링하여 고가용성을 보장합니다.
The Solution: 선언적 인프라로의 전환
문제의 핵심은 '명령형(Imperative)' 관리 방식에서 '선언적(Declarative)' 관리 방식으로의 전환입니다. 우리는 시스템에게 "컨테이너를 하나 더 띄워"라고 명령하는 대신, "항상 컨테이너 3개가 유지되어야 해"라고 선언해야 합니다. 이것이 바로 k8s가 제공하는 핵심 가치입니다.
아래는 기존의 Docker Compose 방식과 이를 Kubernetes 매니페스트로 변환했을 때의 차이를 보여줍니다. 특히 livenessProbe와 resources 설정에 주목하십시오. 이는 단순 실행을 넘어선 '운영 안정성'을 위한 필수 설정입니다.
# [Before] docker-compose.yml (단순 실행 정의)
version: '3.8'
services:
web:
image: my-app:v1
ports:
- "8080:80"
restart: always # 단순 재시작 정책만 존재
---
# [After] kubernetes-deployment.yaml (상태 유지 및 헬스 체크)
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 3 # [핵심] 항상 3개의 복제본 유지 선언
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: my-app:v1
ports:
- containerPort: 80
# [핵심] 애플리케이션이 실제로 응답 가능한지 확인
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 3
periodSeconds: 3
# [핵심] 리소스 격리 (Noisy Neighbor 방지)
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
기술 스펙 비교 분석
실무 도입 전 반드시 확인해야 할 두 기술의 기능적 차이입니다.
| 기능 | Docker (Standalone) | Kubernetes (K8s) |
|---|---|---|
| 주요 역할 | 단일 컨테이너 패키징 및 실행 | 다수 컨테이너의 배포, 확장, 관리 |
| 스케일링 | 수동 (Manual) | 자동 (HPA - Horizontal Pod Autoscaler) |
| 헬스 체크 | 프로세스 상태(Up/Down)만 확인 | 애플리케이션 로직 레벨(Liveness/Readiness) 확인 |
| 네트워킹 | 단일 호스트 브리지 네트워크 | 클러스터 전역 플랫 네트워크 (CNI) |
Conclusion
작은 규모의 프로젝트나 초기 MVP 단계에서 Kubernetes를 도입하는 것은 과도한 엔지니어링(Over-engineering)이 될 수 있습니다. Docker와 Docker Compose만으로도 개발 생산성은 충분히 확보됩니다. 하지만 서비스가 성장하여 무중단 배포(Zero-downtime Deployment), 오토 스케일링, 그리고 정교한 리소스 관리가 필수적인 단계에 도달했다면, 그때가 바로 쿠버네티스로 전환해야 할 시점입니다. 도구의 화려함보다는 현재 인프라가 겪고 있는 고통(Pain Point)을 해결하는 데 집중하십시오.
Post a Comment