F26~F30을 마치고 실제 규모로 — 그런데 왜 이렇게 느릴까
F26에서 환경을, F27~F29에서 파이프라인 순서를, F30에서 실행 환경 자체를 봉인했습니다. 이제 이 모든 것을 실제 HPC 클러스터에서 수백~수천 샘플 규모로 돌릴 차례입니다. 그런데 막상 돌려보면, 계산 자체보다 작업이 큐에서 기다리는 시간과 공유 파일시스템에서의 I/O 대기가 전체 시간의 상당 부분을 차지하는 경우가 흔합니다. F31에서는 이 두 가지 실무적 병목을 다룹니다.
원리 — 스케줄러의 결정과 공유 스토리지의 한계
Slurm — 자원과 작업을 짝짓는 스케줄러
Slurm(Simple Linux Utility for Resource Management)은 다수 사용자가 공유하는 HPC 클러스터에서, 제출된 작업들을 가용 자원(CPU 코어·메모리·GPU·시간)에 맞춰 배정하는 스케줄러입니다. 사용자는 sbatch 스크립트로 필요한 자원을 명시해 작업을 큐에 제출합니다.
#!/bin/bash#SBATCH --job-name=variant_call#SBATCH --array=1-100 # 100개 샘플을 작업 배열로 제출#SBATCH --cpus-per-task=4#SBATCH --mem=16G#SBATCH --time=02:00:00#SBATCH --output=logs/call_%A_%a.log
SAMPLE=$(sed -n "${SLURM_ARRAY_TASK_ID}p" sample_list.txt)
apptainer exec docker://broadinstitute/gatk:4.5.0.0 \ gatk HaplotypeCaller -R ref/genome.fa -I aligned/${SAMPLE}.bam -O vcf/${SAMPLE}.vcf.gz--array=1-100은 **작업 배열(job array)**로, 동일한 스크립트를 100개의 독립적인 작업 인스턴스로 한 번에 제출합니다. 각 인스턴스는 SLURM_ARRAY_TASK_ID 환경변수로 자신이 몇 번째 작업인지 알 수 있습니다.
큐잉 지연 — 계산 시간과 별개의 병목
작업이 클러스터에 제출된 순간 바로 실행되는 것이 아닙니다. 다른 사용자들의 작업과 함께 큐에서 대기하며, 스케줄러가 우선순위·공정 배분 정책(fairshare)에 따라 실행 순서를 정합니다.
작은 자원(예: --cpus-per-task=1, --time=00:10:00)을 요청하는 작업은 스케줄러가 빈 틈에 끼워 넣기 쉬워 대체로 빨리 시작되는 반면, 큰 자원을 오래 요청하는 작업은 그만큼 빈 자리가 나기를 오래 기다려야 하는 경향이 있습니다. 자원 요청을 실제 필요량보다 넉넉하게 여유를 두고 잡는 습관은 큐 대기 시간을 늘리는 흔한 원인입니다.
손 계산 예제: 공유 파일시스템 I/O가 병목이 되는 이유
100개 작업이 동시에 같은 공유 파일시스템(예: Lustre)에서 각각 5GB짜리 BAM 파일을 읽는다고 합시다. 파일시스템의 총 처리량(throughput)이 5GB/s라면, 이론상 필요한 시간은
그런데 이는 대역폭을 완벽히 나눠 썼을 때의 이상적 값이고, 실제로는 수백 개 작업이 동시에 무작위 접근(random I/O)을 시도하면 파일시스템의 메타데이터 서버가 병목이 되어 이보다 훨씬 오래 걸리는 경우가 흔합니다. 개별 작업의 CPU 계산 시간은 5분이지만 I/O 대기로 15분이 걸린다면, 실제 병목은 계산이 아니라 스토리지입니다.
이 문제를 완화하는 실무적 방법으로는 각 노드의 로컬 스크래치 디스크(local scratch)에 작업 시작 시 필요한 파일을 미리 복사해두고 그 로컬 디스크에서 읽고 쓰는 방식, 또는 작업 수를 줄이고 배치 크기를 키워 파일시스템 요청 횟수 자체를 줄이는 방식이 있습니다.
클라우드 온디맨드 — 대기 양상을 바꾸는 대안
기관 HPC 큐가 상시 붐빈다면, AWS Batch·Google Cloud Batch 같은 클라우드 온디맨드(on-demand) 자원으로 작업을 "버스트(burst)"시키는 것도 대안입니다. 필요한 만큼만 인스턴스를 새로 띄울 수 있지만, 계정 할당량·인스턴스 수급·이미지 풀·스케줄링 때문에 대기가 완전히 사라지는 것은 아닙니다. 대신 비용을 사용한 시간만큼 그대로 지불해야 합니다.
실습: 작업 배열 제출과 자원 사용량 확인 (로컬 시뮬레이션)
# 실제 Slurm 클러스터 계정이 없다면, 아래 명령의 문법과 흐름만 확인합니다.
# 작업 제출sbatch variant_call.sbatch
# 큐 상태 확인 — PENDING(대기) 상태 작업이 많다면 큐 병목을 의심합니다.squeue -u $USER
# 완료된 작업의 실제 자원 사용량 확인 — 설치된 클러스터에서는 seff를 사용seff JOB_ID
# seff가 없다면 Slurm 회계 데이터에서 확인sacct -j JOB_ID --format=JobID,State,Elapsed,MaxRSS,AllocTRES
# 예시 출력에서 확인할 것: Memory Efficiency가 지나치게 낮다면(예: 20% 미만)# 다음 제출부터 --mem 요청량을 줄여 큐 대기 시간을 줄일 여지가 있습니다.seff가 설치된 클러스터에서는 자원 효율(efficiency)을, 그렇지 않으면 sacct의 회계 정보를 확인해 다음 작업의 자원 요청량을 현실적으로 조정합니다. 필요 이상으로 넉넉하게 요청하는 습관은 본인의 큐 대기 시간뿐 아니라 클러스터 전체의 자원 활용률도 함께 떨어뜨립니다.
CS 매핑
- 작업 스케줄링(job scheduling): Slurm의 우선순위·공정 배분 정책은 운영체제 수업에서 배우는 CPU 스케줄링 알고리즘(우선순위 큐, fair scheduling)을 클러스터 규모로 확장한 것입니다.
- 큐잉 이론(queueing theory): 작업 도착률과 처리율의 관계로 대기 시간을 분석하는 것은 큐잉 이론의 표준 모델(M/M/1 등)과 같은 틀입니다.
- I/O 병목과 데이터 지역성(data locality): 공유 스토리지 대신 로컬 스크래치 디스크를 활용해 I/O 병목을 줄이는 전략은 분산 시스템에서 데이터 지역성을 활용해 네트워크 비용을 줄이는 설계(예: Hadoop의 데이터 지역성 스케줄링)와 동일한 논리입니다.
자주 만나는 결함
- 자원 요청을 실제 필요량보다 과도하게 크게 설정: 여유를 넉넉히 두려는 의도가 오히려 큐 대기 시간을 늘리고, 클러스터 전체의 자원 활용률을 떨어뜨립니다.
seff로 실사용량을 확인하고 점진적으로 조정하는 것이 안전합니다. - I/O 병목을 계산 병목으로 오진: 작업이 느리다고 곧바로 CPU 코어 수를 늘리는 것은, 병목이 실제로 I/O에 있다면 문제를 전혀 해결하지 못합니다. 실행 시간 로그에서 계산 구간과 I/O 대기 구간을 구분해 진단하는 습관이 필요합니다.
더 깊게 파고 싶다면
본문은 BPD 연구진이 직접 재구성한 서술입니다. 원 논문과 공식 자료로 심화해봅시다.
- Slurm 공식 문서:
sbatch·작업 배열·자원 요청 문법 레퍼런스. - NCBI Training — HPC 실무 자료: 공식 교육 콘텐츠.
- Lustre 파일시스템 공식 문서: 병렬 파일시스템의 I/O 아키텍처 배경.
F2 소주제 중 실무 인프라 5편(F27~F31: Snakemake·Nextflow·nf-core·컨테이너·HPC)을 마쳤습니다. 남은 F32~F35(PanVariants·AI 에이전트·Foundation Model의 미래·연합학습)는 이 시리즈의 마지막 "미래 편" 묶음으로 이어집니다.