453 lines
20 KiB
Markdown
453 lines
20 KiB
Markdown
# GPU Zombie / Orphan Process & Orphaned CUDA Context 재현·해결 실험
|
||
|
||
Kubernetes 위에서 GPU 워크로드가 예기치 않게 종료되거나 signal handler가 잘못 짜였을 때 발생하는
|
||
**좀비 프로세스 / 고아 프로세스 / orphaned CUDA context / ghost GPU memory** 문제를
|
||
단계적으로 재현하고, 각 수정안(tini, preStop hook, code-level signal handler)이
|
||
어디까지 문제를 해결하는지 정량적으로 검증하는 실험 프로젝트.
|
||
|
||
> 실험 설계 전문은 [`plan.md`](./plan.md) 참조. 5개 변형 실험 결과 비교는 [`results/comparison.md`](./results/comparison.md).
|
||
|
||
---
|
||
|
||
## ⭐ 실험 결과 요약 (2026-04-13~14)
|
||
|
||
시나리오 1을 5가지 변형으로 실행한 결과 **plan.md의 핵심 가설("orphan CUDA context 발생")이 현대 driver(580/590)에서 검증되지 않음**.
|
||
대신 **두 가지 다른 진짜 문제**가 더 중요한 것으로 드러남.
|
||
|
||
### 핵심 발견
|
||
|
||
| 가설 (plan.md) | 실제 결과 | 비고 |
|
||
|---|---|---|
|
||
| `kill -9` 후 orphaned CUDA context 잔존 | **❌ 미발생** | driver가 즉시 회수 |
|
||
| Pod 삭제 후 GPU 메모리 잔존 | **❌ 미발생** | cgroup SIGKILL → driver 회수 |
|
||
| 노드 재부팅 필요 가능 | **❌ 불필요** | 모든 케이스 자동 정리 |
|
||
| 좀비/고아 발생 | ✅ **재현 성공** | tini로 해결됨 (시나리오 2 효과 확인) |
|
||
| 30초 grace 동안 GPU 낭비 | **⭐ 발견 (진짜 문제)** | SIGTERM 무시 시 항상 발생 |
|
||
| NCCL hang으로 GPU 점유 | **⭐ 발견 (예상 못한 문제)** | 멀티 GPU 환경에서 발생 |
|
||
|
||
### 진짜 문제 vs plan.md 원래 가설
|
||
|
||
| 진짜 문제 | 발생 조건 | 영향 | 시나리오 X에서 해결 |
|
||
|---|---|---|---|
|
||
| **30초 grace 낭비** | SIGTERM 무시 코드 | GB·sec 낭비 (10w일 때 740 GB·sec) | **시나리오 4 (SIGTERM handler)** |
|
||
| **좀비 누적** | PID 1 ≠ init | 프로세스 슬롯 점유 | **시나리오 2 (tini)** |
|
||
| **NCCL hang** | rank 비정상 종료 | 무한 GPU 점유 | NCCL_ASYNC_ERROR_HANDLING + timeout |
|
||
| **Peer ctx 잔존** | NCCL 멀티 GPU | 작은 누수 (148 MiB) | NCCL hang 해결 시 자동 |
|
||
|
||
### 5개 변형 실험
|
||
|
||
| # | 변형 | 노드/Driver | 핵심 발견 | 결과 디렉토리 |
|
||
|---|---|---|---|---|
|
||
| 1 | 기본 (root, 1 worker) | gpu-4 / 590.48.01 | orphan ctx 미발생 | `results/scenario-1/` |
|
||
| 2 | 구버전 driver | v1003 / 580.126.09 | driver 버전 무관 동일 | `results/scenario-1-driver-580.126.09/` |
|
||
| 3 | non-root (UID 1000) | gpu-4 / 590.48.01 | sudo 없이 kill 가능 | `results/scenario-1-user-soo/` |
|
||
| 4 | 10 workers 동시 | gpu-4 / 590.48.01 | 선형 스케일, 740 GB·sec 낭비 | `results/scenario-1-user-soo-10workers/` |
|
||
| 5 | NCCL 3 GPU | gpu-4 / 590.48.01 | ⭐ **부분 누수 + hang** (유일) | `results/scenario-1-nccl-3gpu/` |
|
||
|
||
자세한 비교 분석: [`results/comparison.md`](./results/comparison.md)
|
||
|
||
### 시나리오 2~4의 재정의된 가치
|
||
|
||
| 시나리오 | 원래 목적 (plan.md) | 실제 가치 |
|
||
|---|---|---|
|
||
| 2 (tini) | orphan ctx 방지 | **좀비 reaping** (init protection) |
|
||
| 3 (preStop) | orphan ctx 방지 | GPU cleanup 명령 grace 활용 |
|
||
| 4 (Full Fix) | orphan ctx 방지 | ⭐ **30초 grace 낭비 제거 + NCCL graceful shutdown** |
|
||
|
||
---
|
||
|
||
## 배경 — 왜 이 실험이 필요한가
|
||
|
||
GPU Pod 하나가 죽어도 GPU 메모리가 해제되지 않거나, `nvidia-smi`에는 프로세스가 안 보이는데
|
||
GPU 메모리는 잡혀 있는 현상(**ghost context**)은 현업에서 자주 관찰된다. 원인은 대체로:
|
||
|
||
1. **PID 1 문제** — 컨테이너의 PID 1이 `bash` 같은 non-init 프로세스면 `waitpid(-1)`을 호출하지 않아 좀비가 쌓임
|
||
2. **고아 프로세스** — 부모가 `wait()` 없이 종료하면 자식은 PID 1로 reparent → PID 1이 bash면 계속 살아남음
|
||
3. **Signal 전달 실패** — `kubectl delete pod` → SIGTERM → PID 1이 자식에게 전달 안 함 → gracePeriod 경과 후 SIGKILL
|
||
4. **Orphaned CUDA context** — 프로세스가 SIGKILL로 급사하면 CUDA driver가 미처 context를 정리하지 못해 GPU 메모리 잔존
|
||
5. **Ghost context** — `nvidia-smi --query-compute-apps`에는 안 잡히는데 `/dev/nvidia*`에 fd가 걸려 있거나 메모리만 점유된 상태
|
||
|
||
이 실험은 **점진적 수정안의 효과 범위**를 시나리오별로 분리해 측정하는 게 목표다.
|
||
|
||
---
|
||
|
||
## 실험 환경
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| 노드 | `gpu-4` (A100 SXM4 80GB × 4) |
|
||
| 컨테이너 런타임 | containerd |
|
||
| cgroup | **v1** (systemd slice 형식) |
|
||
| K8s namespace | `gpu-zombie-test` |
|
||
| 컨테이너 레지스트리 | `harbor.cone-chain.net/aipf` |
|
||
| CI/CD | Gitea Actions + Harbor + ArgoCD |
|
||
| 관리 권한 | `gpu-4` 노드 SSH + sudo 가능 |
|
||
|
||
---
|
||
|
||
## 시나리오 개요
|
||
|
||
총 **4개 시나리오**, **3개 이미지**, **4개 Pod**.
|
||
(시나리오 2·3은 동일 이미지 `:tini`를 공유하며 Pod manifest의 preStop hook 유무로만 차이)
|
||
|
||
| # | 이름 | PID 1 | preStop | Worker 코드 | 이미지 태그 | Pod 이름 |
|
||
|---|---|---|---|---|---|---|
|
||
| 1 | No Fix | `bash` | ❌ | SIGTERM 무시 | `:nofix` | `gpu-zombie-nofix` |
|
||
| 2 | tini only | `tini` | ❌ | SIGTERM 무시 | `:tini` | `gpu-zombie-tini` |
|
||
| 3 | tini + preStop | `tini` | ✅ | SIGTERM 무시 | `:tini` (재사용) | `gpu-zombie-tini-prestop` |
|
||
| 4 | Full Fix | `tini` | ✅ | **SIGTERM handler + CUDA cleanup** | `:fullfix` | `gpu-zombie-fullfix` |
|
||
|
||
### 각 시나리오가 검증하는 가설
|
||
|
||
**시나리오 1 — No Fix** (실험 완료, 5가지 변형으로 검증)
|
||
> 가설: PID 1이 bash면 좀비/고아/orphaned CUDA context가 모두 발생한다.
|
||
|
||
검증 결과:
|
||
- ✅ 고아 발생 — `parent.py` 종료 후 `gpu_worker.py` PPID=1 reparent
|
||
- ✅ 좀비 발생 — kill -9 후 PID 1(sleep)이 reaping 안 함
|
||
- ❌ **orphaned CUDA context 미발생** — driver 590/580 모두 즉시 회수
|
||
- ❌ **Pod 삭제 후 GPU 잔존 미발생** — cgroup SIGKILL이 항상 정리
|
||
- ⭐ **새 발견**: SIGTERM 무시 시 30초 grace 동안 GPU 점유 지속 (배포/스케일 시 리소스 낭비)
|
||
- ⭐ **새 발견 (NCCL)**: rank 0 죽으면 rank 1, 2가 hang → GPU 5.7GB 영구 점유 (Pod 삭제 전까지)
|
||
|
||
> Tip: PID 1 = `sleep infinity` (bash exec 최적화로 sleep이 PID 1을 대체하지만, init 아닌 동작은 동일)
|
||
|
||
**시나리오 2 — tini only** (미실험, 가설 갱신)
|
||
> 가설: tini가 PID 1이면 좀비는 수거되지만, 코드에 signal handler가 없으면 CUDA cleanup은 여전히 실패한다.
|
||
|
||
원래 가설:
|
||
- tini의 `waitpid(-1)` 덕분에 좀비는 사라짐 → ✅ 예상
|
||
|
||
갱신된 가치 (시나리오 1 결과 반영):
|
||
- **좀비 reaping** — 시나리오 1의 좀비 발생을 막는 init 보호
|
||
- ❌ **orphan CUDA ctx 방지 가치는 사실상 없음** (시나리오 1에서 driver가 자동 회수)
|
||
- ⚠️ **30초 grace 낭비는 그대로** (signal handler 없으므로 동일)
|
||
|
||
**시나리오 3 — tini + preStop** (미실험, 가설 갱신)
|
||
> 가설: preStop hook으로 SIGTERM 미리 보내도 worker가 무시하면 SIGKILL fallback 발생.
|
||
|
||
갱신된 가치:
|
||
- preStop이 GPU cleanup 명령(`/usr/local/bin/prestop.sh`)을 grace period 안에 미리 실행
|
||
- 다만 worker가 SIGTERM 무시하면 시나리오 2와 동일하게 30초 후 SIGKILL
|
||
- ⚠️ **본질적으로 application 코드가 SIGTERM을 처리하지 않으면 효과 제한적**
|
||
|
||
**시나리오 4 — Full Fix** (미실험, 가장 큰 가치 기대)
|
||
> 가설: tini + preStop + 코드 signal handler 모두 적용 시 깨끗한 정리.
|
||
|
||
갱신된 가치 (가장 중요):
|
||
- ⭐ **30초 grace 낭비 → 0초로 단축** (시나리오 1에서 발견된 진짜 문제 해결)
|
||
- ⭐ **NCCL `dist.destroy_process_group()` 호출** — multi-GPU에서 5.7GB 누수 방지
|
||
- worker가 SIGTERM 받아서 즉시 `tensors.clear()` → `torch.cuda.empty_cache()` → exit
|
||
- 결과적으로 GPU 메모리 빠른 반환 + application graceful shutdown
|
||
- Pod 삭제 후 GPU 메모리 완전 해제, ghost context 없음
|
||
|
||
### 최종 비교표 (S1 실측 + S2~4 갱신 예상)
|
||
|
||
| 항목 | S1 (실측) | S2 (예상) | S3 (예상) | S4 (예상) |
|
||
|---|---|---|---|---|
|
||
| 좀비 수거 | ❌ 발생 (1~10개) | ✅ tini가 reap | ✅ | ✅ |
|
||
| 고아 발생 | ✅ (PPID=1) | ✅ but tini가 reap | ✅ but reap | 미발생 (handler가 정리) |
|
||
| SIGTERM 처리 | ❌ SIG_IGN | ❌ SIG_IGN | ❌ but preStop이 cleanup 시도 | ✅ graceful |
|
||
| kill -9 후 orphaned CUDA ctx | **❌ 미발생** (driver 회수) | 동일 예상 | 동일 예상 | N/A |
|
||
| Pod 삭제 시 GPU 점유 시간 | **30초** (grace 전체) | 30초 (handler 없음) | 30초 (worker 무시) | **0초** ⭐ |
|
||
| Pod 삭제 후 GPU 메모리 | ✅ 해제 (SIGKILL 후) | ✅ 해제 | ✅ 해제 | ✅ 즉시 해제 |
|
||
| 노드 재부팅 필요 | ❌ 불필요 | ❌ | ❌ | ❌ |
|
||
| **NCCL hang 시 GPU 점유** | **5.7GB 영구 점유** | 동일 (handler 없음) | preStop도 hang 못 풂 | NCCL_ASYNC + destroy_process_group |
|
||
|
||
> **시나리오 2~4는 미실험.** 시나리오 1 결과를 바탕으로 갱신된 예상치. 실측 시 [`results/`](./results/) 업데이트.
|
||
|
||
---
|
||
|
||
## 디렉토리 구조
|
||
|
||
```
|
||
process_error/
|
||
├── README.md # 이 문서
|
||
├── plan.md # 실험 설계 전문 (1500+ 줄)
|
||
├── .claude/
|
||
│ └── settings.json # Claude Code 권한 설정 (이 프로젝트 한정)
|
||
│
|
||
├── .gitea/
|
||
│ └── workflows/
|
||
│ ├── ci.yaml # 시나리오 이미지 빌드/푸시
|
||
│ └── cd.yaml # Helm chart 패키징/푸시
|
||
│
|
||
├── scripts/ # 공통 관찰·운영 스크립트
|
||
│ ├── observe.sh # 컨테이너 내부 관찰 (좀비/고아/GPU 상태)
|
||
│ ├── node-observe.sh # 노드 레벨 GPU 관찰
|
||
│ ├── gpu-pod-trace.sh # GPU PID → Container → Pod 역추적 (노드 root 실행)
|
||
│ └── prestop.sh # 시나리오 3·4 preStop hook 공용 스크립트
|
||
│
|
||
├── scenario-1-nofix/ # 시나리오 1 — bash PID 1
|
||
│ ├── Dockerfile
|
||
│ ├── parent.py # 자식 spawn 후 wait() 없이 종료
|
||
│ ├── gpu_worker.py # SIGTERM 무시, GPU ~2GB 점유
|
||
│ └── pod.yaml # manual apply용 (CI/CD 미사용 시)
|
||
│
|
||
├── scenario-2-tini-only/ # 시나리오 2 — tini PID 1
|
||
│ ├── Dockerfile
|
||
│ ├── parent.py
|
||
│ ├── gpu_worker.py # 시나리오 1과 동일
|
||
│ └── pod.yaml
|
||
│
|
||
├── scenario-3-tini-prestop/ # 시나리오 3 — tini + preStop
|
||
│ └── pod.yaml # 이미지는 scenario-2 재사용
|
||
│
|
||
├── scenario-4-fullfix/ # 시나리오 4 — 전부 적용
|
||
│ ├── Dockerfile
|
||
│ ├── parent.py
|
||
│ ├── gpu_worker_fixed.py # SIGTERM handler + CUDA cleanup
|
||
│ └── pod.yaml
|
||
│
|
||
├── charts/
|
||
│ └── gpu-zombie-test/ # Helm chart (4개 pod template)
|
||
│ ├── Chart.yaml
|
||
│ ├── values.yaml # scenario enable 토글
|
||
│ └── templates/
|
||
│ ├── _helpers.tpl
|
||
│ ├── pod-nofix.yaml
|
||
│ ├── pod-tini.yaml
|
||
│ ├── pod-tini-prestop.yaml
|
||
│ └── pod-fullfix.yaml
|
||
│
|
||
├── argocd-app.yaml # ArgoCD Application 정의
|
||
│
|
||
└── results/ # 시나리오별 실험 결과
|
||
├── scenario-1/
|
||
│ ├── observe-after-spawn.log
|
||
│ ├── observe-after-kill.log
|
||
│ ├── gpu-pod-trace.log
|
||
│ ├── nvidia-smi-before-delete.log
|
||
│ ├── nvidia-smi-after-delete.log
|
||
│ └── notes.md
|
||
├── scenario-2/
|
||
├── scenario-3/
|
||
├── scenario-4/
|
||
└── comparison.md # 최종 비교 분석
|
||
```
|
||
|
||
> **현재 상태:** `plan.md`, `README.md`, `.claude/settings.json`만 존재.
|
||
> 나머지는 `plan.md`의 설계대로 Phase 0부터 순차 생성 예정.
|
||
|
||
---
|
||
|
||
## 실험 흐름 — 어떻게 동작하는가
|
||
|
||
### Phase 0 — CI/CD 환경 구성
|
||
|
||
1. **Gitea repo 생성** (`soo-rnd/gpu-zombie-test`) + Secrets 등록 (REGISTRY_URL/USER/PASSWORD)
|
||
2. **K8s namespace + pull secret** 사전 생성
|
||
3. **`.gitea/workflows/ci.yaml`** 작성 — `scenario-*/` 변경 시 matrix로 3개 이미지를 Harbor에 빌드·푸시
|
||
4. **`.gitea/workflows/cd.yaml`** 작성 — `v*` tag push 시 Helm chart를 Harbor OCI에 푸시
|
||
5. **`charts/gpu-zombie-test/`** Helm chart 작성 — 4개 Pod template
|
||
6. **`argocd-app.yaml`** apply — 이후 chart 버전 업 시 ArgoCD가 자동 sync
|
||
|
||
```
|
||
[코드 변경]
|
||
├─ scenario-*/ 변경 → CI 트리거
|
||
│ └─ 3개 이미지 build → Harbor push (:nofix, :tini, :fullfix + sha 태그)
|
||
│
|
||
└─ git tag v0.1.x → CD 트리거
|
||
└─ Helm chart package → Harbor OCI push
|
||
└─ ArgoCD 자동 sync
|
||
└─ gpu-zombie-test ns에 4개 Pod 배포
|
||
```
|
||
|
||
### Phase 1~2-C — 시나리오 실행 사이클
|
||
|
||
각 시나리오는 다음 공통 흐름을 따름:
|
||
|
||
```bash
|
||
# 1. 코드 수정 → push → CI 자동 빌드 확인 (Gitea Actions UI)
|
||
|
||
# 2. Pod 재생성으로 새 이미지 pull (imagePullPolicy: Always)
|
||
kubectl delete pod gpu-zombie-<scenario> -n gpu-zombie-test
|
||
# → ArgoCD selfHeal로 즉시 재배포
|
||
|
||
# 3. Pod 진입 & 실험
|
||
kubectl exec -it gpu-zombie-<scenario> -n gpu-zombie-test -- bash
|
||
|
||
# 4. 컨테이너 내부 관찰
|
||
observe.sh # 좀비/고아/GPU 상태
|
||
|
||
# 5. 노드에서 역추적 (별도 터미널)
|
||
ssh gpu-4
|
||
bash /path/to/gpu-pod-trace.sh
|
||
|
||
# 6. 특정 액션 수행 후 재관찰
|
||
# - parent.py 실행 후 고아 생성
|
||
# - kill -9 <worker_pid>로 orphaned context 유발
|
||
# - kubectl delete pod로 preStop 동작 확인
|
||
|
||
# 7. 결과 로그 저장 → results/scenario-<N>/
|
||
```
|
||
|
||
---
|
||
|
||
## 관찰 도구 — 무엇을 어떻게 확인하는가
|
||
|
||
### `scripts/observe.sh` (컨테이너 내부)
|
||
컨테이너 안에서 실행. 다음 8가지를 한 번에 점검:
|
||
1. PID 1 cmdline (bash vs tini)
|
||
2. 전체 프로세스 트리 (`ps auxf`)
|
||
3. 좀비 프로세스 (`STAT=Z`)
|
||
4. 고아 프로세스 (PPID=1)
|
||
5. GPU 메모리 상태 (`nvidia-smi --query-gpu`)
|
||
6. GPU 사용 프로세스 (`nvidia-smi --query-compute-apps`)
|
||
7. `/dev/nvidia*` fd 보유 프로세스 (`fuser`)
|
||
8. Orphaned GPU context 종합 체크 (nvidia-smi ∩ fuser ∩ /proc 생존 확인)
|
||
|
||
> **⚠️ 알려진 한계 (시나리오 1 실험에서 발견)**: torch 사용 시 nvidia-smi가 컨테이너 내부 PID가 아닌
|
||
> **노드 PID**를 반환 → observe.sh가 컨테이너 내부 `/proc/<노드 PID>` 조회 실패 → "ORPHANED" 오탐지 발생.
|
||
> 정확한 orphaned context 판정은 노드 레벨 `gpu-pod-trace.sh`에서만 가능.
|
||
> raw CUDA driver API(ctypes) 사용 시는 컨테이너 PID가 일치해서 오탐지 없음.
|
||
|
||
### `scripts/node-observe.sh` (gpu-4 노드 root)
|
||
노드 레벨에서 GPU 전체 상태 + ghost context 감지.
|
||
|
||
### `scripts/gpu-pod-trace.sh` (gpu-4 노드 root) ⭐ 핵심
|
||
cgroup v1 + containerd + systemd slice 포맷에 맞춰 작성된 **GPU PID → Pod 역추적** 스크립트.
|
||
- `/proc/<pid>/cgroup` 파싱 → container ID 추출 → `crictl inspect` → Pod name/namespace/container name
|
||
- nvidia-smi에 안 잡히는 숨은 프로세스(`fuser`로만 감지)도 추적
|
||
- Ghost context (프로세스 0개인데 GPU 메모리 점유) 별도 경고
|
||
|
||
출력 예시:
|
||
```
|
||
PID STATE GPU_MEM NSMI QOS POD NAMESPACE CONTAINER
|
||
12345 R 2048 MiB Y besteffort gpu-zombie-nofix gpu-zombie-test worker
|
||
```
|
||
|
||
### `scripts/prestop.sh` (Pod preStop hook)
|
||
시나리오 3·4의 이미지에 `/usr/local/bin/prestop.sh`로 포함.
|
||
- GPU 점유 PID 수집 (nvidia-smi + fuser)
|
||
- SIGTERM 전송 → 최대 20초 대기
|
||
- 미종료 시 SIGKILL fallback (시나리오 3에서 트리거)
|
||
|
||
---
|
||
|
||
## 결과는 어디서 확인하는가
|
||
|
||
### 1. CI/CD 동작 확인
|
||
|
||
| 항목 | 위치 |
|
||
|---|---|
|
||
| CI 빌드 로그 | `https://gitea.inje-private.com/soo-rnd/gpu-zombie-test/actions` |
|
||
| 푸시된 이미지 | `https://harbor.cone-chain.net/harbor/projects/aipf/repositories/gpu-zombie-test` |
|
||
| 푸시된 Helm chart | Harbor 동 project OCI artifacts |
|
||
| ArgoCD sync 상태 | `https://argocd.gpulive.cloud` → Application `gpu-zombie-test` |
|
||
|
||
### 2. Pod & 클러스터 상태
|
||
|
||
```bash
|
||
# 배포된 Pod 확인
|
||
kubectl get pods -n gpu-zombie-test
|
||
|
||
# 특정 Pod 상세 (events, restarts, container status)
|
||
kubectl describe pod gpu-zombie-<scenario> -n gpu-zombie-test
|
||
|
||
# Pod 로그 (parent.py, gpu_worker.py 출력 + preStop 로그)
|
||
kubectl logs gpu-zombie-<scenario> -n gpu-zombie-test
|
||
|
||
# Pod 삭제 시 preStop 로그를 실시간으로 보려면
|
||
kubectl logs -f gpu-zombie-<scenario> -n gpu-zombie-test &
|
||
kubectl delete pod gpu-zombie-<scenario> -n gpu-zombie-test
|
||
```
|
||
|
||
### 3. GPU 상태 (노드에서)
|
||
|
||
```bash
|
||
ssh ubuntu@gpu-4
|
||
sudo -i
|
||
|
||
# 한 눈에 보기
|
||
watch -n 1 'nvidia-smi'
|
||
|
||
# 전체 진단
|
||
bash /path/to/scripts/node-observe.sh
|
||
|
||
# PID → Pod 역추적
|
||
bash /path/to/scripts/gpu-pod-trace.sh
|
||
```
|
||
|
||
### 4. 실험 결과 파일 (`results/`)
|
||
|
||
각 시나리오별로 다음 파일들을 저장:
|
||
|
||
| 파일 | 내용 |
|
||
|---|---|
|
||
| `observe-after-spawn.log` | parent.py 실행 직후 `observe.sh` 출력 |
|
||
| `observe-after-kill.log` | `kill -9 <worker>` 직후 `observe.sh` 출력 — orphaned context 확인 |
|
||
| `gpu-pod-trace.log` | 노드에서 `gpu-pod-trace.sh` 출력 — PID→Pod 매핑 |
|
||
| `nvidia-smi-before-delete.log` | Pod 삭제 직전 GPU 상태 |
|
||
| `nvidia-smi-after-delete.log` | Pod 삭제 직후 GPU 상태 (메모리 잔존 여부) |
|
||
| `notes.md` | 해당 시나리오 특이사항, 예상과 다른 결과, 타이밍 이슈 등 |
|
||
|
||
### 5. 최종 비교 — `results/comparison.md`
|
||
|
||
모든 시나리오 완료 후, 4개 결과를 한 표로 정리하고 각 수정안의 효과를 정량화.
|
||
plan.md의 "Phase 3: 결과 비교" 템플릿 사용.
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
**전제:** `plan.md` 기준 Phase 0 (CI/CD 환경) 완료 상태.
|
||
|
||
```bash
|
||
# 1. 클러스터 접근 확인
|
||
kubectl config current-context
|
||
kubectl get ns gpu-zombie-test
|
||
|
||
# 2. 배포된 Pod 확인 (ArgoCD가 sync 완료한 상태)
|
||
kubectl get pods -n gpu-zombie-test
|
||
# NAME READY STATUS ...
|
||
# gpu-zombie-nofix 1/1 Running
|
||
# gpu-zombie-tini 1/1 Running
|
||
# gpu-zombie-tini-prestop 1/1 Running
|
||
# gpu-zombie-fullfix 1/1 Running
|
||
|
||
# 3. 시나리오 1부터 순차 실험 — plan.md Phase 1 참조
|
||
kubectl exec -it gpu-zombie-nofix -n gpu-zombie-test -- bash
|
||
# (컨테이너 내부)
|
||
python3 parent.py &
|
||
sleep 10
|
||
observe.sh
|
||
|
||
# 4. 노드에서 역추적
|
||
ssh ubuntu@gpu-4
|
||
sudo bash gpu-pod-trace.sh
|
||
|
||
# 5. 결과 저장 (로컬)
|
||
mkdir -p results/scenario-1
|
||
kubectl exec gpu-zombie-nofix -n gpu-zombie-test -- observe.sh > results/scenario-1/observe-after-spawn.log
|
||
|
||
# 6. 다음 시나리오 전 GPU 초기화 확인
|
||
nvidia-smi
|
||
# (필요 시 nvidia-smi --gpu-reset 또는 노드 재부팅)
|
||
```
|
||
|
||
---
|
||
|
||
## 주의사항
|
||
|
||
1. **실험은 `gpu-4` 노드를 독점한다** — 다른 GPU 워크로드가 있으면 간섭 가능.
|
||
실험 전 `nvidia-smi`로 깨끗한 상태 확인.
|
||
|
||
2. **시나리오 간 GPU 초기화 필수** — orphaned/ghost context가 누적되면 후속 시나리오 결과가 오염된다.
|
||
정리 안 되면 `nvidia-smi --gpu-reset` 시도, 안 되면 **gpu-4 노드 재부팅**.
|
||
|
||
3. **`nvidia-smi --gpu-reset`은 해당 GPU를 쓰는 다른 컨텍스트가 있으면 실패** — 모든 GPU pod 정리 후 시도.
|
||
|
||
4. **`prestop.sh`는 이미지 2개(`:tini`, `:fullfix`)에만 포함** — 시나리오 1의 `:nofix` 이미지에는 없음.
|
||
|
||
5. **시나리오 2와 3은 같은 이미지를 쓴다** — scenario-3는 별도 이미지를 빌드하지 않고 `:tini` 재사용.
|
||
CI matrix에 scenario-3가 없는 것은 의도된 것.
|
||
|
||
---
|
||
|
||
## 참고 문서
|
||
|
||
- **설계 전문:** [`plan.md`](./plan.md) — 모든 스크립트·매니페스트·체크리스트 포함 (1500+ 줄)
|
||
- **Claude Code 권한:** [`.claude/settings.json`](./.claude/settings.json) — 이 프로젝트 한정 권한
|
||
- **CI/CD 참고 템플릿:** `/home/ubuntu/soo/vLLM/A100_npu/` — 동일 Gitea/Harbor/ArgoCD 스택 사용 중
|