17진행자·운영자 Isaac Sim · EKS예상 약 30분, 별도 일정
GPU 시작 시간·캐시·비용 운영
단계별 시작 시간과 캐시를 측정하고 warm·온디맨드·96GB 옵션을 구분합니다.
진행자·운영자 선택 트랙, 예상 약 30분입니다. 실제 결과를 얻은 실행 ID와 검증한 이미지 digest가 필요합니다. 운영자 도움 함수를 사용합니다.
목표는 시작 시간을 구간별로 측정하고 같은 조건에서 캐시 효과를 비교하는 것입니다. 이 장은 이미 달성한 Isaac 벤치마크 수치를 제시하지 않습니다.
1. 실제 실행의 상태와 계측값
set -euo pipefail
cd "$ISAAC_REPO"
read -r -p "측정할 실제 Isaac 실행 ID: " ISAAC_METRIC_ID
export ISAAC_METRIC_ID
isaac run --id "$ISAAC_METRIC_ID" > "$ISAAC_EVIDENCE/metrics-run.json"
jq '{id,state,createdAt,updatedAt,attempts,checkpointCompleted,gpu,cache,timings,
readyEvidence,metrics}' "$ISAAC_EVIDENCE/metrics-run.json"
ik -n robot-atlas-isaac get pods -l "robot-atlas/run=$ISAAC_METRIC_ID" -o json \
> "$ISAAC_EVIDENCE/metrics-pods.json"
ik -n robot-atlas-isaac get events -o json > "$ISAAC_EVIDENCE/namespace-events.json"
완료된 Job·Pod는 TTL로 사라질 수 있으므로 Kubernetes 시간·이벤트는 시험 중에 보관합니다. 이벤트가 없으면 0초로 계산하지 않고 근거 누락으로 기록합니다.
| 구간·필드 | 현재 구현에서의 의미 |
|---|---|
| NodeClaim 생성 → Node Ready | Kubernetes/AWS 시각으로 별도 측정하는 노드 확보 구간 |
| Pod의 Pulling/Pulled·컨테이너 시작 | 이미지 풀·시작 이벤트. 같은 노드의 캐시 여부도 기록 |
cacheRestoreSeconds |
worker 시작부터 lease·요청·GPU 확인·캐시 준비까지. 순수 다운로드 시간만은 아님 |
engineReadySeconds |
시뮬레이터 시작부터 실제 Ready 파일 관측까지 |
attemptToReadySeconds |
해당 worker 시작부터 Ready 관측까지, lease 대기 등을 포함 |
jobToReadySeconds |
실행 createdAt부터 Ready까지. 재개 시 이전 시도 시간도 포함 |
attemptSeconds |
해당 worker의 준비·계산·결과/캐시 저장 구간 |
readyEvidence.startupSeconds |
시뮬레이터 프로세스 관점의 초기화 시간 |
이 값들을 단순히 모두 더하면 겹친 구간을 중복 계산합니다. createdAt·updatedAt은 ISO 문자열이고 deadline·lease는 epoch 초입니다. 물리 기록의 duration은 시뮬레이션 시간이며 실제 대기 시간과 구분합니다.
worker는 /robot-atlas/isaac 로그와 RobotAtlas/IsaacSim 지표에 계측값을 보낼 수 있습니다. 전송은 best effort이므로 지표 부재가 0초·성공을 뜻하지 않습니다. DynamoDB의 상태와 보관한 실제 산출물을 함께 봅니다. 컨트롤러 로그와 worker/Kit 로그도 구분합니다.
2. 노드가 바뀌어도 가져올 수 있는 S3 캐시
현재 캐시는 hostPath에 두지 않습니다. Pod의 임시 cache 볼륨에서 생성하고, 성공한 실행의 후보를 운영자가 검증해 S3에 게시합니다.
Isaac 버전 + image digest + scene revision + GPU 이름 + driver
→ cache identity SHA-256
→ caches/<identity>/manifest.json
→ digest로 고정한 archive
Ada L40S와 Blackwell의 셰이더 캐시를 하나의 키로 공유하지 않습니다. 이미지·드라이버가 달라져도 새 identity입니다. worker는 공용 캐시를 읽으며, 성공하지 않은 실행이 공유 캐시를 직접 바꾸지 못합니다.
jq '{state,metrics,cacheCandidate}' "$ISAAC_EVIDENCE/metrics-run.json"
isaac promote-cache --id "$ISAAC_METRIC_ID" \
> "$ISAAC_EVIDENCE/cache-promotion.json"
jq '{published,identity,sha256,files}' "$ISAAC_EVIDENCE/cache-promotion.json"
promote-cache는 실제 S3 쓰기입니다. 물리 성공, identity, archive checksum·크기·파일 수·안전한 추출 경로를 확인합니다. 같은 identity에 다른 내용을 덮어쓰지 않습니다. 후보가 없거나 실패한 실행이면 거부되는 것이 정상입니다.
3. 캐시 비교 실험
- 이미지·씬·요청을 고정한 성공 실행의
cache.status, GPU·driver, 시간과 노드 정보를 보관합니다. - 그 실행의 캐시 후보를 위 명령으로 게시합니다.
- 같은 요청을 새 run ID로 한 번 실행하고 실제 GPU·driver·image identity를 비교합니다.
.cache.status: restored와 양수 파일 수를 확인한 뒤 엔진 초기화 시간을 비교합니다.- 같은 노드의 재실행인지 새 노드인지, 이미지가 이미 있었는지, 복구 시험 hold가 포함됐는지 함께 기록합니다.
isaac submit --scenario transfer --seed 42 --order red \
> "$ISAAC_EVIDENCE/cache-repeat-submit.json"
export ISAAC_REPEAT_ID="$(jq -er '.id' "$ISAAC_EVIDENCE/cache-repeat-submit.json")"
isaac_wait_run "$ISAAC_REPEAT_ID" 1800
isaac run --id "$ISAAC_REPEAT_ID" > "$ISAAC_EVIDENCE/cache-repeat-run.json"
jq '{id,state,gpu,cache,timings,metrics}' "$ISAAC_EVIDENCE/cache-repeat-run.json"
복원 파일이 있다는 사실과 renderer가 그 캐시를 실제 활용해 빨라졌다는 주장은 다릅니다. restored만으로 개선율을 계산하지 않습니다. GPU 계열이나 driver가 달라진 두 실행을 캐시 A/B로 묶지 않습니다.
4. 콜드 스타트 문서에서 가져온 원칙
사용자가 제공한 2026-09-02-eks-llm-serving-cold-start.md는 vLLM/Gemma 서빙 시험입니다. 그 문서의 428초·226초·sleep 복귀 수치는 이 프로젝트의 Isaac 결과가 아닙니다.
| 가져오는 원칙 | 이 구현에서의 적용 |
|---|---|
| 시작 과정을 분해 | 노드, 이미지, 캐시/자산, PhysX·RTX, 첫 유효 프레임을 구분 |
| 리전 내 이미지·데이터 | 고정 ECR digest, 검토된 S3 캐시·자산 경로 |
| 호환되는 캐시만 재사용 | 이미지·씬·GPU·driver별 identity |
| 실제 서비스 준비 확인 | 장면과 물리 step, 유효 RTX 프레임이 있어야 Ready |
| 유휴 용량과 지연의 비용 교환 | 기본 warm 0, 실제 사용 간격·과금으로 판단 |
Auto Mode의 관리형 이미지 풀 최적화는 kubelet 이벤트와 실제 시작 시간으로 효과를 판단합니다. 별도 SOCI snapshotter 설치, EC2NodeClass userData, hostPath, vLLM의 runai_streamer·sleep API를 이 환경에 복사하지 않습니다. 전체 자산을 S3로 미러링한다면 USD가 참조하는 layer·mesh·material과 사용 조건까지 검토해야 하며 상위 USD 하나만 복사해서 완료했다고 하지 않습니다.
기본값은 standard Spot·warm 0입니다. 다음 선택 과정도 기존 계정·VPC·한도 가드를 바꾸지 않고 구현된 운영자 명령만 사용합니다.
5. 운영자 선택: On-Demand·rtx96·warm
이 절의 명령은 필요할 때만 운영자가 명시적으로 선택합니다. 기본 과정의 필수 실행이 아니며 이 교재가 옵션을 활성화했다는 뜻도 아닙니다. 공개 웹 작업은 계속 standard Spot을 사용합니다.
먼저 현재 프로파일을 처리하는 소스와 실제로 배포된 컨트롤러·worker 이미지가 일치하는지 확인합니다. 불일치하면 15장의 빌드·검사·workloads 순서로 갱신하며 source hash 가드를 지우지 않습니다.
"$ISAAC_PYTHON" - <<'PY'
import json
from infra.isaac import deployment as d, operations
cfg = d.config()
aws = d.session(cfg)
images = operations.image_records()
deployment = operations.kubectl(
aws, cfg, "-n", cfg["namespace"], "get", "deployment", "isaac-controller",
"-o", "json", json_result=True)
container = next(c for c in deployment["spec"]["template"]["spec"]["containers"]
if c["name"] == "controller")
env = {e["name"]: e["value"] for e in container["env"] if "value" in e}
assert container["image"] == images["controller"]
assert env["ISAAC_WORKER_IMAGE"] == images["worker"]
print(json.dumps({"currentProfileImagesDeployed": True, "images": images}, indent=2))
PY
| 선택 | 동작 |
|---|---|
기본 submit |
Spot + standard, 40,000 MiB 초과 어피니티와 런타임 VRAM 검사 |
--capacity-type on-demand |
운영자 전용 On-Demand pool로 해당 작업만 제출 |
--memory-profile rtx96 |
90,000 MiB 초과 어피니티와 런타임 VRAM 검사. 현재 후보에서는 g7e 필요 |
warm --replicas 1 |
낮은 우선순위의 선점 가능한 Spot GPU·이미지 holder |
warm --replicas 0 |
holder 중지. 노드의 실제 종료·잔여 시간은 별도 확인 |
On-Demand가 필요할 때 먼저 비어 있는 전용 Private pool을 만듭니다. 이 명령만으로 GPU를 예약하지 않으며 Spot 부족 시 자동으로 전환하는 설정도 아닙니다.
isaac enable-ondemand
ik get nodepool isaac-sim-ondemand -o json \
> "$ISAAC_EVIDENCE/ondemand-pool.json"
jq '{name:.metadata.name,nodeClass:.spec.template.spec.nodeClassRef,
requirements:.spec.template.spec.requirements,limits:.spec.limits}' \
"$ISAAC_EVIDENCE/ondemand-pool.json"
아래는 On-Demand·rtx96을 실제 선택한 경우에만 실행하는 예입니다. 현재 단가·용량·할당량을 먼저 확인합니다. 다른 작업과 동시에 제출하지 않습니다.
isaac submit --scenario transfer --seed 42 --order red \
--capacity-type on-demand --memory-profile rtx96 \
> "$ISAAC_EVIDENCE/optional-submit.json"
export ISAAC_OPTIONAL_ID="$(jq -er '.id' "$ISAAC_EVIDENCE/optional-submit.json")"
jq -e '.operatorCapacityType == "on-demand" and .minimumVramMiB == 90000' \
"$ISAAC_EVIDENCE/optional-submit.json"
isaac_wait_run "$ISAAC_OPTIONAL_ID" 1800
isaac run --id "$ISAAC_OPTIONAL_ID" > "$ISAAC_EVIDENCE/optional-run.json"
jq '{id,state,operatorCapacityType,minimumVramMiB,gpu,readyEvidence,metrics}' \
"$ISAAC_EVIDENCE/optional-run.json"
ik -n robot-atlas-isaac get job "$ISAAC_OPTIONAL_ID" -o json \
> "$ISAAC_EVIDENCE/optional-job.json"
jq -e '.spec.template.spec.nodeSelector["karpenter.sh/nodepool"] == "isaac-sim-ondemand"
and ([.spec.template.spec.containers[].env[]? |
select(.name == "ISAAC_MIN_VRAM_MIB") | .value] == ["90000"])' \
"$ISAAC_EVIDENCE/optional-job.json"
jq -e '.state == "succeeded" and .gpu.memoryMiB >= 90000' \
"$ISAAC_EVIDENCE/optional-run.json"
standard On-Demand라면 --memory-profile standard, rtx96 Spot이라면 --capacity-type spot --memory-profile rtx96을 선택합니다. rtx96 작업에는 L40S가 대체 후보가 될 수 없습니다. 해당 g7e 용량이나 RTX 호환성을 확인하지 못했다면 성공으로 기록하지 않습니다.
warm을 비교할 필요가 있을 때만 다음을 실행합니다.
isaac warm --replicas 1
ik -n robot-atlas-isaac rollout status deployment/isaac-warm --timeout=900s
ik -n robot-atlas-isaac get deployment isaac-warm -o json \
> "$ISAAC_EVIDENCE/warm-holder.json"
jq '{replicas:.spec.replicas,priority:.spec.template.spec.priorityClassName,
containers:[.spec.template.spec.containers[] | {name,image,command,resources}]}' \
"$ISAAC_EVIDENCE/warm-holder.json"
holder는 worker 이미지를 받은 뒤 /bin/sleep으로 GPU·CPU·메모리를 예약합니다. 준비된 Isaac 프로세스나 vLLM sleep이 아닙니다. 장면·PhysX·RTX 초기화와 실제 Ready 검사는 새 작업에서 수행합니다. worker가 holder를 선점할 수 있지만 여유 노드에 배치되어 작업 중 GPU 두 대가 유지될 수도 있습니다.
namespace quota의 요청 GPU 합계 2는 Spot·On-Demand pool과 holder를 모두 포함합니다. pool별 한도를 더해 동시 작업을 늘리지 않습니다. admission은 여전히 실제 작업 1건이며, holder는 유휴 상태에도 GPU 한 대 비용을 발생시킵니다. 노드 회수 지연·교체 중첩 때문에 quota가 절대 지출 상한은 아닙니다.
비교를 마치면 holder를 중지합니다. warm 출력에는 kubectl 메시지가 섞일 수 있어 stdout 전체를 JSON으로 가정하지 않습니다.
isaac warm --replicas 0
ik -n robot-atlas-isaac get deployment isaac-warm -o json \
| jq '{replicas:.spec.replicas,readyReplicas:(.status.readyReplicas // 0)}'
6. 2026-09-13 가격 스냅샷
다음은 진행자가 보관한 서울 EC2 Linux/UNIX Spot 조회 스냅샷의 인스턴스 한 대·시간당 USD입니다. 계정의 두 AZ 표기를 사용합니다. 현재 가격·할인율·용량 확보를 보장하지 않으며, 행별 가격 변경 시각과 조회 시각도 다를 수 있습니다.
| 단일 GPU 후보 | ap-northeast-2a | ap-northeast-2b |
|---|---|---|
| g6e.2xlarge | 1.4185 | 1.3797 |
| g6e.4xlarge | 1.8501 | 1.8478 |
| g6e.8xlarge | 2.6587 | 2.7142 |
| g7e.2xlarge | 3.7213 | 2.2359 |
| g7e.4xlarge | 2.7737 | 4.3453 |
| g7e.8xlarge | 6.4770 | 2.9147 |
같은 스냅샷의 g7e 배치 점수는 두 AZ에서 1·3으로 낮았고, g6e와 혼합 후보는 9였습니다. 혼합 점수를 g7e 확보 가능성으로 해석하지 않습니다. 배치 점수는 조회 대상·목표 용량·계정·시점에 따른 상대 지표이며 실제 확보를 보장하지 않습니다.
운영 전에 현재 가격을 다시 조회합니다. 아래는 AWS 읽기 전용 요청이며 결과는 비공개 운영 근거에 저장합니다.
"$ISAAC_PYTHON" - <<'PY'
from datetime import datetime, timezone
import json, os
from pathlib import Path
from infra.isaac import deployment as d
cfg = d.config()
aws = d.session(cfg)
now = datetime.now(timezone.utc)
types = [f"{family}.{size}" for family in ("g6e", "g7e")
for size in ("2xlarge", "4xlarge", "8xlarge")]
ec2 = aws.client("ec2")
allowed_azs = {s["AvailabilityZone"] for s in ec2.describe_subnets(
SubnetIds=cfg["privateSubnetIds"])["Subnets"]}
rows = []
pages = ec2.get_paginator("describe_spot_price_history").paginate(
InstanceTypes=types, ProductDescriptions=["Linux/UNIX"], StartTime=now)
for page in pages:
for row in page["SpotPriceHistory"]:
if row["AvailabilityZone"] not in allowed_azs:
continue
rows.append({
"instanceType": row["InstanceType"], "az": row["AvailabilityZone"],
"priceUSDPerHour": row["SpotPrice"], "priceTimestamp": row["Timestamp"].isoformat(),
})
assert rows, "No price records returned; do not assume zero cost or available capacity"
report = {"queriedAt": now.isoformat(), "region": cfg["region"], "prices": rows}
path = Path(os.environ["ISAAC_EVIDENCE"]) / "spot-prices-current.json"
path.write_text(json.dumps(report, indent=2) + "\n")
print(json.dumps(report, indent=2))
PY
7. 과금과 잔여 자원
GPU 비용 소계는 실제 노드별 실행 시간에 해당 시간대 단가를 적용합니다. 여기에 EKS 제어 영역, Auto Mode 관리 요금, CPU 시스템 노드, EBS 용량·IOPS·처리량, CodeBuild, ECR, S3·DynamoDB·로그, NAT·endpoint·전송을 더합니다. 기존 Fargate/AgentCore 비용은 별도이며 같은 항목을 중복 합산하지 않습니다.
WhenEmpty: 5m은 매번 정확히 5분 후 과금이 끝난다는 약속이 아닙니다. 노드·인스턴스가 실제 종료됐는지 확인합니다. GPU worker가 없어도 CPU pool과 EKS 제어 영역, 보존 저장소는 남을 수 있습니다. 월 환산·On-Demand 대비 절감률은 현재 단가와 실제 사용 시간을 확보한 뒤 계산합니다.
실패 시 재개
| 증상 | 확인 |
|---|---|
| 캐시 miss | 현재 identity의 S3 manifest 존재·image/GPU/driver 일치 |
| 복원 성공인데 느림 | 실제 엔진 캐시 활용, 새 노드·이미지 풀·자산 다운로드, 비교 조건 |
| cache checksum·파일 수 불일치 | 실패로 중단. 검증 우회나 같은 키 덮어쓰기 금지 |
| 지표 없음 | CloudWatch 전송 실패와 실제 DDB 시간값·로그 구분 |
| 예상보다 높은 비용 | AZ별 가격, 교체 중 노드 중첩, 빈 노드·CPU pool·EBS·CodeBuild |
| 96 GiB 씬에서 실패 | 기본 L40S 호환 씬 범위를 넘었는지 확인. VRAM 개수와 용량 구분 |
완료 체크리스트
- 각 시간 필드의 범위와 겹치는 구간을 구분했습니다.
- 성공한 실행의 캐시만 게시하고 실제 복원·효과를 분리했습니다.
- 같은 GPU·driver·이미지 조건의 비교 근거를 남겼습니다.
- 현재 가격·계측 사용량과 추가 서비스 비용을 기록했습니다.
- 선택 옵션의 활성화 여부·구매/VRAM 프로파일·warm 종료 상태를 기록했습니다.
- 다른 프로젝트의 GPU나 가드를 변경하지 않았습니다.
AI CLI 카드: 17. GPU 운영 · 다음: 18. GPU 정리
AI CLI 프롬프트 카드
아래 프롬프트를 복사해 실습 EC2에서 선택한 Codex, Kiro CLI, Claude Code의 대화 입력창에 붙여넣습니다. Markdown 카드 다운로드
선택한 AI CLI의 대화 입력창에 붙여넣으세요. 세 도구가 같은 프롬프트를 사용합니다.
Robot Atlas 17장 GPU 운영 점검을 진행하세요.
ISAAC_REPO/workshop/chapters/17-gpu-operations.md,
isaac/cache.py, worker.py와 사용자가 제공한
2026-09-02-eks-llm-serving-cold-start.md를 읽으세요.
할 일:
- 실제 실행의 ISO createdAt/updatedAt, GPU·driver·image·scene, cache 상태와 timings를 확인합니다.
- 노드 생성·이미지 pull·캐시/자산·엔진/첫 RTX 프레임의 측정 범위를 구분합니다.
- 물리 성공한 cacheCandidate만 promote-cache로 게시하고 identity/checksum/파일 수를 확인합니다.
- 같은 조건의 새 실행에서 복원 파일과 실제 초기화 성능을 따로 비교합니다.
- 현재 서울 Spot 가격을 조회하고 실제 노드 시간과 추가 과금을 계산할 입력을 기록합니다.
- 선택한 경우에만 enable-ondemand, submit의 capacity-type/memory-profile, warm 옵션을 사용합니다.
현재 소스와 실제 배포된 컨트롤러·worker 이미지가 일치하는지 먼저 확인하세요.
주의할 해석:
cacheRestoreSeconds는 worker 시작 이후 준비를 포함하며 순수 캐시 전송 시간만이 아닙니다.
jobToReadySeconds는 재개 시 이전 시도 시간도 포함합니다. 겹친 timings를 모두 더하지 마세요.
S3 restored와 실제 shader cache 효과는 다릅니다. 다른 GPU/driver의 결과를 A/B로 묶지 마세요.
vLLM 문서의 428/226초·sleep 수치를 Isaac 성능으로 쓰지 마세요.
hostPath, EC2NodeClass userData, vLLM streamer/sleep, 별도 SOCI 설치를 복사하지 마세요.
교재의 2026-09-13 가격·점수는 스냅샷이며 가격·할인·용량 보장이 아닙니다.
비공개 조사 JSON을 공개 교재 링크나 다운로드에 넣지 마세요.
경계:
gpu-cluster-01과 전용 소유 자원만 조회/변경합니다.
기본 warm 0·GPU slot 2·admission 1과 기존 가드를 유지합니다.
존재하지 않는 capacity 명령을 만들지 마세요.
enable-ondemand는 비어 있는 Private pool을 준비하며 GPU 작업은 별도 명시적 submit으로 만듭니다.
rtx96은 >90000 MiB 어피니티와 runtime VRAM guard이며, 공개 웹은 항상 standard Spot입니다.
warm --replicas 1은 낮은 우선순위의 선점 가능한 GPU·이미지 holder입니다.
준비된 Isaac 프로세스나 vLLM sleep으로 설명하지 마세요. 유휴 GPU 1대와 작업 중 2대 비용이 가능합니다.
namespace GPU quota 2는 pool·holder 합계에 적용합니다. 필요 없으면 warm --replicas 0으로 중지하세요.
기존 GPU01·Jeju·Fargate·계정 전체 managed resource visibility·공유 discovery 태그를 바꾸지 마세요.
노드는 K8s/NodeClaim/providerID와 특정 EC2 instance ID로 확인합니다.
실제 측정값·비교 조건·가격 조회 시각·잔여 위험·미측정 항목을 보고하세요.
읽기 기록
읽음 표시는 이 브라우저에만 저장됩니다.
실제 실습 결과는 터미널에서 확인하세요.