GPU zombie/orphan/orphaned-CUDA-context reproduction experiment
Go to file
selee fc045680c3 docs: 시나리오 1 5가지 변형 실험 결과 반영
실측 결과 plan.md의 핵심 가설("orphan CUDA context 발생")이 미검증.
대신 두 가지 진짜 문제 발견:
- 30초 grace period 동안 GPU 점유 (SIGTERM 무시 시)
- NCCL multi-GPU에서 한 rank 죽으면 나머지 hang으로 GPU 5.7GB 영구 점유

변경 내용:
- results/comparison.md 신규 — 5개 변형(driver, user, worker개수, NCCL) 비교 분석
- README.md — 실험 결과 요약 섹션, 시나리오 가설들에 검증 결과 추가
- plan.md — Phase 1 직전에 실험 결과 요약 + Step 3-B 절차 추가
                + 시나리오 2~4 가치 재정의

시나리오 2~4는 미실험 — 시나리오 1 결과를 반영해서 갱신된 예상치만 기재.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:58:24 +09:00
.gitea/workflows ci: add workflow_dispatch for manual trigger 2026-04-10 17:15:45 +09:00
charts/gpu-zombie-test Initial commit — GPU zombie/orphan/orphaned-CUDA-context 실험 프로젝트 2026-04-10 17:12:38 +09:00
results docs: 시나리오 1 5가지 변형 실험 결과 반영 2026-04-15 15:58:24 +09:00
scenario-1-nofix fix: remove --break-system-packages flag (Ubuntu 22.04 pip 미지원) 2026-04-10 17:21:10 +09:00
scenario-2-tini-only fix: remove --break-system-packages flag (Ubuntu 22.04 pip 미지원) 2026-04-10 17:21:10 +09:00
scenario-3-tini-prestop Initial commit — GPU zombie/orphan/orphaned-CUDA-context 실험 프로젝트 2026-04-10 17:12:38 +09:00
scenario-4-fullfix fix: remove --break-system-packages flag (Ubuntu 22.04 pip 미지원) 2026-04-10 17:21:10 +09:00
scripts Initial commit — GPU zombie/orphan/orphaned-CUDA-context 실험 프로젝트 2026-04-10 17:12:38 +09:00
.gitignore Initial commit — GPU zombie/orphan/orphaned-CUDA-context 실험 프로젝트 2026-04-10 17:12:38 +09:00
README.md docs: 시나리오 1 5가지 변형 실험 결과 반영 2026-04-15 15:58:24 +09:00
argocd-app.yaml Initial commit — GPU zombie/orphan/orphaned-CUDA-context 실험 프로젝트 2026-04-10 17:12:38 +09:00
plan.md docs: 시나리오 1 5가지 변형 실험 결과 반영 2026-04-15 15:58:24 +09:00

README.md

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 참조. 5개 변형 실험 결과 비교는 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

시나리오 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/ 업데이트.


디렉토리 구조

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 — 시나리오 실행 사이클

각 시나리오는 다음 공통 흐름을 따름:

# 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 & 클러스터 상태

# 배포된 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 상태 (노드에서)

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 환경) 완료 상태.

# 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 — 모든 스크립트·매니페스트·체크리스트 포함 (1500+ 줄)
  • Claude Code 권한: .claude/settings.json — 이 프로젝트 한정 권한
  • CI/CD 참고 템플릿: /home/ubuntu/soo/vLLM/A100_npu/ — 동일 Gitea/Harbor/ArgoCD 스택 사용 중