VDI VM이 자꾸 죽는데 범인은 인프라가 아니었다? Citrix BSOD 0x96 해결 가이드

오늘은 억울한 이야기를 좀 해야겠네요. VDI 에 문제가 생겨 범인으로 몰렸던 저의 이야기를 한 번 들어보시죠. 

멀쩡하던 VM이 며칠에 한 번씩 파란 화면(BSOD)을 띄우며 죽어버리면 정말 미칠 노릇이죠. 진짜 억울한 상황입니다. 저도 압니다. 특히 고객사나 현업 팀에서는 "서버(HCI)가 불안정한 거 아니냐"며 인프라 엔지니어를 제일 먼저 들볶기 마련이거든요. 답답한 마음에 하이퍼바이저 로그를 샅샅이 뒤져봐도 서버 하드웨어나 클러스터에는 딱히 에러 기록이 없습니다. 결국 윈도우 덤프를 열어보면 ntoskrnl.exe가 범인이라고 떡하니 찍혀 나옵니다. 환장할 노릇이죠. 그런데 이 문제, 알고 보니 하드웨어나 윈도우 OS의 문제가 아니라 Citrix의 특정 버전에서 발생하는 아주 고약한 버그였습니다.

콘솔에서 블루스크린 발생한 화면
콘솔에서 블루스크린 발생한 화면


위의 이미지처럼 파란 화면이 뜨면 관리자는 일단 당황하기 마련입니다. 하지만 절대 당황하지 마세요. 윈도우가 스스로 죽었다는 것은 전체 시스템이 완전히 망가지는 것을 막기 위해 스스로 보호 조치를 취했다는 뜻입니다. 그리고 그 원인은 메모리 덤프 파일 안에 고스란히 기록으로 남습니다. 우리는 그 '덤프 파일'을 해부해서 진짜 범인을 추적해 나갈 겁니다. 인프라의 결함이 아니라는 빼도 박도 못할 증거를 찾는 첫 단추는 바로 덤프의 환경 정보를 확인하는 것부터 시작합니다.


인프라 엔지니어가 독박 쓰는 억울한 BSOD 상황

보통 VM이 죽으면 습관적으로 스토리지 IOPS나 하이퍼바이저 이벤트 로그부터 열어보시죠? 저도 예전엔 그랬습니다. 하지만 이번 케이스는 가상화 레이어의 결함이 아니더라고요. 윈도우 커널 덤프를 디버거로 분석해보면 INVALID_WORK_QUEUE_ITEM (96)이라는 낯선 오류 코드가 등장합니다. 이건 시스템이 작업을 순서대로 처리하기 위해 줄을 세워둔 '워크 큐(Work Queue)'에서 특정 항목을 꺼내려다 실패했을 때 발생해요. 큐의 앞뒤 연결 고리(flink, blink)가 아예 끊어져 버린 상태인 거죠. 윈도우 입장에서는 "어? 다음 처리할 데이터가 어디로 증발했지?" 하고 패닉에 빠지다가, 더 큰 데이터 오염을 막으려고 스스로 뻗어버리는 겁니다.

덤프 분석의 시작: vertarget으로 타임라인 진단하기

문제를 정확히 진단하려면 먼저 덤프 파일에서 커널 버전을 확인해야 합니다. 명령창에 아래 명령어를 입력해 현재 운영체제의 빌드와 시스템 구동 시간을 체크해 보세요. 단순히 OS 버전을 아는 것을 넘어, 이 서버가 죽기 전까지 과연 며칠이나 버텼는지 파악하는 게 디버깅의 핵심입니다.

0: kd> vertarget
Windows 10 Kernel Version 14393 MP (2 procs) Free x64
Product: Server, suite: Terminal Server
Edition build lab: 14393.6897.amd64fre.rs1_release. 240404-1613
Kernel base = 0xfffff800`5b88e000 PsLoadedModuleList = 0xfffff800`5bb94ca0
Debug session time: Fri May 10 05:53:34.294 2024 (UTC-4:00)
System Uptime: 5 days 2:26:59.025

위의 vertarget 실행 결과를 꼼꼼히 뜯어볼까요? 여기서 제일 눈여겨볼 알짜배기 정보는 바로 맨 아랫줄의 'System Uptime'입니다. 무려 5일 2시간 동안이나 서버가 멀쩡히 돌아가다가 갑자기 죽었습니다. 보통 CPU나 메모리 같은 하드웨어 깡통에 문제가 생기면 부팅 직후에 죽거나 아무 때나 랜덤하게 픽픽 꺼집니다. 하지만 5일이라는 일정한 타임라인을 두고 뻗는다는 건 하드웨어 장애가 아닙니다. 소프트웨어가 백그라운드에서 메모리를 야금야금 갉아먹었거나, 특정 주기로 실행되는 에이전트 작업이 큐에 쓰레기 데이터를 쌓아두고 제대로 비우지 못했을 때 나타나는 전형적인 패턴이거든요. 14393 빌드(Windows Server 2016 계열) 환경에서 이런 식으로 규칙적인 간격을 두고 BSOD가 난다면, 하이퍼바이저 탓을 하기 전에 VDA 에이전트 쪽의 메모리 누수나 드라이버 충돌을 먼저 의심하는 게 맞습니다.

원인 분석: 왜 메모리가 튀고 워크 큐가 박살 났을까?

분석을 한 단계 더 깊게 들어가 보겠습니다. 실제 에러를 터뜨린 지점이 nt!lopProcessWorkItem이라는 것을 스택에서 확인할 수 있습니다. 아래 스택 트레이스를 한 번 훑어보세요. 커널이 내부적으로 작업을 처리하다가 정확히 어느 지점에서 비명을 질렀는지 아주 적나라하게 보여줍니다.

STACK_TEXT:
ffffc700`acb38488 fffff800`5ba30419 : 00000000`00000096 ffff938d`96d10208 ... : nt!KeBugCheckEx
ffffc700`acb38490 fffff800`5b8d0f4a : ffff938d`8c9943c0 fffff800`5bc4e590 ... : nt!KiAttemptFastRemovePriQueue+0xbfe99
ffffc700`acb384d0 fffff800`5b8d0af0 : fffff800`5bc4e140 ffff938d`8c9943c0 ... : nt!KeRemovePriQueue+0x20a
ffffc700`acb38540 fffff800`5b922aa9 : ffff938d`8c9943c0 00000000`00000080 ... : nt!ExpWorkerThread+0x80

스택을 아래에서부터 거꾸로 타고 올라가면서 흐름을 읽어보세요. ExpWorkerThread가 워커 스레드를 열심히 돌리면서 큐에 쌓인 다음 작업을 꺼내려다가 nt!KeRemovePriQueue 단계에서 덜컥 걸려 넘어졌습니다. 왜 그랬을까요? 앞뒤 작업의 연결 고리를 담고 있는 포인터(flink, blink)가 텅 비어있는 null 상태였기 때문이죠. 누군가 큐에 작업을 밀어 넣기만 하고 제대로 된 주소값을 맵핑하지 않은 겁니다. 윈도우 커널은 자기가 철석같이 믿고 관리하던 큐가 완전히 오염된 것을 발견하고는 시스템 보호를 위해 스스로 블루스크린을 띄웠습니다. 겉보기에는 ntoskrnl.exe가 잘못한 것처럼 보일 수 있습니다. 하지만 진짜 범인은 따로 있죠. 커널 영역의 워커 스레드에 엉뚱한 데이터를 쑤셔 넣고 큐를 망가뜨린 서드파티 드라이버가 만악의 근원입니다.

디버깅 화면
윈도우 커널 디버깅 화면

이 화면에서 보이는 lopProcessWorkItem 함수는 워크 큐 내의 아이템을 정상적으로 프로세싱하도록 설계된 윈도우의 기본 루틴입니다. 윈도우는 자기 할 일을 하려 했을 뿐입니다. 서드파티 소프트웨어(이 케이스에서는 Citrix VDA)가 워커 스레드를 잘못된 방식으로 호출하고 관리하지 않았다면 절대 발생하지 않았을 오류죠.


실무에서 주의할 점: 덤프 첫 화면의 ntoskrnl.exe에 속지 마세요

정말 많은 주니어 엔지니어분들이 덤프를 열자마자 첫 화면에 PROCESS_NAME: System이나 MODULE_NAME: ntoskrnl이 찍히는 것을 보고 "아, 마이크로소프트 윈도우 자체 버그네. 업데이트 돌려야겠다" 하고 성급하게 판단해버립니다. 큰일 날 소리죠. 윈도우의 심장부인 핵심 커널은 그렇게 호락호락하게 혼자 죽지 않습니다. 대개 외부 드라이버가 커널 메모리 영역을 무단으로 침범하거나 큐 구조를 오염시키는 게 본질적인 원인입니다. 이럴 때는 반드시 !address 명령어로 해당 메모리 번지를 직접 찔러보는 습관을 들이셔야 합니다.

0: kd> !address fffff8005b8d3780
Usage: Module
Base Address: fffff800`5b88e000
End Address: fffff800`5c0ae000
Region Size: 00000000`00820000
VA Type: Boot Loaded
Module name: ntoskrnl.exe
Module path: [\SystemRoot\system32\ntoskrnl.exe]

!address 명령은 특정 메모리 주소가 정확히 어떤 용도로 쓰이고, 어느 모듈에 속해 있는지 족집게처럼 짚어줍니다. 여기서 fffff8005b8d3780이라는 주소를 찍어보니, 확실히 ntoskrnl.exe 범위 안에 로드되어 있다고 나오네요. 주소만 보면 OS 문제 같습니다. 하지만 앞서 스택 트레이스에서 확인한 lopProcessWorkItem 시그니처와 이 INVALID_WORK_QUEUE_ITEM 오류를 나란히 놓고 조합해보세요. 이 정확한 에러 패턴은 Citrix CVAD 2203 CU4 버전을 사용하는 환경에서 발생하는 고질적인 버그(Known Issue)와 소름 돋게 완벽히 겹칩니다. 백날 스토리지 IOPS 뒤져보고 네트워크 패킷 캡처해 봐야 답이 안 나오는 게 당연했습니다. 소프트웨어 에이전트 자체의 결함이었으니까요.


공식 문서에는 없는 현장 꿀팁: 반박 불가 메일 보내기

알고 보니 이 모든 소동의 원인은 인프라가 아니라 Citrix CVAD 2203 CU4 버전에 포함된 VDA 드라이버의 결함이었습니다. 다행스럽게도 Citrix 제조사 측에서도 이미 이 문제를 공식적으로 인지하고 수정 패치를 내놓은 상태입니다. 우리는 이제 그저 VDI 담당자에게 정확한 팩트와 가이드를 전달하기만 하면 됩니다. 아래 표를 그대로 복사해서 담당자에게 메일로 던져주세요.

구분 상세 내용
영향받는 버전 Citrix CVAD 2203 CU4 (VDA)
장애 시그니처 Bugcheck 0x96 (INVALID_WORK_QUEUE_ITEM)
핵심 원인 함수 nt!lopProcessWorkItem 및 Null Pointer (flink/blink)
해결 방법 2203 LTSR CU4 Update 1 이상으로 업그레이드 조치 요망

위의 표에서 보시는 것처럼 우리가 취해야 할 조치는 아주 명확합니다. 장애가 난다고 인프라 환경을 무턱대고 재구축하거나 서버 사양을 높인다고 해결될 일이 절대 아니에요. VDI 담당자에게 이 디버깅 분석 결과와 함께 Citrix 공식 기술 문서(CTX584794) 링크를 첨부해서 전달하시면 상황은 아주 깔끔하게 종료됩니다. 단순히 "VM 자꾸 죽으니까 확인 좀 해보세요"라고 말하는 것과, "커널 덤프 분석 결과, 0x96 워크 큐 에러가 발생했으며 이는 Citrix 알려진 버그 시그니처와 정확히 일치하니 CU4 Update 1로 패치 바랍니다" 라고 말하는 것은 천지 차이입니다. 여러분의 엔지니어링 전문성도 확 살아나고 억울한 책임에서도 벗어나실 수 있습니다.

현장에서 밤을 새워가며 겪는 이런 트러블슈팅 경험은 정말 돈 주고도 못 사는 귀중한 자산입니다. 이번 기회에 WinDbg 같은 덤프 분석 도구의 기초적인 사용법과 스택 흐름을 읽는 눈을 조금 더 길러두시면, 나중에 비슷한 장애 상황이 터졌을 때 훨씬 당당하고 빠르게 대처하실 수 있을 거예요. 모쪼록 이번 가이드가 여러분의 소중한 퇴근 시간을 지키고 억울한 누명을 벗는 데 큰 도움이 되기를 바랍니다!

이번에도 많은 도움이 되길 바라면서 이만 마치겠습니다. 다음 포스팅에서 만날께요 ~ 

댓글 쓰기

0 댓글

이 블로그 검색

태그

신고하기

프로필

이미지alt태그 입력