1. "야, 너두 쿠버네티스 써야지?"
개발 커뮤니티에 가면 다들 그럽니다. "요즘 누가 EC2에 직접 배포해? 쿠버네티스 써야지." "MSA(마이크로서비스) 하려면 k8s는 필수야."
저도 그 '힙(Hip)'한 흐름에 뒤처지기 싫었습니다. 그래서 제 소중한 토이 프로젝트(일일 방문자 10명, 가입자 100명)에 Kubernetes(EKS)를 도입하기로 결심했습니다. 그게 제 통장과 멘탈을 갉아먹을 줄은 꿈에도 모른 채.
1.5. 비유로 이해하는 쿠버네티스 (해운업)
도커(Docker)가 "컨테이너 박스"라면, 쿠버네티스는 "거대 자동화 항구"입니다.
- 컨테이너: 내 애플리케이션 (상자).
- 포드(Pod): 상자를 싣는 작은 배. (컨테이너 하나 혹은 여러 개를 묶음)
- 노드(Node): 배들이 정박하는 부두 (서버).
- 마스터 노드(Control Plane): 항구의 관제탑. "A부두가 꽉 찼으니 B부두로 보내!"라고 명령함.
- 서비스(Service): 배들이 서로 위치를 몰라도 물건을 주고받게 해주는 "무전기 채널".
2. 첫 번째 고지서 - 144,000원의 충격
한 달 뒤, AWS 청구서를 보고 눈을 의심했습니다.
서버는 제일 싼 t3.small 2대밖에 안 썼는데, 비용이 왜 이렇게 많이 나왔지?
범인은 EKS Control Plane이었습니다. 아마존이 쿠버네티스 마스터 노드를 관리해 주는 비용이 시간당 0.1달러, 한 달에 약 $72 (약 10만 원)였습니다. 여기에 워커 노드 비용, 로드 밸런서(ALB) 비용, NAT 게이트웨이 비용까지 합치니 배보다 배꼽이 더 컸습니다.
"고작 정적 웹사이트랑 API 서버 하나 돌리는데 월 15만 원?"
EC2 한 대에 Docker Compose로 띄웠으면 월 5천 원이면 될 일이었습니다. 쿠버네티스는 학습 비용이 높다. 소규모 서비스에 과하게 도입하면 오히려 비용이 크게 늘어날 수 있다. 확장성(Scalability)이라는 환상이 그 함정입니다.
3. YAML 지옥에 빠지다
돈보다 더 큰 문제는 생산성 저하였습니다. 코드 한 줄 고쳐서 배포하려면 수정해야 할 파일이 너무 많았습니다.
단순히 "웹 서버 하나 띄우기" 위해 제가 작성해야 했던 코드 양을 보세요.
1. Deployment.yaml (애플리케이션 정의)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
2. Service.yaml (내부 네트워크)
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: NodePort
3. Ingress.yaml (외부 노출 & 로드밸런싱)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
"아니, 그냥 포트 3000번 하나 열고 싶은데, 왜 파일이 3개나 필요하고 코드가 50줄이 넘지?"
Docker Compose라면 ports: - "3000:3000" 한 줄이면 끝날 일이었습니다.
kubectl apply를 치고 에러가 나면, 로그를 보기 위해 다섯 단계를 거쳐야 했습니다.
kubectl get pods(이름 찾기)kubectl logs my-app-xz8j9(로그 확인)- "어? 이 파드가 아니네?"
kubectl describe pod my-app-xz8j9(이벤트 확인: ImagePullBackOff? OOMKilled?)- 무한 반복...
개발 시간의 80%를 인프라 설정(YAML 작성)에 쓰고, 정작 비즈니스 로직(기능 개발)에는 20%밖에 못 쓰고 있었습니다. 주객전도(主客顚倒) 그 자체였습니다.
3.5. Pod vs Node: 가장 헷갈리는 개념
많은 분들이 묻습니다. "컨테이너면 컨테이너지 왜 Pod라는 걸 또 만들었나요?"
도커는 컨테이너 단위로 관리하지만, 쿠버네티스는 Pod 단위로 관리합니다.
왜냐하면 "단짝 친구들"이 있기 때문입니다.
예를 들어, 웹서버 컨테이너와 로그 수집기 컨테이너는 항상 붙어 다녀야 합니다.
이 둘을 한 Pod에 넣으면, localhost를 공유해서 서로 대화할 수 있습니다.
마치 한 방(Pod)을 쓰는 룸메이트처럼요.
반면 Node는 이 방들이 있는 건물(서버 컴퓨터)입니다.
4. 시나리오 - 오지 않은 트래픽 폭주
저는 "혹시 백종원 님이 우리 서비스를 언급하면 어떡하지?"라는 김칫국을 마시며 HPA (Horizontal Pod Autoscaler)까지 설정했습니다. CPU 사용량이 50%를 넘으면 자동으로 파드가 늘어나도록 말이죠.
하지만 현실은 잔혹했습니다.
- 동시 접속자: 최대 3명 (나, 내 친구, 구글 봇)
- CPU 사용량: 0.1%
HPA는 할 일이 없어서 멍하니 있었고, 저는 그 HPA를 감시하는 Metrics Server를 돌리기 위해 또 비용을 지불하고 있었습니다. 마치 "손님이 올지도 몰라"라며 손님 없는 식당에 알바생 10명을 대기시켜 놓은 꼴이었습니다.
설상가상(雪上加霜)으로, DB는 RDS(관계형 데이터베이스)를 썼는데, 웹 서버만 오토 스케일링이 되면 뭐합니까? DB가 병목이 오면 말짱 도루묵인데 말이죠. (DB 스케일링은 훨씬 어렵습니다.)
4.5. 숨겨진 비용 - NAT 게이트웨이
많은 초심자가 놓치는 부분이 바로 NAT Gateway입니다. EKS 워커 노드는 보통 Private Subnet에 둡니다. 보안 때문이죠. 그럼 이 노드들이 인터넷(외부)과 통신하려면 NAT Gateway를 거쳐야 하는데... 이게 시간당 $0.045 + 데이터 처리 비용($0.045/GB)입니다. 그냥 켜두기만 해도 월 4만 원이 나갑니다. 도커 이미지 땡겨오고(Pull), 외부 API 호출할 때마다 과금 미터기가 돌아갑니다. EC2 Public Subnet에 띄운 Docker는 이게 공짜인데 말이죠.
5. 숨겨진 복병들 - 네트워킹과 스토리지
YAML만 문제가 아니었습니다. 쿠버네티스의 러닝 커브는 절벽과도 같았습니다.
- 네트워킹의 복잡함: "파드끼리 통신하려면 Service가 필요하고, 외부에서 들어오려면 Ingress가 필요하고, 그 Ingress는 ALB Controller가 필요하고..." 도메인 하나 연결하는 데 3일을 썼습니다.
- 스토리지의 악몽 (PVC/PV): 도커에서는
-v /data:/data하면 끝날 것을, 여기서는 PersistentVolumeClaim을 만들고, StorageClass를 정의하고... 잘못하면 데이터가 날아갑니다. - 디버깅의 어려움: 로컬에서는 잘 돌아가는데 클러스터에만 올리면 안 됩니다. 왜? 네트워크 정책(NetworkPolicy) 때문일 수도 있고, 이미지 태그 문제일 수도 있고, 원인이 너무 많습니다.
결국 저는 "인프라 엔지니어"가 되어가고 있었습니다. "제품 개발자"가 아니라요.
5.2. Ingress와 로드밸런서의 배신
"그냥 Ingress 만들면 되는 거 아냐?"
천만의 말씀입니다. AWS에서는 AWS Load Balancer Controller라는 놈을 따로 설치해야 하고,
얘가 ALB(Application Load Balancer)를 만듭니다.
근데 이 ALB가 개당 월 2만 원입니다.
서비스(앱)가 5개라 Ingress 5개 만들면? LB 비용만 월 10만 원 추가입니다.
결국 "Ingress Controller(Nginx)"를 하나 띄워서 ALB 하나로 모든 트래픽을 받게 구성해야 하는데, 이 설정이 또 기가 막히게 복잡합니다.
5.5. 좀비처럼 부활하는 파드 (Self-Healing)
물론 좋은 점도 있습니다. 가장 큰 매력은 "자가 치유(Self-Healing)" 능력입니다.
"서버 3대 띄워줘"라고 선언(replicas: 3)해두면, 쿠버네티스는 무슨 일이 있어도 3대를 유지하려고 합니다.
심지어 제가 실수로(혹은 고의로) 파드를 kubectl delete로 죽여도, 0.1초 만에 새로운 파드를 띄웁니다.
밤에 서버 프로세스가 OOM(Out of Memory)으로 죽어도 개발자가 깰 필요가 없습니다. 쿠버네티스가 조용히 다시 살려놓으니까요.
(물론 DB가 터지면 깨야 합니다. 그건 쿠버네티스도 못 살립니다.)
6. 쿠버네티스가 필요한 순간은 언제인가?
물론 쿠버네티스는 위대한 도구입니다. 구글이 괜히 만든 게 아니죠. 하지만 "내 서비스"에는 아니었습니다.
제가 깨달은 "쿠버네티스를 써야 할 때"는 다음과 같습니다.
- 서버가 100대 이상일 때: 사람이 수동으로 관리할 수 없는 수준일 때.
- 진짜 MSA를 할 때: 서비스 간 통신이 복잡하고, 각 서비스가 독립적으로 배포/확장되어야 할 때.
- 전담 DevOps 팀이 있을 때: 개발자가 인프라까지 다 챙기기엔 k8s의 러닝 커브가 너무 높습니다.
제 서비스는?
- 서버 2대
- 모놀리식(Monolithic) 아키텍처
- 개발자: 나 혼자
Docker Compose 하나면 충분했습니다.
6.5. 그래도 써야 한다면? (꿀팁: k9s)
회사가 커져서 어쩔 수 없이 쿠버네티스를 써야 한다면, 제발 kubectl만 붙잡고 있지 마세요.
k9s라는 도구를 설치하세요.
터미널에서 텍스트 UI로 파드를 보고, 로그를 보고, 쉘에 접속할 수 있습니다.
이게 없었다면 저는 아마 퇴사했을 겁니다.
7. 다시 Docker Compose로
결국 저는 눈물을 머금고 EKS 클러스터를 삭제했습니다. (terraform destroy를 치는데 속이 다 시원하더군요.)
그리고 AWS Lightsail(월 $5) 서버 한 대를 샀습니다.
# docker-compose.yml
version: '3'
services:
app:
image: my-app:latest
ports:
- "3000:3000"
restart: always
db:
image: postgres:13
volumes:
- ./data:/var/lib/postgresql/data
이 10줄짜리 파일 하나로 모든 게 해결되었습니다.
배포? 그냥 docker-compose up -d 끝.
왜 Docker Compose만으로 충분했나?
많은 사람들이 "서비스 디스커버리(Service Discovery)" 때문에 쿠버네티스가 필요하다고 착각합니다.
하지만 Docker Compose도 내장 DNS를 가지고 있습니다.
app 컨테이너에서 postgres://db:5432라고만 쓰면, 자동으로 db 컨테이너의 IP로 연결됩니다.
CoreDNS도, Kube-Proxy도, 사이드카(Sidecar) 패턴도 필요 없습니다.
또한 쿠버네티스가 자랑하는 "무중단 배포"?
docker-compose up -d --scale app=2 같은 스크립트로 흉내 낼 수 있습니다.
그리고 솔직히, 100명 쓰는 서비스에서 배포하느라 밤 3시에 2초 정도 서버가 멈춘다고 해서 세상이 무너지지 않습니다.
아무도 죽지 않아요.
비용? 월 5달러. 스트레스? 0.
8. 마무리 - 이력서 주도 개발(RDD)을 멈춰라
우리는 종종 "기술의 멋짐"에 취해 "기술의 비용"을 간과합니다. 이걸 이력서 주도 개발 (Resume Driven Development)라고 하죠. "나 쿠버네티스 써봤어"라고 이력서에 한 줄 적고 싶어서, 필요 없는 복잡성을 시스템에 끌어들이는 겁니다.
엔지니어링의 핵심은 "적정 기술(Appropriate Technology)"을 찾는 것입니다. 소잡는 칼로 닭을 잡으려다가는, 닭은커녕 제 손만 베입니다.
지금 여러분의 서비스가 아직 '닭'이라면, 제발 과도(Docker)만 드세요. 소잡는 칼(Kubernetes)은 나중에 서비스가 '황소'가 되면 그때 꺼내도 늦지 않습니다.