
회원 100명 서비스에 쿠버네티스를 도입하고 후회한 썰
'요즘 힙한 기술이니까', '확장성이 좋으니까' 무작정 쿠버네티스(k8s)를 도입했다가 인프라 비용 폭탄과 YAML 지옥을 맛본 경험담입니다. 오버엔지니어링의 위험성과, 언제 쿠버네티스를 써야 하는지에 대한 솔직한 회고를 담았습니다.

'요즘 힙한 기술이니까', '확장성이 좋으니까' 무작정 쿠버네티스(k8s)를 도입했다가 인프라 비용 폭탄과 YAML 지옥을 맛본 경험담입니다. 오버엔지니어링의 위험성과, 언제 쿠버네티스를 써야 하는지에 대한 솔직한 회고를 담았습니다.
서버를 끄지 않고 배포하는 법. 롤링, 카나리, 블루-그린의 차이점과 실제 구축 전략. DB 마이그레이션의 난제(팽창-수축 패턴)와 AWS CodeDeploy 활용법까지 심층 분석합니다.

새벽엔 낭비하고 점심엔 터지는 서버 문제 해결기. '택시 배차'와 '피자 배달' 비유로 알아보는 오토 스케일링과 서버리스의 차이, 그리고 Spot Instance를 활용한 비용 절감 꿀팁.

내 서버가 해킹당하지 않는 이유. 포트와 IP를 검사하는 '패킷 필터링'부터 AWS Security Group까지, 방화벽의 진화 과정.

왜 넷플릭스는 멀쩡한 서버를 랜덤하게 꺼버릴까요? 시스템의 약점을 찾기 위해 고의로 장애를 주입하는 카오스 엔지니어링의 철학과 실천 방법(GameDay)을 소개합니다.

개발 커뮤니티에 가면 다들 그럽니다. "요즘 누가 EC2에 직접 배포해? 쿠버네티스 써야지." "MSA(마이크로서비스) 하려면 k8s는 필수야."
저도 그 '힙(Hip)'한 흐름에 뒤처지기 싫었습니다. 그래서 제 소중한 토이 프로젝트(일일 방문자 10명, 가입자 100명)에 Kubernetes(EKS)를 도입하기로 결심했습니다. 그게 제 통장과 멘탈을 갉아먹을 줄은 꿈에도 모른 채.
도커(Docker)가 "컨테이너 박스"라면, 쿠버네티스는 "거대 자동화 항구"입니다.
한 달 뒤, AWS 청구서를 보고 눈을 의심했습니다.
서버는 제일 싼 t3.small 2대밖에 안 썼는데, 비용이 왜 이렇게 많이 나왔지?
범인은 EKS Control Plane이었습니다. 아마존이 쿠버네티스 마스터 노드를 관리해 주는 비용이 시간당 0.1달러, 한 달에 약 $72 (약 10만 원)였습니다. 여기에 워커 노드 비용, 로드 밸런서(ALB) 비용, NAT 게이트웨이 비용까지 합치니 배보다 배꼽이 더 컸습니다.
"고작 정적 웹사이트랑 API 서버 하나 돌리는데 월 15만 원?"
EC2 한 대에 Docker Compose로 띄웠으면 월 5천 원이면 될 일이었습니다. 쿠버네티스는 학습 비용이 높다. 소규모 서비스에 과하게 도입하면 오히려 비용이 크게 늘어날 수 있다. 확장성(Scalability)이라는 환상이 그 함정입니다.
돈보다 더 큰 문제는 생산성 저하였습니다. 코드 한 줄 고쳐서 배포하려면 수정해야 할 파일이 너무 많았습니다.
단순히 "웹 서버 하나 띄우기" 위해 제가 작성해야 했던 코드 양을 보세요.
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%밖에 못 쓰고 있었습니다. 주객전도(主客顚倒) 그 자체였습니다.
많은 분들이 묻습니다. "컨테이너면 컨테이너지 왜 Pod라는 걸 또 만들었나요?"
도커는 컨테이너 단위로 관리하지만, 쿠버네티스는 Pod 단위로 관리합니다.
왜냐하면 "단짝 친구들"이 있기 때문입니다.
예를 들어, 웹서버 컨테이너와 로그 수집기 컨테이너는 항상 붙어 다녀야 합니다.
이 둘을 한 Pod에 넣으면, localhost를 공유해서 서로 대화할 수 있습니다.
마치 한 방(Pod)을 쓰는 룸메이트처럼요.
반면 Node는 이 방들이 있는 건물(서버 컴퓨터)입니다.
저는 "혹시 백종원 님이 우리 서비스를 언급하면 어떡하지?"라는 김칫국을 마시며 HPA (Horizontal Pod Autoscaler)까지 설정했습니다. CPU 사용량이 50%를 넘으면 자동으로 파드가 늘어나도록 말이죠.
하지만 현실은 잔혹했습니다.
HPA는 할 일이 없어서 멍하니 있었고, 저는 그 HPA를 감시하는 Metrics Server를 돌리기 위해 또 비용을 지불하고 있었습니다. 마치 "손님이 올지도 몰라"라며 손님 없는 식당에 알바생 10명을 대기시켜 놓은 꼴이었습니다.
설상가상(雪上加霜)으로, DB는 RDS(관계형 데이터베이스)를 썼는데, 웹 서버만 오토 스케일링이 되면 뭐합니까? DB가 병목이 오면 말짱 도루묵인데 말이죠. (DB 스케일링은 훨씬 어렵습니다.)
많은 초심자가 놓치는 부분이 바로 NAT Gateway입니다. EKS 워커 노드는 보통 Private Subnet에 둡니다. 보안 때문이죠. 그럼 이 노드들이 인터넷(외부)과 통신하려면 NAT Gateway를 거쳐야 하는데... 이게 시간당 $0.045 + 데이터 처리 비용($0.045/GB)입니다. 그냥 켜두기만 해도 월 4만 원이 나갑니다. 도커 이미지 땡겨오고(Pull), 외부 API 호출할 때마다 과금 미터기가 돌아갑니다. EC2 Public Subnet에 띄운 Docker는 이게 공짜인데 말이죠.
YAML만 문제가 아니었습니다. 쿠버네티스의 러닝 커브는 절벽과도 같았습니다.
-v /data:/data 하면 끝날 것을, 여기서는 PersistentVolumeClaim을 만들고, StorageClass를 정의하고... 잘못하면 데이터가 날아갑니다.결국 저는 "인프라 엔지니어"가 되어가고 있었습니다. "제품 개발자"가 아니라요.
"그냥 Ingress 만들면 되는 거 아냐?"
천만의 말씀입니다. AWS에서는 AWS Load Balancer Controller라는 놈을 따로 설치해야 하고,
얘가 ALB(Application Load Balancer)를 만듭니다.
근데 이 ALB가 개당 월 2만 원입니다.
서비스(앱)가 5개라 Ingress 5개 만들면? LB 비용만 월 10만 원 추가입니다.
결국 "Ingress Controller(Nginx)"를 하나 띄워서 ALB 하나로 모든 트래픽을 받게 구성해야 하는데, 이 설정이 또 기가 막히게 복잡합니다.
물론 좋은 점도 있습니다. 가장 큰 매력은 "자가 치유(Self-Healing)" 능력입니다.
"서버 3대 띄워줘"라고 선언(replicas: 3)해두면, 쿠버네티스는 무슨 일이 있어도 3대를 유지하려고 합니다.
심지어 제가 실수로(혹은 고의로) 파드를 kubectl delete로 죽여도, 0.1초 만에 새로운 파드를 띄웁니다.
밤에 서버 프로세스가 OOM(Out of Memory)으로 죽어도 개발자가 깰 필요가 없습니다. 쿠버네티스가 조용히 다시 살려놓으니까요.
(물론 DB가 터지면 깨야 합니다. 그건 쿠버네티스도 못 살립니다.)
물론 쿠버네티스는 위대한 도구입니다. 구글이 괜히 만든 게 아니죠. 하지만 "내 서비스"에는 아니었습니다.
제가 깨달은 "쿠버네티스를 써야 할 때"는 다음과 같습니다.
제 서비스는?
Docker Compose 하나면 충분했습니다.
회사가 커져서 어쩔 수 없이 쿠버네티스를 써야 한다면, 제발 kubectl만 붙잡고 있지 마세요.
k9s라는 도구를 설치하세요.
터미널에서 텍스트 UI로 파드를 보고, 로그를 보고, 쉘에 접속할 수 있습니다.
이게 없었다면 저는 아마 퇴사했을 겁니다.
결국 저는 눈물을 머금고 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 끝.
많은 사람들이 "서비스 디스커버리(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.
우리는 종종 "기술의 멋짐"에 취해 "기술의 비용"을 간과합니다. 이걸 이력서 주도 개발 (Resume Driven Development)라고 하죠. "나 쿠버네티스 써봤어"라고 이력서에 한 줄 적고 싶어서, 필요 없는 복잡성을 시스템에 끌어들이는 겁니다.
엔지니어링의 핵심은 "적정 기술(Appropriate Technology)"을 찾는 것입니다. 소잡는 칼로 닭을 잡으려다가는, 닭은커녕 제 손만 베입니다.
지금 여러분의 서비스가 아직 '닭'이라면, 제발 과도(Docker)만 드세요. 소잡는 칼(Kubernetes)은 나중에 서비스가 '황소'가 되면 그때 꺼내도 늦지 않습니다.