[Thirdtool] 오케스트레이터가 풀어주는 5가지 문제 — Docker만으로 부족한 순간

junsung kim·2026년 5월 15일

컨테이너를 잘 다루는 것과, 컨테이너로 만든 시스템을 잘 운영하는 것은 완전히 다른 이야기다.


들어가며

docker run은 쉽다. docker-compose up도 쉽다. 그런데 운영 환경에 올리는 순간 사람이 손으로는 도저히 감당이 안 되는 문제들이 갑자기 쏟아진다.

이 글은 그 문제들이 정확히 뭐고, 오케스트레이터(Docker Swarm, Kubernetes)가 그걸 어떻게 풀어주는지 를 다룬다. 도구의 기능 나열이 아니라, 왜 이게 없으면 운영을 못하는가 를 짚는 게 목적이다.- why 오케스트레이터


가정할 상황

서버 5대, 서비스 10개, 컨테이너 30개 정도.
오케스트레이터 없이 docker 명령어만으로 운영한다고 상상해보자.

이게 어떻게 망가지는지 하나씩 본다.


문제 1. "이거 어느 서버에 띄우지?"

새 컨테이너를 띄울 때마다 결정해야 한다.

Server-1은 메모리 여유가 8GB 있고, Server-2는 4GB. Server-3은 CPU가 놀고 있고, Server-4는 이미 다른 서비스가 빡빡하게 돌고 있다... 이번에 띄울 컨테이너는 메모리가 좀 필요하니까 Server-1에 띄울까?

이걸 매번 사람이 판단한다. 서버 수가 5대 정도면 어떻게든 되지만, 20대, 50대가 되면 머리에 안 들어온다. 결과적으로:

  • 어떤 서버는 과부하로 OOM, 어떤 서버는 절반이 놀고 있음
  • 누가 어디에 뭘 띄웠는지 추적이 안 됨

오케스트레이터가 해주는 일

# Swarm
docker service create --name web --replicas 3 nginx

# Kubernetes
kubectl apply -f web-deployment.yaml

띄우라고 말만 한다. 어디에 띄울지는 오케스트레이터가 자원 상황을 보고 결정한다. 메모리·CPU 여유, 라벨 제약(SSD 노드에만, 같은 앱끼리는 다른 노드에)까지 고려해서. 사람은 "5개 띄워줘"만 신경 쓰면 된다.

이게 스케줄링(scheduling) 이다.


문제 2. "새벽 3시에 컨테이너 죽음 알림이 왔다"

운영 중인 컨테이너가 죽는 이유는 많다. OOM, 코드 버그, 외부 의존성 일시 장애, 호스트 하드웨어 문제...

오케스트레이터 없이는 이 흐름이다:

[알림 울림]
   ↓
SSH로 서버 들어감
   ↓
어느 컨테이너가 죽었는지 확인
   ↓
로그 보고 원인 파악
   ↓
docker run으로 다시 띄움 (정확한 옵션 기억해서)
   ↓
다시 잠

그리고 호스트 자체가 죽으면 더 끔찍하다. 그 호스트에 있던 모든 컨테이너를 다른 호스트로 옮겨야 한다. 어디로? 어떻게?

오케스트레이터가 해주는 일

선언만 해두면 된다 — "이 앱은 항상 3개가 떠 있어야 한다."

replicas: 3

그러면 시스템이 끊임없이 현재 상태 와 원하는 상태 를 비교한다.

Desired: 3개          Actual: 3개  ✓ OK

──── 컨테이너 1개 죽음 ────

Desired: 3개          Actual: 2개  ✗
                        ↓
                   "1개 더 띄워야 함"
                        ↓
                  자동으로 새 컨테이너 생성
                        ↓
Desired: 3개          Actual: 3개  ✓ OK

──── 호스트 1대 죽음, 거기 있던 2개 사라짐 ────

Desired: 3개          Actual: 1개  ✗
                        ↓
                  살아있는 다른 노드에
                  2개 자동 재배치
                        ↓
Desired: 3개          Actual: 3개  ✓

새벽 3시 알림이 안 온다. 시스템이 알아서 회복한다. 이게 자가 치유(self-healing) 다.


문제 3. "order-service가 지금 어디에 있지?"

마이크로서비스라면 서비스끼리 서로를 호출한다. payment-service가 order-service를 호출하려고 한다. 그런데 order-service는...

  • Server-3에 있다가, 죽어서 Server-7로 이동했고
  • 트래픽이 늘어서 Server-2에도 추가로 떴고
  • 잠시 후엔 Server-5로 또 이동할 예정이다

Pod의 IP는 계속 바뀐다. 그런데 payment-service는 매번 어디로 요청을 보내야 하는지 어떻게 아는가?

오케스트레이터 없이 풀려면:

  • 설정 파일에 IP 박아두기 → 바뀔 때마다 재배포
  • DNS를 직접 관리 → 갱신 지연

오케스트레이터가 해주는 일

오케스트레이터에 "서비스(Service)" 라는 추상화가 내장돼 있다.

# K8s 예시
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector: { app: order-service }
  ports: [{port: 80}]

이제 payment-service는 order-service라는 이름으로만 부르면 된다.

// payment-service 코드
restTemplate.getForObject("http://order-service/orders/123", Order.class);

내부에서 무슨 일이 일어나나:

  • 오케스트레이터가 "order-service라는 라벨이 붙은 Pod 목록"을 항상 추적
  • Pod이 죽으면 목록에서 빼고, 새로 뜨면 추가
  • 클라이언트는 안정적인 가상 IP / DNS 이름으로만 접근
  • 트래픽은 살아있는 Pod에만 자동 분배

Pod 위치가 바뀌어도 클라이언트 코드는 한 줄도 안 바뀐다. 이게 서비스 디스커버리 + 로드밸런싱 이다.


문제 4. "이거 어떻게 다운타임 없이 배포하지?"

새 버전을 배포해야 한다. 옵션은 두 가지.

옵션 A: 한 번에 다 바꾸기

  • 모든 컨테이너 중지 → 새 버전으로 띄움 → 시작 시간 동안 다운타임 → 새 버전 버그라면 다 같이 죽음

옵션 B: 한 대씩 직접 바꾸기

  • 1번 인스턴스 중지 → 새 버전 띄움 → 헬스 체크 → OK → 2번 인스턴스 중지 → ...
  • 사람이 30분 동안 SSH 창 들여다보며 헬스 체크 함
  • 중간에 문제 생기면 어디까지 진행됐는지 추적해서 롤백
  • 컨테이너 30개면? 노예 노동

오케스트레이터가 해주는 일

선언만 해둔다 — "한 번에 1개씩 교체하고, 새 거 ready 되면 다음 거 교체해."

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

흐름:

v1 v1 v1 v1 v1          (배포 시작)
   ↓
v1 v1 v1 v1 v1 v2       (v2 1개 추가)
   ↓ (v2 ready 됨)
v1 v1 v1 v1 v2          (v1 1개 제거)
   ↓
v1 v1 v1 v2 v2          ... 반복 ...
   ↓
v2 v2 v2 v2 v2          (완료, 다운타임 0)

그리고 새 버전이 ready 못 받으면 자동으로 멈춘다. 롤백 명령 한 줄로 이전 버전으로 돌아갈 수 있다.

kubectl rollout undo deployment/order-service

새벽 SSH 노동이 한 줄짜리 선언으로 바뀐다. 이게 롤링 업데이트(rolling update) 다.


문제 5. "갑자기 트래픽이 10배 뛰면 어떡하지?"

평소 3개 인스턴스로 충분한 서비스가 있다. 그런데 이벤트가 시작되면 10배 트래픽이 몰린다.

선택지는?

A. 평소에도 많이 띄워두기

  • 자원 낭비. 99%의 시간 동안 안 쓰는 인스턴스에 비용 지불.

B. 이벤트 직전에 사람이 늘리기

  • 트래픽 시작 시점 예측 못하면 장애.
  • 끝나면 또 줄여야 함. 사람이 매번 챙겨야 함.

C. 트래픽 보고 자동으로 늘리고 줄이기

  • 메트릭 수집, 임계치 판단, 인스턴스 생성/제거... 직접 구현하면 그 자체가 또 하나의 시스템.

오케스트레이터가 해주는 일

CPU 사용률, 메모리, 커스텀 메트릭(요청 수, 큐 길이 등) 기반으로 자동 스케일링이 내장돼 있다.

# K8s HPA 예시
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
  minReplicas: 3
  maxReplicas: 30
  metrics:
    - type: Resource
      resource:
        name: cpu
        target: { type: Utilization, averageUtilization: 70 }

CPU 평균이 70% 넘으면 자동으로 Pod을 늘리고, 떨어지면 줄인다. 이벤트가 끝나면 다시 3개로 줄어든다. 사람이 안 끼어들어도 된다.

이게 오토스케일링 이다.


정리: 다섯 문제의 공통점

각 문제를 다시 보면 패턴이 보인다.

문제사람이 풀어야 하던 것오케스트레이터의 해결
어디에 띄울지매번 사람이 결정스케줄러가 자동 배치
죽으면 살리기SSH로 재시작자동 재시작/재배치
서비스 위치 추적설정 파일에 IP 박기서비스 디스커버리
무중단 배포한 대씩 수동 교체자동 롤링 업데이트
트래픽 변동미리 띄우거나 장애자동 스케일링

공통점은 하나다.

"원하는 상태"만 선언하면, 시스템이 끊임없이 현재 상태와 비교하면서 자동으로 맞춰준다.

사람이 매번 명령을 내리는 게 아니라, "이렇게 되어 있어야 한다"고 한 번 선언하면 끝이다. 그 뒤로는 시스템이 알아서 desired state를 유지한다.

이게 오케스트레이터의 본질이다.


Docker Swarm과 Kubernetes의 차이

위 5가지 문제를 둘 다 풀어준다. 그럼 뭐가 다른가?

Docker Swarm

Docker가 직접 만든 오케스트레이터. Docker 명령어를 그대로 확장한 형태다.

docker service create --name web --replicas 3 nginx
docker service scale web=5
docker service update --image nginx:1.25 web

docker run을 알면 학습 곡선이 거의 없다. 작은 클러스터, 작은 팀에 잘 맞는다. 위 5가지 문제는 이걸로 충분히 풀린다.

Kubernetes

Google이 내부 시스템(Borg)의 경험을 일반화한 플랫폼. Swarm과 결정적으로 다른 두 가지:

(1) 추상화의 풍부함

  • Swarm의 service 하나로 표현 어려운 워크로드가 많다 — 노드마다 하나씩 떠야 하는 로그 수집기, 일회성 배치 작업, 순서 보장이 필요한 stateful 시스템, 주기적으로 도는 크론 작업...
  • K8s는 이걸 각각 DaemonSet, Job, StatefulSet, CronJob 같은 별도 리소스로 가진다.

(2) 확장 가능한 플랫폼

  • Swarm은 Docker가 만든 닫힌 시스템이다.
  • K8s는 누구나 새 리소스 타입(CRD)과 새 컨트롤러를 만들어 얹을 수 있다.
  • 이 차이가 만들어낸 결과 — Helm, ArgoCD, Istio, Prometheus, Cert-Manager, 각종 DB Operator... 모든 운영 도구의 생태계가 K8s 위에 만들어졌다.

Swarm은 단순함을 무기로 했지만, 결국 이 생태계 격차를 따라잡지 못했다. CNCF 표준이 되고 AWS/GCP/Azure가 모두 매니지드 K8s를 제공하면서 사실상 표준의 자리는 K8s가 가져갔다.


마무리

이 글에서 하고 싶었던 건 하나다.

컨테이너를 잘 띄울 줄 안다고 끝이 아니다.
여러 컨테이너가 여러 호스트에서 동시에 살아 움직이는 시스템을 만들려면, 사람이 매번 결정하던 일들을 시스템이 자동으로 처리하게 만들어야 한다.

오케스트레이터가 푸는 건 결국 그 모든 자동화의 묶음이다.

처음 K8s를 봤을 때 왜 이렇게 복잡한 게 표준이 됐지? 싶었는데, 위 5가지 문제를 하나씩 직접 만나보고 나서야 아, 이걸 자동으로 풀어주는 시스템이 없으면 정말 안 되겠구나 가 느껴졌다. 도구가 복잡한 게 아니라, 풀어야 하는 문제 자체가 복잡한 거였다.


profile
edit하는 개발자! story 있는 삶

0개의 댓글