1. LeaderWorkerSet 기술이란?
왜 필요한가: 기존 방식(Deployment·StatefulSet)의 한계
LLM이 커지면서 모델 하나가 GPU 한 노드에 안 들어가는 일이 일상이 됐습니다. 8×H100(640GB)으로도 Llama-3.1-405B(FP16)나 DeepSeek-R1 671B 같은 모델은 한 노드에 못 올립니다. 그래서 모델을 여러 노드에 샤딩합니다 — 노드 안은 텐서 병렬(TP), 노드 사이는 파이프라인 병렬(PP). vLLM·SGLang·TensorRT-LLM은 이미 멀티노드 추론을 지원하고, 보통 Ray로 노드들을 한 런타임으로 묶습니다. 문제는 그 위, 쿠버네티스 워크로드 계층입니다.
- Deployment: 파드는 서로 무관하고 교체 가능한(fungible) 복제본입니다. 하나가 죽으면 그 하나만 새로 만듭니다. "이 8개 파드가 한 덩어리로 모델 하나를 이룬다"는 개념 자체가 없습니다.
- StatefulSet: 안정적 신원·순서·DNS는 주지만 여전히 파드 단위 관리입니다. 그룹을 한 단위로 스케일·롤링업데이트·올-오어-낫싱 재시작하는 시맨틱이 없어, 그룹 안 워커 하나가 죽어도 리더는 모른 채 남는 좀비 그룹이 생깁니다.
- Job/JobSet: 완료형(batch)이라 상시 떠 있어야 하는 추론 서버와 맞지 않습니다. (멀티 그룹 배치엔 JobSet이 제격이지만, 서빙은 다른 문제입니다.)
그래서 현장에선 StatefulSet + 헤드리스 서비스 + 수제 부트스트랩 스크립트 + 외부 게이트 스케줄러를 손으로 엮어 멀티노드 추론을 굴려 왔습니다. 핵심 고통은 한 문장입니다 — "1 리더 + N 워커가 하나의 모델 복제본을 이루는 그룹을, 그룹 단위로 생성·스케일·롤링업데이트·장애복구하는 표준 워크로드의 부재."
기술 정의
LeaderWorkerSet는 그 빠진 조각을 채웁니다. 한 replica = 1 리더 파드 + (size−1) 워커 파드로 묶인 "슈퍼 파드"이고, 컨트롤러는 이런 그룹을 replicas개 만듭니다. 각 그룹은 0…N−1의 고유 인덱스를 갖고, 그룹 안 파드도 0…size−1의 인덱스를 받아 병렬로 동시에 태어납니다. 그리고 이 그룹 하나가 롤링업데이트·스케일·배타적 토폴로지 배치의 단위입니다. 비유하면, Deployment가 "똑같은 일꾼 N명"이라면 LWS는 "팀장 1명 + 팀원 M명으로 된 팀을 N개" 만드는 것입니다 — 팀(그룹)이 곧 배포·확장·복구의 단위입니다. 런타임(vLLM/Ray) 자체는 바꾸지 않고, 그 아래에서 그룹의 수명주기만 떠받칩니다.
2. LeaderWorkerSet 기술 특징
| 특징 | 설명 | 실무적 의미 |
| 그룹 = 복제 단위 | 1 리더 + (size−1) 워커 = size개 파드를 하나의 단위로 | "여러 노드에 샤딩한 모델 1개"를 정확히 1 replica로 표현 |
| 듀얼 템플릿 | workerTemplate + (선택) leaderTemplate 분리 | 리더(Ray head·서버)와 워커(Ray worker) 역할 차이를 한 객체에 |
| 다중 그룹 + 고유 신원 | replicas개 그룹(0…N−1), 그룹 내 파드 0…size−1, 병렬 생성 | 안정적 호스트네임·DNS로 노드 간 부트스트랩이 결정적 |
| 그룹 단위 롤링업데이트 | rolloutStrategy의 maxUnavailable(기본 1)·maxSurge(기본 0) | 모델을 그룹째 무중단 교체(재고소진=stockout 방지) |
| 올-오어-낫싱 재시작 | RecreateGroupOnPodRestart(기본): 한 파드 죽으면 그룹 전체 재생성 | 부분 재시작으로 Ray 토폴로지가 깨진 좀비 그룹 방지 |
| 토폴로지 인지 배타 배치 | exclusive-topology 주석 — 그룹 1개 ↔ 토폴로지 도메인 1개 | 저지연 인터커넥트(NVLink·같은 랙·TPU 슬라이스)에 그룹을 묶음 |
| 서브그룹 | subGroupPolicy.subGroupSize로 큰 그룹을 더 작은 단위로 분할 | TPU 멀티슬라이스·리더 분리 등 토폴로지를 정밀 표현 |
| HPA scale 서브리소스 | /scale 엔드포인트로 그룹 수를 동적 스케일 | 표준 HPA·커스텀 메트릭 오토스케일러와 그대로 호환 |
| 게이트(올-오어-낫싱) 스케줄 | Kueue·NVIDIA KAI·Volcano 연동(Alpha, API 변경 가능) | 그룹의 절반만 스케줄돼 비싼 GPU를 깔고 앉는 데드락 방지 |
3. LeaderWorkerSet 기술 동작방식
핵심 구성 요소
- 리더 파드(leader): 그룹의 0번 파드. 보통 Ray head로 분산 런타임을 부트스트랩하고, 외부에 노출되는 추론 서버(예: vLLM 서버)를 띄운다. 그룹의 "팀장".
- 워커 파드(worker): 1…size−1 파드. 리더에 합류해 자기 샤드를 실행한다. 그룹별 워커 StatefulSet으로 관리된다.
- size: 그룹당 총 파드 수(= 1 리더 + M 워커). replicas: 그룹(=모델 복제본)의 수 N.
- leaderWorkerTemplate: 리더/워커 파드 템플릿과 restartPolicy를 담는다. startupPolicy: LeaderCreated(기본, 리더·워커 동시 생성) 또는 LeaderReady(리더가 Ready된 뒤 워커 생성 — Ray head가 먼저 떠야 할 때 필수).
- 헤드리스 서비스 + 주입 환경변수: LWS_LEADER_ADDRESS(리더의 DNS 주소), LWS_GROUP_SIZE, LWS_WORKER_INDEX가 모든 파드에 주입돼, 컨테이너가 자기 역할과 리더 위치를 알아 스스로 합류한다.
- restartPolicy: RecreateGroupOnPodRestart(기본, 그룹 전체 재생성) / None(죽은 파드만) / RecreateGroupAfterStart(pending이 없을 때만 재생성 — 큰 이미지 풀 보호; v0.9+ 정식 필드, v0.8은 실험적 주석).
- 컨트롤러 매니저: lws-system 네임스페이스에서 동작. 검증 웹훅은 내부 인증서(기본) 또는 cert-manager로 운영.
데이터(수명주기) 흐름 — "그룹을 한 단위로 태우고, 리더 주위로 묶는다"
운영자가 replicas=N, size=M+1과 리더/워커 템플릿을 선언하면, 컨트롤러는 그룹마다 리더 파드 1개 + 워커 StatefulSet(M)을 만들고 각 파드에 group-index·worker-index 라벨을 붙입니다. startupPolicy=LeaderReady면 리더가 Ready된 뒤 워커가 생성되고, 워커는 주입된 LWS_LEADER_ADDRESS로 리더(Ray head)에 합류해 텐서·파이프라인 병렬 토폴로지를 형성합니다. 리더는 추론 서버를 띄우고 ClusterIP 서비스로 노출하므로, 외부 요청은 리더로만 들어가 그룹 내부에서 분산 실행됩니다. 파드나 노드가 죽으면 기본 정책(RecreateGroupOnPodRestart)에 따라 그룹 전체가 함께 재생성되어 깨진 토폴로지를 남기지 않습니다. 스펙을 바꾸면 그룹 단위 롤링업데이트가 maxUnavailable/maxSurge만큼 그룹을 통째로 교체합니다. 한 줄 요약: LWS 적용 → 그룹별 리더+워커 생성(라벨·DNS) → (LeaderReady면) 리더 선기동 → 워커가 LWS_LEADER_ADDRESS로 합류 → 리더가 서버 노출 → 장애 시 그룹 재생성 → 변경 시 그룹 단위 롤링.
4. LeaderWorkerSet 기술 구성 및 흐름도
단계별 설명
| 단계 | 처리 | 포인트 |
| 1 | LWS 객체 적용(replicas=N, size=M+1, 리더/워커 템플릿) | 컨트롤러가 감지해 그룹들을 만든다 |
| 2 | 그룹마다 리더 파드 + 워커 StatefulSet 생성, 라벨·헤드리스 서비스 부여 | group-index 0…N−1, 안정적 DNS 확정 |
| 3 | startupPolicy=LeaderReady면 리더 Ready 대기 | Ray head 선기동(워커가 head 없이 붙는 사고 방지) |
| 4 | 워커가 LWS_LEADER_ADDRESS로 리더에 합류, 병렬 토폴로지 형성 | LWS_WORKER_INDEX로 랭크·역할 결정 |
| 5 | 리더가 추론 서버 기동, ClusterIP 서비스로 노출 | 요청은 리더만 받아 그룹 내부로 분산 |
| 6 | 파드/노드 장애 → restartPolicy에 따라 그룹 전체 재생성(기본) | 부분 좀비 그룹을 남기지 않음 |
| 7 | 스펙 변경 → 그룹 단위 롤링업데이트(maxUnavailable/maxSurge) | 그룹을 통째로 교체해 무중단 갱신 |
실제 처리 흐름 (텍스트 다이어그램)
LeaderWorkerSet: replicas = N (그룹 N개), size = 1 leader + M workers
client --HTTP--> svc/vllm-leader:8080 <- 요청은 리더(0번)로만 유입
|
v
================== Group i ================== (= 모델 복제본 1개)
+---------------+ join: ray start +---------------+
| leader idx0 | --address= | worker idx1 |
| Ray head | <=== $(LWS_LEADER_ADDRESS) | Ray worker |
| + vLLM server | +---------------+
+-------+-------+ (idx 1..M 워커가 동일하게 리더에 합류)
|
v ClusterIP 서비스로 노출 (그룹 내부에서 TP x PP 분산 실행)
----------------------------------------------------------------------
한 그룹 = 한 배타 토폴로지 (같은 랙 / NVLink / TPU 슬라이스)
파드 하나라도 죽으면 --> (기본) 그룹 전체 재생성 (부분 좀비 금지)
스펙을 바꾸면 --> 그룹 단위 롤링 (maxUnavailable + maxSurge)
5. LeaderWorkerSet 기술 설치 방법
전제 조건은 쿠버네티스 1.26 이상입니다(1.26은 Start Ordinal 피처 게이트 수동 활성 필요, 그 이상은 기본 활성). maxUnavailable 기반 그룹 롤링은 K8s 1.35에서 Beta로 기본 활성됐고, 그 이전 버전에선 MaxUnavailableStatefulSet 게이트를 켜야 하며 아니면 파드가 1개씩 굴러갑니다. 컨트롤러는 lws-system 네임스페이스에 뜨므로 노드에 여유 CPU/메모리가 있어야 합니다.
# 1) 릴리스 매니페스트로 설치 (server-side apply 권장: 큰 CRD/주석 대비)
VERSION=v0.8.0
kubectl apply --server-side -f \
https://github.com/kubernetes-sigs/lws/releases/download/$VERSION/manifests.yaml
# 2) 컨트롤러가 뜰 때까지 대기
kubectl wait deploy/lws-controller-manager -n lws-system \
--for=condition=available --timeout=5m
# 3) (대안) Helm 차트로 설치
helm install lws oci://registry.k8s.io/lws/charts/lws \
--version=0.8.0 --namespace lws-system --create-namespace \
--wait --timeout 300s
# 4) (대안) 최신 개발판
kubectl apply --server-side -k github.com/kubernetes-sigs/lws/config/default?ref=main
# 5) CRD 설치 확인
kubectl get crd leaderworkersets.leaderworkerset.x-k8s.io
주의: 웹훅 인증서는 내부 인증서가 기본이며, 회전이 필요하면 cert-manager로 교체할 수 있습니다. client-side apply는 CRD가 커서 "annotation too long"으로 실패하기 쉬우니 반드시 --server-side를 쓰세요. 설치만으로는 아무 그룹도 생기지 않습니다 — 6절처럼 LWS 객체를 만들어야 동작합니다.
6. LeaderWorkerSet 기술 사용 방법
코드 / 설정 (vLLM 멀티노드 예시)
모델 1개를 2노드에 걸쳐(PP=2, 노드당 TP=8) 띄우고, 그런 복제본을 2개 운영하는 최소 형태입니다. 리더는 Ray head + vLLM 서버, 워커는 주입된 LWS_LEADER_ADDRESS로 head에 붙습니다. 분산추론은 startupPolicy: LeaderReady와 기본 RecreateGroupOnPodRestart가 정석 조합입니다.
apiVersion: leaderworkerset.x-k8s.io/v1
kind: LeaderWorkerSet
metadata:
name: vllm
annotations:
# 그룹을 같은 저지연 도메인(노드풀/랙)에 배타적으로 묶는다
leaderworkerset.sigs.k8s.io/exclusive-topology: cloud.google.com/gke-nodepool
spec:
replicas: 2 # 모델 복제본(그룹) 2개
startupPolicy: LeaderReady # 리더(Ray head) Ready 후 워커 생성
rolloutStrategy:
type: RollingUpdate
rollingUpdateConfiguration:
maxUnavailable: 1
maxSurge: 1 # maxSurge와 maxUnavailable이 동시에 0이면 안 됨
leaderWorkerTemplate:
size: 2 # 그룹당 파드 수 = 리더1 + 워커1 (= PP 2노드)
restartPolicy: RecreateGroupOnPodRestart # 파드 하나 죽으면 그룹 전체 재생성
leaderTemplate:
spec:
containers:
- name: vllm-leader
image: vllm/vllm-openai:latest
command: ["sh","-c"]
args:
- |
ray start --head --port=6379 --disable-usage-stats;
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Meta-Llama-3.1-405B-Instruct \
--tensor-parallel-size 8 --pipeline-parallel-size 2 --port 8080
resources:
limits: { nvidia.com/gpu: "8" }
ports:
- containerPort: 8080
workerTemplate:
spec:
containers:
- name: vllm-worker
image: vllm/vllm-openai:latest
command: ["sh","-c"]
# LWS가 주입한 리더 주소로 Ray head에 합류
args: ["ray start --address=${LWS_LEADER_ADDRESS}:6379 --block"]
resources:
limits: { nvidia.com/gpu: "8" }
---
# 리더만 노출하는 서비스 (요청은 리더로만)
apiVersion: v1
kind: Service
metadata:
name: vllm-leader
spec:
selector:
leaderworkerset.sigs.k8s.io/name: vllm
leaderworkerset.sigs.k8s.io/worker-index: "0" # 리더 = worker-index 0
ports:
- port: 8080
targetPort: 8080
운영 시 고려사항
- startupPolicy를 런타임에 맞춰라: Ray처럼 head 선기동이 필요한 스택은 반드시 LeaderReady. 기본 LeaderCreated면 워커가 head 없이 붙으려다 CrashLoopBackOff에 빠진다(가장 흔한 첫 실패).
- restartPolicy 기본값을 이해하라: 분산추론은 부분 재시작이 토폴로지를 깨므로 RecreateGroupOnPodRestart가 맞다. 다만 모델/이미지가 크면 재생성 비용이 크니, 풀 도중 재생성 폭주는 RecreateGroupAfterStart(v0.9+)나 이미지 프리풀로 막는다.
- 배타 토폴로지를 반드시 박아라: 그룹을 같은 저지연 도메인(랙·슈퍼노드·TPU 슬라이스)에 묶지 않으면 노드 간 병렬이 네트워크에 발목 잡혀 멀티노드가 단일노드만 못해진다. exclusive-topology + NVLink/InfiniBand.
- 게이트 스케줄링을 먼저 깔아라: GPU가 비싸고 부분 점유 데드락이 위험하다 — Kueue/NVIDIA KAI/Volcano로 올-오어-낫싱 스케줄을 보장하라. 없으면 "그룹의 절반만 떠서 GPU를 깔고 앉아 대기"가 난다.
- readiness는 "모델 로드 + 워커 합류 완료"를 봐라: 단순 포트 오픈은 거짓 Ready라 미완성 그룹이 서비스에 노출된다.
- 롤링 여유는 GPU로 환산하라: 여유 GPU가 있으면 maxSurge로 새 그룹을 먼저 띄워 교체, 없으면 maxUnavailable. 둘 다 0은 금지.
7. LeaderWorkerSet 기술 자주 쓰는 명령어 (사례)
| 목적 | 명령 | 메모 |
| 컨트롤러 확인 | kubectl get deploy -n lws-system | lws-controller-manager가 Available인지 |
| LWS 목록/상태 | kubectl get lws / kubectl describe lws NAME | 그룹 수·업데이트 상태·이벤트 확인 |
| 그룹/리더 라벨로 보기 | kubectl get pods -L leaderworkerset.sigs.k8s.io/group-index,leaderworkerset.sigs.k8s.io/worker-index | 파드가 아니라 그룹 단위로 봐야 한다 |
| 특정 그룹만 / 리더만 | ... -l ...group-index=0 / ... -l ...worker-index=0 | worker-index=0이 곧 리더 |
| 그룹 수 스케일 | kubectl scale lws vllm --replicas=4 | scale 서브리소스 → HPA도 동일 경로 |
| 롤링 관찰 | kubectl get lws,pods -w / kubectl edit lws vllm | 그룹이 통째로 교체되는지 확인 |
| 리더 부트스트랩 로그 | kubectl logs vllm-0 | grep -i "Loading model weights" | 분산 텐서 병렬 적재가 됐는지 |
| 서비스 호출 | kubectl port-forward svc/vllm-leader 8080:8080 | 이후 /v1/completions로 요청 |
| 주입 환경변수 확인 | kubectl exec vllm-0-1 -- env | grep LWS_ | LWS_LEADER_ADDRESS·LWS_WORKER_INDEX 검증 |
8. LeaderWorkerSet 기술 활용방안
실제 활용 분야
- 단일 노드를 넘는 거대 모델 서빙: Llama-3.1-405B, DeepSeek-R1 671B처럼 GPU 1노드에 안 들어가는 모델을 2+ 노드로 샤딩(PP×TP). LWS 공식 vLLM 예제가 바로 405B다.
- vLLM·SGLang·TensorRT-LLM·llama.cpp 멀티노드: 모두 LWS 공식 예제를 제공하며, "리더=Ray head, 워커=Ray worker" 패턴을 공유한다.
- 상위 서빙 스택의 토대: 프리필/디코드 분리(disaggregated), llm-d, NVIDIA Dynamo 같은 스택이 그 아래 멀티노드 단위로 LWS를 쓴다.
- TPU 멀티슬라이스 서빙(GKE): 서브그룹 + TPU_WORKER_HOSTNAMES 등으로 슬라이스 토폴로지를 표현한다. Red Hat OpenShift는 LWS Operator로 제공한다.
대안 기술 비교
| 기술 | 방식 | 강점 | 약점 |
| Deployment | 서로 무관한 복제본 | 단순·보편·HPA 성숙 | 그룹 개념 없음 → 멀티노드 1모델 불가 |
| StatefulSet | 안정 신원·순서·DNS | 고정 호스트네임·순차 기동 | 파드 단위 — 그룹 롤링/올오낫싱 없음 → 수제 스크립트 |
| JobSet | 다중 그룹 Job(배치) | 멀티노드 배치/학습에 최적 | 완료형 — 상시 추론 서버엔 부적합 |
| KubeRay (RayCluster) | Ray 클러스터 CRD | 강력한 분산 런타임·자체 오토스케일 | K8s 워크로드 시맨틱(그룹 롤링/토폴로지)은 별도 — LWS와 상보적 |
| LeaderWorkerSet | 그룹 = 복제 단위 워크로드 | 멀티노드 1급·올오낫싱·그룹 롤링·토폴로지·HPA | 런타임(Ray 등)은 미포함, 게이트 스케줄 Alpha |
언제 쓰면 안 되는가
- 모델이 단일 노드/단일 파드에 들어갈 때: 평범한 Deployment + KServe/vLLM이면 충분하다. LWS는 과설계다.
- 완료형 배치/학습 작업: 시작-끝이 있는 일은 Job/JobSet이 맞다.
- 느슨하게 결합된 스테이트리스 워커 풀: 그룹 올오낫싱 시맨틱이 오히려 가용성을 해친다.
- Ray의 동적 스케줄·오토스케일을 그대로 쓰고 싶을 때: KubeRay가 더 직접적일 수 있다(상보적이지만 같은 일을 이중 운영하진 말 것).
- 게이트 스케줄러가 없고 GPU가 빠듯할 때: 올오낫싱 보장이 없으면 부분 스케줄 데드락이 난다 — 먼저 Kueue 등을 도입하라.
일을 할 때 주의해야 할 점들
- "Deployment처럼 보이지만 아니다": 단위는 파드가 아니라 그룹이다. 모니터링·디버깅·알람을 모두 그룹 라벨 기준으로 짜라.
- startupPolicy/Ray 의존이 첫 함정: 리더 선기동을 안 맞추면 워커가 head 없이 붙으려다 무한 크래시루프. 신규 도입 시 80%가 여기서 막힌다.
- 배타 토폴로지를 안 박으면 멀티노드가 손해: 노드 간 병렬은 대역폭/지연에 민감하다. exclusive-topology 없이 흩뿌리면 단일노드보다 느려진다.
- 기본 재시작이 "그룹 전체 재생성"임을 알고 써라: 큰 모델은 재생성 비용이 크다. 이미지 프리풀·RecreateGroupAfterStart로 풀 도중 재생성 폭주를 막아라.
- 게이트 스케줄링 없이는 GPU 데드락: "그룹 절반만 떠서 GPU 깔고 앉기"를 막으려면 쿼터·올오낫싱이 먼저다. 측정 없는 배치는 추측이다.
트러블슈팅 (증상 → 원인 → 해결)
| 증상 | 원인 | 해결 |
| 워커가 CrashLoopBackOff, "cannot connect to head" | 기본 LeaderCreated로 head 전에 워커가 붙음 | startupPolicy: LeaderReady, 워커 진입점에서 LWS_LEADER_ADDRESS 폴링/대기 |
| 그룹 일부만 Pending인데 GPU는 점유한 채 정지 | 게이트(올오낫싱) 스케줄 부재 → 부분 스케줄 | Kueue/KAI/Volcano로 그룹 올오낫싱, 쿼터·노드풀 점검 |
| 워커 1개 죽었는데 리더는 살아 응답이 깨짐(좀비 그룹) | restartPolicy: None이거나 readiness 부실 | 기본 RecreateGroupOnPodRestart 사용, readiness에 워커 합류 반영 |
| 멀티노드인데 처리량이 단일노드만 못함 | 그룹이 노드/랙에 흩어져 노드 간 대역폭/지연이 병목 | exclusive-topology로 같은 저지연 도메인 배치, NVLink/IB |
| 롤링업데이트가 파드 1개씩만, 너무 느림 | K8s <1.35거나 maxUnavailable 미활성 | 1.35+(Beta 기본) 또는 MaxUnavailableStatefulSet 게이트, surge 설정 |
| kubectl apply 시 "annotation too long"/CRD 충돌 | client-side apply로 큰 CRD 적용 | --server-side로 설치(가이드 권장) |
| 큰 이미지 풀 도중 그룹이 계속 재생성 | 기본 정책이 pending 중에도 재생성 | RecreateGroupAfterStart(v0.9+; v0.8은 실험 주석), 이미지 프리풀 |
| ${LWS_LEADER_ADDRESS}로 DNS가 안 풀림 | 헤드리스 서비스/subdomain 정책 문제 | subdomainPolicy 확인, 헤드리스 서비스·NetworkPolicy 점검 |
'AI(Artificial Intelligence)' 카테고리의 다른 글
| AI Agent의 핵심 인프라, MCP(Model Context Protocol) (0) | 2026.06.10 |
|---|---|
| 다중 LLM 키·라우팅 지옥 끝낸 LiteLLM 2.x AI Gateway (0) | 2026.06.09 |
| 상태 기반 AI Agent를 안정적으로 설계하기 위한 Agent Orchestration Framework, LangGraph (0) | 2026.06.08 |
| Mojo 완벽 정리 - Python superset이자 MLIR 기반 AI 시스템 언어 (1) | 2026.05.31 |
| "Lambda는 부족하고 EKS는 무겁다: Modal이 GPU 인프라를 혁신하는 방법" (0) | 2026.05.22 |