장애 대응¶
목표: 증상에서 원인까지 가장 짧은 경로로 확인합니다.
이 문서는 배포 및 운영 중 발생하는 실패를 다룹니다. 스택이 시작되지 않는 경우, 설정한 값이 오류 없이 적용되지 않는 경우, NIC이나 NPU 디바이스 장애로 서빙이 중단되는 경우입니다. 이 문서는 vLLM 엔진 내부의 실패(컴파일 오류, 워밍업 실패, 타임아웃)를 다루지 않습니다. 엔진 시작 실패는 RBLN vLLM 문제 해결을, 런타임 오류 코드의 정의는 에러 코드를 참고하세요.
대응 순서¶
실패한 스택을 반복해서 재배포하지 마세요. 이전 Pod가 NPU claim, RDMA queue pair, 포트를 반환하기 전에 다시 배포하면 새 Pod도 같은 자원을 요청하다 실패합니다. 그러면 처음 실패의 원인을 확인하기 어려워집니다. 다음 순서로 진행하세요.
- 증상별 진단 표에서 증상에 맞는 원인을 찾습니다.
- 기존 Helm 릴리스를 제거해 이전 Pod가 NPU claim, RDMA queue pair, 포트를 반환하게 합니다.
- 원인을 해결한 뒤 Helm 릴리스를 다시 설치합니다.
증상별 진단¶
다음 표는 증상마다 원인과 원인을 확인하는 방법을 정리합니다.
| 증상 | 원인 | 확인 |
|---|---|---|
| 계속 Pending | RBLN NPU Operator가 DRA 모드가 아니어서 Dynamic Resource Allocation(DRA) ResourceSlice가 게시되지 않았거나, ResourceClaimTemplate(이하 RCT)가 없거나, StorageClass가 맞지 않습니다 | kubectl describe pod의 이벤트로 원인을 구분하세요. RCT가 없는 경우는 다음 행을 참고하세요. StorageClass가 맞지 않으면 PersistentVolumeClaim(PVC)에 ProvisioningFailed가, Pod에 FailedScheduling이 나타납니다. 이어서 kubectl get resourceslices, get resourceclaimtemplate npu-rdma, get pvc를 실행하세요 |
FailedResourceClaimCreation이 몇 분 동안 지속됨 | RCT가 없거나 devices.claimTemplateName이 다른 RCT를 지정합니다 | RCT를 적용하거나 devices.claimTemplateName을 적용한 RCT의 이름으로 설정하세요 |
ImagePullBackOff | Image Pull Secret이 없거나 Secret 이름이 맞지 않습니다 | get secret의 출력과 imagePullSecrets를 비교하세요 |
llm-d Endpoint Picker(EPP) CreateContainerConfigError | Secret llm-d-hf-token이 없습니다 | kubectl -n <ns> get secret llm-d-hf-token |
적용 직후 no matches for kind ... | 다음 중 하나의 CustomResourceDefinition(CRD)이 없습니다: Kubernetes Gateway API, Kubernetes Gateway API Inference Extension(GAIE, InferencePool), GatewayClass provider(agentgateway), VictoriaMetrics Operator(기본) 또는 Prometheus Operator | 없는 CRD를 설치하거나, 그 CRD를 사용하는 구성 요소를 비활성화하세요 |
설치는 성공하지만 Gateway가 Accepted=Unknown(Waiting for controller) 상태로 남음 | GatewayClass provider가 실행 중이지 않습니다 | provider인 Istio 또는 agentgateway를 설치하세요(클러스터 기반 구성 › GatewayClass provider 참고) |
Istio 사용 시 HTTPRoute가 ResolvedRefs=False | Istio에서 GAIE가 활성화되지 않았습니다 | llm-d의 Istio 가이드에 따라 GAIE를 활성화해 Istio를 다시 설치하세요 |
요청 실패 및 modelserver 로그의 SYS_EIO / SYS_TASK_ABORTED | NPU 또는 Rebellions Direct Storage(RDS) 디바이스 장애일 수 있습니다. 오류 이름만으로는 NPU와 RDS 중 어느 디바이스의 장애인지 판별할 수 없습니다 | NPU 및 RDS 디바이스 장애의 fw.log 확인 절차를 따르세요 |
LoadBalancer <pending> | Gateway Service에 할당할 로드 밸런서 주소가 없습니다. 주소 풀이 소진되었거나 주소 풀이 이 네임스페이스에 할당되지 않았습니다(AllocationFailed … no available IPs) | 기본 GatewayClass(Istio)에서는 gateway.annotations에 networking.istio.io/service-type: ClusterIP를 설정해 Service 유형을 바꾸세요. agentgateway에서는 gateway.agentgateway.parameters.enabled: true와 gateway.agentgateway.parameters.service.spec.type: ClusterIP를 설정하세요 |
Pending, 이벤트 cannot allocate all claims(NPU ResourceSlice는 게시됨) | Pod를 배치할 노드에서 RDMA 드라이버가 NIC을 인식하지 못했습니다. 레퍼런스 RCT(numa-aligned.yaml)는 RDMA를 쓸 수 있는 NIC만 요청합니다 | 노드에서 ls /sys/class/infiniband/를 확인하세요 |
CrashLoopBackOff(rbln-ccl/RDMA) | 새 Pod가 이전 Pod가 남긴 프로세스, RDMA queue pair, 포트와 경합합니다 | Helm 릴리스를 제거하고, 이전 Pod가 모두 삭제된 뒤 Helm 릴리스를 다시 설치해 오류가 재발하는지 확인하세요 |
시작 중 Not enough memory for N blocks of KV cache 또는 SYS_EBUSY: Lack of device memory | 엔진이 시작 시 계산한 블록 수가 노드의 디바이스 메모리에 맞지 않습니다 | modelserver.extraServeArgs.<role>로 --gpu-memory-utilization을 낮추세요. 뒤의 플래그가 우선합니다 |
--set이 적용되지 않음 | 자유 형식 맵(serveEnv, podOverrides.<role>, gateway.annotations 등)이나 하위 차트(llm-d-router-gateway.*) 아래의 --set 키에 오타가 있습니다. 스키마가 이 위치를 검사하지 않으므로 차트는 오타를 오류 없이 무시합니다. 스키마가 정의한 키의 오타는 렌더링을 실패시킵니다 | helm get values의 출력과 설정한 키를 비교하세요 |
DRA 드라이버를 설치한 직후에는 드라이버가 ResourceSlice를 게시하기 전이라 Pod가 기다리는 중일 수 있습니다.
Pod가 노드에 스케줄링된 뒤 Ready 상태가 되지 않으면, Pod가 통과하지 못한 프로브를 확인하세요. 프로브별 원인과 확인 방법은 시작 및 준비 상태 프로브를 참고하세요.
P/D role별 장애 영향¶
다음 표는 prefill Pod나 decode Pod가 중단될 때의 영향과 복구 방법을 정리합니다.
| 상황 | 영향 | 복구 |
|---|---|---|
| prefill Pod 하나 중단 | EPP가 새 요청을 남은 prefill Pod로 보내므로 첫 토큰까지의 시간(TTFT)이 늘어납니다. prefill Pod가 하나도 없으면 decode가 직접 prefill을 수행하므로 요청은 실패하지 않습니다. prefill Pod가 다시 Ready가 될 때까지 스택은 P/D 분리 없이 동작합니다 | Deployment가 Pod를 재생성합니다. 디바이스 장애라면 NPU 및 RDS 디바이스 장애를 따르세요. NPU가 반환되지 않으면 새 Pod가 Pending 상태로 남습니다(증상별 진단 참고) |
| decode Pod 하나 중단 | 중단된 decode Pod가 처리하던 요청의 토큰 생성이 멈춥니다. 다른 decode Pod가 처리하는 요청에는 영향이 없습니다. EPP는 새 요청을 남은 decode Pod로 보냅니다 | prefill 행과 같습니다 |
prefill Pod가 중단되면 진행 중인 요청이 key-value(KV) 캐시 전송에 실패할 수 있습니다. prefill은 계산한 KV를 high-bandwidth memory(HBM)에 보관하므로, decode가 아직 가져오지 않은 KV는 prefill Pod가 종료될 때 함께 유실됩니다. 전송 실패 시 동작은 P/D 전송 실패 시 동작을 참고하세요.
Pod가 재시작되면 컴파일 캐시 PVC에 저장된 컴파일 결과를 사용하므로 모델을 다시 컴파일하지 않습니다. 반면 재시작 전에 저장한 KV 캐시는 HBM과 tier 2(RDS) 어디에서도 재사용하지 않습니다. 따라서 재시작한 Pod는 재시작 전에 처리한 프롬프트도 prefill을 처음부터 다시 계산합니다.
P/D 전송 실패 시 동작¶
decode는 prefill이 생성한 KV를 RDMA READ로 가져옵니다. KV 전송이 실패하면 decode의 kv_load_failure_policy 값에 따라 동작이 달라집니다.
kv_load_failure_policy | 전송이 실패한 요청 |
|---|---|
fail(기본값) | 오류로 종료됩니다 |
recompute | decode가 prefill을 다시 계산합니다. 클라이언트에는 오류 대신 응답 지연으로 나타납니다 |
차트는 kv_load_failure_policy를 설정하지 않습니다. 따라서 업스트림 기본값인 fail이 적용됩니다.
kv_load_failure_policy를 recompute로 바꾸려면 decode의 --kv-transfer-config JSON에 "kv_load_failure_policy":"recompute"를 추가하세요.
kv_load_failure_policy 값별 동작의 정의는 vLLM NIXL Connector Usage와 llm-d Disaggregated Serving: Operations(vLLM)의 Fault Tolerance 절을 참고하세요.
RoCE NIC 장애¶
레퍼런스 구성에서 prefill Pod와 decode Pod는 각각 RoCE NIC 2개를 할당받습니다(디바이스 할당). 엔진은 NIC 하나로 KV를 전송하고 다른 NIC 하나를 예비로 둡니다. 다음 표는 차트 기본값으로 설치한 스택에서 NIC 링크를 down시킨 뒤 측정한 요청 결과와 복구 동작입니다. 이 동작은 Target Configuration 검증과 별도로 측정했습니다.
| 장애 | 진행 중인 요청 | 새 요청 | 복구 |
|---|---|---|---|
| 사용 중인 NIC 하나의 링크가 down됨 | 실패 | 엔진이 예비 NIC으로 전환하기 전에는 대기하거나 실패하고, 전환한 뒤에는 성공. decode NIC은 약 30초, prefill NIC은 약 1분 30초 뒤 다시 성공 | 자동. 재시작 없음 |
| Pod의 모든 NIC 링크가 down됨 | 실패 | 대부분 오류(500)로 종료 | 링크 하나가 복구되면 자동으로 전송 재개. 재시작 없음 |
모든 NIC 링크가 down되면 decode 로그에 다음 메시지 중 하나가 남습니다.
- decode NIC이 모두 down됨:
every local RDMA link is down - prefill NIC이 모두 down됨:
engine <id> reports every RDMA link down
링크 장애 시 prefill 컨테이너 재시작¶
모든 로컬 RDMA 링크가 일정 시간 down 상태일 때 prefill 컨테이너를 재시작하게 하려면 link_down_exit_s를 설정하세요. 차트는 link_down_exit_s를 설정하지 않으며, 기본값 0에서는 prefill이 종료하지 않습니다.
--kv-transfer-config의 prefillRblnNixlConnector항목에서kv_role을kv_producer로 바꿉니다. 차트 기본값은kv_both이며, 다른kv_role에서link_down_exit_s를 설정하면 엔진이 시작을 거부합니다.- 같은 항목의
kv_connector_extra_config에link_down_exit_s(초)를 추가합니다.
지정한 시간이 지나면 prefill 프로세스가 종료됩니다. Pod는 유지되고 컨테이너만 재시작됩니다.
NPU 및 RDS 디바이스 장애¶
이 스택은 NPU나 RDS 디바이스 장애를 자동으로 복구하지 않습니다. 컨테이너가 재시작되어도 디바이스 복구를 보장하지 않습니다. 장애를 확인한 뒤 장애가 발생한 노드의 Pod를 다른 노드로 옮기세요.
장애 확인¶
장애는 Pod 상태가 아니라 로그로 확인하세요. 디바이스 장애가 발생하면 다음 증상 중 하나가 나타납니다.
- modelserver 로그에
SYS_EIO나SYS_TASK_ABORTED같은 런타임 오류가 기록됩니다(에러 코드). - 요청은 실패하지 않지만 tier 2(RDS)에 저장한 KV 캐시를 재사용하지 못합니다. LMCache가 실패한 RDS 조회를 캐시 미스로 처리하기 때문입니다.
같은 오류 이름이 NPU와 메모리 오류에도 사용되므로 오류 이름만으로는 RDS 장애를 판별할 수 없습니다. RDS 장애는 노드의 coredump 디렉터리에 있는 fw.log로 확인하세요.
- 디바이스가 리셋되면 NPU 시스템 관리 데몬 RSMD(
rbln-smd)가 노드에 진단 디렉터리/var/log/rebellions/<device>.<time>.coredump/를 만듭니다. 이 문서는 이 디렉터리를 coredump라고 부릅니다. - coredump 안의
fw.log파일에는 NPU 펌웨어 로그가 들어 있습니다(RBLN System Management Daemon). - RDS 명령 실패가 NPU 작업을 중단시키면(
err_code 0x10015) 디바이스가 리셋되므로 coredump가 생성됩니다. 디바이스 리셋으로 이어지지 않은 장애는 coredump를 남기지 않습니다. - coredump 안의
meta파일에는 coredump를 만든 상황이situation값으로 기록됩니다. 디바이스 리셋이 실패하면situation = RESET_FAIL이 기록되고, 이 coredump에는fw.log가 없을 수 있습니다. 이때는situation = RESET인 coredump의fw.log를 확인하세요.
RDS 명령 실패가 NPU 작업을 중단시키면 fw.log에 다음 로그가 남습니다. 마지막 줄의 err_code 0x10015가 RDS 장애를 나타냅니다.
[cl0-cp0 ...] ERR /0 0 [rds] -> Command failed: cid=17 status=0x0281
[cl0-cp0 ...] ERR /0 0 [rds] rblnfs_wait_batch: qid=0 NVMe command failed after 0/1 completions
[cl0-cp0 ...] INFO /0 0 hils_set_guilty_ctx: guilty ctx(0x80000001)
[cl0-cp0 ...] ERR /0 0 hils_set_err_reason: err_code 0x10015
coredump 디렉터리가 없으면 노드의 커널 로그에서 rblnfs와 nvme 관련 메시지를 확인하세요. 노드에 셸로 접근할 수 없으면 kubectl debug node/<node>로 디버그 Pod를 실행한 뒤 같은 명령을 실행하세요.
Pod 이동¶
장애가 발생한 노드의 Pod를 다음 순서로 옮기세요.
- 증거를 보존하세요. Pod를 재생성하면 기존 Pod의 로그도 삭제됩니다. Pod 로그(
kubectl -n <ns> logs <pod>, 컨테이너가 재시작했으면--previous추가), 노드 커널 로그, coredump 디렉터리를 복사하세요. - 노드에 새 Pod가 배치되지 않게 하세요.
kubectl cordon <node>를 실행하세요. - Pod를 옮기세요.
kubectl -n <ns> delete pod <pod>를 실행하세요. 다른 노드에 NPU와 RoCE NIC 여유 용량이 있어야 합니다.nodePlacement모드에서는 장애 노드를 지정한nodePlacement항목의node를 바꾼 뒤 uninstall 후 install하세요(확장 및 업그레이드 › 변경 규칙). - 디바이스를 교체한 뒤 노드에 Pod 배치를 다시 허용하세요.
kubectl uncordon <node>를 실행하세요.
RDS를 비활성화한 서빙¶
Pod를 받을 다른 노드가 없으면 RDS를 비활성화하고 서빙을 계속할 수 있습니다. RDS를 비활성화하면 스택은 tier 1(HBM)만 사용합니다. RDS를 비활성화하려면 다음 키에서 LMCACHE_STORAGE_PLUGINS와 LMCACHE_EXTRA_CONFIG를 null로 설정하세요.
| 설치 | 키 |
|---|---|
기본 values, P/D 레시피(examples/models/<model>/pdd.yaml) | modelserver.extraServeEnv.prefill |
AGG 레시피(examples/models/<model>/agg.yaml) | modelserver.extraServeEnv.decode |
예를 들어 기본 values에서는 다음 옵션으로 helm upgrade를 실행하세요. Deployment는 extraServeEnv를 바꾼 P/D role의 Pod만 재생성합니다.