[2026] 컨테이너 오케스트레이션 심화 — 스케줄러·DNS·CNI·스토리지·프로덕션 패턴
이 글의 핵심
오케스트레이터는 "컨테이너를 어디에 띄울지, 어떻게 붙일지, 디스크는 어떻게 줄지"를 지속적으로 결정합니다. 이 글은 Kubernetes와 Docker Swarm의 스케줄링·DNS·오버레이 네트워크·영구 스토리지·프로덕션 운영 패턴을 내부 동작 수준에서 정리합니다.
들어가며: 오케스트레이션의 네 축
컨테이너 오케스트레이션은 배치(스케줄링), 연결(네트워크·DNS), 상태(스토리지), 운영 규율(가용성·배포 전략)의 네 축으로 이해하면 내부 구조가 선명해집니다. 아래에서는 Kubernetes(이하 K8s)와 Docker Swarm을 대비하며, 각 축이 어떤 컴포넌트로 구현되는지와 프로덕션에서의 트레이드오프를 다룹니다.
1. 스케줄러 알고리즘: Kubernetes와 Docker Swarm
1.1 Kubernetes kube-scheduler와 스케줄링 프레임워크
K8s에서 새 파드(또는 바인딩되지 않은 파드)는 스케줄러가 적합한 노드 하나에 할당합니다. 현대 kube-scheduler는 단일 거대 함수가 아니라 스케줄링 프레임워크(Scheduling Framework) 로, 단계별로 확장 플러그인을 끼울 수 있는 구조입니다.
대표 흐름(개념적으로):
- 큐잉: 스케줄링이 필요한 파드를 워크 큐에서 꺼냅니다.
- 필터링(Filter / Predicates에 해당하는 확장): 노드가 하드 제약을 만족하는지 판별합니다. 예: CPU·메모리 요청량(
requests)과 할당 가능 용량, 노드 셀렉터, 테인트/톨러레이션, 노드 어피니티, 파드 어피니티/안티 어피니티, 볼륨 토폴로지 제약(특정 존/리전에 볼륨이 있어야 함), 일부 스토리지는 지연 바인딩과 결합됩니다. - 스코어링(Scoring / Priorities에 해당): 필터를 통과한 노드들에 점수를 매겨 최적 노드를 고릅니다. 예: 리소스 잔여량 균형, 파드 간 스프레드(같은 노드에 몰리지 않게), 이미지 로컬 캐시 존재 여부 등.
- 바인딩: 선택된 노드에 파드를 바인딩하고, 해당 노드의
kubelet이 실제로 컨테이너를 기동합니다.
선점(Preemption): 큐에 있던 파드가 스케줄될 수 없을 때(리소스 부족 등), 스케줄러는 우선순위가 낮은 실행 중 파드를 축출해 자리를 만들 수 있습니다. 이는 클러스터 전체의 SLA·우선순위 클래스 설계와 직결되므로, 운영 정책(어떤 워크로드가 양보하는지)을 명시해야 합니다.
실무 포인트
requests/limits를 맞추지 않으면 스코어링이 현실과 어긋나고, 노드 압축(eviction)과도 충돌합니다.- 안티 어피니티는 고가용을 돕지만, 스케줄 가능한 조합이 없으면 Pending이 됩니다. 반드시 이벤트로 이유를 확인합니다.
- DaemonSet은 스케줄러가 아닌 컨트롤러 성격에 가깝게 모든 노드에 깔리는 패턴으로, 일반 워크로드와 리소스 경쟁을 일으킬 수 있습니다.
1.2 Docker Swarm의 배치 전략
Swarm은 노드 플레이스먼트(placement) 를 서비스 단위로 제어합니다. 주요 아이디어는 스프레드(spread), 팩(binpack), 랜덤(random) 과 플레이스먼트 제약(constraint·preference) 입니다.
- spread: 태스크를 여러 노드에 고르게 분산합니다. 장애 도메인 분리에 유리합니다.
- binpack: 가능한 한 적은 노드에 몰아 넣어 노드 수·비용을 줄입니다. 리소스 단편화는 줄이지만, 단일 노드 장애 시 영향 범위가 커집니다.
- constraint:
node.labels.zone == east처럼 라벨 기반으로 후보 노드를 제한합니다. - global vs replicated: global 모드는 노드당 하나의 태스크(모니터링 에이전트 등)에 적합하고, replicated는 복제 수 중심입니다.
K8s의 필터+스코어 모델과 달리, Swarm은 운영자가 전략 이름으로 분산 정책을 고르는 쪽에 가깝습니다. 팀이 Swarm을 유지보수한다면, 스프레드 vs 빈팩이 비용·가용성에 미치는 영향을 서비스별로 문서화하는 것이 좋습니다.
2. 서비스 디스커버리와 DNS
2.1 Kubernetes: CoreDNS와 서비스 레코드
클러터 내부 DNS는 보통 CoreDNS가 담당합니다. 핵심은 서비스가 안정적인 이름을 제공한다는 점입니다.
- 클러스터 DNS 이름:
my-svc.my-namespace.svc.cluster.local형태의 FQDN이 일반적입니다. 같은 네임스페이스에서는my-svc만으로도 조회됩니다. - ClusterIP 서비스: 가상 IP(ClusterIP)로 kube-proxy(iptables·IPVS 등)가 백엔드 파드로 로드밸런싱합니다. 애플리케이션은 DNS로 IP를 받고, 그 IP로 접속합니다.
- 헤드리스 서비스(
clusterIP: None): 단일 가상 IP 없이 DNS A/AAAA 레코드가 파드 IP 목록으로 풀립니다. 스테이트풀셋의 개별 파드 DNS(pod-name.service.namespace...)와 궁합이 좋고, 클라이언트가 모든 레플리카를 직접 순회해야 할 때 유용합니다.
서비스 디스커버리의 두 층
- 이름 → IP(DNS): 사람이 읽는 이름을 주소로 바꿉니다.
- IP → 실제 엔드포인트(kube-proxy / CNI): ClusterIP 뒤의 엔드포인트 슬라이스가 바뀌면(스케일아웃·노드 이동) 데이터플레인 규칙이 갱신됩니다.
운영 시 DNS 타임아웃·NXDOMAIN은 CoreDNS 자체 이슈뿐 아니라 네임스페이스·서비스 이름 오타, 네트워크 정책으로 DNS(보통 kube-system의 CoreDNS)까지 막힌 경우도 흔합니다.
2.2 Docker Swarm: VIP와 DNSRR
Swarm 서비스는 가상 IP(VIP) 모드가 기본입니다. DNS로 서비스 이름을 조회하면 VIP가 나오고, 로드밸런서(L4) 가 태스크로 분산합니다.
- DNSRR(
--endpoint-mode dnsrr): DNS가 태스크 IP 목록을 돌려줍니다. 클라이언트 측 로드밸런싱·헬스체크 패턴과 맞물릴 때 선택합니다. 반면 짧은 TTL·캐시 이슈에 더 민감할 수 있습니다.
K8s와의 비유: K8s ClusterIP+Vip 유사 모드는 Swarm VIP, K8s 헤드리스는 DNSRR에 가깝습니다(다만 구현 세부는 다름).
3. 네트워크 오버레이: Flannel과 Calico
3.1 CNI가 하는 일
컨테이너 런타임이 만든 네트워크 네임스페이스에 CNI(Container Network Interface) 플러그인이 IP를 붙이고, 라우팅·브리지·오버레이를 구성합니다. 오케스트레이터는 “파드 CIDR을 노드에 나눠 준다”는 상위 정책을 두고, CNI가 실제 데이터플레인을 완성합니다.
3.2 Flannel: 경량 오버레이
Flannel은 대표적으로 VXLAN 오버레이로 노드 간 L2/L3 터널을 만들어 모든 파드가 클러스터 전체에서 라우팅 가능하게 합니다. host-gw 모드는 오버레이 대신 호스트 라우팅 테이블에 경로를 박는 방식으로, 환경에 따라 오버헤드가 적을 수 있지만 L2 인접성 요구가 있습니다.
- 장점: 구성이 단순하고, “일단 다 통신되게” 만드는 데 빠릅니다.
- 한계: 네트워크 정책은 Flannel 단독으로는 부족하고, Calico 등과 조합하거나 다른 정책 레이어를 씁니다(실제 아키텍처는 배포판·매니페스트에 따름).
3.3 Calico: 라우팅·정책·eBPF
Calico는 전통적으로 BGP로 파드 서브넷을 노드 밖에 라우팅 전파하는 모델이 강점입니다(오버레이 없이도 동작 가능한 토폴로지가 있으면 성능·관찰성에 유리). 또한 NetworkPolicy로 L3/L4 정책(네임스페이스·라벨 셀렉터 기반)을 강하게 지원합니다.
최근 데이터플레인으로 eBPF 옵션을 쓰면 kube-proxy 우회·성능 이점을 노릴 수 있지만, 커널 버전·디버깅 난이도가 올라갑니다.
선택 가이드(요약)
| 관점 | Flannel 중심 | Calico 중심 |
|---|---|---|
| 목표 | 빠른 연결·단순 오버레이 | 정책·BGP·대규모 라우팅 |
| 정책 | 별도 컴포넌트 필요할 수 있음 | 내장 정책 강함 |
| 운영 | 단순 | BGP·eBPF 등 옵션만큼 복잡도 증가 가능 |
4. 스토리지 오케스트레이션: PV·PVC·StorageClass
4.1 추상화 계층
- PV(PersistentVolume): 클러스터 수준의 스토리지 조각(NFS, CSI 볼륨, 클라우드 디스크 등).
- PVC(PersistentVolumeClaim): 파드가 요청하는 용량·액세스 모드·스토리지 클래스.
- StorageClass: 동적 프로비저닝의 템플릿.
provisioner,parameters로 실제 스토리지 백엔드(CSI 드라이버)를 가리킵니다.
애플리케이션 매니페스트는 보통 PVC만 참조하고, 관리자가 스토리지 클래스와 바인딩 정책을 맡습니다.
4.2 액세스 모드와 바인딩
- ReadWriteOnce(RWO): 단일 노드에 읽기/쓰기(일반 블록 디스크).
- ReadOnlyMany(ROX) / ReadWriteMany(RWX): 다수 노드 접근이 필요한 워크로드(일부 NFS·파일 스토리지).
WaitForFirstConsumer 볼륨 바인딩 모드는 PVC가 먼저 특정 PV에 묶이지 않고, 파드가 스케줄된 뒤 해당 노드와 호환되는 볼륨을 붙입니다. 존·리전에 묶인 디스크와 스케줄러를 맞출 때 중요합니다.
4.3 운영 시나리오
- PVC Pending: 스토리지 클래스 미지정·프로비저너 실패·용량 부족·액세스 모드 불일치.
- 파드는 뜨는데 마운트 실패: CSI 드라이버·노드 에이전트·권한(IAM)·파일시스템 마운트 옵션.
- 스냅샷·백업: CSI 스냅샷 API·벤더 도구와 복구 RTO/RPO를 문서화합니다.
Swarm은 볼륨 드라이버·로컬 vs 클러스터 볼륨 모델로 유사 문제를 다루지만, K8s만큼 PV/PVC/StorageClass 계층이 표준화되어 있지는 않습니다. 팀이 K8s로 이전 중이라면 CSI 기반 설계를 기준으로 삼는 편이 이후 이식에 유리합니다.
5. 프로덕션 오케스트레이션 패턴
5.1 가용성과 안전한 롤링
- PodDisruptionBudget(PDB): 자발적 중단(노드 드레인, 롤링 업그레이드) 시 동시에 내려갈 수 있는 최대 파드 수를 제한합니다. 클러스터 업그레이드·노드 교체 시 서비스 공백을 방지합니다.
- topologySpreadConstraints: 존(availability zone)·호스트명 등 토폴로지에 파드를 분산합니다. 단일 존 장애에 대비합니다.
- readiness / liveness: 트래픽을 받기 전 준비 완료를 보장하고, 비정상 컨테이너는 재시작합니다. 과도한 라이브니스는 재시작 루프를 유발할 수 있어 임계값 설계가 중요합니다.
5.2 배포와 GitOps
- 롤링·카나리·블루그린: 배포 리소스(Deployment 등)의 전략과 Ingress/서비스 가중으로 점진적 전환을 조합합니다.
- GitOps(Argo CD·Flux 등): 선언적 상태를 Git에 두고 클러스터가 수렴합니다. 변경 추적·롤백에 강하지만, 시크릿 관리·드리프트 대응 프로세스가 필요합니다.
5.3 오토스케일링과 비용
- HPA: CPU·메모리·커스텀 메트릭 기반 파드 수 조절.
- VPA / 클러스터 오토스케일러: 리소스 요청 튜닝·노드 풀 크기 조절과 연동. 스케줄러·스토리지·노드 한도와 함께 보면 비용과 안정성의 균형을 잡습니다.
5.4 관찰 가능성
분산 시스템에서는 메트릭·로그·트레이스가 필수입니다. CNI·CSI·DNS·스케줄러 이벤트는 각각 다른 컴포넌트에 로그가 흩어지므로, 장애 시 징후(어느 레이어에서 끊겼는지) 를 빠르게 좁히는 런북을 마련해 두는 것이 좋습니다.
정리
컨테이너 오케스트레이션의 내부는 스케줄러가 노드를 고르고, DNS·프록시가 이름과 트래픽을 연결하며, CNI가 패킷 경로를 만들고, PV/PVC가 상태를 붙이고, PDB·토폴로지·GitOps가 운영 규율을 완성하는 구조로 이해할 수 있습니다. K8s는 이를 강한 추상화와 확장 포인트로 표준화했고, Swarm은 더 단순한 모델로 일부 축을 다르게 풀었습니다. 동일한 증상(Pending, DNS 실패, 마운트 실패)이라도 원인 레이어가 다를 수 있으므로, 항상 각 레이어의 책임 경계를 의식하며 디버깅하는 것이 전문가적인 접근입니다.
심화 부록: 구현·운영 관점
이 부록은 앞선 본문에서 다룬 주제(「[2026] 컨테이너 오케스트레이션 심화 — 스케줄러·DNS·CNI·스토리지·프로덕션 패턴」)를 구현·런타임·운영 관점에서 다시 압축합니다. 도메인별 세부 구현은 글마다 다르지만, 입력 검증 → 핵심 연산 → 부작용(I/O·네트워크·동시성) → 관측의 흐름으로 장애를 나누면 원인 추적이 빨라집니다.
내부 동작과 핵심 메커니즘
flowchart TD A[입력·요청·이벤트] --> B[파싱·검증·디코딩] B --> C[핵심 연산·상태 전이] C --> D[부작용: I/O·네트워크·동시성] D --> E[결과·관측·저장]
sequenceDiagram participant C as 클라이언트/호출자 participant B as 경계(런타임·게이트웨이·프로세스) participant D as 의존성(API·DB·큐·파일) C->>B: 요청/이벤트 B->>D: 조회·쓰기·RPC D-->>B: 지연·부분 실패·재시도 가능 B-->>C: 응답 또는 오류(코드·상관 ID)
- 불변 조건(Invariant): 버퍼 경계, 프로토콜 상태, 트랜잭션 격리, FD 상한 등 단계별로 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
- 결정성: 순수 층과 시간·네트워크·스케줄에 의존하는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
- 경계 비용: 직렬화, 인코딩, syscall 횟수, 락 경합, 할당·GC, 캐시 미스를 의심 목록에 둡니다.
- 백프레셔: 생산자가 소비자보다 빠를 때 버퍼·큐·스트림에서 속도를 줄이는 신호를 어디에 둘지 정의합니다.
프로덕션 운영 패턴
| 영역 | 운영 관점 질문 |
|---|---|
| 관측성 | 요청 단위 상관 ID, 에러율·지연 p95/p99, 의존성 타임아웃·재시도가 대시보드에 보이는가 |
| 안전성 | 입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가 |
| 신뢰성 | 재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가 |
| 성능 | 캐시·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가 |
| 배포 | 롤백 룬북, 카나리/블루그린, 마이그레이션·피처 플래그가 문서화되어 있는가 |
| 용량 | 피크 트래픽·디스크·FD·스레드 풀 상한을 주기적으로 검증하는가 |
스테이징은 데이터 양·네트워크 RTT·동시성을 프로덕션에 가깝게 맞출수록 재현율이 올라갑니다.
확장 예시: 엔드투엔드 미니 시나리오
앞선 본문 주제(「[2026] 컨테이너 오케스트레이션 심화 — 스케줄러·DNS·CNI·스토리지·프로덕션 패턴」)를 배포·운영 흐름에 맞춰 옮긴 체크리스트입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.
- 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드를 경계에 둔다.
- 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 로그·메트릭·트레이스에서 한 흐름으로 본다.
- 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
- 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지 확인한다.
- 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값을 점검한다.
handle(request):
ctx = newCorrelationId()
validated = validateSchema(request)
authorize(validated, ctx)
result = domainCore(validated)
persistOrEmit(result, idempotentKey)
recordMetrics(ctx, latency, outcome)
return result
문제 해결(Troubleshooting)
| 증상 | 가능 원인 | 조치 |
|---|---|---|
| 간헐적 실패 | 레이스, 타임아웃, 외부 의존성, DNS | 최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검 |
| 성능 저하 | N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스 | 프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거 |
| 메모리 증가 | 캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납 | 상한·TTL·힙/FD 스냅샷 비교 |
| 빌드·배포만 실패 | 환경 변수, 권한, 플랫폼 차이, lockfile | CI 로그와 로컬 diff, 런타임·이미지 버전 핀 |
| 설정 불일치 | 프로필·시크릿·기본값, 리전 | 스키마 검증된 설정 단일 소스와 배포 매트릭스 표준화 |
| 데이터 불일치 | 비멱등 재시도, 부분 쓰기, 캐시 무효화 누락 | 멱등 키·아웃박스·트랜잭션 경계 재검토 |
권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.
배포 전에는 git add → git commit → git push 후 npm run deploy 순서를 권장합니다.