지난 2월, 우리 팀은 꽤 곤란한 상황에 놓였다. 4인 스타트업인데 인프라 담당자가 없었고, 컨테이너 오케스트레이션을 처음부터 새로 결정해야 했다. 이전 직장에서는 수백 명 규모의 팀이 Kubernetes를 썼고 나는 그냥 거기 올라타는 쪽이었는데 — 막상 혼자 처음부터 세팅하려니 머리가 아팠다.
그래서 2주를 잡았다. Docker Swarm, Nomad, Kubernetes를 각각 실제 워크로드에 맞춰 세팅해보고 비교했다. 포트폴리오 사이트 배포가 아니라 실제 API 서버 3개, 백그라운드 워커 2개, PostgreSQL 하나, Redis 하나를 돌리는 환경이었다.
이 글은 그 경험을 정리한 것이다.
Docker Swarm: “아직도 이걸 쓰는 사람이 있어?”라고 묻는다면
솔직히 처음엔 Docker Swarm을 진지하게 고려하지 않았다. 구글에서 뭔가 찾다 보면 “Swarm은 이미 죽었다”는 글이 많아서 편견이 생겼던 것 같다.
그런데 막상 써보니 생각이 달라졌다.
세팅에 걸린 시간은 45분. Docker Engine이 이미 설치돼 있으면 docker swarm init 한 줄로 시작할 수 있고, 기존 docker-compose.yml을 거의 그대로 가져다 docker stack deploy로 올리면 된다. 이미 compose 파일로 로컬 환경을 관리하고 있었으니 이 부분이 특히 좋았다.
# docker-stack.yml — 기존 compose에서 거의 수정 없이 전환
version: "3.9"
services:
api:
image: myapp/api:1.4.2
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
ports:
- "3000:3000"
networks:
- app_net
worker:
image: myapp/worker:1.4.2
deploy:
replicas: 2
networks:
- app_net
networks:
app_net:
driver: overlay
이게 실제 우리가 쓴 파일이랑 거의 동일하다. 롤링 업데이트, 헬스체크, 재시작 정책 — 기본적인 건 다 된다.
한 가지 한계는 분명했다. 오토스케일링이 없다. 트래픽 급증 때 자동으로 레플리카를 늘려주는 기능이 기본으로 없고, 외부 도구를 붙여야 한다. 우리는 트래픽 패턴이 꽤 예측 가능한 편이라 이게 치명적이진 않았지만, B2C SaaS라면 얘기가 달라진다. 네트워크 정책도 빈약해서 서비스 간 트래픽을 세밀하게 제어하고 싶으면 금방 답답해진다.
실용적인 결론: 4인 이하 팀, 트래픽 패턴이 어느 정도 예측 가능, 이미 Docker Compose를 쓰고 있다면 Swarm이 가장 빠른 선택이다. 30분 안에 HA 환경이 뜨고, 그 다음 날부터 제품 개발에 집중할 수 있다. “요즘 아무도 안 쓴다”는 말에 흔들릴 필요 없다.
Nomad가 생각보다 훨씬 좋았다 — 그리고 한 가지 찜찜한 부분
이 도구를 테스트하다가 진짜로 “왜 이게 더 유명하지 않지?”라고 혼자 중얼거렸다.
HashiCorp의 Nomad는 컨테이너만 다루는 게 아니다. Java 앱, Python 스크립트, VM 워크로드까지 동일한 방식으로 스케줄링할 수 있다. 우리 팀은 레거시 스크립트 몇 개를 아직 컨테이너화하지 않은 채로 돌리고 있었는데, Nomad를 쓰면 그것들도 같이 관리할 수 있었다. 처음엔 부수적인 기능처럼 보였는데 실제로 써보니 꽤 중요했다.
아키텍처도 이해하기 쉬웠다. Server(스케줄러)와 Client(워커 노드) 구조가 명확하고, Consul과 통합하면 서비스 디스커버리가 깔끔하게 된다. 우리 팀은 Consul을 이미 쓰고 있었으니 시너지가 있었다.
# api.nomad — job 정의 파일
job "api" {
datacenters = ["dc1"]
type = "service"
group "api" {
count = 3
network {
port "http" { to = 3000 }
}
task "api" {
driver = "docker"
config {
image = "myapp/api:1.4.2"
ports = ["http"]
}
resources {
cpu = 500 # MHz
memory = 256 # MB
}
# Consul 헬스체크 통합
service {
name = "api"
port = "http"
check {
type = "http"
path = "/health"
interval = "10s"
timeout = "2s"
}
}
}
}
}
배포는 nomad job run api.nomad 한 줄이다. 생각보다 훨씬 직관적이었다.
그런데 함정이 있었다. 2023년에 HashiCorp가 라이선스를 BSL(Business Source License)로 바꿨다. 경쟁사 서비스에 Nomad를 내장하거나 재판매하는 형태가 아닌 이상 일반 내부 사용에는 문제없는 것 같다 — 그래도 법무팀 확인은 하고 가는 게 맞다. 오픈소스 라이선스에 민감한 조직이라면 특히. HashiCorp 제품들 전체가 지금 좀 복잡한 상황이라 찜찜한 건 사실이었다.
실용적인 결론: 컨테이너 외에 다양한 워크로드를 다루거나, HashiCorp 스택을 이미 활용 중이라면 Nomad가 Kubernetes보다 훨씬 낮은 운영 비용으로 같은 결과를 낼 수 있다. 라이선스 문제만 해결되면 꽤 매력적인 선택이다.
Kubernetes의 복잡성 세금 — 실제로 얼마를 내야 하나
Kubernetes를 비판하는 글이 넘쳐나는데, 나도 그냥 “너무 복잡하다”는 말을 반복하고 싶지 않다. 구체적으로 어디서 막혔는지가 더 중요하니까.
우리 팀에서 Kubernetes를 세팅하는 데 걸린 시간은 3일이었다. k3s(경량 버전)를 썼는데도 그랬다. k3s가 “가벼운” Kubernetes라고 해서 과소평가했는데, 이해해야 하는 개념 자체가 줄어드는 건 아니다. Deployment, ReplicaSet, Service, Ingress, ConfigMap, Secret, PersistentVolumeClaim — 단순히 API 서버 하나 올리는 데도 여러 리소스를 조합해야 한다.
# api-deployment.yaml — 최소한으로만 써도 이 정도
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapp/api:1.4.2
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: api-svc
spec:
selector:
app: api
ports:
- port: 80
targetPort: 3000
이걸 Ingress랑 TLS까지 붙이면 YAML이 120줄을 넘어간다. Swarm 파일이랑 비교하면 눈에 띄는 차이다.
진짜 힘들었던 건 트러블슈팅이었다. 어느 금요일 오후에 배포를 밀었는데 Pod가 CrashLoopBackOff 상태로 빠졌다. 로그 보고, kubectl describe 보고, events 보고 — 원인은 결국 ConfigMap에서 환경변수 키 이름 오타였다. 1시간 반 날렸다. Swarm이었으면 컨테이너 로그 보는 게 전부였을 텐데. 그날 이후로 Kubernetes 디버깅 레이어가 얼마나 깊은지 체감하게 됐다 — 그리고 그게 4인 팀 한 명의 하루 업무 시간을 잡아먹을 수 있다는 것도.
반면 Kubernetes가 확실히 뛰어난 부분이 있다. HPA(Horizontal Pod Autoscaler)는 진짜 잘 된다. CPU나 커스텀 메트릭 기준으로 자동 스케일링이 깔끔하게 동작하고, Argo CD 같은 도구와 통합하면 GitOps 워크플로우가 매우 매끄럽다. 300명 이상 규모 팀이나 여러 팀이 동일 클러스터를 공유하는 환경에서는 네임스페이스 격리, RBAC, 네트워크 정책이 빛을 발한다.
4인 팀에게 맞는 도구인지는 솔직히 회의적이다. 우리 상황에서 Kubernetes를 선택했다면 인프라 관리에 쓰는 시간이 지금보다 훨씬 늘었을 거다. 스타트업에서 그 시간은 제품 개발 시간과 직결된다.
그래서 뭘 골랐나 — 팀 규모별 실제 권장안
한 달 넘게 써본 결과, 우리 팀은 Nomad를 선택했다. HashiCorp 라이선스 문제는 내부 법무팀에 확인했고 우리 사용 방식에는 문제없다는 결론이 났다.
이유는 단순했다. 컨테이너 외에 Python 스크립트 몇 개를 같이 관리해야 했고, Consul은 이미 쓰고 있었다. Swarm이 매력적이었지만 오토스케일링 없이는 몇 달 뒤에 다시 마이그레이션해야 할 것 같았다. Kubernetes는 지금 우리에게 너무 큰 도구였다.
이 선택이 모든 팀에 맞는 건 아니다. 내 경험을 바탕으로 정리하면:
1~3인 팀, 트래픽 변동 적음: Docker Swarm. 진짜로. 30분 안에 HA 세팅하고 다음 날부터 기능 개발에 집중할 수 있다. “요즘 아무도 안 쓴다”는 말에 휩쓸리지 마라.
3~10인 팀, 혼합 워크로드 또는 HashiCorp 스택 기존 사용: Nomad를 진지하게 보라. 특히 Consul이나 Vault를 이미 쓰고 있다면 통합 비용이 크게 줄어든다. 라이선스 확인은 필수.
10인 이상, 여러 팀이 인프라 공유: Kubernetes다. 이 규모에서는 Kubernetes의 복잡성이 단점이 아니라 필요한 기능이 된다. RBAC, 네임스페이스 격리, 생태계 크기 — 다 필요해진다. k3s나 k0s로 시작해도 충분하다.
그리고 클라우드를 쓰고 있다면 GKE, EKS, AKS 같은 관리형 Kubernetes가 자체 운영보다 나을 수 있다. 컨트롤 플레인 관리를 클라우드에 맡기면 복잡성의 상당 부분이 사라진다. 예산이 맞았다면 나도 그쪽을 선택했을 거다. 우리는 그게 안 됐을 뿐이다.
세 도구 중 “틀린 선택”은 없다. 다만 팀 규모와 운영 여력을 무시하고 고르면 몇 달 뒤에 고생한다. 직접 경험으로 알게 됐다.