AWS PCS 구축 가이드
서울 리전 · p6-b300 · 대규모 LLM 학습 환경

대상 42dot AI Infra / Data&ML 팀  ·  리전 ap-northeast-2  ·  인스턴스 p6-b300.48xlarge (8× NVIDIA B300)
규모 2노드 검증 → 16노드 파일럿 → 최대 125노드(1,000 GPU)  ·  기준 AWS PCS · Slurm 26.05 (또는 25.11) · PCS-ready DLAMI  ·  작성 2026-09-15
이 문서를 읽는 방법

Part 1을 먼저 읽으십시오. 특히 §4 되돌리기 어려운 결정 7가지는 클러스터를 만든 뒤 바꾸면 재생성이나 대규모 재작업이 필요한 항목입니다. Part 2는 순서대로 따라가는 절차이고, 각 단계 안에 그 단계에서 실제로 부딪히는 함정과 팁을 배치했습니다. 건너뛰지 말고 단계 내 경고 박스를 읽어주십시오.

AWS ParallelCluster와 다른 점은 곳곳에 PCS 차이로 표시했습니다. ParallelCluster 경험이 있는 분이 그 감각으로 PCS를 다루다 빠지는 함정이 이 표시 뒤에 있습니다.

1. 무엇을 만드는가

AWS PCS(Parallel Computing Service)는 Slurm 컨트롤러(slurmctld)와 accounting DB(slurmdbd)를 AWS가 관리형으로 운영하고, 컴퓨트 노드는 고객 계정의 EC2로 띄우는 서비스입니다. ParallelCluster처럼 "설정 파일 하나로 CloudFormation을 찍어내는 도구"가 아니라, 클러스터·노드그룹·큐라는 AWS 리소스를 API로 만드는 서비스입니다. 서비스 요금이 있지만 이 규모에서는 GPU 비용의 약 0.4%입니다(비용).

┌──────────────── VPC (권장 /16) ─────────────────────────────────┐ │ │ 사용자 ──SSH/SSM────┼──▶ ┌──────────────┐ Public 또는 Private Subnet │ │ │ Login Nodes │ ← 사용자 접속 · sbatch/squeue │ │ │ CNG 또는 │ (CNG로 만들면 관리 요금, standalone이면 無) │ │ │ standalone │ │ │ └──────┬───────┘ │ │ │ │ ┌──────────────────┼───────────▼──────────────────────┐ │ │ AWS 관리 (PCS) │ PCS Cluster (size = MEDIUM) │ slurmctld · slurmdbd │ │ 요금: 컨트롤러 │ Slurm 26.05 / 25.11 │ ← 고객이 접근 불가, HA·패치 AWS │ └──────────────────┼───────────┬──────────────────────┘ │ │ │ Slurm 제어 (클러스터 SG 자기참조 all-traffic) │ │ ┌────────▼───────────────────────────────────────────┐ │ │ │ GPU Compute Node Group(s) · Private Subnet (/20) │ │ │ │ 단일 AZ · NAT 경유 · static (min = max) │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ p6-b300 #1 │ │ p6-b300 #N │ Launch Template │ │ │ │ │ 8× B300 │◀▶│ 8× B300 │ 17 NIC 수기 정의 │ │ │ │ │ 96 CPU(SMT✕)│ │ 17 IP/노드 │ (efa 타입) │ │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ └── EFA 6,400 Gbps (NCCL) ──┘ │ │ │ └──────────────────┬─────────────────────────────────┘ │ │ │ POSIX mount (lifecycle action) │ │ ┌──────────────▼───────────┐ │ │ │ FSx for Lustre → /fsx │ ← 클러스터 외부에 생성 │ │ │ FSx for OpenZFS → /home │ │ │ └──────────────┬───────────┘ │ └─────────────────────┼─────────────────────────────────────────────┘ │ DRA 동기화 ┌──────▼──────┐ │ S3 (원본) │ Queue(=partition) ──▶ 1개 이상의 CNG 매핑 └─────────────┘

2. 구성요소 개념

PCS의 리소스 계층은 세 단계입니다. Slurm 용어와의 매핑을 정확히 알아야 API 인자가 읽힙니다.

Cluster
관리형 slurmctld(+ 선택 slurmdbd). 사이즈(Small/Medium/Large)와 Slurm 메이저 버전을 가집니다. 사이즈는 생성 후 변경 불가, 버전은 UpdateClusterin-place 업그레이드 가능. 고객은 slurm.conf·slurmdbd.conf를 직접 편집하지 않고 허용 목록(allow-list) 파라미터만 넣습니다.
Compute Node Group
(CNG)
노드 생성 규칙. 인스턴스 타입 · launch template · 서브넷 · min/max · 구매옵션(ONDEMAND/SPOT/CAPACITY_BLOCK) · AMI · IAM instance profile · node lifecycle actions를 정합니다. 기본 한도 클러스터당 10개.
Queue
(= Slurm partition)
1개 이상의 CNG를 매핑합니다. 파티션 레벨 Slurm 설정(AllowQos, MaxTime, GraceTime 등)은 여기서 넣습니다. 기본 한도 클러스터당 10개. PCS 차이 PCS가 임의로 만드는 파티션은 없습니다. 큐를 만든 것만 파티션입니다.
Static 노드
min == max. 항상 켜져 있고 PCS가 수량을 유지합니다. 대규모 학습과 Capacity Block에서는 이 모드가 기본입니다.
Dynamic / Mixed
min = 0(dynamic) 또는 0 < min < max(mixed, Slurm 24.05+). job에 따라 뜨고 지며 scaleDownIdleTime 뒤 종료. 125노드 단일 job을 dynamic으로 던지면 용량 확보 실패 → 반납 → 재시도 루프에 빠질 수 있습니다(Step 0).
Login 노드
두 방식: ① CNG로 생성(노드 관리 요금 발생, 클러스터 "관리 인스턴스" 수에 포함) ② PCS 밖 standalone EC2에 AWS 제공 스크립트로 연결(요금 없음, 여러 클러스터에 연결 가능). 이 문서는 PoC는 CNG, 본운영은 standalone을 권장합니다.
설계 결정 · GPU CNG는 전량 static, 용량은 예약 기반

PCS 문서 원문: job은 전체 노드가 확보되기 전에는 시작하지 않고, 큰 job은 부분씩 확보하다가 전체를 못 채우면 이미 launch한 인스턴스를 반납할 수 있습니다. 그 사이 인스턴스는 과금됩니다. 같은 문서가 대안을 명시합니다: "back the compute node group with a capacity reservation."

ODCR 또는 Capacity Block + static CNG(min = max)가 1,000 GPU 학습의 전제입니다. Capacity Block은 예약 창 전체가 과금되므로 0에서 스케일업하게 두면 유휴 시간만큼 낭비입니다.

3. p6-b300 하드웨어 특성 — PCS 관점

항목설계에 미치는 영향
GPU8 × NVIDIA B300, HBM 합계 2.1 TB노드 내 8 GPU가 NVLink 도메인. TP는 노드 내, PP/DP는 노드 간(EFA)
vCPU192 → PCS에서는 96 CPUPCS 차이 PCS는 부트스트랩에서 SMT를 강제 비활성합니다(설정 불가). Slurm에는 96 물리코어로 등록 → GPU당 12 CPU 기준으로 --cpus-per-task, DataLoader(num_workers)를 설계
메모리4,096 GiBDefMemPerCPU≈40000(MB, 96 CPU 기준). DefMemPerGPU는 PCS 허용 목록에 없음
로컬 NVMe8 × 3,840 GBEnroot 오버레이 + 데이터 캐시. lifecycle action에서 RAID/마운트(EVERY_BOOT)
네트워크 카드17장 — card 0은 ENA 전용(EFA 미지원, 최대 350 Gbps), card 1–16은 EFA 각 최대 400 GbpsPCS 차이 PCS는 EFA를 자동 구성하지 않습니다. launch template에 17개 인터페이스를 직접 정의(Step 5). P5/P6-B200과 배선 규칙이 다릅니다(card 0 = ENA-only, EFA는 card 1–16의 DeviceIndex 0)
노드당 사설 IP17개PCS 차이 PCS 레퍼런스는 InterfaceType: efa(efa-only 아님)를 요구 → 인터페이스마다 IP 소비. 125노드 = 2,125 IP → 서브넷 /20. efa-only 동작 여부는 PoC 실측 확인 필요
EFA 총 대역폭6,400 GbpsNCCL 노드 간 통신. 다중 NIC 인스턴스라 퍼블릭 IP 자동 할당 불가 → 프라이빗 서브넷 + NAT
On-Demand 단가 (서울)$209.1735 / 시간비용의 대부분. 2026-09-14 기준, Linux

4. 되돌리기 어려운 결정 7가지

착수 전 팀 합의 필수

아래 항목은 클러스터를 만든 뒤 바꾸면 클러스터 재생성, 또는 launch template·CNG·서브넷을 전부 다시 만드는 재작업이 필요합니다. 구축을 시작하기 전에 확정하십시오.

#결정 항목왜 되돌리기 어렵나권장
1클러스터 사이즈 변경불가 PCS 문서: "You can't change the cluster size after you create the cluster." login CNG도 관리 인스턴스 수에 포함 Medium (≤512 인스턴스, ≤8,192 job). 2노드 PoC라도 Medium으로 시작해야 125노드까지 한 클러스터로 갑니다. Small(≤32)은 GPU 32대 + login 2대에서 이미 초과
2Availability Zone 변경불가 GPU CNG는 단일 서브넷(=단일 AZ)이어야 EFA가 동작. 서브넷·FSx·placement group이 모두 여기에 묶임 서울에서 p6-b300이 실제 제공되는 AZ를 먼저 조회(Step 0)
3GPU 서브넷 CIDR 확장불가 서브넷은 생성 후 크기를 늘릴 수 없고, PCS 경로는 노드당 17 IP를 소비 /20 이상 (125노드 × 17 = 2,125 IP). login·FSx·NAT는 별도 서브넷(Step 1)
4Slurm 메이저 버전 in-place 업그레이드는 가능하나, SPANK 플러그인을 쓰면 25.11 이하에서 rolling 업그레이드 불가. 25.11은 GPU affinity용 Sockets를 CNG마다 직접 넣어야 함 26.05(PCS 출시 2026-09-10, EOL 2027-11-30). GPU affinity 자동, SPANK 제약 해소. 보수적으로 가려면 25.11(EOL 2027-05-31). 25.05는 2026-11-30 EOL이라 대상 아님
5용량 확보 경로
Capacity Block vs ODCR
launch template 구조(MarketType, CapacityReservationTarget, placement group 유무)와 CNG 분할 수가 달라짐 Step 0 · Step 5. Capacity Block은 CNG당 블록 1개라 125노드 = 최소 2 CNG
6공유 스토리지를
클러스터 외부에 생성
치명적
awslabs 레퍼런스처럼 nested stack 안에서 FSx를 만들면 스택 삭제 시 데이터가 함께 삭제됩니다. 클러스터 사이즈 변경(#1) 등으로 재생성할 때 그대로 사고가 됩니다 FSx를 별도 스택으로 만들고 ID로 참조. lifecycle action에서 마운트(Step 2)
7AMI 전략 PCS-ready DLAMI를 SSM /latest/로 참조하면 stack update마다 AMI가 조용히 바뀝니다(drift). Enroot/Pyxis를 첫 부팅에 설치하면 노드마다 부팅 8–12분 PoC: PCS-ready DLAMI를 ID로 pin. 본운영: Enroot/Pyxis pre-bake 커스텀 AMI(부팅 ~3분). 릴리스 SNS 구독(Step 3)
#1과 #6이 이 문서에서 가장 중요한 항목입니다

클러스터 사이즈를 Small로 시작하면 32노드에서 벽에 부딪혀 클러스터를 새로 만들어야 합니다. 그때 FSx가 클러스터 스택 안에 있으면 학습 데이터와 체크포인트가 사라집니다. 두 결정은 서로 얽혀 있습니다: 처음부터 Medium + 외부 스토리지로 시작하면 재생성 자체가 거의 필요 없어집니다.

PCS는 ParallelCluster와 달리 Slurm 버전 업그레이드에 클러스터 재생성이 필요하지 않습니다(Step 10). 재생성 사유는 사실상 사이즈 변경 하나입니다.

5. 설계 요약

영역결정근거 / 상세
클러스터Medium · Slurm 26.05 · accounting STANDARD사이즈 변경 불가. 26.05는 GPU affinity 자동·SPANK rolling 제약 해소. accounting은 사후 on/off 가능하나 멀티팀 확정이면 처음부터
AMIPCS-ready DLAMI (ID pin) → 본운영 pre-bakeNVIDIA 드라이버·CUDA·EFA·Lustre 클라이언트·PCS Agent·Slurm 내장. Enroot/Pyxis는 미포함 → lifecycle action 또는 Image Builder(Step 3)
EFAlaunch template에 17 NIC 수기 정의, InterfaceType: efaPCS 차이 자동 구성 없음. awslabs add-cng-p6-b300.yaml이 완성된 배선(Step 5)
네트워크GPU 서브넷 프라이빗 /20 + NAT, 단일 AZ17 IP/노드. 다중 NIC은 퍼블릭 IP 자동 할당 불가
노드 구성GPU CNG 전량 static, 예약 기반job-level scaling의 확보 실패 루프 회피. Capacity Block은 CNG당 1블록
GPU 헬스체크관리형 기능 없음 → 직접 구성 (lifecycle action + HealthCheckProgram + prolog/epilog)PCS 차이 HyperPod의 auto-healing에 해당하는 기능이 PCS에는 없습니다. awslabs 스위트로 구현(Step 8)
스토리지/fsx Lustre + /home OpenZFS + 로컬 NVMe + S3외부 생성 후 ID 참조. 마운트는 nodeBootstrapped 스테이지, onError: TERMINATE
멀티팀큐(≤10) + Slurm accounting QoSGrpTRES=gres/gpu=N이 실제 강제 수단. QoS가 DB에 없는 상태로 큐에 AllowQos를 넣으면 25.11+에서 slurmctld가 fatal(Step 7)
관측AMP/AMG 관리형 + ADOT CollectorAWS 공식 observability_for_pcs 레시피. B300은 DCGM 4.5.2 이상 필요
확장2 → 16 → 최대 125노드UpdateComputeNodeGroup으로 min/max 조정, CNG 추가는 큐에 매핑만(Step 10)

Part 2 · 구축 절차

STEP 0

용량과 쿼터 확보

D-14 ~ D-7

일정상 가장 앞에 와야 하는 단계입니다. 다른 모든 작업이 여기에 의존합니다.

0-1. p6-b300 가용 AZ 조회

aws ec2 describe-instance-type-offerings --region ap-northeast-2 \
  --location-type availability-zone \
  --filters Name=instance-type,Values=p6-b300.48xlarge \
  --query 'InstanceTypeOfferings[].Location' --output table

여기서 나온 AZ가 이후 GPU 서브넷·FSx·placement group의 위치를 전부 결정합니다.

0-2. 용량 예약 — 두 경로 중 선택

경로PCS 설정제약적합
Capacity Block
for ML
purchaseOption=CAPACITY_BLOCK + LT MarketType=capacity-block + CapacityReservationId 블록당 최대 64대 · 합계 256대 · PCS는 CNG당 블록 1개, reservation group 불가 · 취소 불가 · placement group 불가 · 최대 8주 전 예약 기간이 정해진 대규모 학습 캠페인
ODCR
(targeted)
purchaseOption=ONDEMAND + LT CapacityReservationTarget Running On-Demand P instances vCPU 쿼터 필요. 예약 ID 직접 참조 = 백필 없음, reservation group 참조 = 백필 가능 상시 운영 · 개발용 소규모
확인됨 · 서울에서 p6-b300 Capacity Block 예약이 가능합니다

EC2 User Guide의 Capacity Block 지원 표에 p6-b300.48xlarge × Asia Pacific (Seoul) 체크가 있습니다(2026-09-14 확인). 서울에서 예약 가능한 P 계열은 현재 p6-b300.48xlargep5en.48xlarge 두 종입니다. PCS도 P6-B300 계열에서 Capacity Block을 공식 지원합니다.

함정 · 125노드는 단일 Capacity Block도, 단일 CNG도 불가능합니다

Capacity Block은 블록당 최대 64대이고 PCS는 CNG 하나에 블록 하나만 연결합니다(여러 블록을 묶은 capacity reservation group은 연결 불가). 따라서 125노드 = 최소 2개 블록 = 최소 2개 CNG입니다. 두 CNG를 같은 큐에 매핑하면 사용자에게는 하나의 파티션으로 보이므로 실사용에는 영향이 없습니다(CNG 한도 10개 안에서 여유).

EC2 문서 단서: "Capacity Block sizes of 64 instances are not supported for all instance types in all AWS Regions." 서울 p6-b300의 실제 최대 블록 크기를 확인하십시오 — 32대라면 CNG가 4개가 됩니다. 확인 필요

함정 · Capacity Block 종료 시각은 UTC 고정 (KST 20:00)

Capacity Block은 UTC 11:30에 종료되고, 마지막 날 UTC 11:00부터 인스턴스 종료 프로세스가 시작됩니다. KST 기준 20:00 종료 시작 / 20:30 완료입니다. 만료 시 CNG·큐 연결은 유지되지만 모든 인스턴스가 종료되고 실행 중 job은 실패, 대기 job은 pending에 멈춥니다. 복구는 새 블록을 가리키는 launch template으로 CNG를 업데이트해야 합니다.

학습 스크립트가 KST 19:30 이전에 최종 체크포인트를 저장하도록 계획하십시오. 예약 창 전체가 과금되고 조기 취소는 불가능합니다.

0-3. 쿼터 신청

쿼터기본값조정125노드 기준
EC2 Running On-Demand P instances (vCPU)계정별Service Quotas24,000 vCPU (ODCR/On-Demand 경로만. Capacity Block은 미적용)
PCS 리전별 클러스터 수5Service Quotas여유
PCS 동시 생성 중 클러스터1조정 불가자동화 파이프라인은 직렬화
PCS 클러스터당 CNG 수10Support caselogin 1 + GPU 2~4 + CPU/MIG 여유 — 팀별 CNG 분리는 금방 소진
PCS 클러스터당 큐 수10Support case팀별 파티션 설계 전 확인
팁 · vCPU 쿼터는 지금 신청하십시오

승인 리드타임이 이 프로젝트에서 가장 긴 대기 항목입니다. Capacity Block만 쓸 계획이면 불필요하지만, 두 경로를 섞을 가능성이 조금이라도 있으면 지금 신청하는 게 안전합니다. 쿼터는 확보만 해두고 쓰지 않아도 비용이 발생하지 않습니다.

0-4. 도구 준비

aws --version                       # pcs 서브커맨드가 있는 최신 AWS CLI v2
aws pcs list-clusters --region ap-northeast-2   # 권한·엔드포인트 확인 (pcs.ap-northeast-2.amazonaws.com)
참고 · CNG당 최대 인스턴스 수

PCS 문서에 CNG당 인스턴스 상한이 명시되어 있지 않습니다. 클러스터 사이즈(Medium 512)가 실효 한도로 보이지만, ODCR 경로에서 125노드를 단일 CNG로 둘지는 AWS 서비스팀 확인이 안전합니다. 확인 필요

STEP 1

네트워크 구축

D-7

1-1. 만들 것

  • VPC — 권장 /16, DNS hostnames + DNS resolution 활성(PCS 요구사항)
  • 퍼블릭 서브넷 1개 — NAT Gateway, (선택) login 노드
  • 프라이빗 GPU 서브넷 1개 — Step 0의 AZ, /20
  • 프라이빗 서브넷 1개 — login CNG / FSx 마운트타깃 / PCS 클러스터 ENI (GPU 서브넷과 분리)
  • 클러스터 보안그룹자기참조(self-referencing) all-traffic 인바운드/아웃바운드. PCS가 컨트롤러 ENI와 모든 노드에 붙이는 SG
  • EFA 보안그룹 — 자기참조 all-traffic (EFA 트래픽은 SG 자기참조가 필수)
  • (선택) SSH 보안그룹 — login 노드 22, 신뢰 CIDR만
  • (선택) S3 / ECR / CloudWatch / SSM VPC 엔드포인트 — NAT 데이터 처리 비용 절감

AWS 공식 레시피 aws-hpc-recipes/recipes/net/hpc_large_scale이 PCS 요구사항을 만족하는 VPC를 만들어 줍니다. CIDR만 아래 표에 맞춰 조정하십시오.

1-2. GPU 서브넷 사이징 — 노드당 17 IP

GPU 노드필요 IP (17/노드)최소 서브넷 (가용 IP)
234/26 (59)
16272/23 (507)
32544/22 (1,019)
641,088/21 (2,043)
1252,125/20 (4,091) — /21부족
함정 · ParallelCluster의 "노드당 1 IP"를 그대로 가정하면 서브넷이 모자랍니다

PCS 차이 ParallelCluster 3.15+는 secondary 카드를 efa-only로 만들어 노드당 IP를 1개로 줄이지만, PCS 레퍼런스 템플릿은 InterfaceType: efa를 요구합니다(awslabs 주석: "PCS requires InterfaceType: efa (not efa-only) to propagate the interface configuration to the launch instances request correctly."). PCS 공식 문서의 EFA 예제도 전부 efa입니다. → 노드당 17 IP로 계산하십시오. 서브넷은 나중에 키울 수 없습니다.

PoC에서 efa-only가 동작하는지 실측해 볼 가치는 있습니다(통하면 17→1). 다만 서브넷은 통하지 않는 경우를 기준으로 잡으십시오. 확인 필요

함정 · 컴퓨트를 퍼블릭 서브넷에 두면 부트스트랩이 실패합니다

EC2는 네트워크 인터페이스가 2개 이상인 인스턴스에 퍼블릭 IP를 자동 할당하지 않습니다. 17-NIC p6-b300은 퍼블릭 서브넷에서 인터넷을 쓸 수 없어 PCS agent가 컨트롤러·S3에 닿지 못합니다. → 프라이빗 서브넷 + NAT Gateway(또는 VPC 엔드포인트)가 필수입니다.

참고 · GPU CNG의 서브넷은 항상 1개

PCS 문서: "If you select multiple subnets, EFA communications won't be available between nodes." CNG의 --subnet-ids에는 GPU 서브넷 하나만 넣으십시오. 1,000 GPU 중 일부를 EKS/HyperPod 등 다른 플랫폼에 둘 경우에도 PCS 쪽 GPU는 같은 AZ에 모아 EFA 지역성을 확보합니다.

STEP 2

공유 스토리지 생성 — 클러스터 외부에

D-5
이 단계를 클러스터 생성 전에 하는 이유

awslabs deploy-all.yaml은 편의상 VPC·FSx·PCS를 하나의 nested stack으로 만듭니다. PoC에는 좋지만, 그 스택을 지우면 FSx도 함께 지워집니다. 본운영 경로는 FSx를 별도 스택으로 먼저 만들고 클러스터 쪽에서는 파일시스템 ID로만 참조하십시오. 클러스터 사이즈 변경(§4 #1)으로 재생성하는 날 데이터가 살아 있는 유일한 방법입니다.

2-1. 계층 구조

마운트서비스용도
/fsxFSx for Lustre
PERSISTENT_2, PerUnitStorageThroughput 250 이상
학습 데이터, 체크포인트, 컨테이너 .sqsh 이미지
/homeFSx for OpenZFS (SINGLE_AZ_HA_2) 또는 EFS홈 디렉터리 (small file / metadata 위주)
/opt/dlami/nvme로컬 NVMe (8 × 3.84 TB)Enroot 오버레이, 데이터셋 로컬 캐시 — 휘발성. lifecycle action EVERY_BOOT로 RAID0+마운트
S3 (+ Lustre DRA)원본 데이터셋, 체크포인트 아카이브

2-2. 만들 때 지킬 것

  • FSx for Lustre를 GPU 서브넷과 같은 AZ에 생성 (AZ 간 전송 비용·지연 회피)
  • 파일시스템 보안그룹에 클러스터 SG 인바운드 허용 (Lustre 988, 1018–1023 / OpenZFS 111·2049·20001–20003)
  • S3 버킷 생성 + Lustre DRA(Data Repository Association) 연동
  • 마운트는 PCS가 대신 해주지 않습니다 PCS 차이Step 3의 lifecycle action mount-fsx.sh가 수행
함정 · 마운트 실패 노드는 즉시 교체되게 하십시오

PCS 문서의 명시적 경고: 마운트 실패/타임아웃은 인스턴스가 job 실행 가능 상태가 되지 못하게 만들고, 이는 예상치 못한 비용으로 이어집니다. 마운트 스크립트는 nodeBootstrapped 스테이지 + onError: TERMINATE로 두어 공유 스토리지 없는 노드가 큐에 들어오지 않게 하십시오(TERMINATE된 노드는 PCS가 교체합니다). nodeReady 스테이지는 slurmd가 이미 떠서 job이 배정될 수 있는 시점이라 늦습니다.

팁 · 작게 만들고 나중에 늘리십시오

FSx for Lustre와 OpenZFS 모두 생성 후 용량·처리량 증설이 가능합니다(데이터 마이그레이션 없음). 큰 파일시스템은 생성 자체에 시간이 오래 걸려 초기 구축 전체를 지연시킵니다.

참고 · GPUDirect Storage

FSx for Lustre에 EFA를 켜면 GPU 메모리로 직접 DMA하는 GDS 경로를 쓸 수 있습니다(P5/P5e/P5en/P6-B200에서 지원). PERSISTENT_2 SSD 전용이고 최소 용량이 19,200 GiB로 올라갑니다. awslabs 지원 목록에 P6-B300이 명시되어 있지 않습니다 확인 필요 — 1단계에서는 켜지 말고, 스토리지가 실제 병목으로 확인된 뒤 검토하십시오.

STEP 3

AMI와 부트스트랩 (node lifecycle actions)

D-5

3-1. AMI — PCS-ready DLAMI를 ID로 고정

# 최신 PCS-ready DLAMI (Deep Learning Base GPU AMI, Ubuntu 24.04 기반)
aws ssm get-parameter --region ap-northeast-2 \
  --name /aws/service/pcs/ami/dlami-base-ubuntu2404/x86_64/latest/ami-id \
  --query "Parameter.Value" --output text

# Description에 소스 DLAMI 날짜 / PCS Agent 버전 / 내장 Slurm 버전이 적혀 있음 — 기록해 두십시오
aws ec2 describe-images --region ap-northeast-2 --image-ids ami-xxxxxxxx \
  --query 'Images[0].[Name,Description,CreationDate]' --output table
포함미포함 (직접 추가)
Ubuntu 24.04 · NVIDIA 드라이버 · CUDA · EFA 드라이버 · Lustre 클라이언트 · EFS utils · PCS Agent · Slurm for PCS(지원 버전 다중 내장, 클러스터 버전에 맞춰 자동 활성)PyTorch 등 프레임워크(컨테이너로) · Enroot / Pyxis · DCGM exporter · 컴파일러·수학 라이브러리
함정 · SSM /latest/를 CloudFormation에 직접 넣으면 AMI가 조용히 바뀝니다

{{resolve:ssm:/aws/service/pcs/ami/.../latest/ami-id}}stack update마다 재해석됩니다. 나중에 노드 수를 올리는 update가 새 AMI를 끌어와 드라이버·CUDA 버전이 노드마다 달라지는 상황이 생깁니다. awslabs 운영 가이드의 명시적 권고: AMI ID를 pin하고, 릴리스 SNS(arn:aws:sns:us-west-2:265886551188:pcs-ready-dlami-release-notifications)를 구독해 의도적으로 올리십시오.

설계 원칙 · PoC는 DLAMI + lifecycle action, 본운영은 pre-bake

Enroot/Pyxis를 lifecycle action으로 첫 부팅에 설치하면 노드 부팅이 ~8–12분, 커스텀 AMI에 미리 굽면 ~3분입니다. 125노드를 static으로 한 번에 올릴 때, 그리고 노드 교체마다 이 차이가 GPU 유휴 비용으로 곱해집니다. AWS가 EC2 Image Builder 템플릿(aws-hpc-recipes/recipes/pcs/dlami_for_pcs_imagebuilder, pcs-ready-dlami-with-enroot-pyxis.yaml)을 제공합니다. PCS 차이 커스텀 AMI는 임의 OS(AL2023/Ubuntu/RHEL/Rocky)에 AWS installer로 PCS agent + Slurm을 얹는 방식이라, 특정 CUDA/NCCL 버전 고정이 HyperPod보다 자유롭습니다.

3-2. Node lifecycle actions — user data 대신 이것을 쓰십시오

PCS Agent 1.5.0-1 이상에서 PCS가 지정 스테이지에 S3 스크립트를 내려받아 실행합니다(3회 재시도·백오프 내장, 스크립트별 로그). launch template user data보다 실패 처리와 관측이 훨씬 낫습니다.

스테이지시점여기에 둘 것
nodeBootstrappedPCS 노드 셋업 완료 후, slurmd 시작 전job을 받기 전에 끝나야 하는 것 전부: 파일시스템 마운트, NVMe RAID, needrestart 가드, 헬스체크 스크립트 로컬 복사, 심층 GPU 진단, DCGM exporter
nodeReadyslurmd 시작·등록 후Slurm 명령이 필요한 것(노드 feature 태깅 등). ⚠ 이 시점엔 이미 job이 배정될 수 있습니다
옵션권장
onErrorTERMINATE(기본) / CONTINUE마운트·GPU 진단 = TERMINATE(불량 노드는 교체됨). 모니터링 에이전트 = CONTINUE
executionPolicyFIRST_BOOT_ONLY(기본) / EVERY_BOOT마운트·RAID·MIG 등 재부팅 후 다시 필요한 것은 EVERY_BOOT(멱등하게 작성). scontrol reboot를 쓰려면 특히 중요
scriptSources3:// 또는 https://, 선택 s3VersionId·checksum(SHA-256)본운영은 버전 ID 또는 checksum 고정 — 스크립트 drift 방지
로그/var/log/amazon/pcs/lifecycle/actions/<stage>/<script-name>.log, 에이전트 executor.log. TERMINATE된 노드의 로그는 사라지므로 AWS 제공 configure-cloudwatch-logs.sh로 CloudWatch에 전송
함정 · lifecycle action 안에서 재부팅하면 안 됩니다

스크립트 안에서 reboot를 호출하면 PCS bootstrap 시퀀스가 깨집니다. 재부팅이 필요한 작업(MIG 모드 전환, 커널 파라미터)은 cloud-init boothook(early boot user data)으로 옮기십시오. 환경변수 PCS_IS_FIRST_BOOT(1/0)로 EVERY_BOOT 스크립트의 분기가 가능합니다.

3-3. 스크립트 목록 (S3 s3://${SCRIPT_BUCKET}/lifecycle/)

순서스크립트스테이지 · 정책내용
1mount-fsx.shnodeBootstrapped · EVERY_BOOT · TERMINATE/fsx Lustre(noatime,flock) + /home OpenZFS NFS 마운트, 실패 시 exit 1
2local-nvme.shnodeBootstrapped · EVERY_BOOT · TERMINATE8× NVMe RAID0 → /opt/dlami/nvme, Enroot 캐시 디렉터리
3node-hardening.shnodeBootstrapped · FIRST_BOOT_ONLY · CONTINUEneedrestart 가드(아래), Lustre 런타임 튜닝(3-4), 헬스체크 스크립트를 S3 → 로컬 /opt/ops/health로 복사
4install-enroot-pyxis.shnodeBootstrapped · FIRST_BOOT_ONLY · TERMINATEawslabs 제공. pre-bake AMI에서는 제외
5gpu-healthcheck-startup.shnodeBootstrapped · EVERY_BOOT · TERMINATEawslabs lightweight 스위트(nvidia-smi / DCGM L2 / EFA 열거 / 토폴로지). 실패 = 노드 교체(Step 8)
6dcgm-exporter.shnodeBootstrapped · FIRST_BOOT_ONLY · CONTINUEDCGM exporter(B300은 4.5.2 이상) + node exporter + EFA exporter
7configure-cloudwatch-logs.shnodeBootstrapped · FIRST_BOOT_ONLY · CONTINUEAWS 제공. lifecycle 로그·slurmd 로그를 CloudWatch Logs로
8warmup-srun.shnodeReady · EVERY_BOOT · CONTINUE알려진 이슈 회피용 버리는 srun(운영 주의사항)

인스턴스 프로파일에 해당 버킷 s3:GetObject(+ s3:GetObjectVersion)와 CloudWatch Logs 쓰기 권한을 부여합니다.

함정 · slurmd 자동 재시작으로 job이 전멸할 수 있습니다 (Ubuntu)

Ubuntu 계열에서 unattended security upgrade가 slurmd가 링크한 라이브러리(glibc 등)를 갱신하면, needrestartslurmd를 재시작하고 그 노드에서 실행 중인 모든 job이 죽습니다. 재현되는 현상이며, awslabs PCS 템플릿은 lifecycle action으로 드롭인을 심어 막습니다. 자체 구성에서도 반드시 포함하십시오.

# /etc/needrestart/conf.d/90-pcs-slurm.conf
$nrconf{override_rc}{qr(^slurmd)} = 0;

3-4. Lustre 런타임 튜닝 — 기본값은 대규모 GPU에 맞지 않습니다

# 데이터 경로 (OSC) — 체크포인트 쓰기 처리량
lctl set_param osc.*.max_rpcs_in_flight=32       # 기본 8
lctl set_param osc.*.max_dirty_mb=512            # 기본 32
lctl set_param osc.*.max_pages_per_rpc=1024      # 기본 256(1MB) → 4MB/RPC

# 메타데이터 경로 (MDC) — Python import, HF 캐시 walk에 결정적
lctl set_param mdc.*.max_rpcs_in_flight=64
lctl set_param mdc.*.max_mod_rpcs_in_flight=32

# read-ahead (llite)
lctl set_param llite.*.max_read_ahead_mb=1024
lctl set_param llite.*.statahead_max=512

디렉터리별 striping은 login 노드에서 1회만 실행합니다.

lfs setstripe -c -1 -S 4M /fsx/checkpoints   # 전 OST 분산 — 대용량 순차 쓰기
lfs setstripe -c -1 -S 4M /fsx/datasets      # 전 노드 순차 읽기
lfs setstripe -c 8  -S 4M /fsx/containers    # .sqsh 이미지 (5–30 GB)
lfs setstripe -c 1        /fsx/logs          # small append — 넓게 펴면 lock 오버헤드만 증가
STEP 4

클러스터 생성

D-3

클러스터는 컨트롤러입니다. 노드는 아직 없습니다. 여기서 정하는 것은 사이즈·버전·accounting·클러스터 레벨 Slurm 파라미터입니다.

aws pcs create-cluster --region ap-northeast-2 \
  --cluster-name llm-poc \
  --scheduler type=SLURM,version=26.05 \
  --size MEDIUM \
  --networking subnetIds=${CTRL_SUBNET_ID},securityGroupIds=${CLUSTER_SG_ID} \
  --slurm-configuration file://cluster-slurm.json

# 상태 확인 (CREATING → ACTIVE, 수 분~수십 분)
aws pcs get-cluster --region ap-northeast-2 --cluster-identifier llm-poc \
  --query 'cluster.[status,endpoints,slurmConfiguration.authKey]' --output json

cluster-slurm.json — 클러스터 레벨 Slurm 파라미터(허용 목록 항목만):

{
  "scaleDownIdleTimeInSeconds": 600,
  "accounting": { "mode": "STANDARD", "defaultPurgeTimeInDays": 180 },
  "slurmCustomSettings": [
    { "parameterName": "DefMemPerCPU",             "parameterValue": "40000" },
    { "parameterName": "PreemptType",              "parameterValue": "preempt/qos" },
    { "parameterName": "PreemptMode",              "parameterValue": "REQUEUE" },
    { "parameterName": "JobRequeue",               "parameterValue": "1" },
    { "parameterName": "PriorityWeightQOS",        "parameterValue": "10000" },
    { "parameterName": "PriorityWeightFairshare",  "parameterValue": "1000" },
    { "parameterName": "PriorityWeightAge",        "parameterValue": "100" },
    { "parameterName": "AccountingStorageEnforce", "parameterValue": "limits,qos" },
    { "parameterName": "AccountingStorageTRES",    "parameterValue": "cpu,mem,node,billing,gres/gpu" },
    { "parameterName": "HealthCheckProgram",       "parameterValue": "/opt/ops/health/periodic-check.sh" },
    { "parameterName": "HealthCheckInterval",      "parameterValue": "300" },
    { "parameterName": "HealthCheckNodeState",     "parameterValue": "IDLE" },
    { "parameterName": "Prolog",                   "parameterValue": "/opt/ops/health/prolog-gpu-quick.sh" },
    { "parameterName": "Epilog",                   "parameterValue": "/opt/ops/health/epilog-on-failure.sh" },
    { "parameterName": "PrologFlags",              "parameterValue": "Contain" }
  ]
}
설정왜 이 값인가
--size MEDIUM§4 #1. 변경 불가. login CNG까지 포함해 125노드 + 여유
version=26.05GPU affinity 자동 설정(25.11은 CNG마다 Sockets 수기), SPANK rolling 업그레이드 제약 해소. 2026-09-10 출시로 매우 최근이라 보수적으로는 25.11
accounting STANDARDQoS·GrpTRES 강제의 전제. DB는 AWS 관리(slurmdbd.conf 접근 불가). UpdateCluster로 사후 on/off 가능. 시간당 요금 있음(비용)
AccountingStorageEnforce=limits,qoslimitsassociations를 내포. 이 순간부터 sacctmgr에 없는 사용자는 제출 거부 — 켜기 전 사용자 목록 준비(Step 7)
DefMemPerCPU=4000096 CPU × 40 GB ≈ 3.84 TB. PCS 허용 목록에 DefMemPerGPU·DefCpuPerGPU는 없음 → 사용자가 --gres=gpu:8 --cpus-per-task=12로 명시
Prolog/Epilog/HealthCheckProgram반드시 로컬 디스크 경로. /fsx에 두면 Lustre 장애 = 전 노드 prolog 실패 = 전 노드 DRAIN(Step 8). 클러스터 레벨 설정이라 모든 CNG(login 포함)에 적용됨 → 스크립트는 GPU 없는 노드에서 exit 0으로 통과하게 작성
함정 · slurm.conf에 넣을 수 있는 것과 없는 것 PCS 차이

PCS는 허용 목록 방식입니다. 클러스터 레벨에 Prolog·Epilog·HealthCheck*·Preempt*·JobRequeue·RequeueExit·PriorityWeight*·AccountingStorageEnforce·SchedulerParameters·SlurmctldParameters·MinJobAge·DefMemPerCPU·MetricsType 등 약 50개가 열려 있습니다.

없는 것 중 이 설계에 영향이 큰 것: KillWait(SIGTERM→SIGKILL 간격, 기본 30초 고정), DefMemPerGPU/DefCpuPerGPU, PriorityType/SchedulerType(PCS가 관리), 파티션의 Hidden/AllowGroups. 체크포인트 여유는 GraceTime(큐 레벨) + 사용자 --signal로 만듭니다(Step 8). 목록은 PCS 문서 slurm-custom-settings-cluster/-queue/-cng에서 버전별로 확인하십시오.

참고 · 동시에 하나만 만들 수 있습니다

Creating 상태 클러스터는 리전당 1개(조정 불가)입니다. dev/prod 두 클러스터를 CI로 동시에 만들면 두 번째가 실패합니다. 직렬로 만드십시오.

STEP 5

Launch Template — 17-NIC, 이 문서에서 가장 중요한 블록

D-2

PCS 차이 ParallelCluster의 Efa: Enabled: true에 해당하는 스위치가 PCS에는 없습니다. EC2 launch template에 17개 네트워크 인터페이스를 직접 정의하고, CNG가 그 템플릿을 참조합니다. 가장 빠른 길은 awslabs add-cng-p6-b300.yaml(p6-b300 전용 CloudFormation)을 그대로 쓰거나 그 launch template 부분을 가져오는 것입니다.

5-1. 규칙 — P5/P6-B200 템플릿을 복사하면 실패합니다

 p5 / p6-b200p6-b300
card 0EFA (ENA+EFA)ENA 전용, InterfaceType 생략
EFA 카드card 1~N, DeviceIndex 1card 1–16, 전부 DeviceIndex 0
인터페이스 수32 등17

5-2. launch template 본문 (요지)

LaunchTemplateData:
  ImageId: ami-xxxxxxxx                    # Step 3에서 pin한 PCS-ready DLAMI / 커스텀 AMI
  IamInstanceProfile: { Arn: ${INSTANCE_PROFILE_ARN} }   # CNG의 --iam-instance-profile-arn과 동일
  BlockDeviceMappings:
    - DeviceName: /dev/sda1
      Ebs: { VolumeSize: 500, VolumeType: gp3, DeleteOnTermination: true }
  Placement:                                # ODCR/On-Demand 경로만. Capacity Block이면 이 블록 삭제
    GroupId: ${CLUSTER_PLACEMENT_GROUP_ID}
  CapacityReservationSpecification:        # ODCR
    CapacityReservationTarget: { CapacityReservationId: ${CR_ID} }
  # Capacity Block이면 위 블록 대신:
  #   InstanceMarketOptions: { MarketType: capacity-block }
  #   CapacityReservationSpecification: { CapacityReservationTarget: { CapacityReservationId: cr-xxxx } }
  MetadataOptions: { HttpTokens: required, HttpPutResponseHopLimit: 2 }
  NetworkInterfaces:
    # ---- card 0: ENA 전용 (InterfaceType 지정하지 않음). 일반 트래픽 SG는 여기에만 ----
    - DeviceIndex: 0
      NetworkCardIndex: 0
      SubnetId: ${GPU_SUBNET_ID}
      Groups: [${CLUSTER_SG_ID}, ${EFA_SG_ID}]
      DeleteOnTermination: true
    # ---- card 1~16: EFA. DeviceIndex는 전부 0 ----
    - DeviceIndex: 0
      NetworkCardIndex: 1
      InterfaceType: efa                   # efa-only 아님 (5-3)
      SubnetId: ${GPU_SUBNET_ID}
      Groups: [${EFA_SG_ID}]
      DeleteOnTermination: true
    # ... NetworkCardIndex 2 ~ 16 동일하게 반복 (총 16개) ...
  TagSpecifications:
    - ResourceType: instance
      Tags: [{ Key: Project, Value: llm-training }]

5-3. 반드시 지킬 것 5가지

  1. InterfaceType: efa(efa-only 아님). awslabs 주석: "PCS requires InterfaceType: efa (not efa-only) to propagate the interface configuration to the launch instances request correctly." → 노드당 17 IP(Step 1).
  2. SecurityGroupIds(템플릿 최상위)를 쓰지 마십시오. NetworkInterfaces를 정의한 템플릿에서는 각 인터페이스의 Groups로만 SG를 지정해야 합니다. 클러스터 SG는 primary에, EFA SG는 전부에.
  3. pcs-<id>-do-not-delete 이름의 PCS 관리형 템플릿을 CNG에 지정하지 마십시오.
  4. 템플릿 버전 추적. CNG를 version=1로 고정하면 이후 템플릿 수정이 노드에 반영되지 않습니다. CloudFormation이면 !GetAtt LaunchTemplate.LatestVersionNumber, CLI면 version='$Latest' 또는 수정 후 CNG 업데이트.
  5. user data는 비워두거나 최소로. 부트스트랩 로직은 lifecycle actions(Step 3)로. 단 MIG 모드 전환처럼 재부팅이 필요한 것만 cloud-init boothook으로 여기에.
함정 · Capacity Block에서는 placement group을 지정할 수 없습니다

EC2 문서 명시: "Capacity Blocks do not support placement groups." Capacity Block 경로의 launch template에는 Placement.GroupId를 넣지 마십시오(launch 실패). 근접 배치는 UltraCluster가 자체적으로 처리합니다. 반대로 ODCR/On-Demand 경로에서는 반드시 cluster placement group을 만들어 지정하십시오 — 안 넣으면 노드가 흩어져 EFA 지연이 늘어납니다.

팁 · 템플릿을 --dry-run으로 먼저 검증하십시오
aws ec2 run-instances --region ap-northeast-2 --dry-run \
  --launch-template LaunchTemplateId=${LT_ID},Version='$Latest' \
  --instance-type p6-b300.48xlarge --count 1
# "DryRunOperation" = 템플릿·서브넷·SG·용량 예약 참조가 문법적으로 유효. "InvalidParameter..."면 배선 오류
STEP 6

Compute Node Group 생성

D-2

6-1. Login CNG (PoC) — 본운영은 standalone 권장

aws pcs create-compute-node-group --region ap-northeast-2 \
  --cluster-identifier llm-poc \
  --compute-node-group-name login \
  --subnet-ids ${LOGIN_SUBNET_ID} \
  --custom-launch-template id=${LOGIN_LT_ID},version='$Latest' \
  --iam-instance-profile-arn ${INSTANCE_PROFILE_ARN} \
  --ami-id ${AMI_ID} \
  --scaling-configuration minInstanceCount=2,maxInstanceCount=2 \
  --instance-configs instanceType=m6i.4xlarge \
  --purchase-option ONDEMAND \
  --node-lifecycle-actions file://lifecycle-login.json
팁 · login 노드는 PCS 밖(standalone)으로 빼면 두 가지가 좋아집니다

① 노드 관리 요금(Standard $0.101/인스턴스·시간)이 빠집니다. ② 클러스터 "관리 인스턴스" 수에서 빠집니다. PCS 문서의 standalone login 노드 절차(working-with_login-nodes_standalone)와 aws-hpc-recipes/recipes/pcs/byo_login이 있고, 한 login 노드를 여러 클러스터에 연결하는 스크립트도 제공됩니다. PoC는 CNG로 빠르게, 본운영 전환 시 standalone으로 바꾸십시오.

6-2. GPU CNG — 전량 static

aws pcs create-compute-node-group --region ap-northeast-2 \
  --cluster-identifier llm-poc \
  --compute-node-group-name gpu-b300-a \
  --subnet-ids ${GPU_SUBNET_ID}                     # ⚠ 단일 서브넷 = 단일 AZ \
  --custom-launch-template id=${GPU_LT_ID},version='$Latest' \
  --iam-instance-profile-arn ${INSTANCE_PROFILE_ARN} \
  --ami-id ${AMI_ID} \
  --scaling-configuration minInstanceCount=2,maxInstanceCount=2   # static. 2 → 16 → 64 \
  --instance-configs instanceType=p6-b300.48xlarge \
  --purchase-option ONDEMAND                       # Capacity Block이면 CAPACITY_BLOCK \
  --slurm-configuration 'slurmCustomSettings=[{parameterName=Features,parameterValue=b300},{parameterName=Weight,parameterValue=10}]' \
  --node-lifecycle-actions file://lifecycle-gpu.json

lifecycle-gpu.json (Step 3의 스크립트 목록을 그대로 옮긴 것):

{ "stages": {
  "nodeBootstrapped": [
    { "name": "Mount FSx",            "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/mount-fsx.sh" },
      "arguments": ["${FSX_ID}", "${FSX_MOUNT_NAME}", "${OPENZFS_DNS}"], "executionPolicy": "EVERY_BOOT", "onError": "TERMINATE" },
    { "name": "Local NVMe",           "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/local-nvme.sh" },
      "executionPolicy": "EVERY_BOOT", "onError": "TERMINATE" },
    { "name": "Node hardening",       "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/node-hardening.sh" },
      "executionPolicy": "FIRST_BOOT_ONLY", "onError": "CONTINUE" },
    { "name": "Install Enroot Pyxis", "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/install-enroot-pyxis.sh" },
      "executionPolicy": "FIRST_BOOT_ONLY", "onError": "TERMINATE" },
    { "name": "GPU healthcheck startup", "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/gpu-healthcheck-startup.sh" },
      "executionPolicy": "EVERY_BOOT", "onError": "TERMINATE" },
    { "name": "DCGM exporter",        "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/dcgm-exporter.sh" },
      "executionPolicy": "FIRST_BOOT_ONLY", "onError": "CONTINUE" },
    { "name": "CloudWatch logs",      "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/configure-cloudwatch-logs.sh" },
      "arguments": ["/aws/pcs/llm-poc"], "executionPolicy": "FIRST_BOOT_ONLY", "onError": "CONTINUE" }
  ],
  "nodeReady": [
    { "name": "Warmup srun",          "scriptSource": { "scriptLocation": "s3://${SCRIPT_BUCKET}/lifecycle/warmup-srun.sh" },
      "executionPolicy": "EVERY_BOOT", "onError": "CONTINUE" }
  ]
} }
함정 · Slurm 25.11이면 Sockets를 넣지 않으면 노드가 INVALID_REG로 drain됩니다

25.11에서는 GPU autodetect(AutoDetect=nvml)가 CPU 토폴로지와 맞지 않으면 노드 등록이 실패합니다. CNG slurmCustomSettingsSockets(p6-b300의 NUMA 도메인 수 확인 필요)와 gresCustomSettings=[{AutoDetect=nvml}]를 넣어야 합니다. 26.05는 PCS가 NUMA 토폴로지와 GPU affinity(AutoDetect=full)를 기본 설정하므로 이 작업이 필요 없습니다. 이 문서가 26.05를 권장하는 실질적 이유입니다.

6-3. Capacity Block 경로 — CNG를 블록 수만큼

# 블록 1 (최대 64대) → CNG gpu-b300-a, LT-A(cr-aaaa 참조)
aws pcs create-compute-node-group ... --compute-node-group-name gpu-b300-a \
  --custom-launch-template id=${LT_A},version='$Latest' \
  --scaling-configuration minInstanceCount=64,maxInstanceCount=64 \
  --purchase-option CAPACITY_BLOCK ...

# 블록 2 (61대) → CNG gpu-b300-b, LT-B(cr-bbbb 참조)
aws pcs create-compute-node-group ... --compute-node-group-name gpu-b300-b \
  --custom-launch-template id=${LT_B},version='$Latest' \
  --scaling-configuration minInstanceCount=61,maxInstanceCount=61 \
  --purchase-option CAPACITY_BLOCK ...

# 두 CNG를 하나의 큐에 매핑 → 사용자에게는 단일 파티션 (Step 7)
  • 블록마다 launch template이 따로 필요합니다(CapacityReservationId가 다름).
  • 블록이 scheduled여도 CNG를 만들 수 있고, active가 되면 노드가 올라옵니다. 상태가 payment-failed 등이면 사용 불가.
  • min = max = 예약 수량으로 두십시오. 예약 창 전체가 과금되므로 0에서 올리면 유휴 시간이 낭비입니다.
  • 만료 후 복구 = 새 블록을 참조하는 새 LT 버전으로 CNG 업데이트(운영 주의사항).
참고 · 확인 명령
aws pcs list-compute-node-groups --region ap-northeast-2 --cluster-identifier llm-poc
aws pcs get-compute-node-group --region ap-northeast-2 --cluster-identifier llm-poc \
  --compute-node-group-identifier gpu-b300-a --query 'computeNodeGroup.[status,errorInfo]'
# 노드가 안 올라오면: EC2 콘솔 인스턴스 상태 → /var/log/amazon/pcs/ (SSM) → CloudWatch Logs(lifecycle)
STEP 7

큐 · QoS · 멀티팀 운영

D-1 ~ D+1
함정 · 순서를 어기면 slurmctld가 죽습니다 (25.11+)

큐의 AllowQos/DenyQos/QOS에 쓰는 QoS는 accounting DB에 먼저 존재해야 합니다. 없으면 Slurm 25.11부터 slurmctld가 fatal error로 종료됩니다(릴리스 노트 명시). 반드시 이 순서로 진행하십시오.

① accounting 활성(Step 4) → ② login 노드에서 sacctmgr로 계정·사용자·QoS 생성 → ③ 큐에 AllowQos 설정 → ④ 검증. ②를 건너뛰고 AccountingStorageEnforce가 적용되면 등록되지 않은 사용자는 어떤 큐에도 제출할 수 없습니다. "갑자기 제출이 안 된다"는 신고의 첫 확인 항목: sacctmgr show assoc user=<name>.

7-1. 두 계층의 역할 — 큐만으로는 독점을 막을 수 없습니다

 Queue (Slurm partition)Slurm Accounting (QoS)
제어 단위사용자 / 계정 / QoS
가능한 제약MaxTime, PriorityTier, PreemptMode, GraceTime, AllowQos/AllowAccounts사용자별 동시 실행/제출 수, 팀별 GPU·CPU·메모리 총량(GrpTRES)
강제력Slurm 기본 동작AccountingStorageEnforce로 강제
역할자원 배분의 실제로 강제하고 기록하는 시스템

7-2. 계정과 QoS 생성 (login 노드에서)

# 팀 계정
sacctmgr --immediate add account team-a Description="Team A"
sacctmgr --immediate add account team-b Description="Team B"

# 사용자 등록 (OS 계정이 먼저 존재해야 함 — SSSD/LDAP 또는 로컬)
sacctmgr --immediate add user name=alice account=team-a

# QoS 생성 및 제한 (여러 개는 name=a,b,c 형식)
sacctmgr --immediate add qos name=debug_qos,normal_qos,large_qos,exclusive_qos
sacctmgr -i modify qos debug_qos     set MaxJobsPerUser=1 MaxSubmitJobsPerUser=2 Priority=1000  MaxWall=00:30:00
sacctmgr -i modify qos normal_qos    set MaxJobsPerUser=4 MaxSubmitJobsPerUser=8 Priority=2000
sacctmgr -i modify qos large_qos     set Priority=3000
sacctmgr -i modify qos exclusive_qos set MaxJobsPerUser=1 MaxSubmitJobsPerUser=2 Priority=10000 PreemptMode=REQUEUE Preempt=normal_qos,large_qos

# ★ 팀별 GPU 총량 상한 — 독점 방지의 핵심
sacctmgr -i modify account team-a set GrpTRES=gres/gpu=64
sacctmgr -i modify account team-b set GrpTRES=gres/gpu=64

# 계정별 사용 가능 QoS와 기본값 (모든 팀 계정에 반복)
sacctmgr -i modify account team-a set qos+=debug_qos,normal_qos,large_qos DefaultQOS=normal_qos
sacctmgr -i modify account team-b set qos+=debug_qos,normal_qos,large_qos DefaultQOS=normal_qos
# exclusive_qos는 필요한 팀에만 별도 부여

sacctmgr show assoc format=account,user,qos,defaultqos,grptres

7-3. 큐 생성 — CNG 매핑 + 파티션 레벨 설정

# 두 CNG(gpu-b300-a, gpu-b300-b)를 하나의 파티션으로. Capacity Block 2블록이면 이렇게
for q in debug normal large exclusive; do :; done   # 아래 4개를 각각 생성

aws pcs create-queue --region ap-northeast-2 --cluster-identifier llm-poc \
  --queue-name normal \
  --compute-node-group-configurations computeNodeGroupId=${CNG_A_ID} computeNodeGroupId=${CNG_B_ID} \
  --slurm-configuration 'slurmCustomSettings=[
      {parameterName=AllowQos,     parameterValue=normal_qos},
      {parameterName=MaxTime,      parameterValue=1-00:00:00},
      {parameterName=PriorityTier, parameterValue=2},
      {parameterName=GraceTime,    parameterValue=600},
      {parameterName=Default,      parameterValue=YES}]'

aws pcs create-queue ... --queue-name debug     --compute-node-group-configurations computeNodeGroupId=${CNG_A_ID} \
  --slurm-configuration 'slurmCustomSettings=[{parameterName=AllowQos,parameterValue=debug_qos},{parameterName=MaxTime,parameterValue=00:30:00},{parameterName=PriorityTier,parameterValue=1},{parameterName=GraceTime,parameterValue=120}]'
aws pcs create-queue ... --queue-name large     --compute-node-group-configurations computeNodeGroupId=${CNG_A_ID} computeNodeGroupId=${CNG_B_ID} \
  --slurm-configuration 'slurmCustomSettings=[{parameterName=AllowQos,parameterValue=large_qos},{parameterName=MaxTime,parameterValue=7-00:00:00},{parameterName=PriorityTier,parameterValue=3},{parameterName=GraceTime,parameterValue=600}]'
aws pcs create-queue ... --queue-name exclusive --compute-node-group-configurations computeNodeGroupId=${CNG_A_ID} computeNodeGroupId=${CNG_B_ID} \
  --slurm-configuration 'slurmCustomSettings=[{parameterName=AllowQos,parameterValue=exclusive_qos},{parameterName=MaxTime,parameterValue=14-00:00:00},{parameterName=PriorityTier,parameterValue=10},{parameterName=PreemptMode,parameterValue=REQUEUE}]'
설정
PreemptType=preempt/qos(클러스터) + QoS Preempt=PCS 큐 레벨 허용 목록에 Priority(partition_prio용)가 없고 PriorityTier만 있으므로, 선점은 QoS 기반으로 설계합니다. exclusive_qosnormal/large를 REQUEUE로 선점
GraceTime=600선점당한 job에 SIGTERM 후 600초 유예. KillWait는 PCS에서 바꿀 수 없으므로(기본 30초) 시간 초과 시의 여유는 사용자 --signal로만 확보(Step 8)
Default=YESPCS 차이 PCS가 임의로 만드는 파티션이 없으므로 기본 파티션도 직접 지정. 하나만 YES
큐 4개 + CNG 2~4개기본 한도 큐 10 / CNG 10. 팀별로 큐를 더 쪼갤 계획이면 Step 0에서 한도 증설
팁 · 실제로 막는 것은 이 두 줄입니다

GrpTRES=gres/gpu=N + AccountingStorageEnforce=limits. 이 조합이 있어야 팀이 배정량을 넘겨 제출할 때 실제로 거부됩니다. 둘 중 하나만 있으면 제한이 기록만 되고 강제되지 않습니다.

함정 · 리포팅 명령 2가지 주의
  • sacct --state=...-E now를 명시하지 않으면 조용히 빈 결과를 반환합니다.
  • sreport는 시간별 롤업을 따라가므로 최신 데이터가 반영되지 않습니다. 실시간 조회는 sacct를 쓰십시오.
STEP 8

GPU 헬스체크와 복원력 — PCS에는 관리형 auto-healing이 없습니다

D+1
현실 인식 · 이 단계는 선택이 아닙니다 PCS 차이

PCS 문서 전체에서 HyperPod의 deep health check·자동 노드 교체에 해당하는 관리형 기능을 찾을 수 없습니다. "PCS도 auto-healing이 됩니다"라고 전제하면 안 됩니다. 정확한 표현은 "PCS는 auto-healing을 제공하지 않고, Slurm 훅과 lifecycle action으로 직접 구현하는 구조"입니다. 재료는 전부 열려 있고(HealthCheckProgram·Prolog·Epilog·lifecycle TERMINATE·scontrol reboot·JobRequeue), awslabs가 DCGM 기반 스위트를 제공합니다. 1,000 GPU가 며칠씩 도는 환경에서 누가 이것을 만들고 운영할지를 착수 전에 정하십시오.

8-1. 5단계 배치

시점어디에무엇을실패 시
① 노드 기동 lifecycle nodeBootstrapped
onError: TERMINATE
awslabs lightweight 스위트: nvidia-smi(Xid/ECC/retired page) · DCGM level-2 · EFA 열거(16) · 토폴로지. 수 분 노드가 큐에 들어오지 않고 종료·교체 — PCS가 TERMINATE된 노드를 교체합니다(문서 명시). 로그는 CloudWatch로 미리 전송
② job 시작 전 Slurm prolog (클러스터 레벨) nvidia-smi + 커널로그 Xid 스캔 + GPU 8개 + EFA 16 + CUDA_VISIBLE_DEVICES 비어있지 않은지. 수 초 이내 노드 DRAIN + job requeue(prolog 실패 시 Slurm 기본 동작)
③ job 종료 후 Slurm epilog job이 실패했을 때만 DCGM level-2 → 다음 job 전에 열화 GPU 격리 노드 DRAIN(Reason 기록)
④ 주기적 HealthCheckProgram 5분, IDLE 온도·ECC·NVLink 상태. 가볍게 노드 DRAIN
⑤ 정비 창 maintenance reservation 안에서 sbatch awslabs intensive(DCGM level-4 EUD·pulse power, NCCL, EFA loopback). 노드당 45분–2.25시간 REBOOT / ISOLATE 판정에 따라 처리

8-2. DRAIN된 노드는 어떻게 되돌리나 — 두 경로

헬스체크가 결함 확정 → scontrol update NodeName=<n> State=DRAIN Reason="..." │ ├─ (a) 소프트 결함 (Xid 일회성, ECC 정정 가능, 프로세스 잔존) │ → scontrol reboot ASAP nextstate=RESUME <n> ← 인스턴스 교체 없이 재부팅 (EVERY_BOOT 스크립트 재실행) │ └─ (b) 하드 결함 (fallen off bus, DBE, EFA 디바이스 소실) → aws ec2 terminate-instances <instance-id> ← static CNG는 PCS가 수량을 유지하므로 새 인스턴스가 올라옴 → 새 노드가 ① lightweight 스위트를 통과해야 큐에 들어옴 │ ▼ job은 JobRequeue=1 + --requeue로 재큐 → 체크포인트에서 재개 (학습 스크립트 책임)
함정 · DRAIN만으로는 아무것도 교체되지 않습니다 PCS 차이

ParallelCluster의 clustermgtd는 DRAIN된 static 노드를 자동 교체하지만, PCS에 그런 동작이 있다는 문서 근거는 없습니다 확인 필요. DRAIN된 노드는 과금되면서 유휴 상태로 남습니다. 따라서 (a) scontrol reboot 또는 (b) 인스턴스 종료를 스크립트나 운영자가 명시적으로 수행해야 합니다. (b)의 근거는 "static CNG는 min 수량을 유지한다"와 "lifecycle TERMINATE 노드는 교체된다"는 두 문서 사실이며, 수동 종료 시에도 동일하게 교체되는지는 PoC 장애 리허설(Step 9)에서 실측하십시오.

자동화한다면 epilog/HealthCheckProgram에서 하드 결함 판정 시 aws ec2 terminate-instances $(ec2-metadata -i)를 호출하는 방식이 가장 단순합니다. 인스턴스 프로파일에 자기 자신만 종료할 수 있는 조건(ec2:ResourceTag/aws:pcs:compute-node-group-id 등)을 거십시오.

함정 · 오탐 하나가 노드 하나입니다 — prolog는 로컬 디스크에서

prolog가 실패하면 Slurm이 그 노드를 DRAIN합니다. prolog/epilog/HealthCheckProgram을 /fsx에 두면 Lustre가 잠깐 마운트를 잃는 순간 모든 노드의 prolog가 동시에 실패 → 전 노드 DRAIN입니다. 스크립트는 lifecycle action(node-hardening.sh)이 S3에서 로컬 /opt/ops/health로 복사해 두고, prolog는 GPU·EFA 결함 이외의 실패(스토리지·네트워크)에는 exit 0으로 끝내고 로그만 남기십시오. 클러스터 레벨 prolog는 login CNG에도 적용되므로 GPU가 없는 노드에서는 즉시 exit 0 해야 합니다.

8-3. job 내결함성 — KillWait를 바꿀 수 없다는 전제에서

  • JobRequeue=1(클러스터) + sbatch --requeue로 제출. 프레임워크 특정 종료코드는 RequeueExit에 등록
  • 선점 시 유예 = 큐 GraceTime=600(SIGTERM → SIGKILL 600초)
  • 시간 초과·scancel 시 유예 = PCS 차이 KillWait(기본 30초)가 허용 목록에 없어 늘릴 수 없습니다. → 사용자 측 sbatch --signal=B:USR1@600으로 종료 600초 전 시그널을 받아 체크포인트를 저장하고 스스로 종료하는 것이 유일한 수단입니다. 학습 스크립트 템플릿에 기본 포함시키십시오
  • UnkillableStepTimeout은 허용 목록에 있지만 체크포인트와 무관합니다(SIGKILL 후에도 죽지 않는 프로세스를 이상 판정하기까지의 대기시간). 공개 예제의 "체크포인트 시간 확보" 설명은 오해입니다

8-4. 스크립트 골격

동작 원리를 보이기 위한 최소 골격입니다. 판정 기준(Xid 목록, 온도, 재시도)은 2노드 검증에서 실측 후 확정하십시오. 네 파일 모두 node-hardening.sh가 S3에서 /opt/ops/health/로 복사합니다.

# node-hardening.sh (nodeBootstrapped, FIRST_BOOT_ONLY)
#!/bin/bash
set -euo pipefail
mkdir -p /opt/ops/health
aws s3 sync s3://${SCRIPT_BUCKET}/health/ /opt/ops/health/ --region ap-northeast-2
chmod 755 /opt/ops/health/*.sh
if [ -d /etc/needrestart ]; then
  mkdir -p /etc/needrestart/conf.d
  echo '$nrconf{override_rc}{qr(^slurmd)} = 0;' > /etc/needrestart/conf.d/90-pcs-slurm.conf
fi
if mountpoint -q /fsx; then bash /opt/ops/health/lustre-tuning.sh || true; fi
# prolog-gpu-quick.sh — 수 초 이내. GPU/EFA 결함만 실패(exit 1). GPU 없는 노드(login)는 즉시 통과
#!/bin/bash
command -v nvidia-smi >/dev/null 2>&1 || exit 0
LOG=/var/log/ops-prolog.log
fail(){ echo "$(date -Is) job=$SLURM_JOB_ID FAIL: $*" >> $LOG
        scontrol update NodeName=$(hostname -s) State=DRAIN Reason="prolog: $*"; exit 1; }
timeout 10 nvidia-smi --query-gpu=count --format=csv,noheader >/dev/null 2>&1 || fail "nvidia-smi"
n=$(nvidia-smi -L 2>/dev/null | wc -l); [ "$n" -eq 8 ] || fail "gpu count $n"
# 부팅 이후 치명 Xid (79 fallen off bus, 74 NVLink, 48 DBE, 63/64 row remap)
if journalctl -k -b --no-pager | grep -E 'NVRM: Xid .*: (79|74|48|63|64),' -q; then fail "xid"; fi
efa=$(ls /sys/class/infiniband/ 2>/dev/null | wc -l); [ "$efa" -eq 16 ] || fail "efa devices $efa"
exit 0    # 스토리지·네트워크 등 그 외 문제는 로그만 남기고 통과
# epilog-on-failure.sh — job 실패 시에만 level-2 진단 (노드에 다른 job이 없을 때만)
#!/bin/bash
command -v dcgmi >/dev/null 2>&1 || exit 0
[ "${SLURM_JOB_EXIT_CODE:-0}" = "0" ] && exit 0
running=$(squeue -h -w $(hostname -s) -t RUNNING | wc -l)
[ "$running" -gt 0 ] && exit 0            # 공유 노드에서 dcgmi diag 금지
if ! timeout 900 dcgmi diag -r 2 > /var/log/ops-epilog-diag.$SLURM_JOB_ID.log 2>&1; then
  scontrol update NodeName=$(hostname -s) State=DRAIN Reason="epilog: dcgmi diag -r 2 failed job=$SLURM_JOB_ID"
  # 하드 결함 자동 교체를 택했다면: aws ec2 terminate-instances --instance-ids $(ec2-metadata -i | cut -d' ' -f2)
fi
exit 0
# periodic-check.sh (HealthCheckProgram, IDLE 노드에서 5분마다) — 가볍게
#!/bin/bash
command -v nvidia-smi >/dev/null 2>&1 || exit 0
nvidia-smi --query-gpu=temperature.gpu,ecc.errors.uncorrected.volatile.total --format=csv,noheader 2>/dev/null \
 | awk -F, '{ if ($1+0 > 90 || $2+0 > 0) bad=1 } END { exit bad }' \
 || scontrol update NodeName=$(hostname -s) State=DRAIN Reason="periodic: temp/ecc"
exit 0
# gpu-healthcheck-startup.sh (nodeBootstrapped, EVERY_BOOT, TERMINATE) — job 없음, 수 분 허용. exit≠0 → 노드 종료·교체
#!/bin/bash
set -e
nvidia-smi topo -m > /var/log/ops-topo.log
[ $(ls /sys/class/infiniband/ | wc -l) -eq 16 ]
timeout 1800 dcgmi diag -r 2
# 또는 awslabs 스위트: /opt/ops/health/gpu-cluster-healthcheck/run-lightweight.sh
STEP 9

검증 — 2노드에서 전부 통과시킬 항목

D+2 ~ D+5

9-1. 인프라 기본 (login 노드에서)

sinfo -N -l
scontrol show nodes | grep -E 'NodeName|State|CfgTRES|Gres|Sockets|CPUTot'
# 기대: CPUTot=96 (SMT 비활성), Gres=gpu:8, State=idle

scontrol show partition | grep -E 'PartitionName|Default|AllowQos|GraceTime|MaxTime'
# 기대: normal이 Default=YES, 각 큐에 AllowQos

# GPU 인식 — 8개
srun -p normal -N1 --gres=gpu:8 --cpus-per-task=12 nvidia-smi

# EFA 열거 — 16 디바이스
srun -p normal -N1 fi_info -p efa | grep -c domain
srun -p normal -N1 ls /sys/class/infiniband/

# 스토리지 · lifecycle 로그
df -h /fsx /home /opt/dlami/nvme
lfs df -h /fsx
srun -p normal -N1 ls /var/log/amazon/pcs/lifecycle/actions/nodeBootstrapped/

9-2. NCCL 다중 노드 — 가장 중요한 성능 검증

# 컨테이너 이미지 (태그 고정). enroot는 '#' 레지스트리 구분자를 씁니다
enroot import -o /fsx/containers/nccl-tests.sqsh \
  "docker://public.ecr.aws#hpc-cloud/nccl-tests:<tag>"

# 2노드 / 16 GPU all_reduce (awslabs nccl-tests-container.sbatch 참고)
srun -p normal -N2 --ntasks-per-node=8 --gres=gpu:8 --cpus-per-task=12 \
  --container-image=/fsx/containers/nccl-tests.sqsh \
  all_reduce_perf -b 8 -e 16G -f 2 -g 1

합격 기준

  • 로그에 NET/OFI Selected provider is efafound 16 nics
  • # Out of bounds values : 0 / OK
  • busbw가 메시지 크기에 따라 증가하고 대형 메시지에서 포화. Grafana EFA 대시보드에서 16 디바이스 모두 트래픽
함정 · nics 개수가 16이 아니면 launch template 배선 오류입니다

found 16 nics가 아니라 1이나 8로 나오면 17-NIC 정의가 잘못 붙은 것입니다(P5용 DeviceIndex 1을 복사한 경우가 대표적). 이 상태로도 job은 돌지만 대역폭이 몇 분의 일로 떨어집니다.

주의 · p6-b300의 공개 기준 대역폭 수치가 없습니다

awslabs 공개치는 p6-b200 4노드 all_reduce 377 GB/s뿐입니다. p6-b300 기준치는 확인하지 못했습니다 확인 필요. 자사 실측값을 기준선으로 기록하고 노드 교체·확장 시 회귀 판정에 쓰십시오.

9-3. 스토리지 성능

  • Lustre 튜닝 적용 전/후 순차 쓰기 처리량 비교
  • 실제 체크포인트 저장 1회 소요시간 측정 → Capacity Block 종료 시각(KST 20:00) 계획에 사용
  • 로컬 NVMe 캐시 적용 시 데이터 로딩 시간 변화
  • 노드 부팅 → idle까지 소요시간 (DLAMI+lifecycle vs pre-bake) → 125노드 기동 시간 외삽

9-4. 장애 리허설 필수 — MTTR을 숫자로

시나리오확인할 것
lifecycle gpu-healthcheck-startup.sh에 결함 주입(exit 1)노드가 TERMINATE되고 PCS가 새 인스턴스를 올리는지, CloudWatch에 로그가 남았는지
prolog 빠른 체크job 시작 지연이 수 초 이내; 결함 주입 시 DRAIN + requeue
인스턴스 강제 종료(콘솔)static CNG가 수량을 회복하는지, job requeue → 체크포인트 재개까지 MTTR 측정
scontrol update State=DRAIN 후 방치아무 일도 일어나지 않는지(PCS 자동 교체 없음 확인) → 운영 절차에 반영 확인 필요
scontrol reboot ASAP nextstate=RESUME재부팅 후 EVERY_BOOT 스크립트(마운트·NVMe·GPU 체크)가 다시 돌고 노드가 복귀하는지
epilogjob 실패 시 level-2 진단이 돌고 결함 노드가 격리되는지
--signal=B:USR1@600 job을 scancel실제로 체크포인트가 남는지
needrestart 가드needrestart -b 출력에 slurmd가 재시작 대상에서 빠졌는지
팁 · 이 리허설의 목적은 MTTR 수치입니다

PCS는 auto-healing이 없으므로 "노드 하나가 죽었을 때 몇 분 만에 복구되는가"가 HyperPod과의 비교에서 결정적 데이터입니다. 감으로 넘기지 말고 측정해서 기록하십시오.

9-5. 멀티유저

  • GrpTRES=gres/gpu=N 초과 제출이 실제로 거부되는지
  • MaxJobsPerUser 초과 제출이 막히는지
  • exclusive_qos job이 normal job을 실제로 preempt(requeue)하고 GraceTime이 지켜지는지
  • 미등록 사용자의 제출이 거부되는지 (그리고 에러 메시지가 무엇인지 — 운영 FAQ에 기록)
  • sacct -a -S <date> -E now 로 사용자별 집계가 나오는지
STEP 10

확장과 버전 업그레이드 — 2 → 16 → 125노드

D+7 이후

10-1. 노드 수 확장

# ODCR/On-Demand 경로: min/max만 올림 (fleet 정지 불필요)
aws pcs update-compute-node-group --region ap-northeast-2 --cluster-identifier llm-poc \
  --compute-node-group-identifier gpu-b300-a \
  --scaling-configuration minInstanceCount=16,maxInstanceCount=16

# Capacity Block 경로: 블록마다 CNG 추가 → 기존 큐에 매핑 추가
aws pcs create-compute-node-group ... --compute-node-group-name gpu-b300-b --purchase-option CAPACITY_BLOCK ...
aws pcs update-queue --region ap-northeast-2 --cluster-identifier llm-poc --queue-name normal \
  --compute-node-group-configurations computeNodeGroupId=${CNG_A_ID} computeNodeGroupId=${CNG_B_ID}
변경 내용영향
min/max 증가실행 중 job 영향 없음. 새 노드는 lifecycle 스위트 통과 후 합류
CNG 추가 + 큐 매핑영향 없음. CNG 10개 한도 확인
min/max 감소PCS가 유휴 노드부터 종료. 제거 대상 노드에 먼저 maintenance reservation을 걸어 새 job 유입을 막으십시오
launch template 수정 (AMI·CR ID 교체)새 버전 발행 → CNG가 $Latest이후 launch되는 노드부터 반영. 기존 노드는 교체(재launch)되어야 새 템플릿 적용
클러스터 사이즈변경 불가 → 재생성. Medium으로 시작했으면 512까지 필요 없음
확장 시 함께 갱신할 항목
  • 팀별 GrpTRES 쿼터 — 총량이 늘었으니 재배분
  • FSx 용량·처리량 — 노드 수에 비례해 스토리지 부하 증가
  • 서브넷 IP 여유 — 17 IP/노드. 125노드 직전 /20에 4,091 중 2,125 소비
  • Capacity Block 경로면 CNG·LT 추가 — 블록당 64대 한도
  • 정비 창 계획 — 125노드 intensive 헬스체크는 노드당 최대 2.25시간. 병렬로 돌리되 학습 일정과 조율
  • MinJobAge / 클러스터 job 추적 수 — 하이퍼파라미터 스윕처럼 짧은 job을 대량 던지면 Medium의 8,192 한도가 먼저 찹니다

10-2. Slurm 버전 업그레이드 — 클러스터 재생성 없음 PCS 차이

aws pcs update-cluster --region ap-northeast-2 --cluster-identifier llm-poc \
  --scheduler type=SLURM,version=26.05 \
  --client-token $(uuidgen)
 Option 1 · RollingOption 2 · Full-fleet 정지
실행 중 job종료 불필요(노드는 컨트롤러 없이도 job을 계속 실행)전부 종료
컴퓨트 fleet일시적 버전 혼재 → 노드를 순차 drain·재부팅전량 새 버전으로 재시작
제약SPANK 플러그인 사용 시 25.11 이하에서 불가(26.05부터 해소). CLI Filter 사용 시 25.05 이하 불가없음
  • 업그레이드 중 컨트롤러 일시 중단: 신규 제출·스케줄러 명령·자동 스케일링 정지, 실행 중 job은 계속(Option 1). accounting 데이터 보존
  • 한 번에 3개 메이저 버전 이상 건너뛰기 불가. 패치 버전은 PCS가 자동 적용
  • EOL 후에는 신규 클러스터 생성 불가, 기존 클러스터는 최대 12개월 무보증 운영. 25.05는 2026-11-30 EOL
  • DLAMI는 지원 Slurm 버전을 다중 내장하므로 컨트롤러 업그레이드 시 노드 AMI 교체가 필수는 아니지만, EOL 근접 AMI는 pin을 갱신하십시오

운영 상시 주의사항

1. Capacity Block 만료 관리

KST 20:00부터 노드가 종료되기 시작해 20:30에 끝납니다. 만료 시 CNG·큐 연결은 유지되지만 모든 인스턴스 종료, 실행 중 job 실패, 대기 job pending 정지. 만료 전 새 블록을 예약하고 launch template 새 버전에 CapacityReservationId를 바꾼 뒤 CNG를 업데이트하십시오. 블록은 취소·이동·분할 불가, 연장(extend)만 됩니다. 캘린더에 "KST 19:30 최종 체크포인트"를 반복 일정으로 넣으십시오.

2. 알려진 이슈 · 신규 노드의 첫 srun이 노드를 drain시킬 수 있음

awslabs 운영 가이드 기록: PCS-ready DLAMI(Ubuntu 24.04)에서 slurmd가 기동 직후 systemd에 의해 재시작되면서 prolog 핸드셰이크가 420초 타임아웃 → job 취소 + 노드 drained (Reason=Prolog error). systemd/cgroup-v2 레이스로 템플릿 레벨에서 해결 불가.

nodeReady 스테이지의 warmup-srun.sh(버려도 되는 srun -N1 -w $(hostname -s) true)로 prolog 경로를 미리 통과시키고, 그래도 발생하면 scontrol update nodename=<n> state=resume reason=cleared. 125노드 static 기동 시 노드마다 한 번씩 겪을 수 있습니다.

3. B300은 DCGM 4.5.2 이상

awslabs 기록: 기본 모니터링 스택의 DCGM 4.2.0은 B200까지만 커버합니다. dcgm-exporter4.5.2 이상(digest pin)을 쓰십시오. "GPU 메트릭이 안 나온다"의 가장 흔한 원인입니다. DLAMI 내장 DCGM 버전도 AMI Description에서 확인.

4. Slurm 메트릭 포트 6817은 인증 없는 HTTP

MetricsType=metrics/openmetrics + CommunicationParameters=enable_http를 켜면 컨트롤러 6817에서 job/node/partition 메트릭이 나옵니다. PCS 문서 명시: "Anyone with network access to port 6817 can read cluster, job, and node metrics." 클러스터 SG를 신뢰 CIDR로 제한하고 PrivateData는 설정하지 마십시오. awslabs login 노드의 nginx도 /prometheus/·/slurmexporter/를 인증 없이 프록시합니다(/grafana/만 보호). 본운영은 AMP/AMG + SSM 포트포워딩.

5. 그 밖에 기억할 것
  • AMI drift — SSM /latest/ 참조 금지. AMI ID pin + SNS 알림 구독으로 의도적 갱신
  • SMT 비활성은 되돌릴 수 없습니다 — 96 CPU 기준으로 문서·템플릿·컨테이너 CPU limit을 통일. SMT가 필수인 워크로드가 있으면 PCS 밖에서
  • MIG를 쓴다면(Slurm 25.11+) — MIG 모드는 GPU 상태라 AMI에 구울 수 없고 매 부팅 boothook에서 켜야 합니다. powered-down 노드에 설정 변경이 전달되지 않고, CUDA_VISIBLE_DEVICES가 빈 채로 job이 시작될 수 있어 MIG CNG는 static이 문서 권고입니다. B300을 쪼개는 것은 학습용으로 비경제적 → 추론·개발용 별도 CNG로 한정
  • 컨트롤러 요금은 노드 0대에도 발생(Medium $4.13/h ≈ $3,000/월). 장기 유휴면 클러스터 삭제를 검토(재생성 시 사이즈 재선택·동시 생성 1개 제약)
  • Slurm 버전 EOL — 25.05는 2026-11-30. 클러스터·AMI 양쪽 버전을 분기별로 점검
  • lifecycle 스크립트 drift — S3 s3VersionId 또는 checksum으로 고정. "노드마다 동작이 다르다"의 원인

비용 라인아이템

PCS는 서비스 요금이 있는 관리형입니다. 서울 리전 실가격(AWS 가격 API, 2026-09-14)으로 계산하면 이 규모에서 GPU EC2 비용의 약 0.4%입니다.

PCS 항목 (서울, USD)단가비고
컨트롤러 Medium$4.1283 / 시간Small $0.7381 · Large $8.2906. 노드 0대에도 발생
노드 관리 Advanced (P·TRN 계열)$0.810992 / 인스턴스·시간GPU 노드 125대 = $101.37/h
노드 관리 Standard (그 외)$0.101374 / 인스턴스·시간login CNG. standalone으로 빼면 0
Accounting 사용료 (Medium)$1.2418 / 시간Small $0.2154 · Large $2.4837
Accounting 스토리지$1.0264 / GB·월100 GB ≈ $103/월
시나리오GPU 노드loginPCS 요금/시간PCS 요금/월 (730h)GPU EC2 On-Demand/시간PCS 비중
Phase 0 검증21$7.09$5,178$4181.70%
Phase 1 파일럿162$18.55$13,541$3,3470.55%
본운영 전량1254$107.15$78,219$26,1470.41%

125노드 계산: Advanced 125 × $0.810992 + Standard 4 × $0.101374 + 컨트롤러 Medium $4.1283 + accounting $1.2418 = $107.15/h. accounting 스토리지 제외. 분 단위 올림 과금.

그 외 라인아이템비고
GPU 컴퓨트 (p6-b300.48xlarge)서울 On-Demand $209.1735/시간. 비용의 대부분. Capacity Block / Savings Plans로 단가 인하
FSx for Lustre / OpenZFS용량 × 처리량 등급. 무시할 수 없는 금액
NAT Gateway데이터 처리량 과금 — S3/ECR VPC 엔드포인트로 절감. 17 NIC이라도 NAT 트래픽은 primary만
CloudWatch Logs / AMP / AMG관측. lifecycle·slurmd 로그 전송량 확인
S3데이터셋 · 체크포인트 아카이브 · 스크립트
팁 · 최대 낭비 요인은 유휴 GPU, 그 다음이 DRAIN된 채 방치된 노드입니다

시간당 $209짜리 노드가 유휴로 도는 것이 가장 큰 낭비이고, PCS에서는 DRAIN된 노드가 자동으로 교체되지 않아 과금되며 남는 것이 두 번째입니다. "DRAIN 상태 N분 초과" CloudWatch 알람을 걸고, AWS Budgets와 Step 7의 팀별 GrpTRES 쿼터로 활용률을 관리하십시오.

참고자료

시작하는 데 가장 빠른 경로 — 이것부터

순위자료무엇인가
1awslabs/awsome-distributed-aiarchitectures/aws-pcs/
github.com/awslabs/awsome-distributed-ai/tree/main/architectures/aws-pcs
p6-b300 전용 17-NIC CloudFormation(add-cng-p6-b300.yaml)을 포함한 ML 학습용 PCS 레퍼런스. VPC + FSx + 클러스터 + login/CPU/GPU CNG + Enroot/Pyxis + 모니터링을 deploy-all.yaml 하나로. PoC 첫 배포는 이것으로, 본운영은 FSx를 분리(§4 #6)
2AI/ML for AWS PCS 워크숍
catalog.workshops.aws/ml-on-pcs/
단계별 핸즈온
3PCS User Guide
docs.aws.amazon.com/pcs/latest/userguide/
1차 근거. 아래 딥링크
4aws-samples/aws-hpc-recipesrecipes/pcs/공식 레시피: getting_started, enable_efa, observability_for_pcs, dlami_for_pcs_imagebuilder, byo_login, multiuser_demo, terraform_awscc
5recipes/net/hpc_large_scalePCS 요구사항을 만족하는 VPC/서브넷. GPU 서브넷 CIDR만 /20으로 조정

PCS User Guide 딥링크 (docs.aws.amazon.com/pcs/latest/userguide/)

주제경로
클러스터 사이즈working-with_clusters_size.html
VPC·서브넷 요구사항working-with_networking_vpc-requirements.html
EFA 사용 · 다중 NICworking-with_networking_efa.html (+ _create-lt, _create-lt-cfn) · working-with_networking_multi-nic.html
하드웨어 토폴로지 / GPU affinityhardware-topology.html
launch template 제약working-with_launch-templates_overview.html
Capacity Blocks · ODCR · job-level scalingcapacity-blocks.html · capacity-reservations-odcr.html · scaling-job-level.html
PCS-ready DLAMI · 커스텀 AMIworking-with_ami_pcs-ready-dlami.html · working-with_ami_custom.html
Node lifecycle actionscng-node-lifecycle-actions.html (+ -configure, -examples, -aws-scripts)
Custom Slurm settings (허용 목록)slurm-custom-settings.html · slurm-custom-settings-cluster.html / -queue.html / -cng.html
GRES · MIG · accounting · metricsgres-custom-settings.html · mig-configuration.html · slurm-accounting.html · slurm-metrics.html
scontrol reboot · 버전 정책 · 업그레이드slurm-reboot.html · working-with_slurm-versions.html · working-with_clusters_version_update.html
login 노드 (CNG / standalone)working-with_login-nodes.html · _standalone
트러블슈팅troubleshooting-compute-node-bootstrap.html · troubleshooting-invalid-registration.html
쿼터service-endpoints-quotas.html

도구 · 벤치마크

블로그

미확정 · AWS 확인 필요 항목

#항목영향언제
1서울 p6-b300 Capacity Block 최대 블록 크기 (64대인지 그 이하인지)125노드를 몇 개 블록/CNG로 나눌지착수 전 최우선
2서울에서 p6-b300 가용 AZ서브넷 · FSx · placement group 배치가 전부 종속착수 전
3CNG당 최대 인스턴스 수ODCR 경로에서 125노드 단일 CNG 가능 여부착수 전
4PCS에서 InterfaceType: efa-only 동작 여부통하면 노드당 IP 17 → 1. 서브넷은 17 기준으로 잡되 PoC에서 실측Step 5·9
5PCS의 DRAIN/장애 노드 처리 — static CNG에서 수동 종료 시 자동 재launch 여부, DRAIN 노드 자동 교체 유무Step 8 복구 경로(b)의 전제Step 9 리허설
6p6-b300 NUMA 도메인 수 / Slurm Sockets 권장값25.11 사용 시 필수(틀리면 INVALID_REG). 26.05면 불필요Step 6
7p6-b300 GPUDirect Storage(FSx Lustre over EFA) 지원 여부스토리지 대역폭 설계Step 2 이후
8p6-b300 다중 노드 NCCL 기대 대역폭성능 합격 판정 기준. 공개치는 p6-b200 377 GB/s뿐Step 9
9EC2 P 인스턴스 vCPU 쿼터 증설 리드타임 (ODCR 경로만)일정의 첫 의존성Step 0
10PCS 서울 리전 100노드+ 실사용 사례고객 레퍼런스 요청 대응
11Capacity Block 합계 256대 한도의 적용 단위 (계정 vs 조직)다른 팀·계정의 CB와 합산되는지Step 0