14진행자·운영자 Isaac Sim · EKS예상 약 55분, 별도 일정
Isaac Sim을 위한 Private EKS Auto Mode
진행자가 기존 VPC에 Auto Mode 클러스터와 g6e·g7e Spot 풀을 구성합니다.
진행자·운영자 선택 트랙, 예상 약 55분입니다. AWS 생성·이미지·용량 대기와 실습·질문에 맞춰 시간을 조정합니다. 14–18장은 00–13장의 참가자 실습과 권한·실행 위치가 다른 별도 트랙입니다.
현재 계정·기존 VPC의 전용 gpu-cluster-01을 운영합니다. 이 장의 명령은 운영자가 원본 저장소에서 실행합니다. 일반 참가자는 실행하지 않으며, 검증 후 제공된 웹을 사용합니다. 먼저 운영자 터미널과 명령 계약을 준비합니다.
1. 기존 전용 클러스터 확인
set -euo pipefail
cd "$ISAAC_REPO"
isaac status > "$ISAAC_EVIDENCE/stacks.json"
jq . "$ISAAC_EVIDENCE/stacks.json"
export ISAAC_KUBECONFIG="$(isaac kubeconfig)"
test "$(ik config current-context)" = gpu-cluster-01
2026-09-13 인프라 확인에서는 이 클러스터가 이미 생성되어 있었습니다. 먼저 실제 상태를 조회하며 같은 이름의 클러스터를 다시 만들지 않습니다.
"$ISAAC_PYTHON" - <<'PY'
import json
from infra.isaac import deployment as d
cfg = d.config()
aws = d.session(cfg)
d.validate_network(aws, cfg)
out = d.outputs(aws, cfg)
cluster = aws.client("eks").describe_cluster(name=cfg["clusterName"])["cluster"]
assert cluster["name"] == "gpu-cluster-01"
assert cluster["arn"] == out["ClusterArn"]
assert cluster.get("tags", {}).get("DeploymentOwner") == d.OWNER
network = cluster["resourcesVpcConfig"]
assert network["vpcId"] == cfg["vpcId"]
assert set(network["subnetIds"]) == set(cfg["privateSubnetIds"])
assert network["endpointPrivateAccess"] is True
assert set(network["publicAccessCidrs"]) == set(cfg["managementCidrs"])
print(json.dumps({
"cluster": cluster["name"], "status": cluster["status"],
"version": cluster["version"], "privateEndpoint": network["endpointPrivateAccess"],
"managementCidrs": network["publicAccessCidrs"], "privateSubnets": network["subnetIds"],
}, indent=2))
assert cluster["status"] == "ACTIVE"
PY
관리용 API는 명시한 운영자 /32에서만 Public 접근을 허용하고 노드는 Private 경로를 사용합니다. 이것은 GPU Pod에 Public IP를 부여한다는 뜻이 아닙니다.
2. 계획과 실제 적용 구분
현재 구성의 신규 생성이나 검토된 변경이 필요한 경우에만 다음 형태로 계획합니다. 이미 완료된 동일 구성에 억지 변경을 만들지 않습니다.
isaac plan --component cluster
jq '{stackName,changeSetType,createdAt,changes}' .local/isaac/cluster-plan.json
출력의 소유 스택·계정·기존 Subnet·접근 CIDR을 확인합니다. 다른 계정 설정, 공유 VPC 생성, 기존 GPU01 변경은 대상이 아닙니다. plan stdout에는 JSON과 파일 경로가 함께 나오므로 저장 파일을 읽습니다.
isaac apply --plan .local/isaac/cluster-plan.json
isaac status
apply는 변경 세트를 실행한 뒤 반환합니다. 상태가 CREATE_COMPLETE 또는 UPDATE_COMPLETE가 될 때까지 status를 조회합니다. 계획은 한 시간 유효하며 삭제·교체 변경은 기본 가드가 거부합니다. “변경 없음”이면 적용할 새 계획이 없는 것이므로 이전 계획을 재사용하지 않습니다.
3. Private NodeClass와 두 NodePool
export ISAAC_KUBECONFIG="$(isaac kubeconfig)"
ik get nodeclasses,nodepools -o json \
> "$ISAAC_EVIDENCE/node-config-before.json"
jq '.items[] | {kind,name:.metadata.name,labels:.metadata.labels,spec}' \
"$ISAAC_EVIDENCE/node-config-before.json"
같은 이름의 기존 자원이 있다면 app.kubernetes.io/part-of: robot-atlas와 아래 설정을 확인한 뒤 적용합니다. 이름이 같다는 이유로 다른 용도의 자원을 인수하지 않습니다.
isaac nodes
ik get nodeclass isaac-sim-private -o json \
> "$ISAAC_EVIDENCE/gpu-nodeclass.json"
ik get nodepool isaac-sim-spot -o json \
> "$ISAAC_EVIDENCE/gpu-nodepool.json"
jq '{name:.metadata.name,subnets:.spec.subnetSelectorTerms,
publicIP:.spec.advancedNetworking.associatePublicIPAddress,
storage:.spec.ephemeralStorage}' "$ISAAC_EVIDENCE/gpu-nodeclass.json"
jq '{requirements:.spec.template.spec.requirements,
taints:.spec.template.spec.taints,disruption:.spec.disruption,limits:.spec.limits}' \
"$ISAAC_EVIDENCE/gpu-nodepool.json"
| 구성 | 실제 기준 |
|---|---|
CPU pool robot-atlas-cpu |
ARM64 On-Demand, 컨트롤러·CoreDNS용 |
GPU NodeClass isaac-sim-private |
기존 Private Subnet 2개, Public IP false, 임시 저장소 200 GiB |
GPU pool isaac-sim-spot |
amd64, Spot, g6e·g7e, 2xlarge·4xlarge·8xlarge, GPU count 1 |
| GPU taint | isaac-sim=true:NoSchedule |
| 통합 | WhenEmpty, 5분 후 검토 |
| 수명·유예 | expireAfter: 336h, NodePool terminationGracePeriod: 30m |
| 용량 | GPU slot 2, 평상시 admission 1, warm replica 0 |
GPU 2개 한도는 교체 여유를 포함합니다. NodePool 한도만으로 절대 과금 상한을 보장하지 않으므로 namespace quota와 단일 admission을 함께 사용합니다. WhenEmpty도 Spot 회수·만료·drift를 막지 않습니다. 30분 유예를 Spot 회수 시 확보된 시간으로 간주하지 않습니다.
g6e/g7e를 한 pool에 넣으면 가용 용량과 조건을 바탕으로 후보를 선택할 수 있습니다. g6e 다음 g7e 순서를 보장하거나 Spot 부족 시 자동 On-Demand로 바꾸는 설정은 아닙니다. 작업은 L40S에 들어가는 범위와 40,000 MiB 초과 어피니티를 사용합니다. 96 GiB가 필수인 다른 씬은 이 기본 과정에 그대로 넣지 않습니다.
운영자가 선택하는 rtx96·On-Demand·warm 옵션은 17장에서 별도로 다룹니다. 이 장의 기본 적용은 그 옵션들을 활성화하지 않으며 공개 웹도 standard Spot을 사용합니다.
AWS 공식 Auto Mode 지원 목록에는 g6e와 g7e가 모두 포함됩니다. 지원 목록에 있다는 사실과 현재 서울 AZ의 Spot 용량 확보는 구분합니다.
4. CoreDNS와 빌드 기반
status에서 해당 component가 없거나 검토된 변경이 있을 때만 계획·적용합니다.
isaac plan --component addons
jq '{stackName,changes}' .local/isaac/addons-plan.json
isaac apply --plan .local/isaac/addons-plan.json
isaac status
ik -n kube-system rollout status deployment/coredns --timeout=300s
isaac plan --component build
jq '{stackName,changes}' .local/isaac/build-plan.json
isaac apply --plan .local/isaac/build-plan.json
isaac status
전용 CodeBuild는 다음 장의 userspace 이미지 보강용입니다. Auto Mode의 호스트 GPU 드라이버·장치 플러그인은 관리형 구성을 사용합니다. GPU Operator, 별도 NVIDIA device plugin, SSH/SSM 드라이버 설치를 추가하지 않습니다.
5. GPU 배치와 드라이버 probe
이미 검증한 probe 결과가 있다면 같은 시점의 NodeClaim·노드 근거를 보관합니다. 재검증이 필요하면 기존 실행과 경합하지 않는 시간에 검토된 전용 probe Job을 적용합니다. 이것도 GPU Spot 비용을 발생시킵니다.
ik -n robot-atlas-isaac get jobs,pods -o wide
ik apply --dry-run=server -f infra/isaac/gpu-probe.yaml
ik apply -f infra/isaac/gpu-probe.yaml
ik -n robot-atlas-isaac wait --for=condition=complete \
job/isaac-gpu-probe --timeout=900s
ik -n robot-atlas-isaac logs job/isaac-gpu-probe \
> "$ISAAC_EVIDENCE/gpu-probe.txt"
ik get nodes -l karpenter.sh/nodepool=isaac-sim-spot -o json \
> "$ISAAC_EVIDENCE/gpu-nodes.json"
jq '.items[] | {name:.metadata.name,type:.metadata.labels["node.kubernetes.io/instance-type"],
capacity:.metadata.labels["karpenter.sh/capacity-type"],
providerID:.spec.providerID,addresses:.status.addresses}' \
"$ISAAC_EVIDENCE/gpu-nodes.json"
cat "$ISAAC_EVIDENCE/gpu-probe.txt"
기존 완료 Job을 같은 이름으로 다시 적용한다고 probe가 재실행되는 것은 아닙니다. 실제 재검증 시 Job UID·생성 시각을 확인합니다. 외부 IP가 없어야 하며, 실제 EC2의 Public IP 확인은 Node의 providerID를 통해 정확한 인스턴스를 조회합니다. Auto Mode 관리형 자원이 기본 EC2 목록에서 숨겨질 수 있으므로 빈 목록만으로 노드가 없다고 판단하지 않습니다.
2026-04-22 이후 생성되는 관리형 인스턴스와 관련 자원은 기본 EC2 목록에서 숨겨질 수 있습니다. 표시 설정은 계정 전체에 적용되므로 이 과정에서 변경하지 않습니다. 전용 클러스터의 Kubernetes 노드가 가리키는 특정 instance ID 조회를 사용합니다.
"$ISAAC_PYTHON" - <<'PY'
import json, os, re
from pathlib import Path
from infra.isaac import deployment as d
cfg = d.config()
aws = d.session(cfg)
nodes = json.loads((Path(os.environ["ISAAC_EVIDENCE"]) / "gpu-nodes.json").read_text())["items"]
ids = []
for node in nodes:
assert node["metadata"]["labels"].get("karpenter.sh/nodepool") == "isaac-sim-spot"
instance_id = node["spec"]["providerID"].rsplit("/", 1)[-1]
assert re.fullmatch(r"i-(?:[0-9a-f]{8}|[0-9a-f]{17})", instance_id)
ids.append(instance_id)
assert ids, "No captured GPU node: inspect a current probe, not an empty EC2 list"
rows = [i for r in aws.client("ec2").describe_instances(InstanceIds=ids)["Reservations"]
for i in r["Instances"]]
assert {i["InstanceId"] for i in rows} == set(ids)
for instance in rows:
assert instance["VpcId"] == cfg["vpcId"] and instance["SubnetId"] in cfg["privateSubnetIds"]
assert not instance.get("PublicIpAddress")
assert all(not eni.get("Association", {}).get("PublicIp")
for eni in instance.get("NetworkInterfaces", []))
print(json.dumps([{"id": i["InstanceId"], "type": i["InstanceType"],
"state": i["State"]["Name"], "publicIp": i.get("PublicIpAddress")}
for i in rows], indent=2))
PY
계정 전체 visibility나 기존 GPU01·VPC의 discovery 태그를 변경하지 않습니다. 공식 근거는 운영자 참고 문서에 연결되어 있습니다.
제공된 관측값은 g6e.2xlarge / L40S / 580.178.04입니다. 새로운 실행에서 다른 허용 후보가 선택될 수 있습니다. 이 probe는 nvidia-smi 확인이며 Isaac Sim·RTX·물리 시험은 15장에서 수행합니다.
실패 시 재개
| 증상 | 먼저 확인 |
|---|---|
| 계정/VPC/소유권 가드 실패 | 진행자 인증 경로와 고정 구성. 가드 해제 금지 |
| API timeout·Unauthorized | 관리 /32, EKS AccessEntry, 선택한 전용 kubeconfig |
| Pod Pending | Spot 용량, 두 AZ, quota, GPU count·VRAM 조건, taint |
ImagePullBackOff |
이미지 접근·NAT·ECR 권한과 digest |
nvidia-smi 실패 |
관리형 GPU 노드 상태·드라이버. 호스트 설치로 우회하지 않음 |
| 기존 probe가 Complete | UID·생성 시각으로 과거 실행과 새 검증 구분 |
완료 체크리스트
- 운영자·참가자 명령의 권한과 실행 위치를 구분했습니다.
- 소유 클러스터와 기존 Private 네트워크·관리 API 범위를 확인했습니다.
- g6e/g7e 단일 GPU 후보, WhenEmpty 5분, 한도·admission을 확인했습니다.
- 실제 노드의 드라이버·Private 배치를 확인했습니다.
- 아직 RTX·물리·복구 성공으로 기록하지 않았습니다.
AI CLI 카드: 14. 운영자 EKS · 다음: 15. Isaac Sim 실행
AI CLI 프롬프트 카드
아래 프롬프트를 복사해 실습 EC2에서 선택한 Codex, Kiro CLI, Claude Code의 대화 입력창에 붙여넣습니다. Markdown 카드 다운로드
선택한 AI CLI의 대화 입력창에 붙여넣으세요. 세 도구가 같은 프롬프트를 사용합니다.
Robot Atlas 14장 진행자·운영자 과정을 도와주세요.
ISAAC_REPO의 workshop/chapters/14-isaac-eks.md와
workshop/reference/isaac-operator.md를 먼저 읽으세요.
권한 범위:
- 현재 계정·기존 VPC의 전용 gpu-cluster-01과 RobotAtlasIsaac 계열 스택입니다.
- infra/isaac/config.json과 deployment.py의 가드를 유지하세요.
- 00–13장의 참가자별 app과 다른 운영 과정입니다. 참가자에게 운영 권한을 복사하지 마세요.
- 기존 GPU01·Jeju·운영 Fargate·공유 VPC/Subnet/NAT·인증서·DNS·discovery 태그는 수정하지 마세요.
할 일:
1. 실제 scripts/isaac.py --help, 소유 스택 상태, 계정·VPC·전용 kubeconfig를 확인합니다.
2. 이미 존재하는 클러스터를 재생성하지 않고, 필요한 변경만 plan 파일로 검토·apply합니다.
3. Private NodeClass, Public IP false, GPU pool의 amd64/Spot/g6e·g7e/2·4·8xlarge/GPU count 1을 확인합니다.
4. WhenEmpty 5분, GPU slot 2, admission 1, warm 0을 구분합니다.
5. CoreDNS·전용 빌드 기반과 실제 nvidia-smi probe를 확인합니다.
6. NodeClaim/node providerID의 특정 instance ID로 Private 배치를 조회합니다.
판단:
- g6e/g7e Auto Mode 지원과 실제 Spot 확보는 다릅니다.
- 관리형 드라이버·device plugin을 사용하며 SSH/SSM·userData·별도 GPU Operator를 추가하지 마세요.
- nvidia-smi 성공은 RTX 카메라·PhysX·Spot 복구 성공이 아닙니다.
- 관리형 EC2 자원은 기본 목록에서 숨겨질 수 있습니다. 계정 전체 visibility를 변경하지 마세요.
- plan stdout에 경로가 섞이므로 저장된 .local/isaac/<component>-plan.json을 읽으세요.
실제 수행 명령·종료 상태·소유 자원·Private/드라이버 근거와 미검증 항목을 보고하세요.
설정이나 테스트 기대값을 바꾸어 아직 실행하지 않은 GPU 성공을 만들지 마세요.
읽기 기록
읽음 표시는 이 브라우저에만 저장됩니다.
실제 실습 결과는 터미널에서 확인하세요.