


[{"content":" 🔧 HPC From Scratch \u0026ndash; $1,300 이하의 일반 PC 부품으로 6노드 클러스터를 직접 구축합니다. 하드웨어 선택, OS 설치, 네트워크, Slurm, Ansible, GPU 워크로드까지. 여기서 시작하세요. 🎓 HPC 101 \u0026ndash; SSH, 모듈 시스템, Slurm 기초, 작업 디버깅. HPC가 처음인 연구자를 위한 시리즈. 여기서 시작하세요. 🐧 Linux 101 \u0026ndash; 터미널이 낯선 분들을 위한 명령줄 기초. 여기서 시작하세요. ","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/","section":"","summary":"","title":"","type":"page"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/a100/","section":"Tags","summary":"","title":"A100","type":"tags"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/benchmarking/","section":"Tags","summary":"","title":"Benchmarking","type":"tags"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/ddp/","section":"Tags","summary":"","title":"DDP","type":"tags"},{"content":"","date":"14 6월 2026","externalUrl":null,"permalink":"/tags/distributed-training/","section":"Tags","summary":"","title":"Distributed Training","type":"tags"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/gpu/","section":"Tags","summary":"","title":"GPU","type":"tags"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/hpc/","section":"Tags","summary":"","title":"HPC","type":"tags"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/series/hpc-special-topics/","section":"시리즈","summary":"","title":"HPC Special Topics","type":"series"},{"content":"","date":"14 6월 2026","externalUrl":null,"permalink":"/portfolio/","section":"Portfolio","summary":"","title":"Portfolio","type":"portfolio"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/pytorch/","section":"Tags","summary":"","title":"PyTorch","type":"tags"},{"content":"A reproducible benchmark suite for characterizing PyTorch Distributed Data Parallel (DDP) training performance on NVIDIA GPU clusters. Built for pre-production validation of large-scale HPC infrastructure, and generalized for broader use on any Slurm-based system or cloud instance.\nWhat It Does # The benchmark runs training on synthetic on-device data to isolate GPU compute and NCCL communication from storage I/O. It measures two complementary scaling modes:\nWeak scaling keeps per-GPU batch size fixed while adding GPUs. This answers whether each GPU stays productive as the system grows. Throughput per GPU should stay flat; a drop reveals communication overhead.\nStrong scaling keeps the global batch fixed while adding GPUs. This answers how much faster the same workload runs with more resources, expressed as speedup and parallel efficiency relative to a single-GPU baseline.\nResults are collected as JSON per run and aggregated into tables and plots. GPU activity is sampled during measurement via nvidia-smi, and low-utilization configurations are flagged automatically.\nResults: V100 vs A100 # Full analysis is in the blog post.\nWeak scaling efficiency (fp16, 1 to 8 GPUs):\nGPU Model Per-GPU BS 1 GPU img/s 8 GPU efficiency V100 SXM2 16GB ResNet-152 128 503 95.4% V100 SXM2 16GB ViT-B/16 128 381 95.9% A100 SXM4 80GB ResNet-152 512 1190 97.4% A100 SXM4 80GB ViT-B/16 1024 1030 98.0% Generation comparison (fp16, each GPU at its max batch size):\nModel V100 img/s/GPU A100 img/s/GPU Ratio ResNet-152 503 1190 2.37x ViT-B/16 381 1030 2.70x A100 SXM4 80GB V100 SXM2 16GB Why It Was Built # Statewide AI research infrastructure serving hundreds of researchers needs to be validated before opening to users. This benchmark was developed to stress-test GPU compute and inter-node communication on B200 and RTX Pro 6000 Blackwell hardware before cluster launch, and to produce numbers that can be reported to stakeholders in a reproducible way.\nTechnical Details # Language: Python 3.10+, Bash Framework: PyTorch with torch.distributed / NCCL Scheduler: Slurm (torchrun + srun for multi-node) or direct instance execution Models: ResNet-152, ViT-B/16, ResNet-50, ResNet-101 Precision: fp16 and bf16 Utilities: find_max_bs.py for per-GPU batch size calibration Outputs: per-run JSON, terminal tables, matplotlib PNG plots Repository # github.com/willgpaik/pytorch-ddp-scaling-benchmark\n","date":"14 6월 2026","externalUrl":null,"permalink":"/portfolio/pytorch-ddp-bench/","section":"Portfolio","summary":"","title":"PyTorch DDP Scaling Benchmark","type":"portfolio"},{"content":"A100이 V100보다 빠르다는 건 다들 알고 있습니다. 그런데 얼마나, 어디서 차이가 나는 걸까요?\n두 세대 모두 NVLink를 지원하고, PyTorch DDP를 실행할 수 있고, 학술 HPC(고성능 컴퓨팅, High-Performance Computing) 클러스터에 설치되어 있습니다. 그런데 막상 수치를 뽑아서 비교하려고 하면 생각보다 까다로운 문제들이 생깁니다. VRAM 용량 차이 때문에 배치 사이즈를 다르게 설정해야 하고, CUDA 빌드 제약 때문에 설치할 수 있는 PyTorch 버전도 달라집니다. 또 \u0026ldquo;최대 처리량\u0026quot;을 보느냐 \u0026ldquo;멀티 GPU 스케일링 효율\u0026quot;을 보느냐에 따라 비교 결과가 달라지기도 합니다.\n이 포스트에서는 동일한 벤치마크 코드를 8xV100 SXM2 16GB와 8xA100 SXM4 80GB에서 실행한 결과를 다룹니다. GPU와 인터커넥트를 다르게 스트레스 테스트하는 두 가지 모델, ResNet-152(컴퓨트 집약적 CNN)와 ViT-B/16(통신 집약적 트랜스포머)를 사용했습니다. 벤치마크 툴은 오픈소스이고, 자세한 설명은 포트폴리오 페이지에서 확인하실 수 있습니다. 코드와 결과는 모두 GitHub 저장소에 있습니다.\n1. 단순 비교가 어려운 이유 # 같은 배치 사이즈로 두 GPU를 돌려서 처리량을 비교하면 될 것 같지만, 실제로는 바로 문제가 생깁니다. V100은 16GB, A100은 80GB이기 때문입니다. V100에서 가능한 최대 배치 사이즈는 A100이 사용할 수 있는 것보다 훨씬 작습니다. A100을 V100 기준 배치 사이즈로 돌리면 성능이 인위적으로 낮게 나오고, V100을 A100 기준으로 돌리면 OOM(Out Of Memory)이 발생합니다.\n그래서 이 벤치마크에서는 비교를 두 부분으로 나눴습니다.\n약 스케일링(Weak Scaling) 효율은 GPU당 배치 사이즈를 고정한 채 1개에서 8개로 늘렸을 때 각 시스템이 얼마나 잘 스케일링되는지를 측정합니다. 이 지표는 정규화된 비율입니다. 1-GPU 처리량 대비 N-GPU에서 얼마를 유지하는지를 퍼센트로 나타냅니다. 8-GPU에서 95%라는 건 5%가 DDP 통신 오버헤드로 손실됐다는 의미입니다. 두 시스템이 다른 배치 사이즈를 사용하더라도 이 비교는 유효합니다.\n절대 처리량은 각 시스템의 최대 안전 배치 사이즈에서 GPU당 초당 이미지 수를 비교합니다. \u0026ldquo;각 GPU 최상의 상태\u0026quot;를 비교하는 것입니다. 배치 사이즈 차이가 변수로 남아 있으니, 통제된 실험이라기보다 \u0026ldquo;각자 최선을 다했을 때\u0026quot;의 비교로 봐야 합니다.\n강 스케일링(Strong Scaling)은 배치 사이즈 차이에 가장 민감합니다. GPU가 늘어날수록 GPU당 배치가 줄어들기 때문에, VRAM이 작은 GPU는 금방 한계에 부딪힙니다. V100의 경우 GBS=64에서 8-GPU 실행 시 GPU당 8장만 처리합니다. 이는 완전히 통신 병목 구간입니다. A100은 GBS=512를 사용해서 8-GPU에서도 GPU당 64장이 들어갑니다. 직접 비교가 유효하지 않기 때문에, 강 스케일링 결과는 각 시스템별로 따로 제시합니다.\n2. 실험 환경 # 하드웨어:\n8xNVIDIA V100 SXM2 16GB (NVLink 2.0), Lambda Cloud 8xNVIDIA A100 SXM4 80GB (NVLink 3.0), Lambda Cloud 소프트웨어:\nV100: PyTorch 2.4.1, CUDA 12.6, Python 3.10 A100: PyTorch 2.12.0, CUDA 13.0, Python 3.12 PyTorch 버전 차이에 대해: 최근 PyTorch 빌드(cu130)는 V100의 컴퓨트 캐퍼빌리티인 sm_70을 더 이상 포함하지 않습니다. V100에서는 구버전 wheel을 사용해야 하기 때문에, 처리량 차이에 PyTorch 버전 차이가 일부 포함됩니다. 약 스케일링 효율 비교는 각 시스템 내부에서 정규화되기 때문에 이 영향이 훨씬 적습니다.\n모든 실행에서 GPU 위에서 직접 생성한 합성 데이터, torchrun DDP, SGD 옵티마이저를 사용했습니다. 각 작업은 60초 워밍업 후 300~600초 측정입니다. 최대 안전 배치 사이즈는 find_max_bs.py로 확인했습니다:\nGPU ResNet-152 fp16 ViT-B/16 fp16 V100 SXM2 16GB 128 128 A100 SXM4 80GB 512 1024 3. 약 스케일링 결과 # 약 스케일링은 GPU당 배치 사이즈를 고정합니다. GPU 수가 늘어도 GPU당 처리량이 일정해야 이상적입니다. 떨어지는 만큼이 NCCL gradient allreduce 통신 오버헤드입니다.\nV100 SXM2 16GB, fp16, GPU당 BS=128:\n모델 1-GPU 2-GPU 효율 4-GPU 효율 8-GPU 효율 ResNet-152 503 img/s 95.6% 95.8% 95.4% ViT-B/16 381 img/s 96.5% 96.5% 95.9% A100 SXM4 80GB, fp16:\n모델 GPU당 BS 1-GPU 2-GPU 효율 4-GPU 효율 8-GPU 효율 ResNet-152 512 1190 img/s 98.9% 98.1% 97.4% ViT-B/16 1024 1030 img/s 98.9% 98.6% 98.0% A100 SXM4 80GB, bf16:\n모델 GPU당 BS 1-GPU 2-GPU 효율 4-GPU 효율 8-GPU 효율 ResNet-152 512 1012 img/s 99.3% 98.9% 98.5% ViT-B/16 1024 1033 img/s 99.6% 99.3% 98.9% 두 시스템 모두 잘 스케일링됩니다. V100은 8-GPU에서 95~96%, A100은 97~99%를 달성했습니다. 2~3%p 차이는 NVLink 3.0(양방향 600 GB/s) vs NVLink 2.0(양방향 300 GB/s)의 대역폭 차이를 반영합니다. 여기서 사용한 배치 사이즈 기준으로, V100은 A100보다 gradient 동기화 대기 시간이 조금 더 깁니다.\n절대 처리량 비교 (fp16, 각 GPU 최대 배치 사이즈 기준):\n모델 V100 1-GPU A100 1-GPU 비율 ResNet-152 503 img/s 1190 img/s 2.37배 ViT-B/16 381 img/s 1030 img/s 2.70배 A100이 ResNet-152에서 약 2.4배, ViT-B/16에서 약 2.7배 더 빠르게 나왔습니다. ViT에서 격차가 더 큰 것은, A100의 개선된 Tensor Core 처리량과 더 높은 메모리 대역폭(2.0 TB/s vs V100 0.9 TB/s)이 GEMM(행렬 곱셈) 연산이 많은 워크로드에서 더 큰 효과를 내기 때문입니다.\n4. A100에서 fp16 vs bf16 # A100은 fp16과 bf16 모두 하드웨어로 지원합니다. 두 포맷은 값 하나에 2바이트를 사용해서 VRAM 사용량은 동일합니다. 성능 차이는 연산 경로에서 발생합니다.\n모델 fp16 bf16 차이 ResNet-152 1190 img/s 1012 img/s fp16이 17% 빠름 ViT-B/16 1030 img/s 1033 img/s 사실상 동일 ResNet-152는 fp16이 17% 빠르고, ViT-B/16은 차이가 없습니다. 이유는 메모리 레이아웃에 있습니다. ResNet은 channels_last (NHWC) 포맷을 사용합니다. cuDNN의 conv 커널은 정밀도에 따라 이를 다르게 처리하는데, A100에서 fp16 NHWC 경로가 bf16보다 더 최적화되어 있습니다. ViT-B/16은 attention 연산이 convolution이 아니라 channels_last를 사용하지 않습니다. 그래서 precision 차이가 메모리 레이아웃과 상호작용하지 않습니다.\n트랜스포머 학습 문서에서 \u0026ldquo;Ampere에서는 bf16을 사용하라\u0026quot;는 말을 자주 볼 수 있는데, 이 조언이 CNN 워크로드에는 그대로 적용되지 않습니다. ResNet 계열 모델이라면 A100에서도 fp16이 더 빠른 선택입니다.\n5. 강 스케일링 결과 # 강 스케일링은 전체 배치 사이즈를 고정하고 GPU가 늘수록 얼마나 빨리 끝나는지를 측정합니다. 이상적인 speedup은 N개 GPU에서 N배입니다.\n직접 비교는 유효하지 않습니다. V100과 A100이 서로 다른 GBS를 사용하는 이유는 V100의 VRAM이 1-GPU 기준 실행을 제약하기 때문입니다. 아래 표는 각 시스템을 독립적으로 보여줍니다.\nV100 SXM2 16GB, fp16, GBS=64 (8-GPU에서 GPU당 8장):\n모델 1-GPU 8-GPU speedup 8-GPU 효율 ResNet-152 461 img/s 1.47배 18.4% ViT-B/16 367 img/s 2.81배 35.1% A100 SXM4 80GB, fp16, GBS=512 (8-GPU에서 GPU당 64장):\n모델 1-GPU 8-GPU speedup 8-GPU 효율 ResNet-152 961 img/s 2.55배 31.9% ViT-B/16 1061 img/s 6.42배 80.3% GPU당 배치 사이즈가 작아지면 강 스케일링 효율이 급격히 떨어집니다. V100 GBS=64에서 8-GPU를 사용하면 GPU당 8장만 처리합니다. 이 크기에서는 gradient allreduce가 순전파+역전파보다 오래 걸립니다. V100 ResNet-152의 8-GPU 효율이 18.4%인 것은, GPU 7개를 더 사용해도 1.47배 speedup밖에 안 된다는 의미입니다.\nViT-B/16이 더 나은 결과(V100 35.1%, A100 80.3%)를 보이는 이유는, ViT가 같은 배치 사이즈에서 ResNet보다 step당 연산이 더 많기 때문입니다. 연산과 통신이 더 많이 겹칠 수 있어서, 동기화 대기 시간의 비율이 낮아집니다.\n6. 수치가 시스템 선택에 주는 의미 # 두 시스템 모두 약 스케일링에서는 잘 동작합니다 (8-GPU에서 95% 이상). 핵심 질문은 비용 차이가 처리량 차이를 정당화하느냐입니다.\nA100은 GPU당 2.4~2.7배 더 많은 처리량을 냅니다. Lambda의 A100 시간당 비용이 V100의 2.4배 미만이라면, 학습 샘플당 비용은 A100이 유리합니다. 큰 전체 배치 사이즈가 필요하고 강 스케일링으로 학습을 빨리 끝내야 한다면, A100이 구조적으로 유리합니다. 더 큰 VRAM 덕분에 GPU 수가 늘어도 GPU당 배치 사이즈를 효율적으로 유지할 수 있기 때문입니다. V100은 금방 한계에 부딪힙니다.\nGPU당 16GB에 워크로드가 들어가고 약 스케일링으로 많은 노드에 걸쳐서 실행하는 경우라면, 가격에 따라 V100의 95% 효율도 충분히 cost-effective할 수 있습니다.\n7. 트러블슈팅 # V100에서 CUDA error: no kernel image is available for execution on the device\n최근 PyTorch wheel(cu130, cu128)은 V100의 sm_70을 포함하지 않습니다. cu126을 설치하세요:\npip install torch torchvision --index-url https://download.pytorch.org/whl/cu126 python -c \u0026#34;import torch; print(torch.cuda.get_arch_list())\u0026#34; # 출력에 sm_70이 포함되어 있는지 확인하세요 1-GPU 강 스케일링 기준 실행에서 OOM\n1-GPU 강 스케일링 실행은 GPU당 BS = GBS가 됩니다. GBS가 GPU의 안전 배치 사이즈를 넘으면 멀티 GPU 실행 전에 OOM이 발생합니다. find_max_bs.py로 상한을 확인하고 그 이하로 GBS를 설정하십시오:\npython find_max_bs.py --model resnet152 --precision fp16 analyze_results.py에서 step time 분산 경고\nGPU 수가 많을 때 강 스케일링에서 GPU당 배치 사이즈가 아주 작아지면 당연히 나타나는 현상입니다. GPU가 통신 병목 상태에서 step 타이밍이 불규칙해집니다. 통신 오버헤드 분석 목적에서는 무시해도 됩니다. 큰 배치 사이즈를 사용하는 약 스케일링 결과에서 분산이 높게 나오면 그때 확인이 필요합니다.\n8. 요약 # 이 벤치마크에서 얻은 세 가지 핵심 결과입니다.\nV100과 A100 모두 약 스케일링에서 8-GPU 기준 95% 이상의 효율을 유지합니다. 두 시스템 사이의 2~3%p 격차는 NVLink 3.0 vs NVLink 2.0 대역폭 차이를 반영하는 것이지, 스케일링 방식 자체가 근본적으로 다른 것은 아닙니다.\nA100은 ResNet-152에서 2.37배, ViT-B/16에서 2.70배 더 많은 처리량을 냅니다. 메모리 대역폭과 Tensor Core 처리량이 모두 기여하고, GEMM 집약적인 ViT에서 격차가 더 큽니다.\nA100에서는 fp16이 bf16보다 ResNet-152 기준 17% 빠른 결과가 나왔습니다. cuDNN NHWC conv 커널 최적화 차이 때문입니다. ViT-B/16은 두 precision 간 차이가 없습니다. \u0026ldquo;Ampere에서는 bf16을 사용하라\u0026quot;는 조언이 CNN 워크로드에는 그대로 적용된다고 가정하면 안 됩니다.\n모든 벤치마크 스크립트, raw JSON 결과, 플롯은 GitHub 저장소에 있습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/posts/hpc-special-topics-02/","section":"포스트","summary":"V100과 A100 모두 8-GPU 약 스케일링에서 95% 이상의 효율을 보여줬지만, 처리량은 A100이 2.4~2.7배 더 높습니다. ResNet-152와 ViT-B/16, fp16과 bf16을 포함한 실측 결과와 시스템 선택에 적용할 수 있는 분석을 정리했습니다.","title":"PyTorch DDP 스케일링: 8-GPU V100 vs A100, ResNet-152와 ViT-B/16 실측 비교","type":"posts"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/tags/v100/","section":"Tags","summary":"","title":"V100","type":"tags"},{"content":"각 시리즈는 독립적인 흐름으로 구성되어 있으며, 1편부터 순서대로 따라가는 것을 권장합니다. 시리즈 내 포스트들은 서로 자동으로 연결됩니다.\n","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/series/","section":"시리즈","summary":"","title":"시리즈","type":"series"},{"content":"","date":"2026년 6월 14일","externalUrl":null,"permalink":"/ko/posts/","section":"포스트","summary":"","title":"포스트","type":"posts"},{"content":"","date":"13 6월 2026","externalUrl":null,"permalink":"/tags/ai/","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"13 6월 2026","externalUrl":null,"permalink":"/tags/monte-carlo/","section":"Tags","summary":"","title":"Monte Carlo","type":"tags"},{"content":" Overview # A text-based Texas Hold\u0026rsquo;em poker game built in Python using only the standard library. The AI opponent uses Monte Carlo simulation to estimate win probability from incomplete information and decides whether to raise, call, fold, or bluff.\nGitHub: github.com/willgpaik/montecarlo-poker\nGoals # The initial goal was to implement a working poker game using OOP without external packages, relying only on random, math, copy, and collections from the standard library. The rules were implemented from scratch based on Texas Hold\u0026rsquo;em specs, which meant writing the full hand evaluator, betting logic, and blind structure by hand.\nHow the AI Works # Each AI turn triggers a Monte Carlo simulation:\nDeep copy the remaining deck and shuffle it Randomly complete the community cards up to 5 Randomly assign hole cards to each simulated opponent Evaluate all hands and check if the AI wins Repeat 1000 times and compute win rate = wins / 1000 Each AI is assigned a random personality at game start that determines its decision thresholds. The personality is not revealed to the player.\nPersonality Raise threshold Fold threshold Bluff chance aggressive 0.50 0.25 15% passive 0.75 0.45 5% bluffer 0.60 0.30 20% Thresholds also have ±0.4 random noise applied per decision to prevent predictable behavior. A separate 30% random action layer runs on top regardless of win rate.\nWin rate above raise threshold → raise. Win rate above fold threshold → call. Otherwise → fold. Win rate above 0.9 triggers all-in at 30% chance.\nDevelopment Notes # The initial version had the structure and classes in place but several broken pieces.\nHand evaluator bugs # The royalflush() function compared the full card tuple instead of the value field (card == 1 instead of card[1] == 1), so royal flush never triggered. The straightflush() function checked the correct suit in the outer condition but then filtered for 'heart' cards in all four suit branches, a copy-paste error that broke straight flush detection for club, diamond, and spade entirely.\nStraight detection # cards.sort() on (suit, value) tuples sorts alphabetically by suit first, so straight() received values like [3, 7, 2, 5, 4, 8, 6] instead of a numerically sorted list. Straights were rarely detected. The fix was sorted(set(card[1] for card in cards)) to get deduplicated, numerically sorted values.\nMonte Carlo was deterministic # The simulate loop used copy.deepcopy(deck) but never shuffled the copy. Every simulation drew the same cards in the same order, so the win rate was always 0 or 1. Adding remainingDeck.shuffle() inside the loop fixed this.\nAction Prompt # Flop, turn, and river each start a new callAll() call with betHigh=0. Since roundBet is also initialized to 0, every player immediately satisfied roundBet[idx] == betHigh and was skipped. The game auto-played through all rounds without asking the human for input. The fix was a hasActed[] boolean array that tracks whether each player has acted in the current round, separate from whether their bet amount matches the current high.\nHandling re-raise # The original callCnt counter incremented per call but never reset when a raise occurred. If player A called and player B raised, player A was not prompted again. The rewrite uses hasActed[] and resets it for all other players when a raise occurs.\nMoney deducted twice # callHuman() and callAI() deducted from player.money directly. callAll() then tracked the same amounts in roundBet and added them to the pot. The fix restructured all helper functions to return amounts only, with a single deduction point in callAll(). This touched callHuman, callAI, raiseHuman, and raiseAI.\nFirst player wins on a tie # The function used player.score, which is set by getScore(). That method was never called during gameplay, so every player\u0026rsquo;s score stayed at 0. The fix was to compare the playerScore tuples (already computed by think()) directly, using the high card and low card fields for tiebreaking.\nHand evaluator refactor # The original evaluator had 10 separate functions, one per hand rank, totaling around 250 lines. straightflush() alone was 90 lines of the same logic copy-pasted for each of the four suits. The rewrite uses a single evaluate_hand() function with collections.Counter for value and suit frequency, and a nested find_straight() helper shared by both straight and straight flush detection. The result is around 50 lines and removes all the repetition.\nKnown Limitations # Side pot not implemented. When a player goes all-in for less than the current bet, the correct behavior is to split the pot. This version gives the full pot to the winner regardless of all-in amounts. No opponent modeling. The AI has a fixed personality per game but does not adapt to observed player behavior across hands. Text interface only. A browser version using the same Monte Carlo logic running in JavaScript is a future direction. Stack # Python 3.10+ · Standard library only (random, math, copy, collections)\n","date":"13 6월 2026","externalUrl":null,"permalink":"/portfolio/montecarlo-poker/","section":"Portfolio","summary":"","title":"montecarlo-poker","type":"portfolio"},{"content":"","date":"13 6월 2026","externalUrl":null,"permalink":"/tags/side-project/","section":"Tags","summary":"","title":"Side Project","type":"tags"},{"content":"","date":"13 6월 2026","externalUrl":null,"permalink":"/tags/simulation/","section":"Tags","summary":"","title":"Simulation","type":"tags"},{"content":"스케줄러는 돌아가고 있습니다. 이제 누가 무엇을 얼마나 쓸 수 있는지 가르쳐줄 차례입니다.\n에피소드 5에서는 Slurm을 설치하고, slurmdbd를 MariaDB에 연결하고, 첫 번째 작업을 제출했습니다. 클러스터는 잘 돌아갑니다.\n하지만 지금 상태는 사실상 무법지대입니다. 작업에 시간 제한이 없고, 한 명이 큐에 작업을 수백 개씩 넣어도 아무도 막을 수 없습니다. 지난 일주일 내내 클러스터를 혼자 쓴 유저와 처음 작업을 제출한 유저를 스케줄러는 똑같이 취급합니다. 혼자 쓰는 클러스터라면 괜찮지만, 여러 명이 공유하는 순간 문제가 생깁니다.\n이번 에피소드에서는 accounting 레이어를 처음부터 만듭니다. sacctmgr으로 account 계층을 구성하고, QOS 정책을 설정하고, 페어 쉐어(Fair Share) 스케줄링을 활성화합니다. 현재 시간 제한이 전혀 없는 파티션에도 wall time 제한을 추가합니다.\n사전 요구사항: 이 에피소드는 에피소드 5를 완료한 상태를 전제로 합니다. slurmdbd가 MariaDB에 연결된 Slurm 설치 환경과 idle 상태의 컴퓨트 노드가 최소 하나 있어야 합니다.\n1. Slurm Accounting 구조 이해하기 # Slurm accounting에는 세 가지 레벨이 있습니다.\nCluster가 최상단입니다. 에피소드 5에서 sacctmgr -i add cluster cluster로 등록한 그 cluster입니다.\nAccount는 cluster 아래에 있는 그룹입니다. 부서, 연구 그룹, 또는 PI 랩 단위로 생각하면 됩니다. Account가 페어 쉐어 예산을 가지고 있습니다. research가 클러스터 전체 share의 80%를 갖고 demo가 20%를 가지면, 큐가 경합할 때 그 비율이 우선순위를 결정합니다.\nUser는 하나 이상의 account에 속합니다. 작업을 제출하면 Slurm이 사용량을 해당 account에 기록하고, 그것이 account의 페어 쉐어 점수에 영향을 줍니다.\n에피소드 5 이후 현재 상태는 다음과 같습니다:\ncluster └── root ├── root (user) └── wpaik 모든 것이 root account 아래에 구조 없이 쌓여 있습니다. 여기에 sub-account 두 개를 추가하고 유저를 적절한 위치에 배치합니다:\ncluster └── root ├── research (share=80) │ ├── wpaik │ └── testuser1 └── demo (share=20) └── testuser2 2. Account 트리 만들기 # root 아래에 두 개의 sub-account를 만듭니다. parent=root 옵션이 기존 root account 아래에 위치시킵니다.\n[wpaik@arbiter ~]$ sudo sacctmgr -i add account research parent=root \\ Description=\u0026#34;Research Group\u0026#34; Organization=\u0026#34;Cluster\u0026#34; fairshare=80 [wpaik@arbiter ~]$ sudo sacctmgr -i add account demo parent=root \\ Description=\u0026#34;Demo Group\u0026#34; Organization=\u0026#34;Cluster\u0026#34; fairshare=20 참고: sacctmgr write 명령어(add, modify, delete)는 admin 권한이 필요합니다. wpaik의 sacctmgr AdminLevel이 None이므로 데이터베이스를 수정하는 모든 명령어에 sudo가 필요합니다. sacctmgr show 같은 read-only 명령어는 sudo 없이 실행할 수 있습니다.\nSlurm accounting에 유저를 추가하기 전에, testuser1과 testuser2가 실제 시스템 유저로 존재해야 합니다. 이 클러스터는 FreeIPA를 사용하므로 거기서 먼저 만듭니다. 데모용이라면 홈 디렉토리 없이 최소한의 계정만 만들어도 됩니다.\n[wpaik@arbiter ~]$ ipa user-add testuser1 --first=Test --last=User1 [wpaik@arbiter ~]$ ipa user-add testuser2 --first=Test --last=User2 이제 accounting 데이터베이스에 유저를 추가합니다:\n# wpaik은 에피소드 5에서 root account에 이미 등록되어 있습니다. # research에 추가하면 두 번째 association이 생기고 기본 account가 바뀝니다. [wpaik@arbiter ~]$ sudo sacctmgr -i add user wpaik account=research defaultaccount=research [wpaik@arbiter ~]$ sudo sacctmgr -i add user testuser1 account=research defaultaccount=research [wpaik@arbiter ~]$ sudo sacctmgr -i add user testuser2 account=demo defaultaccount=demo 유저는 여러 account에 동시에 속할 수 있습니다. wpaik은 이제 root와 research 두 곳에 association이 생겼습니다. 기본 account는 research로 바뀌었습니다.\nAdmin account에 대한 참고: 이 시리즈에서는 wpaik이 sacctmgr 명령어, slurm.conf 수정, sudo 작업을 포함한 모든 클러스터 관리를 담당합니다. 홈 랩에서는 흔한 구조입니다. 다만 실제 HPC 환경에서는 관리 작업을 별도의 서비스 계정으로 분리하는 것이 일반적입니다. 여기서 중요한 것은 wpaik의 sacctmgr AdminLevel이 None이라는 점입니다. Linux sudoer 권한은 스케줄러가 전혀 인식하지 않습니다.\n트리를 확인합니다:\n[wpaik@arbiter ~]$ sacctmgr show associations format=cluster,account,user,share,qos,defaultqos Cluster Account User Share QOS DefQOS ---------- ---------- ---------- --------- -------------------- ------ cluster root 1 cluster root root 1 cluster research 80 normal,high,gpu normal cluster research wpaik 1 normal,high,gpu normal cluster research testuser1 1 normal,high,gpu normal cluster demo 20 normal normal cluster demo testuser2 1 normal normal wpaik이 두 곳에 보이는 이유: wpaik은 root(에피소드 5에서 생성)와 research(이번에 추가) 두 곳에 association이 있습니다. sshare -l을 실행하면 wpaik이 두 줄로 나옵니다. root 항목에는 에피소드 5에서 돌렸던 작업의 사용량이 쌓여 있고, research 항목은 이번에 새로 만들어져서 사용량이 0입니다. 앞으로 제출하는 작업은 기본적으로 research account에 청구됩니다.\n3. QOS: 작업에 가중치 부여하기 # QOS(Quality of Service)를 사용하면 작업에 규칙을 붙일 수 있습니다. 얼마나 오래 돌릴 수 있는지, 리소스를 얼마나 요청할 수 있는지, 큐에서 얼마나 높은 우선순위를 가지는지 등을 정의합니다. QOS 없이는 모든 작업이 같은 조건에서 경쟁합니다.\n세 가지 레벨을 만듭니다:\nQOS Priority MaxWall 용도 normal 0 24시간 모든 작업의 기본값 high 100 4시간 큐를 앞당기는 짧고 긴급한 작업 gpu 50 8시간 GPU 파티션 작업 생성하고 설정합니다:\n[wpaik@arbiter ~]$ sudo sacctmgr -i add qos normal [wpaik@arbiter ~]$ sudo sacctmgr -i modify qos normal set Priority=0 MaxWallDurationPerJob=1-00:00:00 [wpaik@arbiter ~]$ sudo sacctmgr -i add qos high [wpaik@arbiter ~]$ sudo sacctmgr -i modify qos high set Priority=100 MaxWallDurationPerJob=04:00:00 [wpaik@arbiter ~]$ sudo sacctmgr -i add qos gpu [wpaik@arbiter ~]$ sudo sacctmgr -i modify qos gpu set Priority=50 MaxWallDurationPerJob=08:00:00 각 account에 사용 가능한 QOS를 지정합니다. research는 세 가지 모두, demo는 normal만 사용할 수 있습니다.\n[wpaik@arbiter ~]$ sudo sacctmgr -i modify account name=research set qos=normal,high,gpu defaultqos=normal [wpaik@arbiter ~]$ sudo sacctmgr -i modify account name=demo set qos=normal defaultqos=normal 기본이 아닌 QOS를 사용하려면 작업 스크립트에 다음과 같이 작성합니다:\n#SBATCH --qos=high demo 소속 유저가 --qos=high로 제출하면 Slurm이 큐에 들어가기도 전에 즉시 거부합니다:\nsbatch: error: Batch job submission failed: Invalid qos specification 참고: high QOS는 4시간 wall limit이 있습니다. --qos=high와 --time=08:00:00을 동시에 지정할 수 없습니다. 우선순위 부스트를 받는 대신 더 짧은 런타임 제한을 감수하는 구조입니다.\n4. 페어 쉐어: 많이 쓰면 기다리게 하기 # 페어 쉐어(Fair Share) 없이는 Slurm이 제출 순서대로(FIFO) 스케줄링합니다. 지난 주에 작업을 몇 백 개 돌렸든 처음 제출하든 상관없이 먼저 들어온 작업이 먼저 실행됩니다.\n페어 쉐어는 사용 이력을 추적해서 우선순위를 조정합니다. 본인 할당량보다 많이 쓴 유저는 우선순위가 낮아지고, 적게 쓴 유저는 높아집니다. 오늘 많이 쓰면 내일 우선순위가 낮아지는 방식으로 자동 조정됩니다.\n페어 쉐어 활성화하기 # arbiter의 /etc/slurm/slurm.conf에 다음 줄을 추가합니다:\n[wpaik@arbiter ~]$ sudo vim /etc/slurm/slurm.conf # Priority / Fair Share PriorityType=priority/multifactor PriorityWeightFairShare=100000 PriorityWeightAge=1000 PriorityDecayHalfLife=5-0 PriorityMaxAge=7-0 AccountingStorageEnforce=associations,qos PriorityType=priority/multifactor Slurm을 FIFO에서 가중치 기반 멀티팩터 우선순위 모델로 전환합니다. 이 한 줄이 이 섹션의 나머지 설정들을 활성화시킵니다.\nPriorityWeightFairShare=100000 페어 쉐어를 우선순위 계산의 핵심 요소로 만듭니다. 작업 대기 시간 같은 다른 요소들도 반영되지만, 사용 이력이 스케줄링 결정을 주도합니다.\nPriorityWeightAge=1000 오래 기다린 작업에 조금씩 보너스를 추가합니다. 이 덕분에 페어 쉐어 점수가 낮아진 유저의 작업도 결국 실행될 수 있습니다. 무한정 기다리는 상황(starvation)을 방지합니다.\nPriorityDecayHalfLife=5-0 스케줄러가 과거 사용량을 얼마나 오래 기억하는지를 결정합니다. 5일마다 누적 사용량이 절반으로 줄어듭니다. 오늘 쓴 CPU 1시간이 5일 전 쓴 CPU 1시간보다 두 배 영향을 미칩니다.\nPriorityMaxAge=7-0 대기 시간 보너스를 7일에서 제한합니다. 2주 동안 큐에서 기다렸다고 해서 우선순위가 계속 쌓이지는 않습니다.\nAccountingStorageEnforce=associations,qos Slurm이 잡 제출 시점에 accounting 규칙을 실제로 강제하도록 합니다. associations는 accounting 데이터베이스에 없는 유저의 잡을 거부합니다. qos는 계정에 허용되지 않은 QOS를 요청하는 잡을 거부합니다. 이 줄이 없으면 sacctmgr에서 설정한 QOS 제한이 데이터베이스에만 기록되고 실제로 검사되지 않습니다. demo 계정의 유저가 --qos=high로 제출해도 그냥 돌아가는 문제가 생깁니다.\nDecay vs. Reset # 과거 사용량을 처리하는 방법은 두 가지입니다:\n방식 파라미터 동작 점진적 감소 PriorityDecayHalfLife=5-0 지수적 감소, 오래된 사용량이 서서히 영향력을 잃음 완전 리셋 PriorityUsageResetPeriod=MONTHLY 고정된 주기마다 전체 사용량이 0으로 초기화됨 완전 리셋은 개념적으로 단순합니다. 매월 1일에 모두가 깨끗하게 시작합니다. 하지만 벼랑이 생깁니다. 2일에 쌓인 사용량은 리셋 직전까지 그대로인데 0시에 갑자기 0으로 떨어집니다. 월초에 클러스터를 독점한 유저가 나머지 기간 동안 절약할 이유가 없어집니다.\n점진적 감소는 그 벼랑이 없습니다. 5일 half-life라면 지난주 사용량은 오늘의 약 1/4 수준으로 반영됩니다. 갑작스러운 리셋 없이 우선순위가 연속적으로 조정됩니다. 5일이라는 값은 합리적인 시작점입니다. 단발성 작업 하나로 수 주 동안 불이익을 받을 만큼 길지 않으면서, 스케줄러가 실제로 기억할 만큼은 깁니다. 실제 HPC 클러스터는 대개 1-7일 범위 안에서 설정합니다.\n실제로 어떻게 작동하나요? # research가 share=80, demo가 share=20인 상황에서:\nwpaik이 3일 내내 작업을 돌렸다면 FairShare 점수가 1.0 아래로 크게 내려갑니다. demo의 testuser2는 아무것도 돌리지 않았습니다. FairShare 점수가 1.0을 유지합니다. 둘이 동시에 작업을 제출하면, testuser2의 account share가 작더라도 testuser2 작업이 먼저 실행될 수 있습니다. 아무것도 소비하지 않았기 때문입니다. 이것이 의도된 동작입니다. 페어 쉐어는 할당량 자체가 아니라 할당량 대비 실제 사용량을 기준으로 합니다. research가 share=80을 가진다는 것은 모두가 동시에 경쟁할 때 80%를 가져간다는 의미입니다. 항상 먼저 실행된다는 뜻이 아닙니다.\n5. 파티션 제한 설정하기 # 두 파티션 모두 현재 MaxTime=UNLIMITED이고 DefaultTime이 없습니다. --time을 지정하지 않고 작업을 제출하면 무제한 wall time이 적용됩니다. 작업이 멈추면 리소스를 무한정 점유할 수 있습니다.\narbiter의 slurm.conf에서 기존 PartitionName= 줄을 아래로 교체합니다:\nPartitionName=cpu Nodes=interceptor-[01-02] Default=YES MaxTime=1-00:00:00 DefaultTime=01:00:00 State=UP PartitionName=gpu Nodes=corsair-01 Default=NO MaxTime=08:00:00 DefaultTime=01:00:00 AllowQos=normal,gpu State=UP DefaultTime=01:00:00 --time을 지정하지 않은 작업에 1시간 제한을 자동으로 부여합니다. 두 파라미터 중 더 중요한 것입니다. 기본값이 없으면 --time을 빠뜨리는 것이 무제한 런타임을 요청하는 것과 같습니다.\nMaxTime=1-00:00:00 CPU 작업을 24시간으로 제한합니다. 그보다 오래 걸리는 작업은 어차피 24시간 단위로 체크포인팅을 해야 합니다.\nAllowQos=normal,gpu GPU 파티션에서 high QOS를 사용하지 못하도록 막습니다. 우선순위 부스트 QOS는 짧은 CPU 작업용이지, GPU 큐를 앞당기는 용도가 아닙니다.\n6. 변경사항 적용하기 # sacctmgr 변경사항(account, user, QOS)은 이미 데이터베이스에 반영됐습니다. 재시작이 필요 없습니다.\nslurm.conf 변경사항(priority 설정, 파티션 제한)은 전체 노드에 배포하고 slurmctld를 재시작해야 합니다.\n# 업데이트된 slurm.conf를 전체 노드에 배포 [wpaik@arbiter ~]$ ansible all_nodes -b -m copy \\ -a \u0026#34;src=/etc/slurm/slurm.conf dest=/etc/slurm/slurm.conf owner=slurm group=slurm mode=0644\u0026#34; # arbiter에서 slurmctld 재시작 [wpaik@arbiter ~]$ sudo systemctl restart slurmctld # 모든 slurmd 데몬에 config 재읽기 신호 전송 [wpaik@arbiter ~]$ sudo scontrol reconfigure # 확인 [wpaik@arbiter ~]$ sudo systemctl status slurmctld [wpaik@arbiter ~]$ tail -n 20 /var/log/slurm/slurmctld.log scontrol reconfigure 단계가 중요합니다. slurm.conf를 배포하고 slurmctld를 재시작하면 컨트롤러가 새 파일 기반으로 config hash를 다시 계산합니다. 이 명령 없이는 컴퓨트 노드의 slurmd 데몬이 예전 hash를 그대로 들고 있어서 Slurm 로그에 config mismatch 경고가 계속 나타납니다.\n7. 검증하기 # Account 및 QOS 구조 # [wpaik@arbiter ~]$ sacctmgr show associations format=cluster,account,user,share,qos,defaultqos [wpaik@arbiter ~]$ sacctmgr show qos format=name,priority,maxwall,flags 페어 쉐어 트리 # [wpaik@carrier ~]$ sshare -l Account User RawShares NormShares RawUsage EffectvUsage FairShare -------------------- ---------- ---------- ----------- --------- ------------- ---------- root 1 0.000000 0 0.000000 1.000000 root wpaik 1 0.009804 0 0.000000 1.000000 research 80 0.784314 0 0.000000 inf research wpaik 1 0.500000 0 0.000000 1.000000 research testuser1 1 0.500000 0 0.000000 1.000000 demo 20 0.196078 0 0.000000 inf demo testuser2 1 1.000000 0 0.000000 1.000000 wpaik이 root와 research 두 줄로 나오는 것은 정상입니다. root 항목에는 에피소드 5에서 쌓인 사용량이 있고, research 항목은 이번에 새로 만들어져서 0입니다. inf는 research와 demo account의 사용량이 아직 0이라 비율 계산이 안 된다는 의미입니다. 작업을 돌리면 숫자로 바뀝니다.\n작업 우선순위 분석 # [wpaik@carrier ~]$ sprio -l 큐에 있는 각 작업의 우선순위를 FairShare 기여도, Age 기여도, QOS 기여도별로 나눠서 보여줍니다. 왜 어떤 작업이 다른 작업보다 먼저 실행되는지 확인할 때 유용합니다.\n작업 실행 이력 # [wpaik@carrier ~]$ sacct -u wpaik --format=JobID,JobName,Partition,Account,AllocCPUS,State,Elapsed 파티션 제한 확인 # [wpaik@carrier ~]$ scontrol show partition cpu [wpaik@carrier ~]$ scontrol show partition gpu GPU 파티션은 아래와 같이 나와야 합니다:\nPartitionName=gpu AllowGroups=ALL AllowAccounts=ALL AllowQos=normal,gpu DefaultTime=01:00:00 MaxTime=08:00:00 Nodes=corsair-01 ... 확인할 항목: AllowQos=normal,gpu (ALL이 아님), MaxTime=08:00:00, DefaultTime=01:00:00. AllowQos=ALL이나 MaxTime=UNLIMITED이 여전히 보이면 아래 트러블슈팅을 참고하십시오.\n8. 트러블슈팅 # scontrol show partition gpu에서 AllowQos=ALL이 계속 표시되는 경우\n먼저 arbiter의 slurm.conf에 PartitionName=gpu 줄이 실제로 업데이트됐는지 확인합니다:\n[wpaik@arbiter ~]$ grep \u0026#34;PartitionName=gpu\u0026#34; /etc/slurm/slurm.conf AllowQos=normal,gpu가 포함되어 있어야 합니다. 포함되어 있다면 파일이 모든 노드에 배포됐는지 확인합니다:\n[wpaik@arbiter ~]$ ansible all_nodes -b -m shell \\ -a \u0026#34;grep \u0026#39;PartitionName=gpu\u0026#39; /etc/slurm/slurm.conf\u0026#34; 노드 중 예전 버전을 가진 곳이 있으면 ansible copy를 다시 실행합니다. 그다음 재시작합니다:\n[wpaik@arbiter ~]$ sudo systemctl restart slurmctld [wpaik@arbiter ~]$ sudo scontrol reconfigure [wpaik@carrier ~]$ scontrol show partition gpu | grep AllowQos slurmctld 로그에 slurm.conf hash mismatch 경고\nslurmctld 재시작 후 다음과 같은 에러가 보일 수 있습니다:\nerror: Node interceptor-01 appears to have a different slurm.conf than the slurmctld. 파일 내용이 동일해도 나오는 에러입니다. slurmctld는 재시작하면서 새 파일의 hash를 계산했는데, 컴퓨트 노드의 slurmd는 아직 예전 hash를 들고 있어서 생기는 불일치입니다.\n[wpaik@arbiter ~]$ sudo scontrol reconfigure 이 명령이 모든 slurmd 데몬에 config 재읽기 신호를 보냅니다. 이후 로그에 해당 경고가 사라지면 정상입니다.\nPriorityType 추가 후 slurmctld 시작 실패\n먼저 컨트롤러 로그를 확인합니다:\n[wpaik@arbiter ~]$ tail -n 50 /var/log/slurm/slurmctld.log 가장 흔한 원인은 slurmctld가 시작될 때 slurmdbd가 준비가 안 된 경우입니다. PriorityType=priority/multifactor는 시작 시 accounting 데이터베이스 접근이 필요합니다.\n[wpaik@arbiter ~]$ sudo systemctl status slurmdbd [wpaik@arbiter ~]$ sudo systemctl restart slurmdbd [wpaik@arbiter ~]$ sudo systemctl restart slurmctld slurmdbd를 먼저 시작하고 그 다음 slurmctld를 시작해야 합니다.\nsacctmgr -i add account가 \u0026ldquo;already exists\u0026rdquo; 에러 반환\nsetup 스크립트는 idempotent하지 않습니다. 일부를 이미 실행했다면 account가 이미 있을 수 있습니다.\n[wpaik@arbiter ~]$ sacctmgr show account account가 이미 있으면 add 명령은 건너뛰고 modify를 사용합니다. fairshare 값이 잘못됐다면:\n[wpaik@arbiter ~]$ sudo sacctmgr -i modify account name=research set fairshare=80 sshare가 모두 0이거나 데이터가 없음\nPriorityType=priority/multifactor가 아직 활성화되지 않은 상태입니다. slurm.conf 변경이 적용되지 않았거나, 변경 후 slurmctld를 재시작하지 않은 경우입니다.\n# 설정이 실제로 적용됐는지 확인 [wpaik@arbiter ~]$ scontrol show config | grep PriorityType 아직 basic으로 나오면 slurmctld를 재시작하고 다시 확인합니다.\n작업 제출 시 \u0026ldquo;Invalid qos specification\u0026rdquo; 에러\n해당 유저의 account에 그 QOS가 허용되지 않은 상태입니다.\n[wpaik@arbiter ~]$ sacctmgr show associations format=account,user,qos where user=testuser2 QOS가 없으면 추가합니다:\n[wpaik@arbiter ~]$ sudo sacctmgr -i modify account name=demo set qos=normal,high 9. 다음 에피소드는? # 이제 클러스터에 제대로 된 멀티 유저 accounting 구조가 생겼습니다. 작업은 정해진 share 가중치를 가진 account 아래에서 실행되고, 많이 쓴 유저는 자동으로 우선순위가 낮아지고, 파티션에는 시간 제한이 생겨서 runaway 작업으로부터 다른 유저를 보호할 수 있습니다.\n다음 에피소드는 Lmod입니다. 클러스터에 Lmod 모듈 시스템을 설치하고 실제 소프트웨어 모듈을 구성합니다.\n이번 에피소드의 설정 파일과 sacctmgr setup 스크립트는 GitHub 저장소에 있습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 6월 7일","externalUrl":null,"permalink":"/ko/posts/hpc-from-scratch-06/","section":"포스트","summary":"Accounting이 없으면 Slurm은 사용량에 상관없이 모든 유저를 동일하게 취급합니다. HPC From Scratch 에피소드 6에서는 sacctmgr으로 account 계층 구조를 만들고, QOS로 작업 제한을 설정하고, 사용량 기반 페어 쉐어 스케줄링을 활성화합니다. 에피소드가 끝나면 클러스터가 실제 사용 내역을 기억하고 우선순위를 자동으로 조정합니다.","title":"[HPC From Scratch] 에피소드 6: Slurm Accounting, QOS, Fair Share 설정하기","type":"posts"},{"content":"","date":"2026년 6월 7일","externalUrl":null,"permalink":"/ko/tags/cluster/","section":"Tags","summary":"","title":"Cluster","type":"tags"},{"content":"","date":"2026년 6월 7일","externalUrl":null,"permalink":"/ko/tags/home-lab/","section":"Tags","summary":"","title":"Home Lab","type":"tags"},{"content":"","date":"2026년 6월 7일","externalUrl":null,"permalink":"/ko/series/hpc-from-scratch/","section":"시리즈","summary":"","title":"HPC From Scratch","type":"series"},{"content":"","date":"2026년 6월 7일","externalUrl":null,"permalink":"/ko/tags/linux/","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"2026년 6월 7일","externalUrl":null,"permalink":"/ko/tags/slurm/","section":"Tags","summary":"","title":"Slurm","type":"tags"},{"content":"","date":"25 5월 2026","externalUrl":null,"permalink":"/tags/ansible/","section":"Tags","summary":"","title":"Ansible","type":"tags"},{"content":"","date":"25 5월 2026","externalUrl":null,"permalink":"/tags/bash/","section":"Tags","summary":"","title":"Bash","type":"tags"},{"content":"A command-line utility for detecting and remediating RPM package inconsistencies across HPC cluster nodes. Built to address a real operational problem: nodes that silently diverge over time cause hard-to-diagnose job failures, and tracking down the cause by hand does not scale.\nWhat it does # The tool compares installed packages between a baseline node and one or more target nodes, separating results into three distinct categories:\nMissing packages are present in the baseline but absent on the target. A dnf install quick-fix command is included in the report.\nExtra packages exist on the target but not in the baseline. These are reported separately and left untouched by default, since they are often installed intentionally (GPU-specific tools, local debugging utilities).\nVersion mismatches are packages present on both sides but at different versions. Each mismatch includes an action field (upgrade or downgrade) derived from RPM\u0026rsquo;s own version comparison logic, so downstream automation knows exactly what to do.\nIn partition sweep mode, the tool queries Slurm for all active nodes, prompts for an interactive baseline selection, audits every target node, and writes per-node report files only for nodes with differences. A separate extras_summary.txt groups all extra packages by node across the full sweep.\nWhy it was built # Managing a multi-node HPC cluster means nodes drift. A one-off dnf install here, a skipped update there, and the environment across nodes is no longer consistent. The existing approach of SSHing into nodes individually and comparing rpm -qa output by hand does not work at scale and misidentifies version differences as missing packages. This tool was built to replace that workflow with something repeatable and automation-friendly.\nSample output # Partition sweep # [INFO] Fetching node list for partition: cpu Found 4 up node(s): compute-[01-04] SSH : ssh Format : text Select a baseline node: [ 1] compute-01 [ 2] compute-02 [ 3] compute-03 [ 4] compute-04 Enter node number or hostname: 1 [INFO] Starting Partition Sweep Partition : cpu Baseline : compute-01 Targets : 3 node(s) Parallel : 1 job(s) ====================================================== Summary: [OK] compute-02 [DIFF] compute-03 (2 issue(s)) [DIFF] compute-04 (1 issue(s)) ====================================================== Results: Clean : 1 / 4 Diffs : 2 / 4 Reports saved to: ./pkg_audit_reports/ ./pkg_audit_reports/audit_compute-03.txt ./pkg_audit_reports/audit_compute-04.txt Extras summary: ./pkg_audit_reports/extras_summary.txt ====================================================== Per-node report (text) # ====================================================== Package Audit Report Baseline : compute-01 Target : compute-03 Generated: Thu May 22 10:30:01 EDT 2026 ====================================================== [MISSING] 1 package(s) in baseline but NOT in compute-03: ------------------------------------------------------ nvtop (baseline: 3.3.1-2.el10_1) \u0026gt;\u0026gt; Quick Fix: ssh compute-03 \u0026#39;sudo dnf install -y nvtop\u0026#39; [VERSION MISMATCH] 1 package(s) with different versions: ------------------------------------------------------ curl baseline: 8.12.1-2.el10_1.2 target: 8.12.1-1.el10_1 action: upgrade ====================================================== Per-node report (JSON) # { \u0026#34;node\u0026#34;: \u0026#34;compute-03\u0026#34;, \u0026#34;baseline\u0026#34;: \u0026#34;compute-01\u0026#34;, \u0026#34;generated\u0026#34;: \u0026#34;2026-05-22T14:30:01Z\u0026#34;, \u0026#34;missing\u0026#34;: [ {\u0026#34;name\u0026#34;: \u0026#34;nvtop\u0026#34;, \u0026#34;baseline_ver\u0026#34;: \u0026#34;3.3.1-2.el10_1\u0026#34;, \u0026#34;action\u0026#34;: \u0026#34;install\u0026#34;} ], \u0026#34;extra\u0026#34;: [], \u0026#34;version_mismatch\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;curl\u0026#34;, \u0026#34;baseline_ver\u0026#34;: \u0026#34;8.12.1-2.el10_1.2\u0026#34;, \u0026#34;target_ver\u0026#34;: \u0026#34;8.12.1-1.el10_1\u0026#34;, \u0026#34;action\u0026#34;: \u0026#34;upgrade\u0026#34; } ] } Ansible integration # JSON output is structured for direct use with the included Ansible playbooks:\nremediate.yml reads each node\u0026rsquo;s JSON report and installs missing packages, upgrading or downgrading version mismatches as the action field specifies. remove_extra.yml is kept as a separate file to require a deliberate choice before removing anything. It supports --check dry runs and is designed to be reviewed against extras_summary.txt before execution. Technical details # Language: Bash 4+ Package query: rpm --queryformat for clean name/version separation Version comparison: python3-rpm for RPM-native version ordering Scheduler integration: Slurm (sinfo, scontrol) for partition sweep mode Output formats: text (human-readable), JSON (Ansible-ready), CSV (scripting/spreadsheet) Parallelism: GNU Parallel with xargs -P fallback; sequential by default for login node safety SSH: plain ssh by default, optional sudo mode via -s flag for clusters with restricted inter-node access Target platform: RPM-based Linux (Rocky Linux 9/10, RHEL, CentOS) Repository # github.com/willgpaik/pkg_audit\n","date":"25 5월 2026","externalUrl":null,"permalink":"/portfolio/pkg-audit/","section":"Portfolio","summary":"","title":"pkg_audit: Cluster Package Consistency Audit Tool","type":"portfolio"},{"content":"","date":"25 5월 2026","externalUrl":null,"permalink":"/tags/sysadmin/","section":"Tags","summary":"","title":"SysAdmin","type":"tags"},{"content":"스토리지와 인증 시스템이 갖춰졌습니다. 이제 클러스터에 두뇌를 달아줄 차례입니다.\n에피소드 4에서는 NFS 공유 스토리지, FreeIPA 중앙 집중식 인증, Ansible 클러스터 관리를 설정했습니다. 모든 노드가 동일한 홈 디렉토리를 공유하고 사용자 계정이 어디서든 작동합니다.\n하지만 지금 상태에서 작업을 실행하려면 컴퓨트 노드에 직접 SSH 접속해서 바로 실행해야 합니다. 한 명이 한 노드에서 쓸 때는 괜찮습니다. 두 사람이 같은 노드를 동시에 쓰거나, 여러 노드에 걸쳐 작업을 조율해야 할 때는 무너져요. 그게 작업 스케줄러가 해결하는 문제입니다.\n이번 에피소드에서는 Slurm을 다룹니다. 소스에서 빌드하는 이유, Munge가 노드 간 인증을 처리하는 방식, slurm.conf가 실제로 제어하는 것들, 그리고 첫 번째 실제 클러스터 작업 제출까지요.\n1. Slurm이 실제로 하는 일 # 작업 스케줄러 없는 공유 클러스터는 조율이 없는 주방과 같습니다. 모두가 원할 때 리소스를 가져갑니다. 한 사람의 작업이 다른 사람을 굶겨요. 두 노드를 동시에 요청하면서 동시에 둘 다 사용 가능하다는 보장을 받을 방법이 없습니다.\nSlurm은 HPC 101 시리즈에서 말한 프런트 데스크 직원입니다. 확장된 버전으로요. 모든 노드의 모든 CPU, 모든 기가바이트의 메모리, 모든 GPU를 추적합니다. 작업을 제출하면 Slurm이 요청한 리소스가 사용 가능할 때까지 큐에 넣고, 적절한 노드에 배정해서 실행합니다.\n필요한 세 가지 컴포넌트입니다.\nslurmctld는 관리 노드(arbiter)에서 실행됩니다. 컨트롤러입니다. 큐를 유지하고, 스케줄링 결정을 내리고, 컴퓨트 노드와 통신합니다.\nslurmd는 각 컴퓨트 노드에서 실행됩니다. 컨트롤러로부터 작업 배정을 받고, 실제 작업을 실행하고, 결과를 보고합니다.\nslurmdbd도 arbiter에서 실행됩니다. Slurm을 MariaDB 데이터베이스에 연결하고 모든 작업을 기록합니다. 누가 실행했는지, 얼마나 걸렸는지, CPU와 메모리를 얼마나 썼는지요. 이게 seff, sacct, 그리고 페어 쉐어(Fair Share) 스케줄링을 가능하게 합니다.\n2. 소스에서 빌드하는 이유 # 당연한 질문은 왜 dnf install slurm을 안 쓰냐는 겁니다. 두 가지 이유가 있습니다.\n버전 제어. 모든 노드에서 dnf upgrade를 실행하면 Slurm도 업그레이드됩니다. slurmctld와 slurmd 간 버전 불일치는 클러스터를 망가뜨려요. 컨트롤러와 컴퓨트 노드는 동일한 버전을 실행해야 합니다. 소스에서 빌드하고 RPM으로 배포하면 Slurm 업데이트 시점을 시스템의 나머지와 분리해서 직접 제어할 수 있습니다.\n기능 지원. Rocky Linux 10은 기본적으로 cgroup v2를 사용합니다. 오래된 Slurm 빌드는 cgroup v1을 기본으로 해서 작업 어카운팅과 메모리 추적이 소리 없이 실패합니다. 소스에서 빌드하면 --with cgroupv2를 명시적으로 전달할 수 있습니다. 마찬가지로 MPI 작업 실행을 위한 PMIx 지원도 표준 배포 패키지에 포함되지 않은 빌드 플래그가 필요합니다.\n빌드 프로세스는 관리 노드(arbiter)에서 Slurm을 컴파일하고 RPM으로 패키징한 다음 Ansible로 모든 노드에 배포합니다.\n# arbiter에서 빌드, Slurm 25.11.1 대상 rpmbuild -ta slurm-25.11.1.tar.bz2 \\ --define \u0026#34;_slurm_sysconfdir /etc/slurm\u0026#34; \\ --with cgroupv2 \\ --with pmix 런타임 의존성을 위한 EPEL # 빌드가 gtk2-devel을 개발 의존성으로 가져오면서 결과 slurm 기본 RPM이 GTK2 런타임 라이브러리(libgdk-x11-2.0.so.0, libgtk-x11-2.0.so.0)에 의존하게 됩니다. Rocky Linux 10의 기본 저장소에는 이 라이브러리가 없습니다. EPEL에 있거든요. 섹션 4의 설치 전에 모든 노드에서 EPEL이 활성화되어야 합니다. 안 그러면 dnf가 depsolve 오류로 로컬 RPM을 거부합니다.\n[wpaik@arbiter ansible]$ ansible all_nodes -b -m dnf -a \u0026#34;name=epel-release state=present\u0026#34; GTK2 의존성을 완전히 피하고 싶다면 rpmbuild에 --without gtk를 전달하면 sview가 빌드에서 제외됩니다. HPC 컴퓨트 노드에서는 어차피 sview를 실행하지 않으니 헤드리스 클러스터에서는 더 깔끔한 옵션입니다.\n모든 빌드 의존성, 전체 빌드 플레이북, RPM 배포 플레이북은 GitHub 저장소에 있습니다.\n3. Munge: 인증 레이어 # Slurm이 노드 간 통신을 하려면 메시지가 클러스터에서 실제로 오는 것인지 다른 곳이 아닌지 확인할 방법이 필요합니다. 그게 Munge의 역할입니다.\nMunge는 공유 비밀 키를 사용해서 암호화된 토큰을 생성합니다. 클러스터의 모든 노드가 /etc/munge/munge.key에 동일한 키를 가지고 있습니다. slurmctld가 slurmd에 메시지를 보낼 때 Munge 토큰을 첨부합니다. 컴퓨트 노드가 공유 키로 복호화하고 메시지가 합법적인지 확인합니다.\n키는 arbiter에서 한 번 생성되고 Ansible로 모든 노드에 배포됩니다.\n# arbiter에서 키 생성 dd if=/dev/urandom bs=1 count=1024 \u0026gt; /etc/munge/munge.key chmod 400 /etc/munge/munge.key chown munge:munge /etc/munge/munge.key 중요: Slurm UID는 모든 노드에서 일치해야 합니다.\nMunge는 키뿐만 아니라 토큰을 생성한 프로세스의 UID도 검증합니다. slurm 사용자가 arbiter에서 UID 386이고 interceptor-01에서 UID 990이면, Munge가 보안 위반 오류로 토큰을 거부합니다. 클러스터가 시작되는 것처럼 보이지만 작업이 절대 실행되지 않습니다.\nSlurm 설치 전에 모든 노드에서 Slurm 사용자의 UID를 1111로 고정합니다.\ngroupadd -g 1111 slurm useradd -u 1111 -g slurm -s /bin/bash -d /var/lib/slurm slurm 모든 노드의 UID가 일치하는지 확인합니다.\n[wpaik@arbiter ansible]$ ansible all_nodes -m shell -a \u0026#34;id slurm\u0026#34; -b arbiter.cluster.local | rc=0 \u0026gt;\u0026gt; uid=1111(slurm) gid=1111(slurm) groups=1111(slurm) interceptor-01.cluster.local | rc=0 \u0026gt;\u0026gt; uid=1111(slurm) gid=1111(slurm) groups=1111(slurm) interceptor-02.cluster.local | rc=0 \u0026gt;\u0026gt; uid=1111(slurm) gid=1111(slurm) groups=1111(slurm) corsair-01.cluster.local | rc=0 \u0026gt;\u0026gt; uid=1111(slurm) gid=1111(slurm) groups=1111(slurm) carrier.cluster.local | rc=0 \u0026gt;\u0026gt; uid=1111(slurm) gid=1111(slurm) groups=1111(slurm) 모두 일치합니다. Munge가 실행 중이고 공유 키가 작동하는지 확인합니다.\n# 로컬에서 Munge 인증 테스트 $ munge -n | unmunge # 노드 간 테스트 $ munge -n | ssh interceptor-01.cluster.local unmunge STATUS: Success (0) ENCODE_HOST: arbiter.cluster.local (192.168.50.50) DECODE_HOST: interceptor-01.cluster.local (192.168.50.15) MUNGE_UID: slurm (1111) 방화벽 참고: 워커 노드는 firewalld가 비활성화되어 있습니다. 로그인 노드(carrier)는 내부 인터페이스가 trusted 존에 있습니다. 컴퓨트 노드에서 firewalld를 실행 중이라면 포트 6817(slurmctld), 6818(slurmd), 6819(slurmdbd)를 엽니다.\n4. Slurm 설치 # arbiter에서 RPM을 빌드한 후 Ansible이 클러스터 전체에 배포하고 설치합니다. 각 노드는 역할에 따라 다른 패키지 세트를 받습니다.\n노드 유형 패키지 관리 노드 (arbiter) slurm, slurmctld, slurmdbd, mariadb 컴퓨트 노드 (interceptor, corsair) slurm, slurmd, slurm-libpmi 로그인 노드 (carrier) slurm, slurm-contribs (seff 포함) 컴퓨트 노드의 slurm-libpmi는 MPI 구현체가 srun으로 병렬 프로세스를 실행할 때 사용하는 PMI2와 PMIx 라이브러리를 제공합니다. 없으면 MPI 작업이 srun을 런처로 쓸 때 PMI 버전 오류로 실패합니다.\n로그인 노드의 slurm-contribs에는 작업 효율성 도구인 seff가 포함됩니다. slurmdbd의 어카운팅 데이터를 읽어서 작업이 요청한 것 대비 실제로 CPU와 메모리를 얼마나 썼는지 정확하게 보여줍니다.\n설치 플레이북이 전제하는 두 가지 조건이 있습니다. 모든 노드에서 EPEL이 활성화되어 있어야 하고 (섹션 2), Ansible 컨트롤러의 remote_tmp가 대상 노드의 로컬 경로를 가리켜야 합니다 (에피소드 4의 ansible.cfg 설정). 두 번째가 중요한 이유는, 설치가 Ansible의 임시 디렉토리를 통해 RPM을 복사하기 때문입니다. 그 디렉토리가 NFS 위에 있으면 (이 클러스터에서는 /home이 NFS 마운트되어 있어서 기본값이 NFS 위입니다) RPM이 nfs_t SELinux 컨텍스트를 상속받아서, 파일이 디스크에 분명히 있는데도 dnf가 No match for argument 오류로 거부합니다. ansible.cfg의 remote_tmp = /var/tmp/.ansible-${USER}/tmp 줄이 임시 영역을 로컬 디스크에 두어 이 함정을 피합니다.\n설치가 성공적으로 완료된 후 dnf에서 Slurm 버전을 고정해서 dnf upgrade가 다른 빌드를 가져오지 못하도록 합니다. 설치 플레이북의 마지막 단계가 이걸 처리합니다.\nansible all_nodes -b -m shell -a \u0026#34;echo \u0026#39;exclude=slurm*\u0026#39; \u0026gt;\u0026gt; /etc/dnf/dnf.conf\u0026#34; # 확인 ansible all_nodes -b -m shell -a \u0026#34;grep slurm /etc/dnf/dnf.conf\u0026#34; 순서가 중요합니다. 설치 성공 후에 고정하고, 절대 그 전에 하면 안 됩니다. 설치 전에 고정하면 dnf가 slurm 설치 자체를 거부합니다. 이것도 No match for argument 오류로요. Slurm을 업그레이드해야 할 때는 먼저 그 줄을 제거하고, 재빌드, 재설치한 다음 플레이북이 마지막에 고정을 다시 추가합니다.\n전체 설치 플레이북은 GitHub 저장소의 ep05-slurm/playbooks/에 있습니다.\n5. Slurm 설정 # 모든 Slurm 설정은 모든 노드의 /etc/slurm/slurm.conf에 있습니다. 파일은 클러스터 전체에서 동일해야 합니다. arbiter에서 생성하고 Ansible로 배포합니다.\n이 클러스터의 전체 slurm.conf입니다.\n# 클러스터 아이덴티티 ClusterName=cluster SlurmctldHost=arbiter SlurmUser=slurm AuthType=auth/munge # 스케줄링 SchedulerType=sched/backfill SelectType=select/cons_tres SelectTypeParameters=CR_Core_Memory # 로깅 SlurmctldDebug=info SlurmctldLogFile=/var/log/slurm/slurmctld.log SlurmdDebug=debug SlurmdLogFile=/var/log/slurm/slurmd.log # 상태 및 PID 파일 StateSaveLocation=/var/spool/slurmctld SlurmdSpoolDir=/var/spool/slurmd SlurmctldPidFile=/run/slurm/slurmctld.pid SlurmdPidFile=/run/slurm/slurmd.pid # Cgroup (v2) ProctrackType=proctrack/cgroup TaskPlugin=task/cgroup,task/affinity # 작업 어카운팅 JobAcctGatherType=jobacct_gather/cgroup JobAcctGatherFrequency=30 AccountingStorageType=accounting_storage/slurmdbd AccountingStorageHost=arbiter.cluster.local AccountingStoragePort=6819 JobCompType=jobcomp/none AccountingStorageTRES=gres/gpu AccountingStoreFlags=job_comment,job_env,job_script # GPU 지원 ReturnToService=1 GresTypes=gpu # MPI 기본값 MpiDefault=pmix # 노드 NodeName=interceptor-01 CPUs=8 Sockets=1 CoresPerSocket=4 ThreadsPerCore=2 RealMemory=15413 State=UNKNOWN NodeName=interceptor-02 CPUs=8 Sockets=1 CoresPerSocket=4 ThreadsPerCore=2 RealMemory=15413 State=UNKNOWN NodeName=corsair-01 CPUs=16 Sockets=1 CoresPerSocket=8 ThreadsPerCore=2 RealMemory=30802 Gres=gpu:nvidia_geforce_gtx_1660_super:1 State=UNKNOWN # 파티션 PartitionName=cpu Nodes=interceptor-01,interceptor-02 Default=YES MaxTime=INFINITE State=UP PartitionName=gpu Nodes=corsair-01 Default=NO MaxTime=INFINITE State=UP 몇 가지 짚어볼 점입니다.\nRealMemory 값은 에피소드 2의 iGPU 메모리 함정에서와 마찬가지로 각 노드에서 free -m을 실행해서 구합니다. 여기 있는 값들은 하드웨어 예약 후 OS가 실제로 보고하는 값을 반영합니다. 설치된 RAM 숫자를 쓰면 안 됩니다.\nMpiDefault=pmix는 srun의 기본 MPI 프로세스 관리 인터페이스를 PMIx로 설정합니다. 없으면 srun이 기본적으로 PMI2를 쓰는데, 병렬 작업 실행 시 OpenMPI와 호환성 오류가 생겨요. MPI 작업이 멈추거나 PMI 버전 오류로 실패하면 여기를 먼저 확인합니다.\nSelectTypeParameters=CR_Core_Memory는 리소스 할당 시 코어와 메모리를 모두 추적하라고 Slurm에 알려줍니다. seff가 메모리 사용량을 정확하게 보고하려면 필요합니다.\ncgroup 설정은 별도 파일에 있습니다.\n# /etc/slurm/cgroup.conf ConstrainCores=yes ConstrainRAMSpace=yes ConstrainSwapSpace=no ConstrainDevices=yes ConstrainCores와 ConstrainRAMSpace는 작업 스크립트에서 요청한 리소스 한도를 강제합니다. 작업이 요청한 것보다 많은 메모리를 쓰려 하면 Slurm이 조용히 소비하게 두는 대신 메모리 부족 오류로 종료합니다. cgroup v2가 필요한데, 이 클러스터에서 확인합니다.\n$ stat -fc %T /sys/fs/cgroup cgroup2fs MariaDB와 slurmdbd는 어카운팅 데이터를 저장합니다. slurm_acct_db 데이터베이스와 slurm 데이터베이스 사용자를 만들고, slurmdbd가 연결하도록 설정합니다. /etc/slurm/slurmdbd.conf는 모드 600이고 slurm 사용자 소유여야 합니다. 안 그러면 slurmdbd가 시작을 거부합니다.\n6. 컴퓨트 노드에서 스왑 비활성화 # Slurm 작업 실행 전에 컴퓨트 노드에서 스왑을 비활성화해야 합니다. cgroup.conf에 ConstrainRAMSpace=yes가 설정되면 Slurm이 cgroup으로 메모리 한도를 강제합니다. 스왑이 활성화되면 RAM 한도에 걸린 프로세스가 종료되는 대신 스왑으로 넘어갈 수 있어서 메모리 제약이 무력화되고 seff 메모리 보고가 부정확해져요.\n로그인 노드(carrier)와 관리 노드(arbiter)는 컴퓨트 작업을 실행하지 않으므로 스왑을 유지해도 됩니다.\nsystemd를 통해 컴퓨트 노드에서 영구적으로 스왑 비활성화:\nansible workers,gpu -b -m systemd \\ -a \u0026#34;name=swap.target state=stopped enabled=no\u0026#34; 다음 재부팅 후 확인:\n$ cat /proc/swaps Filename Type Size Used Priority # 출력이 없으면 스왑이 꺼진 상태입니다 참고: 스왑 UUID가 /etc/fstab에 여전히 있을 수 있습니다. systemd에서 swap.target이 비활성화되어 있으면 괜찮습니다. 부팅 시 dependency 오류로 유닛이 활성화에 실패하는데, 이게 예상된 동작입니다.\n7. 클러스터 시작 # 서비스는 순서대로 시작해야 합니다. slurmctld가 연결을 시도하기 전에 slurmdbd가 실행 중이어야 합니다.\n# arbiter에서 $ sudo systemctl start mariadb $ sudo systemctl start slurmdbd $ sudo systemctl start slurmctld # 각 컴퓨트 노드에서 $ sudo systemctl start slurmd 서비스가 시작되면 어카운팅 데이터베이스를 초기화합니다.\n$ sacctmgr -i add cluster cluster $ sacctmgr -i add account root Description=\u0026#34;Root\u0026#34; Organization=\u0026#34;Cluster\u0026#34; $ sacctmgr -i add user wpaik Account=root 클러스터 상태 확인:\n[wpaik@carrier ~]$ sinfo PARTITION AVAIL TIMELIMIT NODES STATE NODELIST cpu* up infinite 2 idle interceptor-[01-02] gpu up infinite 1 idle corsair-01 모든 노드가 idle 상태로 준비됐습니다. 노드가 idle 대신 down이나 drain으로 나오면 재시작합니다.\n$ scontrol update NodeName=ALL State=RESUME 8. 첫 번째 작업 제출 # 인터랙티브 작업 # [wpaik@carrier ~]$ srun --pty bash [wpaik@interceptor-01 ~]$ hostname interceptor-01 [wpaik@interceptor-01 ~]$ exit srun이 기본 cpu 파티션의 첫 번째 노드인 interceptor-01에 배정했습니다.\n배치 작업 # 간단한 배치 스크립트를 만듭니다.\n#!/bin/bash #SBATCH --job-name=hello #SBATCH --partition=cpu #SBATCH --nodes=1 #SBATCH --ntasks=1 #SBATCH --mem=500M #SBATCH --time=00:05:00 #SBATCH --output=hello_%j.out echo \u0026#34;Running on: $(hostname)\u0026#34; echo \u0026#34;Job ID: $SLURM_JOB_ID\u0026#34; date sleep 10 echo \u0026#34;Done.\u0026#34; 제출 및 모니터링:\n$ sbatch hello.sh Submitted batch job 1 $ squeue JOBID PARTITION NAME USER ST TIME NODES NODELIST 1 cpu hello wpaik R 0:03 1 interceptor-01 $ cat hello_1.out Running on: interceptor-01 Job ID: 1 Fri May 9 21:00:00 EDT 2026 Done. 멀티노드 작업 # #!/bin/bash #SBATCH --job-name=multinode #SBATCH --partition=cpu #SBATCH --nodes=2 #SBATCH --ntasks-per-node=4 #SBATCH --mem-per-cpu=1G #SBATCH --output=multinode_%j.out srun hostname $ sbatch multinode.sh Submitted batch job 2 $ cat multinode_2.out interceptor-01 interceptor-01 interceptor-01 interceptor-01 interceptor-02 interceptor-02 interceptor-02 interceptor-02 두 개의 물리적 머신에 걸쳐 8개 태스크가 Slurm의 조율로 실행됐습니다.\nGPU 작업 # #!/bin/bash #SBATCH --job-name=gpu_test #SBATCH --partition=gpu #SBATCH --nodes=1 #SBATCH --gres=gpu:1 #SBATCH --output=gpu_%j.out nvidia-smi seff로 효율성 확인 # 작업이 완료되면 요청한 리소스를 얼마나 효율적으로 썼는지 확인합니다.\n$ seff 1 Job ID: 1 Cluster: cluster User/Group: wpaik/wpaik State: COMPLETED (exit code 0) Cores: 1 CPU Utilized: 00:00:01 CPU Efficiency: 10.00% of 00:00:10 core-walltime Job Wall-clock time: 00:00:10 Memory Utilized: 1.20 MB Memory Efficiency: 0.24% of 500.00 MB sleep 10이 아무것도 안 해서 CPU 효율성이 낮습니다. 500MB를 요청했지만 거의 안 써서 메모리 효율성도 낮고요. seff가 주는 피드백이 딱 이런 겁니다. 실제로 쓰는 것에 맞게 리소스 요청을 조정합니다.\n9. 일반적인 문제들 # 시작 후 노드가 down 또는 drain 상태에서 멈춤\n$ scontrol update NodeName=ALL State=RESUME 계속 down으로 돌아오면 해당 노드의 slurmd 로그를 확인합니다.\n$ ssh interceptor-01 \u0026#34;sudo tail -n 50 /var/log/slurm/slurmd.log\u0026#34; Slurm UID 불일치 (보안 위반)\nsrun이 멈추거나 로그에 인증 오류가 보이면 모든 노드의 slurm 사용자 UID가 같은지 확인합니다.\n$ ansible all_nodes -m shell -a \u0026#34;id slurm\u0026#34; -b UID가 다르면 GitHub 저장소의 08_sync_slurm_uid.yaml로 수정합니다.\nMPI 작업이 PMI 오류로 실패\nslurm.conf에 MpiDefault=pmix가 있는지, 컴퓨트 노드에 slurm-libpmi가 설치되어 있는지 확인합니다.\n$ cat /etc/profile.d/pmix.sh export PMIX_MCA_psec=native slurmdbd가 시작에 실패\n/etc/slurm/slurmdbd.conf 권한을 확인합니다. 모드 600이고 slurm 사용자 소유여야 합니다.\n$ ls -la /etc/slurm/slurmdbd.conf -rw------- 1 slurm slurm 312 Apr 27 09:00 /etc/slurm/slurmdbd.conf slurmdbd 시작 전에 MariaDB가 실행 중인지도 확인합니다.\nseff가 메모리 데이터를 표시하지 않음\nslurm.conf에 JobAcctGatherType=jobacct_gather/cgroup이 있고 cgroup.conf에 ConstrainRAMSpace=yes가 있어야 합니다. 둘 다 cgroup v2가 필요합니다. stat -fc %T /sys/fs/cgroup으로 확인합니다.\n파일이 디스크에 있는데 dnf install이 No match for argument로 실패\n두 가지 원인이 있는데 모두 같은 오류로 나옵니다.\nNFS에서 상속된 SELinux 컨텍스트. Ansible의 임시 디렉토리가 기본적으로 ~/.ansible/tmp/이고, 이 클러스터에서는 NFS 마운트된 /home에 있습니다. 여기를 통해 복사된 파일이 nfs_t SELinux 컨텍스트를 받아서 dnf가 로컬 RPM으로 조용히 처리를 거부합니다. ls -lZ /tmp/slurm_rpms/로 확인합니다. 컨텍스트가 nfs_t면 이 문제입니다. 영구 수정은 에피소드 4의 ansible.cfg에 있는 remote_tmp = /var/tmp/.ansible-${USER}/tmp 줄입니다. 즉각적인 해결책:\nsudo restorecon -Rv /tmp/slurm_rpms/ 설치 전에 dnf exclude 고정이 추가된 경우. /etc/dnf/dnf.conf에 이미 exclude=slurm*이 있으면 dnf가 해당 인수를 제거하고 없다고 보고합니다. grep slurm /etc/dnf/dnf.conf로 확인합니다. 재설치의 경우 그 줄을 먼저 지우거나 --disableexcludes=all을 전달합니다.\nsudo dnf install -y --disableexcludes=all /tmp/slurm_rpms/slurm-*.rpm dnf install이 nothing provides libgdk-x11-2.0.so.0 오류로 실패\n실패한 노드에서 EPEL이 활성화되지 않은 겁니다. EPEL을 설치하고 재시도합니다.\n```bash sudo dnf install -y epel-release ``` 또는 --without gtk로 Slurm을 재빌드해서 GTK2 의존성을 완전히 제거합니다.\n10. 다음 에피소드 # 클러스터가 이제 진짜 HPC 시스템입니다. 작업이 스케줄링되고, 리소스가 추적되고, seff가 각 실행 후 효율성 데이터를 보여줍니다.\n다음 에피소드에서는 Slurm 어카운팅을 심도 있게 다룹니다. slurmdbd에서 계정과 사용자 설정, 리소스 한도가 있는 파티션 설정, 그리고 무거운 사용자가 클러스터를 독점하지 못하도록 하는 페어 쉐어(Fair Share) 스케줄링입니다.\n이 에피소드의 모든 Ansible 플레이북, 설정 파일, Slurm 빌드 스크립트는 GitHub 저장소에 있습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 5월 20일","externalUrl":null,"permalink":"/ko/posts/hpc-from-scratch-05/","section":"포스트","summary":"단계별로: 홈 클러스터에서 Slurm과 MUNGE를 소스에서 빌드하기. 에피소드 5에서는 slurmctld, slurmd, 파티션 설정, 그리고 첫 번째 실제 배치 작업 제출을 다룹니다.","title":"[HPC From Scratch] 에피소드 5: Rocky Linux에서 Slurm 소스 컴파일 설치하기","type":"posts"},{"content":"","date":"2026년 5월 20일","externalUrl":null,"permalink":"/ko/tags/munge/","section":"Tags","summary":"","title":"Munge","type":"tags"},{"content":"하나의 드라이브. 하나의 로그인. 모든 노드가 동일한 홈 디렉토리를 공유합니다.\n에피소드 3에서는 네트워크를 설정하고, 6개 노드에 Rocky Linux를 설치했으며, DHCP와 NAT를 구성하고, SSH를 강화했습니다. 클러스터가 네트워크에 연결되고 보안이 갖춰졌습니다. 이제 Slurm을 쓰기 전에 두 가지가 더 필요합니다. 공유 스토리지와 중앙 집중식 인증입니다.\n이 두 가지가 없으면 모든 노드에 파일을 수동으로 복사하고, 동일한 사용자 계정을 6번 만들어야 합니다. 이번 에피소드에서 그 두 문제를 모두 해결합니다.\n1. 공유 스토리지가 왜 중요할까요? # NFS 없이는 두 노드에 걸친 MPI 작업을 제출할 때 입력 데이터가 두 노드에 모두 있어야 합니다. 수동으로 복사하거나 동기화 스크립트를 짜야 합니다. 어느 쪽도 지속 가능하지 않습니다.\nNFS가 있으면 arbiter(관리 노드)에 있는 Samsung 990 Pro가 하나의 /home 디렉토리를 내보냅니다. 클러스터의 모든 노드가 그걸 마운트합니다. 로그인 노드에서 스크립트를 작성하고 어느 컴퓨트 노드에서든 실행하면 됩니다. 파일은 이미 거기 있습니다.\nSlurm에서도 중요합니다. 작업이 출력 파일을 쓰면 NFS 공유의 /home에 저장됩니다. 결과를 가져오기 위해 컴퓨트 노드에 SSH로 들어갈 필요가 없습니다.\n사전 조건\n이 에피소드를 시작하기 전에:\n모든 노드에 Rocky Linux 10이 설치되고 네트워크가 구성되어야 합니다 (에피소드 3) arbiter에 Samsung 990 Pro NVMe 드라이브가 설치되어야 합니다 (에피소드 2) arbiter에서 다른 모든 노드로 SSH 키 기반 로그인이 작동해야 합니다 2. Ansible 설정 # 이 에피소드부터 Ansible로 모든 노드에 동시에 설정을 적용합니다. Ansible 없이는 변경할 때마다 6개 머신에 개별적으로 SSH 접속해야 합니다.\nAnsible은 arbiter에서 실행합니다. NFS 공유가 아닌 /opt/ansible에 두는데, Ansible 설정 파일에는 SSH 키와 vault 패스워드가 담겨 있어서 클러스터의 모든 노드에 노출되면 안 됩니다.\nAnsible 설치 # [wpaik@arbiter ~]$ sudo dnf install ansible-core [wpaik@arbiter ~]$ sudo mkdir -p /opt/ansible [wpaik@arbiter ~]$ sudo chown wpaik:wpaik /opt/ansible [wpaik@arbiter ~]$ cd /opt/ansible SSH 키 # Ansible 전용 키를 생성하고 모든 노드에 배포합니다.\n[wpaik@arbiter ansible]$ mkdir .ssh [wpaik@arbiter ansible]$ ssh-keygen -t ed25519 -f .ssh/worker_ed25519 -N \u0026#34;\u0026#34; [wpaik@arbiter ansible]$ for node in 192.168.50.1 192.168.50.15 192.168.50.32 192.168.50.11 192.168.50.19; do ssh-copy-id -i .ssh/worker_ed25519.pub wpaik@$node done 인벤토리와 설정 # hosts.ini를 만듭니다.\n[head] carrier.cluster.local ansible_host=192.168.50.1 [management] arbiter.cluster.local ansible_host=192.168.50.50 ansible_connection=local [workers] interceptor-01.cluster.local ansible_host=192.168.50.15 interceptor-02.cluster.local ansible_host=192.168.50.32 [gpu] corsair-01.cluster.local ansible_host=192.168.50.11 [visualization] observer.cluster.local ansible_host=192.168.50.19 [compute:children] workers gpu [all_nodes:children] head management workers gpu visualization [all_nodes:vars] ansible_user=wpaik cluster_network=192.168.50.0/24 cluster_domain=cluster.local cluster_realm=CLUSTER.LOCAL arbiter는 Ansible 컨트롤러 자체이므로 ansible_connection=local을 사용합니다.\nansible.cfg를 만듭니다.\n[defaults] private_key_file = /opt/ansible/.ssh/worker_ed25519 inventory = ./hosts.ini host_key_checking = False log_path = ./log/ansible.log vault_password_file = /opt/ansible/.ansible_vault_pw remote_tmp = /var/tmp/.ansible-${USER}/tmp 마지막 줄 remote_tmp는 나중에야 문제가 드러나는 설정이라 따로 설명이 필요합니다. 기본적으로 Ansible은 원격 노드의 ~/.ansible/tmp/에 작업별 임시 파일을 씁니다. 섹션 3에서 NFS를 설정하면 모든 노드의 /home이 NFS 공유에 있게 되므로, 그 임시 디렉토리도 NFS 위에 올라갑니다. 거기에 쓰인 파일은 SELinux의 nfs_t 컨텍스트를 받는데, dnf는 나중 에피소드에서 로컬 RPM을 설치할 때 이를 거부합니다. 디스크에 명백히 존재하는 RPM 파일에 대해 dnf가 No match for argument라고 보고하기 때문에 실패가 혼란스럽습니다. remote_tmp를 각 노드의 로컬 경로(/var/tmp는 항상 로컬)로 고정하면 이 문제를 미리 피할 수 있습니다.\n연결 확인:\n[wpaik@arbiter ansible]$ ansible all -m ping carrier.cluster.local | SUCCESS =\u0026gt; { \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } arbiter.cluster.local | SUCCESS =\u0026gt; { \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } interceptor-01.cluster.local | SUCCESS =\u0026gt; { \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } interceptor-02.cluster.local | SUCCESS =\u0026gt; { \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } corsair-01.cluster.local | SUCCESS =\u0026gt; { \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } observer.cluster.local | SUCCESS =\u0026gt; { \u0026#34;ping\u0026#34;: \u0026#34;pong\u0026#34; } 6개 노드 모두 응답합니다. 이제부터 플레이북이 반복적인 작업을 처리합니다.\n3. NFS 서버 설정 # 이 섹션의 모든 명령어는 arbiter에서 실행합니다.\nLVM으로 NVMe 드라이브 파티셔닝 # 단일 대용량 파티션도 되지만, LVM을 쓰면 홈 디렉토리, 작업 스토리지, 공유 소프트웨어, 스크래치 공간에 별도 볼륨을 유연하게 할당할 수 있습니다. 실제 HPC 클러스터에서 스토리지를 구성하는 방식을 그대로 반영한 겁니다.\n먼저 NVMe 드라이브를 확인합니다.\n[wpaik@arbiter ~]$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 223.6G 0 disk ├─sda1 8:1 0 600M 0 part /boot/efi ├─sda2 8:2 0 1G 0 part /boot └─sda3 8:3 0 222G 0 part ├─rl-root 253:0 0 70G 0 lvm / └─rl-swap 253:1 0 7.7G 0 lvm [SWAP] nvme0n1 259:0 0 931.5G 0 disk SATA 부트 드라이브는 sda, NVMe는 nvme0n1입니다. 물리 볼륨, 볼륨 그룹, 논리 볼륨 4개를 만듭니다.\n# LVM 툴 설치 $ sudo dnf install -y lvm2 # 물리 볼륨과 볼륨 그룹 생성 $ sudo pvcreate /dev/nvme0n1 $ sudo vgcreate vg_nfs /dev/nvme0n1 # 논리 볼륨 생성 $ sudo lvcreate -L 167G -n lv_home vg_nfs $ sudo lvcreate -L 251G -n lv_work vg_nfs $ sudo lvcreate -L 84G -n lv_shared vg_nfs $ sudo lvcreate -L 251G -n lv_scratch vg_nfs # XFS로 포맷 $ sudo mkfs.xfs /dev/vg_nfs/lv_home $ sudo mkfs.xfs /dev/vg_nfs/lv_work $ sudo mkfs.xfs /dev/vg_nfs/lv_shared $ sudo mkfs.xfs /dev/vg_nfs/lv_scratch 마운트 포인트 생성 및 마운트:\n$ sudo mkdir -p /nfsdata/{home,work,shared,scratch} $ sudo mount /dev/vg_nfs/lv_home /nfsdata/home $ sudo mount /dev/vg_nfs/lv_work /nfsdata/work $ sudo mount /dev/vg_nfs/lv_shared /nfsdata/shared $ sudo mount /dev/vg_nfs/lv_scratch /nfsdata/scratch 재부팅 후에도 유지되도록 /etc/fstab에 추가:\n$ echo \u0026#39;/dev/vg_nfs/lv_home /nfsdata/home xfs defaults 0 0\u0026#39; | sudo tee -a /etc/fstab $ echo \u0026#39;/dev/vg_nfs/lv_work /nfsdata/work xfs defaults 0 0\u0026#39; | sudo tee -a /etc/fstab $ echo \u0026#39;/dev/vg_nfs/lv_shared /nfsdata/shared xfs defaults 0 0\u0026#39; | sudo tee -a /etc/fstab $ echo \u0026#39;/dev/vg_nfs/lv_scratch /nfsdata/scratch xfs defaults 0 0\u0026#39; | sudo tee -a /etc/fstab arbiter 자체도 NFS 스토리지를 사용하도록 /nfsdata/home을 /home에 바인드 마운트합니다.\n$ echo \u0026#39;/nfsdata/home /home none bind 0 0\u0026#39; | sudo tee -a /etc/fstab $ sudo mount -a 최종 레이아웃 확인:\n[wpaik@arbiter ~]$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 223.6G 0 disk ... nvme0n1 259:0 0 931.5G 0 disk ├─vg_nfs-lv_home 253:2 0 167G 0 lvm /home │ /nfsdata/home ├─vg_nfs-lv_work 253:3 0 251G 0 lvm /nfsdata/work ├─vg_nfs-lv_shared 253:4 0 84G 0 lvm /nfsdata/shared └─vg_nfs-lv_scratch 253:5 0 251G 0 lvm /nfsdata/scratch 바인드 마운트 덕분에 lv_home이 두 번 나옵니다. /nfsdata/home(실제 마운트 포인트)과 /home(arbiter 자체가 쓰는 바인드 마운트)으로요. 나머지 세 볼륨은 arbiter의 /nfsdata 경로에만 마운트됩니다. 클라이언트 노드들은 NFS를 통해 /work, /shared, /scratch에 마운트할 겁니다.\nNFS 서버 설정 # $ sudo dnf install -y nfs-utils $ sudo systemctl enable --now nfs-server /etc/exports 설정:\n/nfsdata/home 192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check) /nfsdata/work 192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check) /nfsdata/shared 192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check) /nfsdata/scratch 192.168.50.0/24(rw,sync,no_root_squash,no_subtree_check) 옵션 설명: rw는 읽기/쓰기 허용, sync는 응답 전 디스크에 커밋 (더 안전), no_subtree_check는 서브디렉토리 내보낼 때 성능 패널티 회피, no_root_squash는 클라이언트 노드의 root가 공유에서도 root로 동작하게 해서 Slurm이 나중에 필요로 합니다.\nno_root_squash 참고: 신뢰할 수 있는 내부 클러스터 네트워크에 적합한 설정입니다. 이 클러스터는 192.168.50.x 서브넷에서 물리적으로 격리되어 있습니다. 신뢰할 수 없는 사용자가 있는 공유 클러스터에서는 root_squash를 사용합니다.\n적용 및 방화벽 열기:\n$ sudo exportfs -ra $ sudo firewall-cmd --permanent --add-service={nfs,rpc-bind,mountd} $ sudo firewall-cmd --reload # 확인 $ sudo showmount -e localhost Export list for localhost: /nfsdata/scratch 192.168.50.0/24 /nfsdata/shared 192.168.50.0/24 /nfsdata/work 192.168.50.0/24 /nfsdata/home 192.168.50.0/24 4. NFS 클라이언트 설정 # 각 노드에 수동으로 SSH 접속하는 대신 Ansible을 씁니다. arbiter의 /opt/ansible에서 실행합니다.\n[wpaik@arbiter ansible]$ ansible-playbook playbooks/nfs_setup.yaml -K 플레이북이 각 클라이언트 노드에서 하는 일: nfs-utils 설치, NFS 홈 디렉토리용 SELinux 부울 설정, /work, /shared, /scratch 마운트 포인트 생성, _netdev를 붙여 /etc/fstab에 NFS 마운트 4개 추가, 마운트 실행.\n_netdev 옵션은 네트워크가 준비될 때까지 마운트를 기다리라고 시스템에 알려줍니다. 없으면 arbiter보다 빠르게 부팅되는 노드가 마운트에 실패하고 부팅이 멈출 수 있습니다.\n재부팅 후 carrier에서 확인:\n[wpaik@carrier ~]$ df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rl-root 70G 5.4G 65G 8% / arbiter.cluster.local:/nfsdata/home 167G 8.2G 159G 5% /home arbiter.cluster.local:/nfsdata/work 251G 4.9G 247G 2% /work arbiter.cluster.local:/nfsdata/shared 84G 23G 62G 27% /shared arbiter.cluster.local:/nfsdata/scratch 251G 22G 230G 9% /scratch 참고: 플레이북이 워커 노드와 GPU 노드를 자동으로 재부팅합니다. carrier(헤드 노드)는 SSH 진입점이므로 플레이북 완료 후 수동으로 재부팅해야 합니다. carrier 재부팅 후 df -h로 마운트를 확인합니다.\n다음으로 넘어가기 전에 Chrony 플레이북을 실행해서 모든 노드의 시간을 동기화합니다.\n[wpaik@arbiter ansible]$ ansible-playbook playbooks/chrony_setup.yaml -K 공유가 잘 작동하는지 테스트합니다.\n# interceptor-01에서 테스트 파일 생성 [wpaik@interceptor-01 ~]$ touch /home/nfs_test.txt # interceptor-02에서 보이는지 확인 [wpaik@interceptor-02 ~]$ ls /home/nfs_test.txt /home/nfs_test.txt 파일 하나, 어디서든지 보입니다.\n5. 시간 동기화 (Chrony) # FreeIPA 설정 전에 모든 노드가 같은 시간 소스에 동기화되어야 합니다. FreeIPA는 Kerberos를 인증에 사용하는데, Kerberos는 노드 간 시계 차이가 5분을 넘으면 티켓을 거부합니다. 새 클러스터에서는 보통 괜찮지만, 명시적으로 설정해두는 게 낫습니다.\ncarrier가 클러스터의 NTP 서버 역할을 합니다. 외부 소스(time.cloudflare.com, pool.ntp.org)에서 동기화하고 모든 내부 노드에 시간을 제공합니다. 다른 노드들은 carrier에서 동기화합니다.\n[wpaik@arbiter ansible]$ ansible-playbook playbooks/chrony_setup.yaml -K 플레이북 완료 후 임의의 노드에서 동기화 상태 확인:\n$ chronyc tracking Reference ID : C0A83201 (carrier.cluster.local) Stratum : 3 System time : 0.000123456 seconds fast of NTP time Last offset : +0.000045678 seconds RMS offset : 0.000089012 seconds Reference ID가 carrier.cluster.local을 가리키면 그 노드가 carrier에서 동기화 중인 겁니다.\n6. 로컬 사용자의 문제 # NFS가 파일 공유 문제를 해결합니다. 하지만 새로운 문제를 만듭니다.\nNFS는 파일 권한을 처리할 때 사용자 이름이 아닌 UID(User ID)와 GID(Group ID) 번호를 씁니다. interceptor-01의 사용자 will이 UID 1001이고, interceptor-02의 will이 UID 1002라면 (계정을 다른 순서로 만들었기 때문에), 같은 NFS 파일에서 다른 권한을 보게 됩니다.\n# interceptor-01에서 $ id will uid=1001(will) gid=1001(will) # interceptor-02에서 $ id will uid=1002(will) gid=1002(will) # interceptor-01의 will(uid=1001)이 소유한 NFS 파일이 # interceptor-02에서는 다른 사용자 소유처럼 보입니다 모든 노드의 UID를 수동으로 동기화하는 방법이 있습니다. 6노드 클러스터에 사용자 몇 명이면 지루하지만 관리 가능합니다. 수백 명의 사용자가 있는 실제 클러스터에서는 불가능합니다.\n적절한 해결책은 **중앙 집중식 인증(Centralized Authentication)**입니다. 사용자 계정이 한 곳에서 정의되고, 모든 노드가 그 소스에서 가져오는 겁니다. FreeIPA가 바로 그 역할을 합니다.\n사전 준비: UID 정렬 # NFS는 사용자 이름을 비교하지 않습니다. 파일마다 찍힌 숫자 UID와 GID를 비교합니다. 진행하기 전에 확인합니다.\n[wpaik@arbiter ansible]$ ansible all_nodes -a \u0026#34;id wpaik\u0026#34; 모든 노드가 동일한 uid=와 gid=를 보고해야 합니다. 차이가 있으면 계속하기 전에 arbiter의 값(보통 1000, 하지만 확인 필요)을 기준으로 맞춰요.\n수정은 맞지 않는 노드에서 다른 sudo 계정이나 root로, wpaik의 활성 세션이 없는 상태에서 실행합니다. 아래 예시는 arbiter의 wpaik가 UID 1000이고 맞지 않는 노드가 현재 1001인 경우입니다.\n# 맞지 않는 노드에서 root 또는 다른 sudo 계정으로 [root@interceptor-01 ~]# who | grep wpaik # 활성 세션 없는지 확인 [root@interceptor-01 ~]# pkill -KILL -u wpaik # 남은 프로세스 종료 # NFS가 이미 마운트되어 있다면 먼저 언마운트 [root@interceptor-01 ~]# umount /home # 바쁘면 -l 사용 # 계정 번호 변경 [root@interceptor-01 ~]# groupmod -g 1000 wpaik [root@interceptor-01 ~]# usermod -u 1000 -g 1000 wpaik # 이전 UID로 된 파일의 소유권 수정 # -xdev는 find를 로컬 파일시스템에만 제한해서 다른 파티션과 NFS 마운트를 건드리지 않아요 [root@interceptor-01 ~]# find / -xdev -uid 1001 -exec chown -h 1000 {} + [root@interceptor-01 ~]# find / -xdev -gid 1001 -exec chgrp -h 1000 {} + # 확인 [root@interceptor-01 ~]# id wpaik uid=1000(wpaik) gid=1000(wpaik) groups=1000(wpaik),10(wheel) 이건 임시방편입니다. 섹션 7의 FreeIPA가 로컬 계정을 중앙 집중식 아이덴티티로 대체하면 이 문제는 사라져요.\n7. FreeIPA 서버 설치 # FreeIPA는 여러 서비스를 하나의 패키지로 묶습니다. LDAP(디렉토리), Kerberos(인증), DNS, 인증 기관입니다. 설치가 독립적이고 모든 것을 함께 설정합니다.\n이 섹션의 모든 명령어는 arbiter에서 실행합니다.\n사전 조건 # FreeIPA는 완전한 도메인 이름(FQDN)이 필요합니다. 진행하기 전에 올바르게 해석되는지 확인합니다.\n[wpaik@arbiter ~]$ hostname -f arbiter.cluster.local [wpaik@arbiter ~]$ ping -c 1 arbiter.cluster.local PING arbiter.cluster.local (192.168.50.50) 56(84) bytes of data. 사용 가능한 RAM이 최소 1.5GB인지도 확인합니다. 설치 프로그램이 메모리를 많이 씁니다.\n$ free -h total used free Mem: 15Gi 800Mi 14Gi 서버 설치 및 실행 # $ sudo dnf install -y freeipa-server freeipa-server-dns $ sudo ipa-server-install \\ --domain=cluster.local \\ --realm=CLUSTER.LOCAL \\ --ds-password=\u0026lt;your_directory_manager_password\u0026gt; \\ --admin-password=\u0026lt;your_admin_password\u0026gt; \\ --hostname=arbiter.cluster.local \\ --ip-address=192.168.50.50 \\ --no-ntp \\ --unattended 참고: --realm은 반드시 대문자, --no-ntp는 Chrony로 시간 동기화를 따로 관리하므로 NTP 설정을 건너뜀, --unattended는 대화형 프롬프트를 스킵합니다. 설치 프로그램이 5~10분 걸리고 LDAP, Kerberos, CA를 설정합니다.\n완료 후 필요한 방화벽 포트 열기:\n$ sudo firewall-cmd --permanent --add-service={freeipa-ldap,freeipa-ldaps,kerberos,dns,http,https} $ sudo firewall-cmd --reload 설치 확인 # $ kinit admin Password for admin@CLUSTER.LOCAL: $ klist Ticket cache: KCM:0 Default principal: admin@CLUSTER.LOCAL Valid starting Expires Service principal 04/27/26 09:00:00 04/28/26 09:00:00 krbtgt/CLUSTER.LOCAL@CLUSTER.LOCAL $ ipa user-find --------------- 0 users matched --------------- 아직 사용자가 없습니다. 등록 후에 추가할 겁니다.\n기본 셸을 bash로 설정합니다 (FreeIPA 기본값은 /bin/sh):\n$ ipa config-mod --defaultshell=/bin/bash 8. FreeIPA 클라이언트 등록 # 등록 전에 모든 노드의 /etc/hosts에 arbiter를 추가합니다. 등록 프로세스가 arbiter.cluster.local을 해석해야 하는데, 이 시점에서 SSSD가 아직 설정 안 됐습니다. 미리 해두면 DNS 해석 실패로 등록이 실패하는 걸 막을 수 있습니다.\nAnsible 플레이북이 자동으로 처리합니다.\n[wpaik@arbiter ansible]$ ansible-playbook playbooks/freeipa_setup.yaml -K 수동으로 각 노드에서 하려면:\n# /etc/hosts에 arbiter 추가 $ echo \u0026#34;192.168.50.50 arbiter.cluster.local arbiter\u0026#34; | sudo tee -a /etc/hosts # 설치 및 등록 $ sudo dnf install -y freeipa-client oddjob-mkhomedir $ sudo ipa-client-install \\ --server=arbiter.cluster.local \\ --domain=cluster.local \\ --realm=CLUSTER.LOCAL \\ --principal=admin \\ --password=\u0026lt;your_admin_password\u0026gt; \\ --mkhomedir \\ --no-ntp \\ --unattended --mkhomedir는 처음 로그인 시 홈 디렉토리를 생성하라고 시스템에 알려줍니다. /home이 arbiter에서 NFS 마운트되어 있으므로 디렉토리가 NFS 공유에 생성되고 모든 노드에서 즉시 보입니다.\n등록 후 각 노드가 IPA 서버에 연결할 수 있는지 확인합니다.\n[wpaik@interceptor-01 ~]$ ipa user-find --------------- 0 users matched --------------- 응답이 오면 (0명이라도) 클라이언트가 등록되어 서버와 통신하고 있는 겁니다.\n테스트 사용자 생성 # arbiter로 돌아와서:\n[wpaik@arbiter ~]$ kinit admin $ ipa user-add testuser \\ --first=Test \\ --last=User \\ --password $ ipa user-find testuser -------------- 1 user matched -------------- User login: testuser First name: Test Last name: User Home directory: /home/testuser Login shell: /bin/bash UID: 99100XXXX GID: 99100XXXX UID 범위를 보세요. FreeIPA가 로컬 시스템 계정 범위보다 훨씬 높은 UID를 할당합니다. 정확한 시작 범위는 설치 중 구성에 따라 다르지만, 할당된 값은 클러스터의 모든 노드에서 동일합니다.\n지속적인 사용자 관리를 위해 GitHub 저장소의 scripts/user_creation.sh 스크립트가 전체 프로세스를 처리합니다. FreeIPA 계정 생성, 올바른 NFS 소유권으로 홈 디렉토리 설정, XFS 쿼타, Slurm 어카운팅 항목까지요.\nFreeIPA 웹 UI 접근 # FreeIPA 웹 인터페이스는 VPN처럼 SSH를 통해 트래픽을 라우팅하는 sshuttle 도구를 사용해서 클러스터 외부에서 접근할 수 있습니다.\n로컬 머신에서:\n# sshuttle 설치 $ sudo dnf install sshuttle # Fedora/RHEL # 또는: pip install sshuttle # 로컬 /etc/hosts에 arbiter 추가 $ echo \u0026#34;192.168.50.50 arbiter arbiter.cluster.local\u0026#34; | sudo tee -a /etc/hosts # 터널 열기 (이 터미널을 계속 열어두세요) $ sshuttle -r wpaik@carrier 192.168.50.0/24 --dns 그런 다음 브라우저에서 https://arbiter.cluster.local/ipa/ui/를 엽니다. 자체 서명 인증서 경고를 수락하고 admin 계정으로 로그인합니다.\n9. 검증 # 로그인 노드에서 새 사용자로 컴퓨트 노드에 SSH 접속합니다.\n[wpaik@carrier ~]$ ssh testuser@interceptor-01 Password: Creating home directory for testuser. [testuser@interceptor-01 ~]$ pwd /home/testuser [testuser@interceptor-01 ~]$ id uid=99100XXXX(testuser) gid=99100XXXX(testuser) groups=99100XXXX(testuser) 이제 다른 노드에서 같은 사용자를 확인합니다.\n[testuser@interceptor-02 ~]$ id uid=99100XXXX(testuser) gid=99100XXXX(testuser) groups=99100XXXX(testuser) 두 노드에서 UID가 같습니다. interceptor-01에서 쓴 파일이 interceptor-02에서 올바른 권한으로 보입니다. 어느 노드에 접속해도 홈 디렉토리가 동일한 NFS 경로입니다.\n모든 노드에서 하나의 계정, 하나의 홈 디렉토리를 확인 할 수 있습니다.\n일반적인 문제 해결 # DNS 오류로 등록 실패: 플레이북이 등록 전에 /etc/hosts에 arbiter.cluster.local을 추가합니다. 여전히 실패하면 실패한 노드에 항목이 있는지 확인합니다.\n$ getent hosts arbiter.cluster.local 192.168.50.50 arbiter.cluster.local arbiter 없으면 수동으로 추가합니다.\n$ echo \u0026#34;192.168.50.50 arbiter.cluster.local arbiter\u0026#34; | sudo tee -a /etc/hosts FreeIPA 등록 후 NFS 마운트 실패: FreeIPA가 /etc/nsswitch.conf를 업데이트합니다. passwd와 group에서 files가 sss 앞에 오는지 확인합니다.\n$ grep -E \u0026#34;^(passwd|group)\u0026#34; /etc/nsswitch.conf passwd: sss files systemd group: sss files systemd 등록 후 NFS 마운트가 멈추면:\n$ sudo setsebool -P use_nfs_home_dirs 1 첫 로그인 시 홈 디렉토리가 생성되지 않음:\n$ sudo systemctl enable --now oddjobd NFS 설정 후 노드가 부팅 중 멈춤: GRUB의 오래된 resume=UUID가 부팅 멈춤을 유발할 수 있습니다. GRUB 메뉴에서 e를 눌러 resume=UUID=... 인수를 제거한 다음 Ctrl+X로 부팅합니다. 부팅 후:\n$ grubby --update-kernel=ALL --remove-args=\u0026#34;resume=UUID=\u0026lt;UUID\u0026gt;\u0026#34; 10. 다음 에피소드 # 클러스터에 공유 스토리지와 중앙 집중식 인증이 갖춰졌습니다. 모든 노드가 동일한 홈 디렉토리를 공유하고 모든 사용자가 모든 노드에서 일관된 아이덴티티를 가집니다.\n다음 에피소드에서는 작업 스케줄러인 Slurm을 설치합니다. NFS와 FreeIPA가 이미 갖춰져 있어서 Slurm은 노드 간 작업을 스케줄링하고 출력 파일을 공유 위치에 쓸 준비가 된 겁니다.\n이 에피소드의 모든 설정 파일과 Ansible 플레이북은 GitHub 저장소에 있습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 5월 5일","externalUrl":null,"permalink":"/ko/posts/hpc-from-scratch-04/","section":"포스트","summary":"노드마다 따로 사용자와 파일을 관리하는 데 지쳤나요? 에피소드 4에서는 NFS 공유 스토리지와 FreeIPA 중앙 인증을 설정해서 클러스터 전체가 하나의 시스템처럼 동작하게 만듭니다.","title":"[HPC From Scratch] 에피소드 4: NFS 스토리지 \u0026 FreeIPA: 하나의 드라이브, 하나의 로그인","type":"posts"},{"content":"","date":"2026년 5월 5일","externalUrl":null,"permalink":"/ko/tags/freeipa/","section":"Tags","summary":"","title":"FreeIPA","type":"tags"},{"content":"","date":"2026년 5월 5일","externalUrl":null,"permalink":"/ko/tags/nfs/","section":"Tags","summary":"","title":"NFS","type":"tags"},{"content":"Date: April 28, 2026 Venue: Northeastern University, Boston, MA\nOverview # A hands-on workshop for university researchers who want to scale computation beyond a single CPU core. This session walks through core parallel computing concepts, real benchmark results, and working code examples that can be run directly on the cluster.\nTopics Covered # Serial vs. parallel execution: pipelining and data parallelism Flynn\u0026rsquo;s Taxonomy: SISD, SIMD, MISD, MIMD Shared vs. distributed memory models and when to use each Amdahl\u0026rsquo;s Law, Gustafson\u0026rsquo;s Law, and strong vs. weak scaling CPU parallelism in practice: Conway\u0026rsquo;s Game of Life (serial, OpenMP, MPI+OpenMP) GPU computing fundamentals: CUDA workflow and memory model Scaling ML workloads with PyTorch: single GPU, multi-GPU, and multi-node DDP Parallel tools for Python, R, and MATLAB Mapping parallelism to Slurm: --ntasks vs. --cpus-per-task Materials # Workshop Slides \u0026amp; Materials (GitHub) Workshop Recordings (Spring 2026) ","date":"28 4월 2026","externalUrl":null,"permalink":"/talks/neu-talk-02/","section":"Talks \u0026 Workshops","summary":"","title":"Introduction to Parallel Computing","type":"talks"},{"content":"","date":"28 4월 2026","externalUrl":null,"permalink":"/tags/parallel-computing/","section":"Tags","summary":"","title":"Parallel Computing","type":"tags"},{"content":"","date":"28 4월 2026","externalUrl":null,"permalink":"/talks/","section":"Talks \u0026 Workshops","summary":"","title":"Talks \u0026 Workshops","type":"talks"},{"content":"","date":"28 4월 2026","externalUrl":null,"permalink":"/tags/workshop/","section":"Tags","summary":"","title":"Workshop","type":"tags"},{"content":"노트북 한 대, 홈 라우터, 기가비트 스위치. 격리된 클러스터 서브넷 하나.\n에피소드 2에서는 M715q 4대에 듀얼 채널 메모리와 NVMe 드라이브를 업그레이드하고, Slurm 작업을 망가뜨리는 iGPU 메모리 함정을 수정했습니다. 이번 에피소드에서는 클러스터를 온라인으로 올려요. Rocky Linux 설치, 네트워크 설계, 그리고 노트북을 내부 클러스터 서브넷의 DHCP 서버, NAT 게이트웨이, SSH 요새(bastion)로 만드는 작업입니다.\n1. 토폴로지 결정 # 실제 HPC에서는 관리망과 컴퓨트망이 엄격하게 유선으로 분리되고, 물리적으로 나뉘며, VLAN이 설정된 관리형 스위치에 연결됩니다. 엔터프라이즈급 관리형 스위치 하나가 이 클러스터 전체 비용보다 비쌀 수 있습니다.\n홈 빌드에서는 현실적인 경로가 두 가지입니다.\n평탄한 홈 네트워크. 모든 노드를 홈 라우터에 연결합니다. 쉽지만 모든 노드가 스마트폰, TV, IoT 기기와 같은 네트워크에 노출됩니다. 격리가 없고, 기기 하나가 뚫리면 클러스터 전체에 접근할 수 있습니다. 전용 스위치로 물리적 격리. 모든 클러스터 노드가 자체 서브넷 뒤의 비관리형 스위치에 연결됩니다. 로그인 노드가 두 세계를 연결합니다. 저는 2번을 선택했습니다. Netgear GS308E가 격리를 담당합니다. 로그인 노드는 경계에 위치해서 내부 클러스터 서브넷의 DHCP, DNS, NAT를 처리합니다. 워커 노드는 홈 네트워크를 직접 볼 일이 없습니다.\n결과는 실제 HPC와 동일한 패턴입니다. 로그인 노드가 가장자리에, 내부 패브릭이 그 뒤에, 컴퓨트 노드에는 외부에서 직접 접근 불가. 차이는 규모입니다. InfiniBand 대신 기가비트 이더넷, 스파인-리프 토폴로지 대신 비관리형 소비자 스위치. 아키텍처는 같고 규모의 차수만 다릅니다.\n참고: HP Envy GPU 노드(corsair-01)는 동일한 스위치에 연결되고 다른 노드와 동일한 기본 OS 및 네트워크 설정을 받습니다. 그 박스의 GPU 쪽은 이후 에피소드에서 설정할 겁니다.\n2. OS 설치 # 모든 노드는 Rocky Linux 10, 최소 설치입니다. NanoKVM으로 ISO를 마운트하고 브라우저로 인스톨러를 구동해서 기기를 하나씩 돌아가며 설치했습니다. 모니터와 키보드가 있다면 같은 방식으로 해도 됩니다.\n설치 자체는 별게 없습니다. ISO 부팅, 최소 설치 선택, 부트 드라이브 지정, 실행, 재부팅.\n설치 중에 미리 계획해둬야 할 두 가지가 있습니다.\n모든 노드에 sudo 사용자를 만드세요. 나중에 SSH로 접속할 계정입니다. root SSH는 비활성화할 거라, 이 계정 없이는 잠기게 됩니다.\n모든 노드에서 동일한 사용자 이름을 쓰세요. 나중에 ssh-copy-id를 실행할 때 기본적으로 로컬 사용자 이름이 사용되므로, 양쪽 사용자 이름이 일치하면 ssh-copy-id arbiter처럼 간단하게 됩니다. FreeIPA가 이후 에피소드에서 이 로컬 계정들을 중앙 집중식 아이덴티티로 대체하지만, 일관성이 있으면 전환이 더 매끄럽습니다.\n3. WiFi의 로그인 노드 # 로그인 노드인 carrier는 리퍼비시된 Lenovo IdeaPad 1 노트북입니다. WiFi와 이더넷 포트 하나가 있습니다. 대부분의 가이드는 로그인 노드를 유선 연결해야 한다고 하는데, 저는 의도적으로 WiFi를 썼습니다.\n외부 쪽에 왜 WiFi를 쓰나요? 로그인 노드는 패키지 업데이트, 데이터셋 다운로드, 외부에서 SSH 접속을 위해 인터넷이 필요합니다. 홈 라우터까지 이더넷 케이블을 연결해도 되지만, 클러스터 노드에 필요한 스위치 포트 하나를 낭비하고 방을 가로질러 케이블이 하나 더 생깁니다. WiFi는 로그인 노드가 실제로 필요하지 않은 대역폭을 희생해서 그 제약을 없애줍니다.\n내부 쪽에 왜 이더넷을 쓰나요? 모든 무거운 트래픽(NFS 읽기, MPI 메시지, 스케줄러 하트비트)은 전체 기가비트 속도로 유선 스위치에 머물러야 합니다. 로그인 노드의 이더넷 포트가 그 패브릭으로 들어가는 게이트웨이입니다.\n다른 것들이 작동하기 전에 노트북 특유의 세 가지 설정이 필요합니다.\n필수 패키지. 이 시리즈 전반에서 컴파일러, git, 에디터가 필요합니다.\nsudo dnf upgrade -y sudo dnf install -y epel-release sudo dnf install -y vim git wget tree curl gcc-c++ cmake m4 덮개 닫힘 수정. 기본적으로 노트북 덮개를 닫으면 systemd-logind가 머신을 절전 모드로 전환합니다. 로그인 노드에서는 재앙입니다. 덮개를 닫는 순간 클러스터 전체가 DHCP 서버, NAT 게이트웨이, SSH 진입점을 잃습니다. /usr/lib/systemd/logind.conf의 한 줄 변경으로 수정합니다.\nHandleLidSwitch=ignore sudo systemctl restart systemd-logind 후에는 노트북을 덮개 닫은 채로 클러스터 스택 위에 올려두어도 절전으로 빠지지 않습니다.\n라우팅 우선순위. 두 개의 활성 인터페이스(WiFi, 이더넷)가 있으면 Linux는 라우트 메트릭 기준으로 어느 쪽으로 인터넷 트래픽을 보낼지 결정합니다. 낮은 메트릭이 우선입니다. 기본적으로 유선 연결이 WiFi보다 낮은 메트릭을 가질 때가 많아서, 인터넷 트래픽이 홈 라우터로의 경로가 없는 클러스터 스위치로 나가게 될 수 있습니다. WiFi의 메트릭을 강제로 낮추는 게 해결책입니다.\nnmcli connection modify \u0026lt;WIFI NAME\u0026gt; ipv4.route-metric 10 nmcli connection down \u0026lt;WIFI NAME\u0026gt; \u0026amp;\u0026amp; nmcli connection up \u0026lt;WIFI NAME\u0026gt; 연결 이름은 nmcli connection show로 확인합니다. 이후 ip route show default에서 WiFi가 첫 번째 (기본) 경로로 표시되어야 합니다.\n4. DHCP: IP 주소 배포 # 워커 노드들은 IP 주소가 필요합니다. 홈 라우터에 연결이 없으니 홈 라우터의 DHCP가 워커 노드에 닿지 않습니다. 로그인 노드가 DHCP 서버 역할을 해야 합니다.\n먼저 로그인 노드의 클러스터 쪽에 고정 주소를 부여합니다. 워커들이 이걸 게이트웨이로 사용할 겁니다.\nnmcli connection modify \u0026lt;WIRED NAME\u0026gt; ipv4.addresses 192.168.50.1/24 ipv4.method manual nmcli connection up \u0026lt;WIRED NAME\u0026gt; 이제 dnsmasq를 설치하고 설정합니다. isc-dhcp-server 대신 선택한 이유는 가볍고, 단일 바이너리이고, DHCP와 DNS를 동시에 처리하기 때문입니다. 6노드 클러스터에서 이보다 더 복잡한 건 과도합니다.\nsudo dnf install -y dnsmasq sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak 새 /etc/dnsmasq.conf는 약 열 줄 정도입니다.\ninterface=\u0026lt;WIRED INTERFACE\u0026gt; dhcp-range=192.168.50.10,192.168.50.50,12h dhcp-option=3,192.168.50.1 dhcp-option=6,1.1.1.1,8.8.8.8 log-queries log-dhcp 인터페이스 이름은 nmcli device로 확인합니다. 각 줄이 하는 일입니다.\ninterface=는 dnsmasq를 유선 쪽에만 제한합니다. 없으면 dnsmasq가 WiFi에서도 DHCP 요청에 응답하려 해서 홈 라우터와 충돌합니다. dhcp-range=는 dnsmasq가 배포할 IP 풀과 임대 기간(12시간)을 정의합니다. dhcp-option=3,192.168.50.1은 로그인 노드를 기본 게이트웨이로 광고합니다. 워커들이 인터넷 목적지 트래픽을 어디로 보낼지 이걸로 배워요. dhcp-option=6,1.1.1.1,8.8.8.8은 워커들에게 사용할 DNS 서버를 알려줍니다 (Cloudflare와 Google을 공개 폴백으로). log-queries와 log-dhcp는 상세 로깅을 켭니다. 초기 구동 중에는 필수입니다. 클러스터가 안정화되면 꺼도 됩니다. 방화벽을 열고 서비스를 시작합니다.\nsudo firewall-cmd --permanent --add-service=dhcp sudo firewall-cmd --permanent --add-service=dns sudo firewall-cmd --reload sudo systemctl enable --now dnsmasq 팁: 워커 부팅 중 로그인 노드에서 journalctl -u dnsmasq -f를 실행하면 DHCP 핸드셰이크 전체(DHCPDISCOVER, DHCPOFFER, DHCPREQUEST, DHCPACK)가 실시간으로 보입니다. 워커가 주소를 못 받을 때 원인 진단에 매우 유용합니다.\n5. NAT: 워커가 인터넷에 접근하도록 # DHCP가 192.168.50.x 범위의 IP를 배포했습니다. 이것들은 RFC 1918에서 인터넷에서 라우팅 불가로 정의된 사설 주소입니다. 워커가 dnf.rocky.example.com에 패킷을 보내면 클러스터 스위치로 나갔다가 길을 잃고 사라집니다. 외부로 나가는 경로가 없거든요.\n해결책은 NAT(Network Address Translation, 네트워크 주소 변환)입니다. 로그인 노드가 모든 아웃바운드 패킷의 소스 주소를 자체 WiFi 쪽 IP로 다시 씁니다. 응답 패킷이 WiFi IP로 돌아오면 로그인 노드가 어느 내부 소스로 돌려보낼지 조회해서 포워딩합니다. 홈 라우터가 집 안의 모든 기기에 하는 것과 동일한 방식입니다.\n두 가지가 필요합니다.\nIP 포워딩. 기본적으로 Linux 머신은 인터페이스 간 패킷을 전달하지 않습니다. 명시적으로 허용해야 합니다.\nsudo sysctl -w net.ipv4.ip_forward=1 echo \u0026#34;net.ipv4.ip_forward = 1\u0026#34; | sudo tee /etc/sysctl.d/99-ipforward.conf 첫 번째 명령어가 즉시 포워딩을 활성화하고, 두 번째가 재부팅 후에도 유지되게 합니다.\n마스커레이드 규칙. 포워딩이 활성화되면 커널이 인터페이스 간 패킷을 라우팅하지만, 소스 주소는 다시 쓰지 않습니다. firewalld의 마스커레이드 규칙이 그 재작성을 하도록 커널에 지시합니다.\nsudo firewall-cmd --permanent --add-masquerade sudo firewall-cmd --reload 확인:\nsudo firewall-cmd --list-all | grep masquerade masquerade: yes가 표시되어야 합니다.\n워커를 온라인으로 올리고 테스트. 워커 노드를 켜세요. 로그인 노드에서 임대 파일을 확인합니다.\ncat /var/lib/dnsmasq/dnsmasq.leases 각 줄에 타임스탬프, MAC 주소, IP, 호스트명이 있습니다. 설치 중에 만든 sudo 계정으로 워커에 SSH 접속합니다.\nssh \u0026lt;user\u0026gt;@192.168.50.11 ping -c 3 1.1.1.1 ping이 되면 DHCP, 라우팅, NAT, DNS의 모든 요소가 제대로 작동하는 겁니다.\n6. IP 대신 호스트명 사용 # IP 주소를 매번 입력하는 건 금방 지치는 일입니다. 게다가 서브넷에 새 번호를 매기면 모든 스크립트, 설정 파일, 커밋 히스토리에 잘못된 주소가 하드코딩됩니다. 호스트명은 간접 참조입니다. 간접 참조는 저렴한 보험이고요.\n사용하는 이름입니다.\n호스트명 IP 역할 carrier 192.168.50.1 로그인 노드 arbiter 192.168.50.50 관리/NFS interceptor-01 192.168.50.15 컴퓨트 interceptor-02 192.168.50.32 컴퓨트 observer 192.168.50.19 시각화 corsair-01 192.168.50.11 GPU 로그인 노드를 포함한 각 노드에서:\nsudo hostnamectl set-hostname \u0026lt;HOSTNAME\u0026gt; 그런 다음 로그인 노드의 /etc/hosts에 모든 노드를 추가합니다.\n192.168.50.1 carrier.cluster.local carrier 192.168.50.15 interceptor-01.cluster.local interceptor-01 192.168.50.32 interceptor-02.cluster.local interceptor-02 192.168.50.11 corsair-01.cluster.local corsair-01 192.168.50.19 observer.cluster.local observer 192.168.50.50 arbiter.cluster.local arbiter 이제 ssh 192.168.50.50 대신 ssh arbiter가 가능합니다. 이건 임시방편입니다. 이후 에피소드에서 FreeIPA가 제대로 된 DNS 서버를 올려서 각 노드의 /etc/hosts를 수정하지 않아도 클러스터 전체에서 호스트명이 해석됩니다.\n7. 노출된 표면 강화 # 로그인 노드만 홈 WiFi에서 접근 가능합니다. 워커들은 자체 서브넷 뒤 NAT에 있어서 홈 네트워크에서 직접 접근할 수 없습니다. 강화 작업은 carrier에 집중합니다.\n중요한 세 가지입니다. SSH 설정 자체, 브루트포스 방지, 그리고 노트북 특유의 systemd 수정입니다.\nSSH 드롭인 설정. Rocky 10의 기본 /etc/ssh/sshd_config는 /etc/ssh/sshd_config.d/*.conf에서 파일을 포함하고, 동일한 설정이 여러 파일에 있으면 첫 번째 값이 우선합니다. 이건 드롭인 구성 시스템입니다. 메인 설정을 편집하지 않고, 변경할 것들만 담은 새 파일을 추가합니다.\n실질적으로 변경하는 건 root SSH 직접 로그인 비활성화 하나입니다.\nsudo tee /etc/ssh/sshd_config.d/99-custom.conf \u0026gt; /dev/null \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; PermitRootLogin no EOF sudo sshd -t # 문법 검증 sudo systemctl reload sshd 몇 가지 설정은 의도적으로 기본값을 유지합니다.\n공개 키 인증은 기본으로 활성화돼 있습니다. 설정 변경 없이 ssh-copy-id가 동작합니다. 패스워드 인증도 기본으로 활성화되어 있고 그대로 둬요. 대학 클러스터를 쓰던 HPC 사용자들은 패스워드 로그인에 익숙하고, 이후 에피소드의 FreeIPA가 어차피 중앙 집중식 인증으로 라우팅할 겁니다. fail2ban과 적절한 패스워드 정책의 조합으로 충분히 방어가 됩니다. 호스트 키는 HostKey 지시문이 없을 때 자동으로 로드됩니다. Rocky 10이 첫 부팅 시 RSA, ECDSA, Ed25519 호스트 키를 생성합니다. SSH 포트가 firewalld를 통과하는지 확인합니다.\nsudo firewall-cmd --permanent --add-service=ssh sudo firewall-cmd --reload fail2ban. 포트 22는 홈 네트워크에서도 브루트포스 시도를 받습니다. 같은 WiFi의 침해된 IoT 기기 하나로도 시작할 수 있습니다. fail2ban은 인증 로그를 감시해서 짧은 시간 안에 같은 IP에서 너무 많은 실패가 오면 그 IP의 트래픽을 차단하는 임시 방화벽 규칙을 추가합니다.\n업스트림 fail2ban 가이드에 따라 변경할 것들만 담은 짧은 jail.local을 작성합니다.\nsudo dnf install -y fail2ban sudo tee /etc/fail2ban/jail.local \u0026gt; /dev/null \u0026lt;\u0026lt;\u0026#39;EOF\u0026#39; [DEFAULT] bantime = 10m maxretry = 3 [sshd] enabled = true mode = aggressive EOF sudo systemctl enable --now fail2ban 하나의 IP에서 3번 인증 실패하면 10분 동안 방화벽 수준에서 차단됩니다. mode = aggressive는 normal, ddos, extra SSH 필터를 합쳐요. 현재 차단 상태는 sudo fail2ban-client status sshd로 확인합니다.\n클러스터 전체 패스워드 없는 SSH. 키 인증이 활성화되고 SSH 감시가 설정된 다음, 마지막은 키 배포입니다. 로그인 노드에서 일반 sudo 사용자로 (root는 어차피 SSH 불가):\nssh-keygen -t ed25519 ssh-copy-id \u0026lt;user\u0026gt;@arbiter ssh-keygen이 ~/.ssh/에 개인/공개 키 쌍을 만듭니다. ssh-copy-id가 패스워드 인증으로 대상 머신에 한 번 로그인한 다음, 공개 키를 그 머신의 ~/.ssh/authorized_keys에 추가합니다. 이후 SSH 시도에서 서버가 공개 키를 보고 대응하는 개인 키가 있는지 확인하고, 패스워드 없이 접속을 허용합니다. 각 워커에 반복합니다. 그러면 로그인 노드에서 ssh arbiter가 패스워드를 요구하지 않습니다.\n선택적: 노트북 로그인 노드용 sshd 시작 오버라이드. 이 노트북 기반 로그인 노드에서 이더넷 인터페이스가 완전히 설정되기 전에 sshd가 시작 실패하는 부팅 타임 문제가 간혹 있었습니다. 정확한 원인을 당시에 캡처하지 못해서 확실하지는 않습니다. 표준 수정은 sshd가 network-online.target을 기다리고 실패 시 재시도하는 systemd 오버라이드입니다. 재부팅 후 sshd가 실패 상태라면 journalctl -u sshd -b로 확인합니다. 데스크탑이나 서버 하드웨어에서 빌드하면 이 설정이 필요 없을 겁니다.\nsudo systemctl edit sshd.service로 적용하고 붙여넣습니다.\n[Unit] Wants=network-online.target After=network-online.target [Service] Restart=on-failure RestartSec=5s StartLimitIntervalSec=0 systemctl edit이 /etc/systemd/system/sshd.service.d/override.conf에 드롭인 파일을 만들고 daemon-reload를 자동으로 실행합니다.\n내부 노드: 방화벽 끄기 # 이 섹션의 내용은 전부 carrier에 관한 거였습니다. 나머지 다섯 노드는 다른 이야기입니다.\n그 노드들은 NAT 뒤 192.168.50.0/24에 있습니다. 홈 WiFi에서 직접 접근할 수 없고, 유일한 인바운드 경로는 carrier를 통해서입니다. 이 노드들의 firewalld는 실질적인 방어를 제공하지 않지만, 동작해야 하는 것들을 막습니다. NFS 콜백, Kerberos와 LDAP를 통한 FreeIPA 등록, srun과 스텝 실행을 위해 Slurm이 사용하는 동적 포트의 긴 목록입니다. 이 전체에 걸쳐 정확한 방화벽 규칙을 유지하는 건 지루하고 실수하기 쉽습니다.\n더 단순한 접근법은 로그인 노드가 아닌 모든 노드에서 끄는 겁니다. 격리된 HPC 패브릭의 표준 관행이기도 합니다.\nsudo systemctl disable --now firewalld 보안 경계선은 carrier입니다. 경계 안의 모든 노드들은 서로 신뢰할 수 있습니다.\n8. WiFi가 병목이 아닌 이유 # 이 토폴로지에서 가장 많이 받는 질문은 로그인 노드의 WiFi가 클러스터 병목이 되는지입니다. 그렇지 않습니다. 트래픽 경로가 비대칭이거든요.\nSSH로 코드 편집, dnf로 패키지 가져오기, git pull 실행, 브라우저로 시스템 모니터링, 이 모든 게 WiFi로 나갑니다. 대역폭이 민감한 것들이 아닙니다. 수백 Mbps의 WiFi 처리량으로도 충분합니다.\n무거운 작업은 전부 기가비트 스위치에서 일어납니다. interceptor-01이 arbiter의 NFS 공유에서 데이터셋을 읽을 때, 그 트래픽은 노드에서 스위치를 거쳐 노드로 갑니다. 로그인 노드는 건드리지 않습니다. 두 워커에서 MPI 작업이 메시지를 주고받을 때도 마찬가지입니다. 전체가 기가비트로 연결되고, 레이턴시도 예측 가능하며, WiFi도 없습니다.\n컴퓨트 패브릭은 완전히 유선입니다. WiFi 쪽은 관리와 인터넷 접속에만 씁니다. 홈 네트워크에서 누군가 4K 동영상을 스트리밍해도 클러스터 성능에 영향이 없습니다.\n9. 다음 에피소드 # 클러스터가 네트워크에 연결되고, 주소가 지정되고, 접근 가능해졌습니다. 모든 노드에 OS가 있습니다. 호스트명이 해석됩니다. 로그인 노드가 내부 서브넷의 DHCP, NAT, SSH를 처리합니다. 홈 네트워크의 어떤 기기에서든 carrier로 SSH 접속하고 거기서 어떤 워커로든 패스워드 없이 접속할 수 있습니다.\n에피소드 4에서는 arbiter에 Samsung 990 Pro를 NFS 공유 스토리지로 마운트하고 중앙 집중식 사용자 관리를 위한 FreeIPA를 올립니다. 그 이후에는 한 번 만든 사용자 계정이 클러스터의 모든 노드에서 동작하고, 모든 노드가 홈 디렉토리 트리를 공유하게 됩니다.\n이 에피소드의 모든 설정 파일과 전체 명령어 레퍼런스는 GitHub에 있습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 4월 21일","externalUrl":null,"permalink":"/ko/posts/hpc-from-scratch-03/","section":"포스트","summary":"관리형 스위치 없이도 괜찮습니다. 이 에피소드에서는 홈 HPC 로그인 노드를 WiFi로 연결하고, 고정 IP와 SSH 접근을 설정하고, 확장 가능한 네트워크 레이아웃을 만드는 방법을 보여줍니다.","title":"[HPC From Scratch] 에피소드 3: Rocky Linux 설치, DHCP, NAT, 그리고 전 노드 SSH 연결","type":"posts"},{"content":"","date":"2026년 4월 21일","externalUrl":null,"permalink":"/ko/tags/networking/","section":"Tags","summary":"","title":"Networking","type":"tags"},{"content":"노드 4개, 각 16GB. BIOS 설정 하나가 Slurm 작업을 조용히 망가뜨릴 수 있습니다.\n에피소드 1에서는 클러스터 전체 아키텍처, 비용 분석, 네트워크 레이아웃을 다뤘습니다. 이번 에피소드는 컴퓨트 핵심부에 집중합니다. Lenovo ThinkCentre M715q 4대에 듀얼 채널 메모리(Dual-channel memory)와 NVMe 스토리지를 업그레이드하고, 메모리를 조용히 빼앗아 가는 BIOS 설정을 수정하는 내용입니다.\n1. 작업 대상 하드웨어 # 각 M715q는 아주 작은 마이크로 폼팩터 PC입니다. eBay에서 구입했을 때의 기본 사양입니다.\n사양 기본 구성 CPU AMD Ryzen 5 Pro 2400GE (4C/8T, 35W TDP) RAM 8GB DDR4 SO-DIMM (단일 슬롯, 싱글 채널) 부트 드라이브 240GB 2.5\u0026quot; SATA SSD M.2 슬롯 비어 있음 (NVMe 지원) 통합 GPU AMD Radeon RX Vega 11 Ryzen 5 Pro 2400GE는 35W 부품입니다. 조용하고 전력 소비도 적어서 책상 위에 4대를 올려두기에 딱 맞습니다. 노드당 4코어 8스레드로, M715q 노드 전체에서 총 32스레드가 나옵니다.\n싱글 채널 8GB RAM은 대부분의 HPC 워크로드에 부족합니다. 부트 드라이브가 2.5\u0026quot; SATA SSD라는 점은 스토리지 업그레이드에서 오히려 유리하게 작용했습니다.\n2. 스토리지 업그레이드 경로 # 첫 번째 M715q를 열어보니 2.5\u0026quot; 베이에 240GB SATA SSD가 있고, 마더보드에 비어 있는 M.2 NVMe 슬롯이 있었습니다.\nOS가 SATA 드라이브에서 부팅되니까 고속 M.2 슬롯이 비어 있는 겁니다. 관리 노드에 Samsung 990 Pro 1TB를 설치해서 클러스터 전체의 NFS 스토리지로 쓸 수 있겠다는 걸 바로 알아챘습니다.\n만약 부트 드라이브가 M.2 SSD였다면 (이 모델의 기본 옵션 중 하나로 알고 있습니다) 업그레이드 경로가 달라졌을 겁니다. 그 경우엔 NFS용 일반 SATA SSD를 따로 샀겠죠. 손에 있는 하드웨어에 맞춰 작업하는 겁니다.\nPCIe Gen 4 NVMe 드라이브는 기가비트 이더넷 네트워크에는 사실 과사양입니다. 게다가 M715q의 M.2 슬롯이 PCIe 3.0이라 990 Pro도 Gen 3 속도로 작동합니다. 드라이브보다 네트워크가 먼저 병목이 되는 구조입니다. 실제 처리량은 이후 에피소드에서 벤치마킹할 예정입니다.\n참고: NVMe 드라이브는 관리 노드에만 설치합니다. 나머지 M715q 세 대는 기존 240GB SATA SSD를 부트 드라이브로 유지합니다. 작업이 NFS에서 데이터를 읽을 때 컴퓨트 노드에 로컬 고속 스토리지는 필요 없거든요.\n3. RAM 업그레이드: 8GB에서 16GB 듀얼 채널로 # 8GB는 대부분의 HPC 워크로드에 부족합니다. 기존 스틱을 16GB 단일 모듈로 교체하는 대신, 두 번째 8GB 스틱을 추가했습니다.\n각 M715q는 한 슬롯에 8GB DDR4 SO-DIMM이 하나 꽂혀 있고 나머지 슬롯은 비어 있습니다. 8GB 스틱을 추가로 구입해서 빈 슬롯에 설치하면 두 가지 장점이 생깁니다.\n용량 두 배 (8GB에서 16GB로) 듀얼 채널 메모리 대역폭 컴퓨팅에서 듀얼 채널은 중요합니다. 단일 스틱에서는 CPU가 하나의 채널로 메모리에 접근합니다. 두 슬롯에 스틱이 모두 꽂히면 두 채널을 동시에 읽고 쓸 수 있습니다. 이론적으로 메모리 대역폭이 약 두 배가 되는데, 이는 MPI와 수치 연산 같은 메모리 바운드 워크로드 성능에 직접 영향을 줍니다.\nRAM 호환성\nM715q는 DDR4 SO-DIMM (노트북 사이즈) 메모리를 씁니다. 중고 RAM을 살 때는 기존 스틱과 사양을 최대한 맞춰야 합니다.\n사양 맞춰야 할 항목 폼 팩터 DDR4 SO-DIMM 용량 8GB (기존 스틱과 맞추기 위해) 속도 DDR4-2666 이상 (2400GE는 최대 2933 지원) 전압 1.2V (표준 DDR4) (제가 구입한 M715q에는 DDR4-2666이 들어 있었습니다)\nRAM 스틱은 eBay에서 구입했습니다. 4개에 총 $78이었는데, 노드당 업그레이드 비용이 약 $20입니다. 새 16GB 키트(2x8GB)를 사면 더 비싸지만 호환성은 보장됩니다.\n팁: 호환성이 확실하지 않으면 공식 스펙 시트를 확인하고 그에 맞는 부품을 검색해보세요.\n설치\nM715q를 여는 건 어렵지 않습니다. 뒤판 나사 하나를 빼고 상단 커버를 밀면 내부가 완전히 보입니다. 2.5\u0026quot; SATA 베이를 빼면 (나사 하나, 앞으로 밀어내기) 두 개의 SO-DIMM 슬롯이 바로 보입니다. 새 스틱을 빈 슬롯에 꽂고 클립이 찰칵 고정될 때까지 밀어 넣으면 됩니다.\n4개 노드 전부 업그레이드했습니다. 관리 노드는 Samsung 990 Pro NVMe 드라이브도 함께 설치해서 약간 더 걸렸고, 나머지 세 대는 RAM만이라 빠르게 처리했습니다.\n4. iGPU 메모리 함정 # 나중에 몇 시간의 디버깅을 아낄 수 있는 내용입니다.\nRAM을 설치한 뒤 NanoKVM을 사용해 관리 노드를 Linux Live USB로 부팅했습니다 (모니터나 키보드 없이 브라우저로 제어 가능). 터미널을 열고 실행했습니다.\n$ free -m total used free shared buff/cache available Mem: 15661 1656 10369 73 3989 14005 15,661 MiB가 나왔습니다. 16GiB (16,384 MiB)를 설치했는데, 나머지 ~700 MiB는 어디 갔을까요?\n답은 통합 Vega GPU입니다. Ryzen APU는 시스템 RAM을 iGPU(통합 GPU)와 공유합니다. GPU가 물리적 메모리 일부를 VRAM(비디오 메모리)으로 예약하는데, 운영체제는 그 부분을 볼 수 없습니다.\n다음 명령어로 확인했습니다.\n$ dmesg | grep VRAM 출력에서 256MB가 VRAM으로 할당된 걸 확인했습니다.\nBIOS 설정: UMA Frame Buffer Size\niGPU에 예약되는 RAM의 양은 UMA Frame Buffer Size라는 BIOS 설정으로 조정합니다. 제 기기의 기본값은 Auto였고, 256MB를 잡아가고 있었습니다.\nAuto가 이미 256MB를 선택하고 있는데 왜 굳이 바꿀까요? Auto는 펌웨어가 알아서 결정하기 때문에, BIOS 업데이트나 하드웨어 구성 변경 후에 그 결정이 달라질 수 있습니다. iGPU가 갑자기 256MB 대신 512MB를 가져가면 Slurm 작업이 실패하기 시작하는데, 오류 메시지는 BIOS를 가리키지 않습니다.\n값을 고정해두면 그런 불확실성이 사라집니다.\nSlurm에서 이게 중요한 이유\n이후 시리즈에서 Slurm을 설정할 때 각 노드의 메모리를 slurm.conf의 RealMemory 파라미터로 선언해야 합니다. 16GB를 설치했으니까 RealMemory=16000이라고 설정하면, Slurm이 존재하지 않는 메모리를 할당하려 합니다. 작업이 메모리 부족 오류로 크래시가 나게됩니다.\n올바른 방법은 이러합니다.\n노드 부팅 free -m 실행 후 total 값 확인 그 숫자(또는 약간 낮은 값)를 RealMemory로 사용 # slurm.conf 예시 NodeName=interceptor-01 CPUs=8 RealMemory=15600 State=UNKNOWN 메가바이트 하나하나가 중요합니다. 지금 기록해두면 나중에 디버깅 시간을 아낄 수 있습니다.\n5. 업그레이드 비용 요약 # 이번 에피소드의 업그레이드 비용입니다.\n품목 수량 단가 (USD) 합계 (USD) DDR4 8GB SO-DIMM (Micron) 2 15.00 30.00 DDR4 8GB SO-DIMM (Hynix) 2 24.00 48.00 Samsung 990 Pro 1TB NVMe 1 109.90 109.90 에피소드 합계 $187.90 에피소드 1의 M715q 4대($343.60)와 합치면 컴퓨트 백본 누적 비용은 $531.50입니다. 16GB 듀얼 채널 RAM과 1TB NVMe 스토리지를 갖춘 Ryzen 노드 4대가 그 가격입니다.\nNFS 공유 드라이브를 제외한 노드당 단가: 16GB RAM을 갖춘 Ryzen 4C/8T 컴퓨트 노드 하나에 약 $105입니다.\n6. 다음 에피소드 # 컴퓨트 백본이 조립되고 검증됐습니다. 하지만 네트워크 없는 하드웨어는 그냥 금속 더미입니다.\n에피소드 3에서는 클러스터의 나머지 부분을 살펴볼 겁니다. Intel i7-10700F가 들어간 HP Envy TE01 GPU 노드, 모든 것을 연결하는 기가비트 네트워크 스위치, 그리고 로그인 노드가 WiFi로 클러스터를 인터넷에 연결하는 이유를 다룹니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 3월 25일","externalUrl":null,"permalink":"/ko/posts/hpc-from-scratch-02/","section":"포스트","summary":"미니 PC 안의 iGPU가 컴퓨트 노드 RAM을 조용히 가져가고 있습니다. 에피소드 2에서는 RAM과 NVMe 업그레이드, 그리고 경고 없이 메모리를 잃게 만드는 설정 함정을 다룹니다.","title":"[HPC From Scratch] 에피소드 2: RAM, NVMe, 그리고 iGPU 메모리 함정","type":"posts"},{"content":"","date":"2026년 3월 25일","externalUrl":null,"permalink":"/ko/tags/hardware/","section":"Tags","summary":"","title":"Hardware","type":"tags"},{"content":"6노드 클러스터, $1,264. 서버 랙도, 기업 예산도 필요 없습니다.\nHPC 101과 Special Topics 시리즈에서는 HPC 클러스터를 사용하는 방법을 다뤘습니다. 이 시리즈는 클러스터를 직접 만드는 방법을 다룹니다.\n앞으로 여러 에피소드에 걸쳐 소비자 하드웨어로 실제 작동하는 HPC 클러스터를 만드는 전 과정을 함께 걸어갈 겁니다. 부품 조달, OS 설치, Slurm 구성, FreeIPA로 아이덴티티 관리 설정, 벤치마킹, 업그레이드까지 전부요. 모든 설정 파일은 GitHub에 공개할 예정입니다.\n이번 첫 에피소드에서는 클러스터 구성 부품, 각 부품을 구한 경로, 네트워크 레이아웃, 그리고 클라우드 인스턴스와의 비용 비교를 다룹니다.\n1. 왜 클러스터를 만들까요? # 일반적인 대안은 두 가지인데, 둘 다 장단점이 있습니다.\n클라우드 (AWS, GCP, Azure): 멀티노드 컴퓨팅 인스턴스를 24/7 돌리면 비용이 금방 쌓입니다. 3년 세이빙 플랜을 써도 적당한 EC2 인스턴스 2개에 연간 $2,300 이상이 나갑니다 (섹션 5 참고). 순간적인 버스트 워크로드에는 괜찮지만, 항상 켜두고 실험하기에는 현실적이지 않습니다.\n싱글 워크스테이션: 고성능 데스크탑은 원시 컴퓨팅 파워는 되지만, 분산 시스템을 배울 수가 없습니다. 단일 머신에서는 네트워크 병목을 경험하거나, Slurm 스케줄링 충돌을 디버깅하거나, MPI 문제를 다루기 어렵거든요. 그러려면 여러 노드가 필요합니다.\n실제 슈퍼컴퓨터 아키텍처의 축소판을 책상 위에서 테스트하고, 망가뜨리고, 고칠 수 있는 환경을 만들고 싶었습니다. 대학 연구 클러스터와 동일한 소프트웨어 스택으로 운영됩니다. 작업 스케줄링에는 Slurm, 아이덴티티 관리에는 FreeIPA, 공유 스토리지에는 NFS(Network File System), 병렬 워크로드에는 MPI를 사용합니다.\n2. 부품 목록 (Bill of Materials) # 아래 가격은 2024년 말부터 2025년 말 사이에 실제로 지불한 금액입니다. PC 부품 시장의 최근 가격 상승으로 지금 동일하게 구성하면 총액이 더 높을 수 있습니다.\n품목 수량 단가 (USD) 합계 (USD) 상태 Lenovo IdeaPad 1 1 161.00 161.00 리퍼비시 Lenovo ThinkCentre M715q 4 85.90 343.60 중고 HP Envy TE01 1 400.00 400.00 중고 DDR4 SODIMM (Micron) 2 15.00 30.00 중고 DDR4 SODIMM (Hynix) 2 24.00 48.00 중고 Netgear GS308E 1 21.50 21.50 신품 Samsung 990 Pro 1TB 1 109.90 109.90 신품 Sabrent USB-C Hub 1 59.90 59.90 신품 Cat 6 이더넷 케이블 10Gbps (x5) 1 9.90 9.90 신품 NanoKVM 1 69.90 69.90 신품 고무 받침발 1 9.90 9.90 신품 총 비용 1,263.60 부품 구입처:\nM715q 4대와 RAM은 eBay에서, HP Envy TE01은 Craigslist 현금 거래로 구했습니다 (영수증은 없습니다). Samsung 990 Pro, Netgear 스위치, USB-C 허브, 케이블, 고무 받침발은 Amazon에서, NanoKVM은 제조사에서 직접 주문했습니다. IdeaPad 1은 Lenovo 리퍼비시 제품입니다.\n한국에서는 중고나라, 당근마켓 등에서 유사한 중고 미니 PC와 RAM을 찾을 수 있습니다.\n핵심은 조급하지 않는 거였습니다. 한 번에 전부 사지 않고 eBay 매물을 몇 주간 지켜봤고, Craigslist 매물이 뜨면 바로 잡았습니다. M715q 4대의 평균 단가는 $86 미만이었는데, 그 가격이면 4대 합쳐서 중급 GPU 한 개보다 쌉니다.\n향후 업그레이드 계획: RTX 5060 Ti와 새 파워서플라이가 GPU 노드에 추가될 예정입니다. 위 비용에는 포함하지 않았는데, 초기 빌드의 필수 구성은 아니거든요. GPU 업그레이드는 별도 에피소드에서 다룰 예정입니다.\n3. 클러스터 아키텍처 # 호스트명 역할 하드웨어 CPU 비고 carrier 로그인 노드(Login Node) Lenovo IdeaPad 1 AMD Ryzen 3 7920U (8vCPU, 8GB RAM) WiFi로 인터넷 연결, 이더넷으로 클러스터 스위치 연결 arbiter 관리 노드 Lenovo ThinkCentre M715q Ryzen 5 Pro 2400GE (8 vCPU, 16GB RAM) Slurm 컨트롤러, FreeIPA 서버 interceptor-01 CPU 컴퓨트 노드 Lenovo ThinkCentre M715q Ryzen 5 Pro 2400GE (8 vCPU, 16GB RAM) Slurm 컴퓨트 interceptor-02 CPU 컴퓨트 노드 Lenovo ThinkCentre M715q Ryzen 5 Pro 2400GE (8 vCPU, 16GB RAM) Slurm 컴퓨트 corsair-01 GPU 컴퓨트 노드 HP Envy TE01 Intel i7-10700F (16 vCPU, ~32GB RAM) GTX 1660 Super (업그레이드 예정) observer 시각화 노드 Lenovo ThinkCentre M715q Ryzen 5 Pro 2400GE (8 vCPU, 16GB RAM) 시각화/모니터링 작업 AMD Ryzen과 Intel이 섞인 구성은 처음에 어색해 보일 수 있습니다. 하지만 실제 HPC에서도 이기종 아키텍처는 표준입니다.\n2024년 11월 TOP500 기준 세계에서 가장 빠른 슈퍼컴퓨터인 El Capitan을 보면, AMD MI300A APU를 사용해서 CPU와 GPU 코어를 하나의 패키지에 담습니다. 이 클러스터에서는 그 역할을 별도 노드로 분리한 겁니다. 기본 아이디어는 같습니다. 서로 다른 프로세서가 워크로드의 서로 다른 부분을 처리하는 구조를 책상 위 규모로 구현한 거죠.\n모든 노드는 Rocky Linux로 운영됩니다. 소프트웨어 스택은 작업 스케줄링용 Slurm 25.11, 중앙 집중식 아이덴티티 및 인증용 FreeIPA, 공유 스토리지용 NFS (Samsung 990 Pro에서 서빙), 병렬 워크로드용 OpenMPI입니다. 모니터링은 Prometheus와 Grafana가 담당하고, 모든 구성은 Ansible 플레이북으로 관리합니다.\n4. 네트워크 구성 # 네트워크 토폴로지는 단순합니다.\n모든 클러스터 노드는 192.168.50.x 서브넷의 Netgear GS308E 기가비트 스위치에 연결됩니다. 스위치는 실제로는 비관리 방식으로 운영합니다. VLAN도, 트렁킹도 없습니다. 내부 클러스터 트래픽은 이 스위치에서 물리적으로 격리됩니다.\n로그인 노드(carrier)는 두 개의 네트워크 인터페이스를 가져요. WiFi는 홈 라우터를 통해 인터넷에 연결되고, 이더넷은 클러스터 스위치에 연결됩니다. 로그인 노드가 외부 세계와 내부 클러스터 네트워크 사이의 다리 역할을 하는 겁니다.\n이 패턴은 실제 HPC에서도 그대로 씁니다. 로그인 노드가 외부 네트워크와 내부 패브릭의 경계에 위치하는 구조입니다. 차이는 규모와 대역폭입니다. InfiniBand나 Slingshot 대신 기가비트 이더넷을, 스파인-리프 토폴로지 대신 소비자용 스위치를 쓸 뿐입니다.\n5. AWS 비용 비교 # 빌드 비용의 맥락을 잡기 위해 AWS에서 비슷한 구성을 운영할 때의 비용을 비교해봤습니다.\n비교 기준은 CPU 컴퓨트 노드(interceptor-01, interceptor-02)와 코어 수 및 메모리가 비슷한 c6g.2xlarge 인스턴스 2개입니다. 관리 노드, 시각화 노드, 로그인 노드, GPU 노드는 포함되지 않았습니다. 실제 클러스터 용량은 EC2 인스턴스 2개보다 훨씬 큽니다.\n홈 클러스터 (CPU 노드 2개) AWS EC2 (2x c6g.2xlarge) 노드당 vCPU 8 8 노드당 메모리 16 GB 16 GB 아키텍처 x86 (AMD Ryzen 5 Pro) ARM (AWS Graviton2) 네트워크 1 Gbps (관리형 스위치) 최대 10 Gbps 총 초기 비용 $1,264 N/A 연간 비용 전기요금만 $2,300 (3년 세이빙 플랜, N. Virginia) 손익분기점 클라우드 대비 ~7개월 N/A 주의: 이 비교는 노드 수와 메모리 기준이지 원시 성능 기준이 아니에요. c6g.2xlarge 인스턴스는 더 최신 ARM(Graviton2) 코어를 사용하고 네트워킹도 훨씬 빠릅니다. 홈 클러스터가 EC2보다 성능이 뛰어나다는 말이 아니에요. 실제로는 EC2가 훨씬 빠르죠. 하지만 분산 시스템, 작업 스케줄링, 클러스터 관리를 직접 배우는 데는 자체 하드웨어를 구축하는 쪽이 비용 대비 훨씬 빠르게 본전을 뽑고, 클라우드 인스턴스에서는 얻을 수 없는 경험을 쌓을 수 있습니다.\nAWS 비용 추정은 AWS Pricing Calculator를 사용했습니다. 구성은 c6g.2xlarge 2개, US East (N. Virginia), Linux, Compute Savings Plans (3년, 선불 없음), 24/7 지속 워크로드 기준입니다.\n6. 다음 에피소드 # 에피소드 2에서는 Lenovo ThinkCentre M715q를 직접 열어서 하드웨어를 자세히 살펴볼 겁니다. RAM 업그레이드 방법과, 통합 Vega GPU가 기본적으로 시스템 메모리 일부를 예약하는 BIOS 설정 문제를 어떻게 수정하는지 보여드리겠습니다.\n이후 시리즈에서 다룰 내용입니다.\n운영체제 설치 및 초기 설정 Slurm 설치와 멀티노드 작업 스케줄링 중앙 집중식 인증을 위한 FreeIPA 설정 NFS 공유 스토리지 구성 GPU 업그레이드 (RTX 5060 Ti 교체 및 파워서플라이 교체) 벤치마킹 및 성능 튜닝 케이블 정리 (언젠간 하게 될 겁니다) 모든 설정 파일과 Ansible 플레이북은 진행하면서 GitHub에 공개할 예정입니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 3월 13일","externalUrl":null,"permalink":"/ko/posts/hpc-from-scratch-01/","section":"포스트","summary":"소비자용 하드웨어로 6노드 HPC 클러스터를 $1,264에 만들었습니다. 에피소드 1에서는 하드웨어 결정 과정, 생략해도 되는 것들, 그리고 엔터프라이즈 장비 없이도 실제 HPC를 구동할 수 있는 이유를 설명합니다.","title":"[HPC From Scratch] 에피소드 1: $1,264로 만든 6노드 HPC 클러스터","type":"posts"},{"content":"Date: February 24, 2026 Venue: Northeastern University, Boston, MA\nOverview # A hands-on workshop designed for university researchers and faculty who are new to Linux and high-performance computing (HPC) environments. This session covers essential command-line skills needed to navigate and work efficiently on HPC clusters.\nTopics Covered # Navigating the Linux filesystem and managing files Working with text editors and file permissions Environment variables and shell configuration Essential command-line utilities for research workflows Tips for transitioning from GUI-based workflows to the terminal Materials # Workshop Slides \u0026amp; Materials (GitHub) Recording # Workshop Recordings (Spring 2026) ","date":"24 2월 2026","externalUrl":null,"permalink":"/talks/neu-talk-01/","section":"Talks \u0026 Workshops","summary":"","title":"Linux Essentials for HPC Researchers","type":"talks"},{"content":"노트북을 중간 다리로 쓰는 걸 멈추세요.\n첫 번째 HPC Special Topics 포스트에 온 걸 환영합니다. 특정 주제를 깊게 파고드는 독립 포스트 시리즈입니다.\n데이터 전송 포스트에서 scp와 rsync로 파일을 옮기는 방법을 배웠습니다. 노트북에서 클러스터로 전송할 때는 잘 됩니다. 그런데 클라우드 스토리지는요?\n상황을 상상해봅니다. 교수님이 Google Drive에 200GB 데이터셋을 공유해줬습니다. 적절한 도구 없이는 노트북에 먼저 내려받고 (운 좋으면 2시간), 다시 클러스터에 scp로 올려야 합니다 (또 2시간). 파일 전송만 4시간입니다.\n노트북을 완전히 건너뛰고 Google Drive에서 /scratch 디렉토리로 명령어 하나로 직접 다운받을 수 있다면요?\n그게 바로 Rclone이 하는 일입니다.\n기본 사용법을 넘어서, 경험 많은 HPC 엔지니어들이 자주 논하는 최적화 기법도 알아볼 겁니다. 병렬 스레드가 전송 속도를 극적으로 바꿀 수 있는 경우와 그렇지 않은 경우입니다.\n1. 왜 Rclone을 쓸까요? # Rclone은 클라우드 스토리지 파일을 관리하는 커맨드라인 프로그램입니다. rsync라고 생각하되 클라우드용입니다. Google Drive, Dropbox, OneDrive, Box, AWS S3, SFTP까지 70개 이상의 클라우드 스토리지 제공업체를 지원합니다.\nHPC에서 이게 왜 중요할까요?\n직접 전송: Google Drive에서 클러스터의 /scratch 공간으로 노트북을 거치지 않고 바로 데이터를 옮겨요. 내려받고 다시 올리고하는 사이클이 없어져요.\n병렬화: 파일 하나를 단일 스트림으로 보내는 scp와 달리, Rclone은 여러 파일을 동시에 전송할 수 있습니다. 여기서 흥미로운 부분이 생겨요 (섹션4에서 자세히 다뤄요).\n안정성: Rclone은 재시도, 체크섬, 중단된 전송을 자동으로 처리합니다. 99%에서 연결이 끊겨도 이어서 진행합니다. 클라우드 스토리지용 rsync -P입니다.\n다양성: 도구 하나로 백엔드 70개 이상. 협업자가 Google Drive에서 데이터를 공유하든, 기관이 Box를 쓰든, 파이프라인이 S3에 결과를 저장하든, 같은 인터페이스로 모두 처리합니다.\n2. HPC에서 Rclone 설정하기 # 중요: 많은 HPC 클러스터에서 로그인 노드에서 대용량 데이터 전송을 금지합니다. 클러스터 정책을 먼저 확인하세요. 대용량 전송이 제한된다면 컴퓨트 작업 안에서 Rclone을 실행하세요.\n$ srun --pty bash $ module load rclone $ rclone copy ... 1단계: Rclone 로드 # 많은 HPC 클러스터에 Rclone이 모듈로 이미 설치되어 있습니다.\n$ module avail rclone $ module load rclone 모듈이 없다면 홈 디렉토리에 직접 설치할 수 있습니다.\n# 다운로드 및 압축 해제 $ curl -O https://downloads.rclone.org/rclone-current-linux-amd64.zip $ unzip rclone-current-linux-amd64.zip # 바이너리를 로컬 bin으로 이동 $ mkdir -p ~/bin $ cp rclone-*/rclone ~/bin/ # 확인 $ ~/bin/rclone version 참고: 직접 설치했다면 ~/bin이 $PATH에 있어야 합니다. 아니면 ~/bin/rclone으로 전체 경로를 씁니다.\n2단계: Google Drive 연결 (헤드리스 인증) # 대부분의 초보자가 막히는 부분입니다. HPC 클러스터에는 웹 브라우저가 없으니 헤드리스 설정으로 인증해야 합니다.\nrclone config를 실행하고 새 원격지를 위해 n을 선택합니다. 기억하기 쉬운 이름을 붙여요 (예: gdrive). Google Drive에 해당하는 번호를 선택합니다. 나머지 프롬프트(클라이언트 ID, 시크릿, 스코프, 루트 폴더 ID, 서비스 계정, 고급 설정)는 모두 Enter를 눌러 기본값을 쓰세요. \u0026ldquo;auto config를 사용할까요?\u0026rdquo; 라는 질문에 n을 선택합니다. 브라우저 없는 원격 서버에서는 이게 중요합니다. Rclone이 URL을 알려줍니다. 이 URL을 로컬 노트북 브라우저에 복사해서 붙여넣습니다. Google 계정에 로그인하고 Rclone을 승인한 뒤 인증 코드를 HPC 터미널에 복사합니다. Team Drive 관련 질문에는 n을 선택합니다. (Team Drive를 쓰지 않는다면) y로 저장을 확인합니다. (Rclone overview를 확인해 자세한 설명과 다른 클라우드 서비스를 사용하는 방법을 알아보세요.)\n$ rclone config # 위 단계를 따라하세요 # ... # 연결 확인 $ rclone lsd gdrive: # Google Drive 폴더 목록이 보여야 합니다 폴더가 보인다면 연결된 겁니다.\n팁: 같은 과정이 Dropbox, OneDrive, Box에도 적용됩니다. 3단계에서 다른 제공업체 번호를 선택하면 됩니다. 제공업체마다 인증 단계가 약간 다르지만 Rclone이 대화식으로 안내해줍니다.\n3. 핵심 명령어 # 최적화로 들어가기 전에 일상적으로 쓸 명령어들을 먼저 살펴봅니다.\n목록 조회 및 탐색\n# 클라우드의 최상위 디렉토리 목록 $ rclone lsd gdrive: # 특정 폴더의 파일 목록 $ rclone ls gdrive:my_project/data # 디렉토리 트리 표시 (탐색에 유용합니다) $ rclone tree gdrive:my_project --max-depth 2 # 저장 공간 사용량 확인 $ rclone about gdrive: 데이터 복사\n# 클라우드 -\u0026gt; 클러스터 (가장 흔한 사용 사례) $ rclone copy gdrive:my_data ~/scratch/my_data -P # -P: 실시간 진행 상황, 속도, 예상 완료 시간을 보여줍니다. # 클러스터 -\u0026gt; 클라우드 (결과 백업) $ rclone copy ~/scratch/results gdrive:results -P copy vs. sync 차이 알기\n# copy: 새 파일만 추가합니다. 대상에서 아무것도 삭제하지 않아요. $ rclone copy gdrive:data ~/scratch/data -P # sync: 대상을 소스와 동일하게 만들어요. 소스에 없는 # 파일을 대상에서 삭제합니다. 주의해서 사용하세요! $ rclone sync gdrive:data ~/scratch/data -P 경고: rclone sync는 소스에 없는 파일을 대상에서 삭제합니다. 실행 전에 명령어를 꼭 다시 확인하세요. 확실하지 않다면 copy를 쓰세요.\n여기까지가 Rclone을 일상 도구로 쓰기 위해 필요한 모든 것입니다. 다음 섹션에서는 어떻게 더 빠르게 만들 수 있는지 알아봅니다.\n4. 최적화 과제: 스레드 vs. 대역폭 # 실제 WAN 시나리오에서는 단일 TCP 스트림이 지연 시간, TCP 윈도우 제한, 제공업체 측 쓰로틀링 때문에 가용 대역폭을 완전히 활용하지 못하는 경우가 많습니다. 해결책은 뭘까요? 스트림을 더 열면 됩니다.\nRclone에는 이를 위한 핵심 플래그가 있습니다.\n--transfers=N # 동시에 전송할 파일 수 (기본값: 4) 그럼 이런 의문이 생길 수 있습니다.\n스레드를 늘리면 항상 빨라질까요? 수확 체감 지점이 있을까요? 업로드(전송)와 다운로드(수신)가 같은 방식으로 작동할까요? 실험 설계\n환경: 4코어 HPC 컴퓨트 노드, 1Gbps 네트워크, 기본 Google Drive API(공유 클라이언트 ID)로 Rclone 사용. 시나리오 A: 단일 대용량 파일 5GB (압축 효과를 막기 위해 /dev/urandom으로 생성). 시나리오 B: 소용량 파일 1,000개 (각 1MB, 동일하게 랜덤 데이터). 변수: --transfers를 1, 4, 8, 16, 32로 설정. 반복: 일관성 확인을 위해 조건당 3회 실행. 5. 벤치마크 결과 # 시나리오 A: 단일 대용량 파일 (5GB) # (스레드 수에 따른 5GB 단일 파일 전송 시간.)\n그래프가 평평합니다. --transfers를 1로 설정하든 32로 설정하든 전송 시간이 거의 변하지 않습니다.\n왜일까요? --transfers는 파일 수준 병렬성을 제어하기 때문입니다. 동시에 전송할 파일 슬롯 수를 결정합니다. 파일이 하나뿐이라면 병렬화할 게 없습니다. 파일 하나, 스트림 하나, 스레드 수와 관계없이요.\n흔한 오해가 있습니다. --transfers=16은 단일 파일을 16개 청크로 분할하지 않습니다. 16개의 별도 파일을 위한 16개의 슬롯을 여는 겁니다.\n추가 참고: Rclone은 지원되는 백엔드에서 단일 대용량 파일의 청크 수준 병렬 다운로드를 위한 --multi-thread-streams를 제공합니다. 다운로드에서만 작동하고 제공업체에 따라 효과가 다릅니다. 대부분의 사용 사례에서는 여기서 다룬 --transfers 플래그가 맞습니다.\n핵심: 대용량 단일 파일에서는 --transfers 증가가 효과가 없습니다. 전송 속도는 네트워크 대역폭과 클라우드 제공업체의 스트림당 처리량이 결정합니다.\n시나리오 B: 소용량 파일 (1,000 × 1MB) # 여기서 스레드의 진가가 나옵니다.\n(스레드 수에 따른 소용량 파일 1,000개(각 1MB) 전송 시간.)\n단일 스레드로 1,000개 파일을 업로드하는 데 1,293초 (약 21분)가 걸렸습니다. 8개 스레드에서는 199초 (약 3분)로 줄었습니다. 플래그 하나만 바꿨는데 6.5배 속도 향상입니다.\n다운로드는 조금 다른 이야기입니다. 스레드 1개에서 307초였고 4개에서 93초 (3.3배 향상)로 내려갔습니다. 4개 스레드를 넘어서는 다운로드 속도가 거의 변하지 않았습니다.\n소용량 파일이 왜 스레드에 그렇게 민감할까요? 각 파일 전송에는 API 호출, 메타데이터 확인, 체크섬 검증, 연결 오버헤드가 수반됩니다. 단일 스레드에서는 다음 파일을 시작하기 전에 이 모든 게 완료되길 기다려야 합니다. 여러 스레드는 전송을 겹치게 해서 파일당 지연 시간을 숨깁니다. 그래서 속도 향상이 극적인 겁니다.\n6. 최적 지점 찾기 # (단일 스레드 기준 속도 향상 배율.)\n평탄화 효과 # 8개 스레드 이후에는 성능 향상이 사실상 멈추네요. 왜일까요?\nAPI 속도 제한. Google Drive(대부분의 클라우드 제공업체)는 초당 API 요청 수를 제한합니다. 제공업체의 한도를 넘는 스레드를 추가하면 쓰로틀링과 재시도만 생겨요. 모든 Rclone 사용자가 공유하는 기본 API 클라이언트 ID를 쓸 때 특히 엄격합니다.\n파워 유저 팁: 직접 Google API 클라이언트 ID를 만들면 API 할당량을 크게 늘릴 수 있고 최적 스레드 수가 더 높아질 수 있습니다. 자세한 내용은 Rclone Google Drive 문서를 참고하세요.\n오버헤드. 동시 전송 32개를 관리하면 연결 설정, 체크섬 확인, 재시도 로직 자체에 오버헤드가 생깁니다. 이 모든 것이 리소스를 놓고 경쟁합니다.\n전송 (업로드) vs. 수신 (다운로드) # 모든 조건에서 다운로드가 업로드보다 훨씬 빠르고 더 빨리 포화 상태에 도달합니다.\n업로드할 때 클라우드 제공업체는 각 파일이 도착하는 대로 확인, 인덱싱, 저장해야 합니다. 다운로드할 때는 최적화된 CDN 인프라에서 파일을 제공하므로 파일당 처리 오버헤드가 적습니다. 이 비대칭성 때문에 전송 방향에 따라 최적 --transfers 값이 달라져요.\n효율: 왜 8이 마법의 숫자인가 # 각 스레드가 속도 향상에 얼마나 효율적으로 기여하는지 측정할 수 있습니다.\n$$ Efficiency = \\frac{Speedup}{Number \\: of \\: Threads} \\times 100\\% $$ 스레드 전송 속도 향상 효율 1 1.0x 100% 4 3.9x 98% 8 6.5x 81% 16 6.6x 41% 32 6.6x 21% 8개 스레드에서 81% 효율이 나옵니다. 각 스레드가 제 몫을 하고 있습니다. 32개 스레드에서는 21%로 떨어집니다. 리소스는 4배 더 쓰는데 속도 향상은 거의 없습니다.\n이 특정 환경(1Gbps 네트워크, 기본 Google Drive API 클라이언트)에서는 8개 스레드가 최적 지점이었습니다. 네트워크 속도, 클라우드 제공업체, API 설정에 따라 최적 수가 달라질 수 있지만 찾는 방법은 같습니다. 테스트하고, 측정하고, 비교합니다.\n참고: 이 수치는 기본 공유 API 클라이언트 ID를 쓰는 Google Drive에서 나온 것입니다. 클라우드 제공업체, 네트워크 속도, API 설정에 따라 결과가 달라질 수 있습니다. 방법론은 어디서나 적용 가능합니다.\n7. 요약 \u0026amp; 권장사항 # Rclone은 단순한 편의 도구가 아닙니다. 클라우드 스토리지와 클러스터 사이의 직접적인 파이프라인입니다.\n핵심 정리:\n노트북을 건너뛰세요. Rclone으로 클라우드와 클러스터 사이에 직접 전송하세요. 소용량 파일에는 스레드가 중요합니다. 스레드는 파일당 지연 시간 오버헤드를 숨겨요. 파일이 수천 개라면? --transfers 8 또는 --transfers 16을 씁니다. 단일 대용량 파일에는 스레드가 도움 안 됩니다. --transfers는 파일 수준 병렬성이지 파일 분할이 아닙니다. 업로드와 다운로드가 다르게 작동합니다. 다운로드가 더 빨리 포화됩니다. 방향에 따라 계획하세요. 과하게 하지 마세요. 스레드를 64로 설정하면 API 쓰로틀링이 걸려 오히려 느려져요. 가능하면 묶습니다. Rclone을 써도 소용량 파일 100,000개는 느립니다. 먼저 tar로 묶는 걸 고려하세요 (데이터 전송 포스트에서 다뤘습니다). 시나리오 권장 명령어 소용량 파일 다수 rclone copy remote:path local:path --transfers 8 -P 대용량 파일 소수 rclone copy remote:path local:path -P 디렉토리 동기화 rclone sync remote:path local:path -P (주의해서 사용) 전송 전 확인 rclone lsd remote: 와 rclone about remote: 다음은?\nHPC 툴킷에 핵심 도구를 하나 더 추가했습니다. 다음 시리즈에서는 방향을 완전히 바꿀 겁니다. 클러스터를 사용하는 것에서 직접 만드는 것으로요. 하드웨어, 네트워킹, 그리고 부품 더미를 실제 HPC 시스템으로 만드는 방법을 다룰 겁니다.\n다음 시리즈에서 만나요!\n즐거운 컴퓨팅 되세요!\n","date":"2026년 2월 16일","externalUrl":null,"permalink":"/ko/posts/hpc-special-topics-01/","section":"포스트","summary":"Rclone은 클라우드 스토리지와 HPC 클러스터 사이에서 데이터를 직접 전송할 수 있습니다. 기본 설정은 많은 속도를 버리고 있습니다. 올바르게 설정하고 벤치마킹하는 방법을 소개합니다.","title":"[HPC Special Topics] Rclone: 클라우드 스토리지 전송 벤치마킹과 튜닝","type":"posts"},{"content":"","date":"2026년 2월 16일","externalUrl":null,"permalink":"/ko/tags/cloud-storage/","section":"Tags","summary":"","title":"Cloud Storage","type":"tags"},{"content":"","date":"2026년 2월 16일","externalUrl":null,"permalink":"/ko/tags/performance-tuning/","section":"Tags","summary":"","title":"Performance Tuning","type":"tags"},{"content":"","date":"2026년 2월 16일","externalUrl":null,"permalink":"/ko/tags/rclone/","section":"Tags","summary":"","title":"Rclone","type":"tags"},{"content":"실제 세계에서는 제출 버튼을 누르는 게 시작일 뿐입니다.\nHPC 101 시리즈의 마지막 편에 온 걸 환영합니다.\n지금까지 필수 내용을 다뤘습니다. 로그인, 데이터 이동, 환경 관리. 마침내 작업을 제출했습니다.\n그런데 가끔 일이 틀어지죠.\n작업이 영원히 \u0026lsquo;대기 중\u0026rsquo; 상태입니다. 시작하고 2초 만에 충돌합니다. 3일 동안 돌았는데 빈 파일만 남았습니다. 오늘은 HPC \u0026lsquo;생존 기술\u0026rsquo;을 배울 겁니다. 실패한 작업을 디버깅하는 방법, 리소스 효율을 확인하는 방법, 그리고 대기열에서 막힌 이유를 알아보는 방법입니다.\n1. 상세 모니터링 (scontrol) # 작업을 제출했습니다. squeue --me를 입력했더니 P (대기 중)라고 나옵니다. 10분이 지났는데 여전히 대기 중입니다. 아니면 실행 중인데 어디서 돌고 있는지 모를 수도 있습니다.\n$ squeue --me JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 12345 cpu bash user123 P 0:00 1 (Priority) 12346 gpu bash user123 P 0:00 1 (Resources) squeue는 간단한 요약만 보여줍니다. 전체 보고서가 필요할 때가 있습니다. scontrol show job \u0026lt;JOBID\u0026gt; 명령어를 씁니다.\n$ scontrol show job 12345 JobId=12345 JobName=bash UserId=user123(123456) GroupId=users(1000) ... JobState=PENDING Reason=Resources ... StartTime=2026-01-25T21:00:00 EndTime=Unknown NodeList=(null) WorkDir=/home/user123/my_project Command=/bin/bash ... 살펴볼 핵심 필드:\nJobState \u0026amp; Reason: 왜 기다리고 있는지 정확히 알려줍니다 (예: Resources, Priority). StartTime: 스케줄러가 예상하는 시작 시간입니다. (다른 우선순위 높은 작업이 들어오면 바뀔 수 있습니다). NodeList: 실행 중이라면 어느 컴퓨트 노드를 쓰는지 보여줍니다 (예: compute-node-01). WorkDir: 스크립트가 어디서 실행되고 출력 파일이 어디에 저장되는지 확인해줍니다. Linux 팁: grep이 뭔가요? scontrol 출력이 너무 길어지면 파이프 |와 grep으로 필터링할 수 있습니다.\n| (파이프): 왼쪽 명령어의 출력을 오른쪽 명령어로 전달합니다. grep: 터미널의 Ctrl + F입니다. 키워드가 포함된 줄만 출력합니다. # StartTime 줄만 보여줘 $ scontrol show job 12345 | grep StartTime StartTime=2026-01-25T22:00:00 EndTime=2026-01-25T23:00:00 2. 비상 버튼 (scancel) # 실수로 노드를 100개 요청했습니다. 아니면 코드가 무한 루프에 빠졌습니다.\n그냥 알아서 실패하도록 두지 마세요. 바로 취소하세요.\n# 특정 작업 취소 $ scancel 12345 # 내 모든 작업 취소 $ scancel -u user123 # 특정 작업 취소 $ qdel 12345 # 내 모든 작업 취소 (시스템에 따라 다를 수 있습니다) $ qselect -u user123 | xargs qdel 3. 탐정 작업 (sacct \u0026amp; 로그) # 커피 한 잔 하고 왔는데 작업이 큐에서 사라졌습니다. 끝났을까요, 아니면 실패했을까요? 큐(squeue)에 없으니 히스토리를 확인해야 합니다.\n1단계: 상태 확인 (sacct) # 명령어는 sacct (Slurm Accounting)입니다. 기본 출력이 정리가 안 돼 있어서 형식 옵션을 씁니다.\n$ sacct -j 12345 --format=JobID,State,AllocCPUS,ReqMem,MaxRSS,Elapsed,ExitCode JobID State AllocCPUS ReqMem MaxRSS Elapsed ExitCode ------------ ---------- ---------- ---------- ---------- ---------- -------- 12345 FAILED 1 2G 00:10:15 137:0 12345.batch FAILED 1 00:10:15 137:0 흔한 상태:\nCOMPLETED: 성공했습니다! (종료 코드 0:0) CANCELLED: 작업이 취소됐습니다. TIMEOUT: --time으로 요청한 시간을 초과했습니다. FAILED: 코드가 충돌했습니다 (0이 아닌 종료 코드). 2단계: 로그 읽기 # sacct는 무슨 일이 있었는지 알려주지만 왜는 알려주지 않습니다. \u0026lsquo;왜\u0026rsquo;를 찾으려면 스크립트에서 지정한 출력 파일(예: #SBATCH -o result.out)을 봐야 합니다.\n# 파일 끝부분을 먼저 확인합니다 $ tail -n 20 result.out 흔한 오류 메시지:\ncommand not found: module load를 했나요? ModuleNotFoundError: conda activate를 했거나 패키지를 설치했나요? killed / oom-kill: 메모리가 부족했습니다. 3단계: 알림 받기 (꿀팁) # 작업이 감시하지 않을 때 실패하는 경우가 많습니다. Slurm이 이메일로 알려주게 합니다. 작업 스크립트에 추가하세요.\n#SBATCH --mail-type=FAIL,END #SBATCH --mail-user=your@email.com FAIL: 충돌 시에만 알려줍니다. END: 완료 시 알려줍니다 (성공이든 실패든). 4. 리소스 효율 (seff) # 파워 유저가 되기 위한 가장 중요한 부분입니다.\n40인 연회용 테이블을 예약했는데 혼자 밥 먹었다고 상상해봅니다. 식당 매니저(스케줄러)가 화낼 겁니다. HPC에서도 --cpus-per-task=40을 요청했는데 Python 스크립트가 코어 1개만 쓸 때 이런 일이 생겨요.\n효율을 어떻게 확인하나요? seff를 씁니다.\n$ seff 12345 Job ID: 12345 Cluster: cluster User/Group: user123/users State: COMPLETED (exit code 0) Cores: 8 CPU Utilized: 00:01:25 CPU Efficiency: 10.23% of 00:01:30 core-walltime Job Wall-clock time: 00:01:30 Memory Utilized: 12.09 MB Memory Efficiency: 0.15% of 8.00 GB (8.00 GB/node) 참고: 일부 클러스터에서는 seff가 비활성화돼 있을 수 있습니다. 그럴 때는 sacct에서 AveCPU, MaxRSS를 확인합니다.\n$ sacct -j 12345 --format=JobID,State,AveCPU,MaxRSS JobID State AveCPU MaxRSS ------------ ---------- ---------- ---------- 12345 COMPLETED 12345.batch COMPLETED 00:01:30 12384K 결과 해석:\nCPU 효율:\n나쁨 (50% 미만): 코어를 너무 많이 요청했습니다. 코드가 병렬화되지 않았다면 코어 1개만 요청하세요.\n좋음 (~90%): 리소스를 잘 활용하고 있습니다.\n메모리 효율:\n나쁨 (10% 미만): RAM을 너무 많이 요청했습니다. 다음번에는 --mem을 줄이세요.\n위험 (95% 초과): 충돌(OOM) 직전입니다. --mem을 약간(20% 정도) 늘리세요.\n왜 중요한가요? 작은 작업은 클러스터의 \u0026lsquo;빈 틈\u0026rsquo;을 채우기 더 쉽습니다. 꼭 필요한 만큼만 요청하면 작업이 더 빨리 시작됩니다!\n5. 작업이 왜 대기 중 일까요? (페어 쉐어) # 빈 노드가 있어 보이는데 PD (대기 중) 상태가 Priority 또는 Resources 이유로 계속 남아 있을 때가 있습니다.\n이건 페어 쉐어(Fair Share) 때문일 가능성이 높습니다. \u0026lsquo;카르마 시스템\u0026rsquo;이라고 생각하면 됩니다.\n클러스터는 공유 자원입니다. 지난주에 무거운 작업을 수천 개 돌렸다면 \u0026lsquo;카르마\u0026rsquo;가 낮아져요. 줄 서서 기다려야 합니다. 한동안 클러스터를 안 썼다면 \u0026lsquo;카르마\u0026rsquo;가 높습니다. 줄에서 앞으로 갑니다. 정확한 이유 확인하기\n추측하지 말고 Slurm에게 직접 물어보세요.\n$ squeue -j 12345 -o \u0026#34;%.18i %.9T %.30R\u0026#34; JOBID STATE NODELIST(REASON) 12345 PENDING (Priority) 구체적인 이유 코드를 보여줍니다.\nPriority: 기다리면 됩니다. 페어 쉐어 때문입니다. Resources: 클러스터가 바쁘거나 특정 노드가 사용 중입니다. QOSMaxJobsLimit: 동시 실행 가능한 작업 수 한도에 도달했습니다. Dependency: 다른 작업이 끝나길 기다리고 있습니다. 당황하지 마세요. 대부분의 경우 그냥 기다리면 됩니다.\n6. 요약 \u0026amp; 치트시트 # 디버깅 마인드셋 (한 번만 읽어두세요) # 작업이 실패했다면 순서대로 이 질문을 해보세요.\n시작은 됐나요? (squeue, scontrol) -\u0026gt; 안 됐다면 스크립트 문법을 확인하세요. 끝났나요, 충돌했나요? (sacct) -\u0026gt; 상태(State)를 확인하세요. 왜 충돌했나요? (로그 파일) -\u0026gt; .err 파일을 읽으세요. 리소스를 올바르게 요청했나요? (seff) -\u0026gt; 메모리 사용량을 확인하세요. 더 작게 만들 수 있나요? -\u0026gt; 작은 작업이 더 빨리 실행됩니다. 축하합니다! HPC 101을 공식적으로 졸업했습니다. 이제 클러스터의 손님이 아니라 주민입니다.\n목표 명령어 상세 정보 확인 scontrol show job \u0026lt;JOBID\u0026gt; 작업 취소 scancel \u0026lt;JOBID\u0026gt; 히스토리 확인 sacct -j \u0026lt;JOBID\u0026gt; 효율 확인 seff \u0026lt;JOBID\u0026gt; 다음은? 다음 시리즈에서는 방향을 완전히 바꿀 겁니다. 클러스터 \u0026lsquo;사용자\u0026rsquo;에서 벗어나 \u0026lsquo;엔지니어\u0026rsquo;처럼 생각하기 시작할 겁니다. HPC 클러스터를 직접 만드는 새 시리즈가 시작됩니다.\n다음 시리즈에서 만나요!\n즐거운 컴퓨팅 되세요!\n","date":"2026년 1월 28일","externalUrl":null,"permalink":"/ko/posts/hpc101-04/","section":"포스트","summary":"HPC 101 시리즈 마지막 편입니다. scontrol로 실행 중인 작업을 살펴보고, sacct로 실패 원인을 파악하고, seff로 리소스 효율을 확인하는 방법을 배웁니다.","title":"[HPC 101] 작업 디버깅: 내 작업이 왜 실패했을까?","type":"posts"},{"content":"","date":"2026년 1월 28일","externalUrl":null,"permalink":"/ko/tags/debugging/","section":"Tags","summary":"","title":"Debugging","type":"tags"},{"content":"","date":"2026년 1월 28일","externalUrl":null,"permalink":"/ko/series/hpc-101/","section":"시리즈","summary":"","title":"HPC From Scratch","type":"series"},{"content":"","date":"2026년 1월 28일","externalUrl":null,"permalink":"/ko/tags/seff/","section":"Tags","summary":"","title":"Seff","type":"tags"},{"content":"","date":"2026년 1월 28일","externalUrl":null,"permalink":"/ko/tags/troubleshooting/","section":"Tags","summary":"","title":"Troubleshooting","type":"tags"},{"content":"잘 된 것처럼 보였습니다. 그런데 사실 숨겨진 문제가 생긴 겁니다.\nHPC 101 시리즈에 돌아온 걸 환영합니다.\n이전 포스트에서 데이터를 전송하는 방법을 배웠습니다. 이제 Python 코드를 실행할 준비가 됐죠. 로그인하고 pip install numpy를 입력하고 Enter를 눌렀습니다.\n예전과 달리 \u0026lsquo;권한 없음\u0026rsquo; 오류가 안 뜰 수도 있습니다. 대신 이런 메시지가 나옵니다.\n[user123@compute-node-01 ~]$ pip install numpy Defaulting to user installation because normal site-packages is not writeable Collecting numpy Using cached numpy-2.3.5... Installing collected packages: numpy Successfully installed numpy-2.3.5 \u0026lsquo;Successfully installed\u0026rsquo;라고 나왔으니 다 됐죠?\n아니요. 방금 \u0026lsquo;사용자 설치\u0026rsquo; 함정에 빠진 겁니다.\n오늘은 왜 이 \u0026lsquo;자동\u0026rsquo; 설치 방식이 HPC에서 위험한지, 그리고 가상 환경(Virtual Environment)으로 올바른 \u0026lsquo;개인 실험실\u0026rsquo;을 만드는 방법을 배울 겁니다.\n1. 함정: \u0026lsquo;배낭\u0026rsquo; 문제 # 시스템이 전역 라이브러리에 쓸 수 없다고 판단하면, 홈 폴더의 숨겨진 경로(보통 ~/.local/lib/python3.x/site-packages)에 조용히 패키지를 설치합니다.\n비유를 들어보겠습니다.\n시스템 Python: 이건 레스토랑 주방 팬트리입니다. 기본 재료가 갖춰져 있고 손댈 수 없습니다. 사용자 설치 (pip install): 이건 내 배낭입니다. 팬트리를 못 쓰니까 재료를 배낭에 막 넣는 겁니다. 가상 환경: 이건 따로 마련한 도시락통입니다. 배낭(사용자 설치)이 나쁜 이유는 뭘까요?\n격리 없음 (의존성 지옥): 프로젝트 A는 NumPy 1.20이 필요하고 프로젝트 B는 NumPy 2.0이 필요합니다. 배낭에 둘 다 넣으면 뒤엉켜요. 프로젝트 B를 고치려다 프로젝트 A가 망가지는 겁니다.\n2. 해결책: 나만의 도시락통 # 배낭에 다 쑤셔 넣는 대신 가상 환경(Virtual Environment)을 써야 합니다.\n가상 환경은 내 개인 도시락통입니다.\n격리: \u0026lsquo;프로젝트 A 통\u0026rsquo;과 \u0026lsquo;프로젝트 B 통\u0026rsquo;을 따로 만듭니다. 서로 섞이지 않습니다. 안전: 한 통에서 설치가 꼬여도 그냥 그 통만 버리면 됩니다. 다른 프로젝트는 안전합니다. HPC에서 환경을 쓰는 건 \u0026lsquo;좋은 습관\u0026rsquo; 수준이 아니에요. 살아남기 위한 유일한 방법입니다.\n3. 도구 선택: Conda vs. Venv # 환경을 만드는 주요 도구는 두 가지입니다. Conda와 Venv 중 뭘 써야 할까요?\nConda는:\n플랫폼 무관 패키지 관리자로, Python 패키지뿐만 아니라 외부 라이브러리(C, C++, CUDA)까지 설치합니다.\n장점으로는 Python 버전 관리(오늘은 3.8 환경, 내일은 3.12 환경), 바이너리 의존성 처리(GPU 지원 라이브러리 CUDA/cuDNN을 자동으로 처리)가 있습니다.\n단점으로는 용량이 크고(venv보다 디스크를 많이 씁니다), 느린 경우가 있고(의존성 해결에 시간이 걸릴 수 있습니다), 셸 오염 문제가 있습니다(conda init을 잘못 쓰면 터미널이 망가질 수 있습니다. 2단계 참고).\n주의: 최근 Anaconda 라이선스 변경으로 많은 HPC 센터가 Anaconda/Miniconda에서 Miniforge로 전환하고 있습니다. 기본값으로 무료 conda-forge 채널을 씁니다. 명령어와 워크플로는 동일합니다. 자세한 내용은 Anaconda 이용약관을 참고하세요.\n추천: 과학 연구, 엔지니어링, AI/ML 프로젝트에 가장 좋은 선택입니다.\nVenv는:\n가벼운 가상 환경을 만드는 Python 내장 모듈입니다.\n장점으로는 가볍고 빠름(Python 내장, 즉시 생성), 깔끔함(셸 설정 파일을 건드리지 않습니다)이 있습니다.\n단점으로는 제한적(CUDA 드라이버 같은 Python 외부 도구 설치가 어렵습니다), Python 버전 종속(시스템 Python 버전에 묶입니다)이 있습니다.\n추천: 단순한 Python 스크립트나 순수 소프트웨어 개발에 좋습니다.\n4. 환경 만들어보기 # 실제로 해보겠습니다.\n1단계: 환경 생성 # # 1. 모듈 로드 $ module load miniconda3 # 2. 환경 생성 $ conda create --name myenv python=3.13 # 홈 디렉토리 공간 절약을 위해 연구실 그룹 디렉토리에 저장하기 $ conda create --prefix /projects/myLAB/myCondaEnv python=3.13 # 1. Python 모듈 로드 $ module load python/3.13.10 # 2. 환경 생성 $ python3 -m venv /projects/myLAB/myenv # 홈 디렉토리 공간 절약을 위해 연구실 그룹 디렉토리에 저장하기 $ python3 -m venv /projects/myLAB/myVenv 2단계: 활성화 (올바른 방법) # 경고: conda init을 실행하지 마세요\n많은 튜토리얼에서 conda init을 실행하라고 합니다. HPC에서는 위험합니다. .bashrc 파일을 수정해서 로그인할 때마다 (base) 환경이 자동으로 활성화되게 만드는데, 이러면,\n시스템 모듈(OpenMPI, GCC)과 충돌이 생길 수 있습니다. Open OnDemand 실패: Jupyter나 RStudio 세션이 시작 안 될 수 있습니다. 이미 실행했다면 자동 활성화를 끄세요.\n$ conda config --set auto_activate_base false 대신 source activate \u0026lt;ENV\u0026gt; 또는 전체 경로를 쓰세요.\n# \u0026#34;HPC 안전\u0026#34; 방법 (권장) $ source activate /projects/myLAB/myCondaEnv # 또는 모듈 시스템을 올바르게 쓰고 있다면: $ conda activate /projects/myLAB/myCondaEnv # activate 스크립트를 source로 실행 $ source /projects/myLAB/myVenv/bin/activate 3단계: 패키지 설치 # # 바이너리 의존성(CUDA 등)을 더 잘 처리합니다 $ module load cuda/12.8 # CUDA 버전을 맞춰야 합니다 (myCondaEnv) $ conda install -c conda-forge cupy cuda-version=12.8 (myCondaEnv) $ conda install numpy pandas # Conda와 Venv 모두에서 작동합니다 (myVenv) $ pip install matplotlib huggingface-hub 기본 원칙: 활성화된 환경 안에서만 pip install을 실행합니다.\n4단계: 비활성화 # # Conda의 경우 $ conda deactivate # Venv의 경우 $ deactivate 5. 유지 관리: 캐시 청소 # 언젠가 이런 오류를 보게 될 겁니다: Disk quota exceeded.\n폴더를 확인해봤는데 별로 크지 않습니다. 공간은 어디로 간 걸까요? Conda와 pip 모두 다운로드한 파일을 숨겨진 캐시 폴더(~/.conda/pkgs 또는 ~/.cache/pip)에 저장합니다. 10GB 이상 쉽게 불어나요.\n올바른 청소 방법:\n# 사용하지 않는 패키지와 캐시 제거 $ conda clean --all # pip 캐시 제거 $ pip cache purge 최후의 수단: 디스크가 100% 꽉 찼다면 위 명령어가 잠금 파일을 생성하지 못해 실패할 수 있습니다. 그럴 때는 직접 삭제합니다.\n# 주의: rm -rf는 신중하게 사용하세요 # Conda의 경우 $ rm -rf ~/.conda/pkgs/* # pip의 경우 $ rm -rf ~/.cache/pip/* 캐시를 삭제해도 설치된 환경은 망가지지 않습니다. 다운로드된 설치 파일만 삭제됩니다.\n노트: 기본적으로 Conda는 다운로드한 패키지를 ~/.conda/pkgs에 저장합니다. 대부분의 HPC 클러스터는 홈 디렉터리 용량 제한(quota)이 있어서, 이 캐시가 생각보다 빠르게 공간을 차지할 수 있어요. 캐시 위치를 스크래치나 프로젝트 디렉터리로 바꾸려면:\n[user@login ~]$ conda config --add pkgs_dirs /scratch/user123/pkgDir 또는 ~/.condarc 파일을 직접 수정해도 됩니다:\npkgs_dirs: - /scratch/user123/pkgDir 6. 요약 \u0026amp; 치트시트 # HPC에서 환경을 쓰는 건 작업 공간을 깔끔하게 유지하고 \u0026lsquo;디스크 할당량 초과\u0026rsquo; 오류를 예방하기 위해서입니다.\n작업 Conda 명령어 Venv 명령어 생성 conda create --prefix \u0026lt;경로\u0026gt; python -m venv \u0026lt;경로\u0026gt; 활성화 source activate \u0026lt;경로\u0026gt; source \u0026lt;경로\u0026gt;/bin/activate 설치 conda install / pip install pip install 청소 conda clean --all pip cache purge 조언: 새로 시작한다면 AI/HPC 프로젝트에는 Miniforge가 가장 안전한 선택입니다. Conda 워크플로 그대로 쓰면서 conda-forge 패키지를 쓸 수 있고 라이선스 걱정도 없습니다. 클러스터가 이미 Miniconda나 Anaconda를 제공한다면 그것도 괜찮습니다. 그리고 제발, 로그인을 깔끔하게 유지하기 위해 conda init은 피하세요.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 1월 18일","externalUrl":null,"permalink":"/ko/posts/hpc101-03/","section":"포스트","summary":"HPC에서 pip install을 그냥 실행하면 나중에 골치가 아파집니다. Conda와 venv로 프로젝트별 가상 환경을 만들면 충돌도 없고 디스크 할당량 초과도 예방할 수 있습니다.","title":"[HPC 101] HPC에서 Python 쓰기: root 없이 Conda와 venv로 환경 만들기","type":"posts"},{"content":"","date":"2026년 1월 18일","externalUrl":null,"permalink":"/ko/tags/conda/","section":"Tags","summary":"","title":"Conda","type":"tags"},{"content":"","date":"2026년 1월 18일","externalUrl":null,"permalink":"/ko/tags/miniconda/","section":"Tags","summary":"","title":"Miniconda","type":"tags"},{"content":"","date":"2026년 1월 18일","externalUrl":null,"permalink":"/ko/tags/python/","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"2026년 1월 18일","externalUrl":null,"permalink":"/ko/tags/venv/","section":"Tags","summary":"","title":"Venv","type":"tags"},{"content":"무서운 검은 화면을 해커의 놀이터로 만들어 봅시다.\n리눅스를 처음 접하면 보통 검은 터미널 화면을 보며 잘못 건드렸다가 폭발할 것 같은 무서운 도구라고 느낍니다. 실수로 뭔가를 눌러서 홈 디렉토리를 날려버릴까봐 겁이 나는 것이죠. 하지만 일단 익숙해지면, 이 화면은 여러분을 멋진 해커처럼 보이게 만들어 줍니다.\n이 포스트를 읽고 나면 리눅스 터미널에서 자유롭게 이동하고, 파일을 관리하고, 당황하지 않고 텍스트를 편집할 수 있습니다.\n1. 어두운 집 # CLI(Command Line Interface, 명령줄 인터페이스)를 탐색하는 건 한밤중에 잠에서 깨는 것과 비슷합니다.\n누군가 예쁜 GUI(Graphical User Interface, 그래픽 사용자 인터페이스)를 빼앗고 CLI 화면 앞에 던져놓는다면, 정전이 난 것처럼 막막하게 느껴질 겁니다.\n소파에서 낮잠을 자다가 새벽 3시에 깼다고 상상해 봅시다. 사방이 캄캄하고 침대까지 가는 길은 알고 있지만, 아무것도 보이지 않습니다.\n터미널을 사용하는 것은 정확히 이와 같습니다. 한 걸음 내딛기 전에 내가 어디 있는지, 주변에 무엇이 있는지 확인해야 합니다. 어디로 가야 할지 정확히 안다면 곧장 방으로 걸어갈 수 있습니다. 하지만 테이블에 발부리를 걸리지 않도록 항상 조심해야 합니다.\n2. 집 안을 돌아다니기 # 새벽 3시, 불은 꺼지고, 손전등도 없습니다. 침대까지 가고 싶습니다.\n먼저 자신의 위치를 확인해야 합니다. pwd(Print Working Directory)가 이 역할을 합니다. 지금 내가 서 있는 곳을 정확히 알려줍니다.\n$ pwd /home/my_family/first_floor 이동하기 전에 주변에 무엇이 있는지 알아야 테이블에 걸려 넘어지지 않습니다. ls(List) 명령어는 주변을 손으로 더듬어 확인하는 것과 같습니다. 옵션을 추가하면 숨겨진 항목이나 상세 정보도 볼 수 있습니다.\n$ ls couch kitchen lamp restroom staircase trash_bin TV $ ls -a couch kitchen lamp .phone .remote restroom TV user123_room # Now you found a phone and a remote hidden under the couch! # (Files starting with \u0026#39;.\u0026#39; are hidden in Linux) $ ls -l total 32 -rwxr-xr-x. 1 family family 4096 Dec 27 20:22 couch drwxr--r--. 1 family family 4096 Dec 6 20:02 kitchen -rwxr--r--. 1 family family 10517 Dec 26 18:51 lamp drwxr-xr-x. 1 family family 4096 Dec 26 17:49 restroom -rwxr-xr-x. 1 parents family 840 Dec 26 18:03 TV -rwxr-xr-x. 1 family family 840 Dec 26 18:03 trash_bin drwxr-xr-x. 1 user123 family 4096 Dec 26 18:03 user123_room # You can see full details about each item or room # You won\u0026#39;t see hidden items here 참고: -a와 -l을 합쳐 -la로 사용하면 모든 항목의 상세 정보를 볼 수 있습니다.\n상세 보기(-l)를 켜면 암호 같은 코드들이 나타납니다. 비밀 코드처럼 보이지만 걱정하지 마세요. 해독 해보겠습니다.\n-rwxrw-r-- 1 user group 46 Feb 14 16:37 File.txt ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | | 1 2 3 4 5 6 7 8 파일 유형: -(파일) 또는 d(디렉토리) 권한(Permissions): rwxrw-r-- (누가 무엇을 할 수 있는지) 소유자 및 그룹: 이 항목을 소유한 사람 크기 및 시간: 크기와 마지막으로 수정된 시각 첫 번째 부분을 자세히 봅시다. 방에 들어오거나 물건을 만질 수 있는 사람을 정의합니다.\nType Owner Group Others [-] [rwx] [rw-] [r--] | | | | File Read Read Read Write Write Exec rwx가 보이면 소유자가 모든 권한(읽기, 쓰기, 실행)을 갖고 있다는 뜻입니다.\n이제 지도 읽는 법을 배웠으니 이동해 봅시다.\n먼저 화장실(restroom)로 가겠습니다. 목록에 보이므로 cd(Change Directory) 명령어로 바로 들어갈 수 있습니다.\n$ cd restroom $ pwd /home/my_family/first_floor/restroom $ ls bath_tub body_wash hand_soap shampoo shower sink toilet towel 볼일을 마치고 침실(user123_room)로 가고 싶습니다. 그런데 ls로 보니 방이 없습니다! 두 가지 방법이 있습니다.\n복도로 나가서 위치를 확인한 뒤 방으로 들어가기. $ cd .. $ cd user123_room 복도로 나가면서 바로 방으로 들어가기. $ cd ../user123_room ..이 뭔가요? 리눅스에서 점 하나(.)는 여기(현재 위치), 점 두 개(..)는 **부모 디렉토리(한 단계 위)**를 나타냅니다. (아쉽게도 점은 두 개가 최대입니다. \u0026ldquo;...\u0026ldquo;이나 \u0026ldquo;....\u0026ldquo;은 없습니다.)\n상대 경로 vs. 절대 경로 점(. 또는 ..)을 사용하는 방법은 현재 위치 근처에서만 작동합니다. 친구 집에 있거나 현재 위치에서 멀리 떨어져 있다면 어떨까요? 친구 집 화장실에서 나올 수는 있지만(..), 거기서 내 방(user123_room)을 찾을 수는 없습니다. 이럴 때는 절대 경로(전체 주소)가 필요합니다.\n# Relative path (Works only if you are in the hallway) $ cd user123_room # Absolute path (Works from anywhere in the universe) $ cd /home/my_family/user123_room 3. 마법 주문: 파일 조작 # 이제 상상력이 좀 더 필요합니다. 캄캄한 집 안을 걷는 사람이 아니라 마법사가 되었습니다. 마법 지팡이로 방과 물건을 만들거나, 쓰레기를 순식간에 없앨 수 있습니다.\n생성 (mkdir, touch) 먼저 빈 방을 만들어 봅시다. 주문은 mkdir(Make Directory)입니다.\n$ mkdir new_room 빈 파일(물건)을 만들고 싶다면 touch를 사용합니다.\n$ touch magic_scroll.txt 순간이동 (mv) 거실에 있는 TV를 새 방으로 옮기고 싶습니다. mv(Move) 주문을 외웁니다.\n$ mv /home/my_family/first_floor/TV /home/my_family/first_floor/new_room/ 참고: 리눅스에서 파일 이름을 바꾸는 것은 같은 위치에 새 이름으로 이동하는 것과 같습니다.\n$ mv old_name.txt new_name.txt 복제 (cp) TV를 가져가면 아빠가 슬퍼할 것입니다. cp(Copy)로 복제본을 만들어 봅시다.\n# Copy TV to the parent directory $ cp ./TV ../ 이제 모두가 행복합니다!\n소멸 (rm) 엄마가 쓰레기를 버려달라고 했습니다. 마법 능력으로 그냥 소각해 버리면 됩니다. rm(Remove)을 사용합니다.\n$ rm ./trash_bin 팁: rm을 사용할 때는 정확히 무엇을 삭제하는지 알 수 있도록 상대 경로를 사용하는 것이 좋습니다.\n경고: Windows나 Mac과 달리 리눅스의 rm은 파일을 휴지통에 보내지 않습니다. rm으로 삭제된 파일은 영원히 사라집니다. 소각된 것입니다. 이 주문을 사용할 때는 신중하게 하십시오.\n4. X-Ray 시야 (파일 확인하기) # 밖에 나갔다 오니 부모님이 테이블에 메모를 남겨두었습니다.\n사촌 데리러 공항에 간다.\n부엌 좀 치워놔.\n저녁 내내 TV만 보지 말고.\n\u0026hellip; (100줄 더) \u0026hellip;\n우리 오기 전에 숙제 꼭 다 끝내놓아.\n저녁에 뭐 사다줄 거 있으면 전화해.\n어떻게 읽을까요?\ncat: 메모 전체를 한 번에 열기 less: 내용 뷰어에서 위아래로 스크롤하며 읽기 head: 처음 몇 줄만 확인 tail: 마지막 몇 줄만 확인 추천 사용법:\n명령어 사용 상황 cat 짧은 파일 (한 화면에 들어오는 분량) less 긴 파일 (로그 파일이나 코드) head 첫 부분만 확인하고 싶을 때 tail 최신 내용 확인 (로그 파일 끝부분) 참고: less는 내용 뷰어입니다. 닫으려면 q를 누르세요. ESC로는 닫히지 않습니다.\n# Read the first 2 lines $ head -2 note.txt Hey, we are leaving to pick up your cousin from the airport. You have a few things to do once you are back. # Read the last 2 lines $ tail -2 note.txt Make sure to finish your homework before we are back. Call if you want us to pick up anything for dinner. 5. 기록하기 (에디터) # 답장을 쓰고 싶습니다. 터미널에서는 Microsoft Word를 열 수 없습니다. nano나 vim 같은 터미널 에디터가 필요합니다.\n그리고\u0026hellip; emacs 이야기는 지금 하지 않겠습니다. emacs 팬이시라면 양해 부탁드립니다.\n옵션 1: Nano (메모장) 간단한 메모지와 펜이 필요하다면 nano를 사용하세요. 초보자에게 매우 친화적입니다.\n$ nano reply.txt 원하는 내용을 그냥 입력하면 됩니다. 단축키는 화면 하단에 표시됩니다.\n^는 Ctrl 키를 의미합니다. 저장: Ctrl + O (Write Out)를 누른 뒤 Enter. 종료: Ctrl + X. 옵션 2: Vim (전문가 도구) 강력하지만 조금 까다로운 도구입니다. 가장 중요한 개념은 모드입니다.\n일반 모드(Normal Mode): 텍스트를 입력할 수 없습니다. 내용을 보거나 명령을 입력하는 모드입니다. 입력 모드(Insert Mode): 실제로 텍스트를 입력하고 편집할 수 있습니다. Vim에서 살아남는 법:\nvim reply.txt를 입력합니다. i를 눌러 입력을 시작합니다 (Insert Mode). 다 작성했으면 Esc를 누릅니다 (Insert Mode 종료). :wq를 입력하고 Enter를 누릅니다 (저장 후 종료). 막혀서 패닉 상태라면? Esc를 누르고 :q!를 입력합니다 (저장 없이 강제 종료). 6. 비밀 팁 # 이 팁들은 우리끼리만 알기로 합시다.\n1. Tab 자동완성 (마법 키) 긴 파일 이름을 직접 타이핑하지 마세요. 앞 글자 몇 개만 입력하고 TAB 키를 누르면 됩니다.\n$ cd /home/my_family/first_f [TAB] # Becomes: $ cd /home/my_family/first_floor/ 2. 히스토리 (방향키) 전에 사용한 명령어가 있나요? 다시 타이핑하지 말고 위아래 방향키로 이전 명령어를 찾아 실행하세요.\n3. 중단 버튼 (Ctrl + C) 실행 중인 프로그램에서 빠져나오고 싶거나, 잘못 입력한 명령어를 취소하고 싶다면? Ctrl + C를 누르세요.\n[user@linux]$ i_wrote_a_very_long_random_command [Ctrl+C] [user@linux]$ i_wrote_a_very_long_random_command^C [user@linux]$ (Canceled!) 4. 화면 초기화 (clear) 화면이 너무 지저분해졌나요? clear를 입력하면 화면이 깨끗하게 지워집니다.\n금지된 주문 마지막 경고입니다. 절대로 이 명령어는 실행하지 마세요.\n$ rm -rf / 이것은 리눅스 세계의 핵폭탄 버튼입니다. 루트 디렉토리에서 모든 것을 삭제하려고 시도합니다. 한 번 실행하면 되돌릴 수 없습니다.\n정리\n탐색: pwd (어디 있지?), ls (주변에 뭐 있지?), cd (이동하기). 관리: mkdir (디렉토리 생성), touch (파일 생성), cp (복사), mv (이동/이름 변경), rm (삭제). 보기: cat (짧은 파일), less (긴 파일), head/tail (처음/끝 부분). 편집: nano (간단한 도구), vim (고급 도구). 생존: TAB으로 자동완성, Ctrl+C로 중단. 잘 하셨습니다! 이제 어두운 곳에서도 편안하게 이동할 수 있습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 1월 9일","externalUrl":null,"permalink":"/ko/posts/linux101-01/","section":"포스트","summary":"많은 분들의 첫 터미널 경험은 어딘가에서 명령어를 복사해 붙여넣고 되길 바라는 겁니다. 이 포스트는 다른 접근을 택합니다. 실제로 무슨 일이 일어나는지 설명하고, 매일 쓸 명령어만 다루고, 터미널이 함정이 아닌 도구로 느껴지도록 돕습니다.","title":"[Linux 101] Linux 터미널 입문: 실제로 쓰는 명령어 모음","type":"posts"},{"content":"","date":"2026년 1월 9일","externalUrl":null,"permalink":"/ko/series/linux-101/","section":"시리즈","summary":"","title":"Linux 101","type":"series"},{"content":"","date":"2026년 1월 9일","externalUrl":null,"permalink":"/ko/tags/terminal/","section":"Tags","summary":"","title":"Terminal","type":"tags"},{"content":"컴퓨팅 파워는 생겼습니다. 이제 데이터가 필요합니다.\n로컬 머신(랩탑이나 워크스테이션)과 HPC 클러스터 사이를 오가며 파일을 주고받는 건 연구자의 일상입니다. 코드, 입력 데이터, 그리고 결과물까지 모두요. 파일 압축부터 대용량 데이터셋 처리까지, 파일 전송 기초를 정리했습니다.\n1. 기본 규칙: 옮기기 전에 먼저 묶습니다 # 이사할 때를 생각해보겠습니다.\n이전 포스트에서 HPC 클러스터를 호텔에 비유했습니다. 그럼 제 랩탑은 예전에 살던 집입니다. 짐(데이터)을 새 집(HPC 클러스터)으로 옮겨야 합니다.\n양말이 10,000켤레 있다고 상상해봅니다 (작은 파일들). 이사 트럭에 하나씩 들고 나를 건가요? 당연히 아니죠. 먼저 박스에 넣을 겁니다.\nHPC에서도 마찬가지입니다. 작은 파일 수천 개를 하나씩 전송하면 네트워크 오버헤드 때문에 성능이 뚝 떨어져요. 그래서 파일이나 폴더를 먼저 아카이브로 묶어야 합니다.\nTar vs. Zip 선택하기 # # 묶기 (아카이브 생성) $ tar -czf my_data.tar.gz my_folder # -c: 생성 (Create) # -z: Gzip 압축 # -f: 파일 이름 지정 # 풀기 (아카이브 압축 해제) $ tar -xf my_data.tar.gz # -x: 압축 해제 (Extract) # -f: 파일 이름 지정 # (최신 시스템에서는 압축 방식을 자동으로 감지합니다) # 묶기 (아카이브 생성) $ zip -r my_data.zip my_folder # -r: 하위 폴더 포함 (Recursive) # 풀기 (아카이브 압축 해제) $ unzip my_data.zip 2. 직접 다운로드 (웹에서 HPC로) # 상황: 데이터가 웹사이트에 올라와 있습니다.\n랩탑에 먼저 받아서 다시 클러스터에 올리지 마세요. 쓸데없는 이중 작업입니다. 택배를 새 집(클러스터)으로 직접 보내면 됩니다.\n클러스터의 컴퓨트 노드(또는 전용 데이터 전송 노드가 있다면 거기서) wget 또는 curl을 씁니다. 로그인 노드에서 파일 전송을 직접 실행하는 건 보통 권장하지 않습니다.\n# 방법 1: wget 사용 # wget \u0026lt;파일 주소\u0026gt; $ wget https://example.com/dataset.tar.gz # 방법 2: curl 사용 # curl -o \u0026lt;저장할 파일 이름\u0026gt; \u0026lt;파일 주소\u0026gt; $ curl -o dataset.tar.gz https://example.com/dataset.tar.gz 3. 전송 도구: SCP vs. Rsync # 상황: 파일이 제 랩탑에 있습니다. (아래 명령어는 로컬 터미널에서 실행해야 합니다. 클러스터 안에서 하는 게 아니에요.)\nSCP (단순 던지기) # 작은 파일이나 압축된 아카이브 하나를 보낼 때는 scp(Secure Copy)를 씁니다. 간단하고 빠르거든요.\n# 업로드: 랩탑 -\u0026gt; 클러스터 $ scp my_data.tar.gz \u0026lt;USER\u0026gt;@\u0026lt;HOST_NAME\u0026gt;:~/ # 예: scp data.tar.gz user123@data.university.edu:~/ # 다운로드: 클러스터 -\u0026gt; 랩탑 $ scp \u0026lt;USER\u0026gt;@\u0026lt;HOST_NAME\u0026gt;:~/results.tar.gz ./ # 예: scp user123@data.university.edu:~/data.tar.gz ./ Rsync (스마트 이동) # 100GB짜리 파일을 전송하다가 WiFi가 99%에서 끊기면? scp는 실패하고 0%부터 다시 시작해야 합니다. 정말 끔찍하죠.\nrsync를 써보세요. 소스와 목적지 사이의 차이를 비교해서 연결이 끊겨도 이어서 전송합니다.\n$ rsync -azP my_big_data \u0026lt;USER\u0026gt;@\u0026lt;CLUSTER\u0026gt;:~/ # 예: rsync -azP data_tar.gz user123@data.university.edu:~/ 플래그 설명 (-azP):\n-a: 아카이브 모드. 권한, 타임스탬프, 심볼릭 링크를 보존합니다. -z: 전송 중 파일 데이터를 압축해서 속도를 높입니다. -P: 진행 표시줄을 보여주고 부분 전송 이어받기가 됩니다. 선택 기준:\n작은 파일이나 단순 전송? SCP 큰 파일이나 불안정한 네트워크? Rsync 4. GUI 클라이언트 (WinSCP \u0026amp; FileZilla) # \u0026ldquo;터미널은 질색이에요. 그냥 드래그 앤 드롭으로 딸깍은 안 되나요?\u0026rdquo;\n됩니다! 아직 커맨드라인이 익숙하지 않거나 파일을 시각적으로 탐색하고 싶다면 SFTP 클라이언트를 써보세요.\n추천 도구 # Windows 전용: WinSCP (가장 인기 많습니다) Windows/Mac/Linux: FileZilla 또는 Cyberduck 연결 방법 # SSH 접속 정보와 똑같이 입력하면 됩니다.\n파일 프로토콜: SFTP 호스트 이름: 클러스터 주소 (예: data.university.edu) 포트 번호: 22 (기본 SSH 포트) 사용자/비밀번호: 본인 계정 정보 연결되면 왼쪽에 내 랩탑 파일이, 오른쪽에 클러스터 파일이 보입니다. 드래그 앤 드롭으로 전송하면 됩니다.\nGlobus 사용자 참고: 기관 간이나 클러스터 간에 수 테라바이트, 페타바이트 단위 대용량 데이터를 전송해야 한다면 시스템 관리자에게 Globus를 물어보세요. 연구 센터에서 많이 지원하는 고성능 전송 서비스입니다. SCP/SFTP보다 훨씬 빠르고 안정적입니다.\n5. Git으로 코드 관리하기 # 상황: Python이나 C++ 스크립트를 옮겨야 합니다.\nrsync로 코드를 복사할 수는 있습니다. 그런데 더 좋은 방법이 있습니다. 코드를 도서관의 책처럼 관리하는 겁니다. 오래된 판을 남기면서 새 판을 추가하고 대출도 할 수 있는 방식이요. Git을 쓰세요.\n랩탑에서: 코드를 commit하고 GitHub/GitLab에 push합니다. # 변경사항 커밋 $ git commit -a -m \u0026#34;커밋 메시지\u0026#34; # GitHub에 push $ git push 클러스터에서: 저장소를 clone하거나 pull합니다. # 클러스터에서 $ git clone https://github.com/username/my-project.git # 변경사항 받아오기 $ git pull 버전 히스토리가 안전하게 남고 협업도 훨씬 편해져요.\n6. 저장 공간 할당량 # 주의: \u0026lsquo;호텔 방\u0026rsquo; 비유 기억하시나요? 방에는 수용 인원 제한이 있습니다. HPC에서는 이걸 Quota(할당량) 라고 부릅니다.\n디스크가 꽉 차면 작업이 실패하고 파일을 저장하지 못하거나 심지어 로그인도 안 될 수 있습니다.\n확인 방법은? 기관마다 명령어가 다릅니다. 흔한 예시를 들면,\n$ quota -s $ lfs quota -u user123 /home/user123 $ check_usage 정확한 명령어는 사용자 문서를 확인하거나 지원팀에 확인해보세요. 대용량 데이터셋을 전송하기 전에 항상 여유 공간을 확인합니다.\n정리\n작은 파일은 먼저 묶습니다 (tar 또는 zip). 웹 데이터는 wget 으로 바로 받습니다. 빠른 소규모 전송에는 scp 를 씁니다. 크거나 안정적인 전송에는 rsync -azP 를 씁니다. 코드에는 git 을 씁니다. 잘 하셨습니다! 데이터를 준비하는 방법을 배웠습니다. 다음 포스트에서는 Conda로 소프트웨어 환경을 관리하는 방법을 다룰 겁니다.\n즐거운 컴퓨팅 되세요!\n","date":"2026년 1월 2일","externalUrl":null,"permalink":"/ko/posts/hpc101-02/","section":"포스트","summary":"HPC 클러스터와 로컬 머신 사이에서 파일을 주고받는 방법을 다룹니다. 작은 파일에는 SCP, 큰 파일이나 불안정한 연결에는 Rsync, 코드에는 Git을 쓰는 게 핵심입니다.","title":"[HPC 101] 파일 전송: SCP, Rsync, Git 완전 정리","type":"posts"},{"content":"","date":"2026년 1월 2일","externalUrl":null,"permalink":"/ko/tags/file-transfer/","section":"Tags","summary":"","title":"File Transfer","type":"tags"},{"content":"","date":"2026년 1월 2일","externalUrl":null,"permalink":"/ko/tags/git/","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":"","date":"2026년 1월 2일","externalUrl":null,"permalink":"/ko/tags/rsync/","section":"Tags","summary":"","title":"Rsync","type":"tags"},{"content":"","date":"2026년 1월 2일","externalUrl":null,"permalink":"/ko/tags/scp/","section":"Tags","summary":"","title":"SCP","type":"tags"},{"content":"클러스터 접속 권한이 생겼습니다. 터미널도 열려 있습니다. 이제 뭘 해야 할까요?\n방금 클러스터 접속 권한을 받았습니다. 호스트명, 사용자 이름, 그리고 빈 터미널이 있습니다. 이 글에서는 첫 번째 실제 작업까지 도달합니다. SSH로 접속하고, 모듈 시스템으로 소프트웨어를 로드하고, Slurm으로 실제 컴퓨트 노드에서 무언가를 실행하는 과정입니다.\n1. HPC란 무엇인가요? # HPC(고성능 컴퓨팅, High-Performance Computing)는 슈퍼컴퓨터나 컴퓨터 클러스터를 사용해서 복잡한 계산 문제를 해결합니다. 일반 워크스테이션이 일상적인 작업을 처리하는 반면, HPC는 공학, 과학에서 금융, 심리학까지 다양한 분야에서 사용되는 대규모 계산을 위해 설계됩니다. AI와 머신 러닝의 시대에 급격히 성장하는 기술이기도 합니다.\n한국에서는 KISTI의 누리온(Nurion)/뉴론(Neuron), NIPA AI 컴퓨팅 센터, 또는 소속 대학의 연구 클러스터 등에서 HPC 자원을 접할 수 있습니다.\n전 세계의 연구 기관과 기업이 새로운 제품 개발이나 집중적인 시뮬레이션 실행에 HPC를 사용합니다. 세계에서 가장 빠른 HPC 시스템 중 하나인 El Capitan은 Lawrence Livermore National Laboratory에 있습니다.\nHPC를 쓰는 이유 # 일반 데스크탑 PC로는 처리할 수 없는 고성능 계산이 필요한 문제를 해결하기 위해 HPC를 씁니다. 몇 가지 예시입니다.\nAI/ML: 여러 GPU를 사용해서 대규모 모델 학습 제약: 새로운 의약품 개발을 위한 분자 동역학 시뮬레이션 물리/화학: 양자 화학 계산이나 단백질 폴딩 시뮬레이션 기상학: 정확한 날씨 예보를 위한 대규모 데이터 처리 2. HPC 클러스터에 SSH 접속하기 # 계산하기 전에 클러스터에 연결해야 합니다.\nSSH란 무엇인가요? # SSH(Secure Shell)는 컴퓨터 간 보안 연결을 가능하게 하는 네트워크 프로토콜입니다. 원격 접속, 명령 실행, 파일 전송에 사용합니다. 너무 기술적으로 들려도 걱정하지 마세요. 간단히 말해서 내 PC와 HPC 클러스터를 연결하는 보안 터널입니다.\n접속해봅니다! # 터미널 창을 엽니다.\nLinux/Mac: 기본 Terminal 앱을 엽니다 Windows: Command Prompt(CMD), PowerShell, 또는 PuTTY나 MobaXterm 같은 서드파티 도구를 사용합니다 다음 명령어를 입력합니다.\n$ ssh \u0026lt;YOUR_ID\u0026gt;@\u0026lt;CLUSTER_HOST_NAME\u0026gt; # 예시: $ ssh user123@login.university.edu ($ 기호는 커맨드라인 프롬프트입니다. 타이핑하지 않아도 됩니다.)\n보안 프롬프트: 처음 연결하면 \u0026ldquo;Are you sure you want to continue connecting?\u0026rdquo; 메시지가 나옵니다. yes를 입력하고 Enter를 누르세요.\n패스워드 입력:\n패스워드를 입력합니다.\n참고: 입력할 때 별표(****)나 커서 움직임이 보이지 않습니다. Linux의 표준 보안 기능입니다. 패스워드를 입력하고 Enter를 누르면 됩니다.\n성공:\n아래와 같은 화면이 보이면 로그인에 성공한 겁니다! [user123@login-01 ~]$ 3. 모듈 사용법 # HPC에서는 sudo apt-get이나 sudo dnf로 소프트웨어를 설치할 수 없습니다. 대신 모듈 시스템(Module System)을 사용합니다.\n모듈 시스템이란 무엇인가요? # 대부분의 HPC 클러스터는 Environment Modules나 Lmod 같은 모듈 시스템으로 소프트웨어를 관리합니다. 개인 컴퓨터와 달리 HPC 클러스터는 모듈로 소프트웨어를 관리해서 다음을 지원합니다.\n충돌 없음: 서로 다른 사용자가 동시에 서로 다른 소프트웨어 버전을 사용할 수 있습니다 재현성: 연구 환경을 일관성 있게 유지할 수 있습니다 자동 로딩: 모듈을 로드하면 (예: OpenMPI) 필요한 의존성(예: GCC 컴파일러)이 자동으로 로드됩니다 주요 명령어 # 모듈 명령어 치트시트입니다.\n# 시스템의 사용 가능한 모든 모듈 보기 $ module avail # 특정 모듈 로드 $ module load \u0026lt;NAME\u0026gt;/\u0026lt;VERSION\u0026gt; # 예시: module load openmpi/4.1.8 # 현재 로드된 모듈 목록 보기 $ module list # 모듈 언로드 $ module unload \u0026lt;NAME\u0026gt; # 모든 모듈 언로드 $ module purge 권장 사항 # .bashrc 피하기: .bashrc 파일에 module load 명령어를 넣지 마세요. 충돌과 로그인 문제가 생길 수 있습니다. 먼저 가용성 확인: module avail로 정확한 이름과 버전을 확인합니다. 구체적으로 지정: 항상 버전 번호를 지정합니다 (예: module load openmpi/4.1.8). 지정하지 않으면 기본 버전이 로드되는데, 나중에 변경될 수 있습니다. 4. Slurm으로 첫 번째 작업 제출하기 # 이제 작업을 제출할 준비가 됐습니다.\n작업 스케줄러(Job Scheduler)란? # HPC 환경에서는 로그인 노드(Login Node)에서 직접 무거운 계산을 실행하지 않습니다. 대신 Slurm, PBS, SGE, LSF 같은 스케줄러(Scheduler)에 \u0026lsquo;작업(Job)\u0026lsquo;을 제출합니다. 스케줄러가 리소스를 관리하고 사용 가능한 컴퓨트 노드(Compute Node)에 작업을 배정해줍니다.\n참고: 이 튜토리얼은 현대 HPC 시스템에서 가장 널리 쓰이는 스케줄러인 Slurm에 초점을 맞춥니다. PBS/Torque 예시도 참고용으로 제공하지만, 명령어와 옵션이 다를 수 있습니다. 스케줄러별 문법은 항상 소속 클러스터의 문서를 확인하세요.\n인터랙티브 작업: 개발, 디버깅, GUI가 필요한 작업에 유용합니다. 컴퓨트 노드에서 셸을 받습니다. 배치 작업: 오래 걸리는 작업에 유용합니다. 스크립트를 제출하면 시스템이 리소스가 사용 가능할 때 실행합니다. 호텔 비유 # 초보자들이 로그인 직후 바로 무거운 작업을 실행하는 실수를 곧잘 합니다. 하지만 그러면 안 됩니다.\nHPC 클러스터를 호텔로 생각해봅시다.\n로그인 노드 = 호텔 로비: 체크인하는 곳입니다. 공용 공간입니다. 로비에 텐트 치고 자지는 않잖아요? 컴퓨트 노드 = 객실: 실제로 일(잠)할 수 있는 개인 공간입니다. 스케줄러 = 프런트 데스크: 프런트 데스크 직원(스케줄러)에게 방(리소스)을 요청하면 배정해줍니다. Slurm 같은 작업 스케줄러를 사용해서 리소스를 요청합니다.\n인터랙티브 작업 제출해보기 # 실시간으로 코드를 테스트하거나 디버깅할 때 사용합니다.\n세션 요청 (방 받기): [user123@login-01]$ srun --pty bash srun: job 12345 queued and waiting for resources srun: job 12345 has been allocated resources [user123@compute-01]$ # 참고: 클러스터에 따라 파티션 지정이 필요할 수 있습니다: # $ srun -p interactive --pty bash 호스트명이 login-01에서 compute-01로 바뀌었습니다. 이제 \u0026ldquo;객실\u0026quot;에 있는 겁니다.\n작업이 끝나면 exit를 입력해서 로그인 노드(로비)로 돌아갑니다. [user123@compute-01 ~]$ exit [user123@login-01 ~]$ 배치 작업 제출해보기 # 오래 걸리는 시뮬레이션을 위한 방식입니다. \u0026ldquo;배치 스크립트\u0026rdquo;(예약 요청서)를 작성하고 제출합니다.\n스크립트 만들기 (예: job_script.sh). vim이나 nano 같은 텍스트 에디터를 사용합니다. #!/bin/bash # Bash 스크립트임을 시스템에 알려줘요 #SBATCH --account=myAcct # 계정 이름 #SBATCH --partition=myPart # 파티션 이름 #SBATCH --job-name=first_job # 작업 이름 #SBATCH --output=result.out # 표준 출력 로그 #SBATCH --error=result.err # 표준 오류 로그 #SBATCH --nodes=1 # 노드 수 #SBATCH --ntasks=1 # 태스크(프로세스) 수 #SBATCH --time=00:10:00 # 시간 제한 (HH:MM:SS) #SBATCH --mem-per-cpu=4G # CPU당 메모리 # 필요한 모듈 로드 module load python/3.12.12 # 명령어 실행 echo \u0026#34;Hello, HPC World!\u0026#34; python3 --version #!/bin/bash # Bash 스크립트임을 시스템에 알려줘요 #PBS -A myAcct # 계정 이름 #PBS -q myQueue # 큐 이름 #PBS -N first_job # 작업 이름 #PBS -o result.out # 표준 출력 로그 #PBS -e result.err # 표준 오류 로그 #PBS -l nodes=1:ppn=1 # 노드와 노드당 프로세서 수 #PBS -l walltime=00:10:00 # 시간 제한 (HH:MM:SS) #PBS -l pmem=4gb # CPU당 메모리 # 필요한 모듈 로드 module load python/3.12.12 # 작업 제출 디렉토리 변경 cd $PBS_O_WORKDIR # 명령어 실행 echo \u0026#34;Hello, HPC World!\u0026#34; python3 --version 주의사항: 요구 사항에 맞게 스크립트를 수정해야 합니다\n(중요: \u0026ldquo;myAcct\u0026quot;와 \u0026ldquo;myPart\u0026quot;는 시스템 관리자가 제공한 실제 계정과 파티션 이름으로 바꿉니다) #SBATCH: Slurm 스케줄러가 읽는 지시문입니다\n(#SBATCH는 \u0026ldquo;# SBATCH\u0026quot;가 아닌 하나의 단어입니다) 실제 작업 내용은 Slurm 지시문 아래에 위치합니다 작업이 완료되면 자동으로 종료됩니다 작업 제출: $ sbatch job_script.sh Submitted batch job 12345 $ qsub job_script.sh 12345.headnode (이 작업 ID (12345)를 기억해두고 문의 시 이 번호를 참조합니다!)\n상태 확인: $ squeue --me JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 12345 myPart first_job user123 R 0:02 1 compute-01 $ qstat -u user123 Job ID Name User Time Use S Queue -------- -------- -------- -------- - ----- 12345 first_job user123 0:02 R myQueue 작업 상태 열 (Slurm): 컬럼 설명 JOBID 작업에 할당된 ID PARTITION 파티션 이름 NAME 작업 이름 USER 사용자 이름 ST 작업 상태: R=실행중, PD=대기중, F=실패, S=일시중지, CG=완료중 TIME 작업 시작 후 경과 시간 NODES 요청한 노드 수 작업을 취소하려면 scancel \u0026lt;JOBID\u0026gt;를 사용합니다.\n$ scancel 12345 $ qdel 12345 결과 확인: 작업이 완료되면 (squeue에서 사라지면) 출력 파일을 확인합니다. # 성공 로그 $ cat result.out Hello, HPC World! Python 3.12.12 # 오류 로그 (문제가 있을 때) $ cat result.err 요약\nSSH: 클러스터에 접속하는 보안 터널 모듈: 소프트웨어 로드 로그인 노드 (로비): 체크인만 하는 곳 컴퓨트 노드 (객실): 실제 작업을 실행하는 곳. 스케줄러가 배정해줍니다 작업 제출: 배치 스크립트에는 sbatch, 인터랙티브 작업에는 srun 축하합니다! 로그인하고, 환경을 설정하고, 첫 번째 작업을 실행했습니다. 다음 글에서는 짐(데이터)을 이 새 호텔 방으로 옮기는 방법을 배워보겠습니다.\n도움이 필요하면:\n클러스터 문서에서 Slurm 구성 확인 man sbatch로 모든 사용 가능한 옵션 확인 대부분의 클러스터에는 help 채널이나 지원 이메일이 있습니다 즐거운 컴퓨팅 되세요!\n","date":"2025년 12월 27일","externalUrl":null,"permalink":"/ko/posts/hpc101-01/","section":"포스트","summary":"HPC 클러스터 첫날, 막막함은 당연합니다. 이 글은 시스템 문서 40페이지를 뒤지지 않고도 SSH 로그인부터 실행 중인 Slurm 작업까지 도달할 수 있게 해줍니다.","title":"[HPC 101] HPC 시작하기: SSH 로그인, 모듈 시스템, 첫 번째 Slurm 작업","type":"posts"},{"content":"","date":"2025년 12월 27일","externalUrl":null,"permalink":"/ko/tags/ssh/","section":"Tags","summary":"","title":"SSH","type":"tags"},{"content":"","date":"2025년 12월 27일","externalUrl":null,"permalink":"/ko/tags/tutorial/","section":"Tags","summary":"","title":"Tutorial","type":"tags"},{"content":"클러스터에 SSH로 접속했습니다. 그런데 로그인 프롬프트에서 100개의 GPU에서 작업이 실제로 실행되기까지 무슨 일이 일어나는 걸까요?\n그 사이를 기록하고 싶습니다.\n저는 Will Paik입니다. Northeastern University에서 HPC 머신러닝 성능 엔지니어로 일하고 있으며, 시스템 관리자(\u0026ldquo;안정적으로 유지\u0026rdquo;)와 연구자(\u0026ldquo;지금 당장 더 빠르게\u0026rdquo;) 사이의 간극을 메우는 것이 제 일입니다. Penn State에서 시작해 지금까지 9년째 비슷한 일을 하고 있습니다.\n집에서는 소비자용 하드웨어로 6노드 HPC 클러스터를 직접 조립하고 있습니다. 클러스터가 필요해서가 아닙니다. 직접 만들어 보면 남이 만든 시스템을 수년간 사용해도 배우지 못하는 것들을 배울 수 있기 때문입니다.\n1. 왜 이 블로그를 시작했나요? # 두 가지 빈자리가 이 블로그를 시작하게 만들었습니다.\n첫 번째는 콘텐츠 자체의 빈자리입니다. HPC 관련 문서 대부분은 두 극단 중 하나에 있습니다. 수억 원 예산과 전담 IT 팀을 전제로 하는 엔터프라이즈 가이드이거나, 특정 문제 하나만 해결하고 왜 그런 문제가 생겼는지는 설명하지 않는 Stack Overflow 답변입니다. 시스템 전체가 어떻게 맞물려 돌아가는지 이해하고 싶은 사람을 위한 콘텐츠는 많지 않습니다.\n두 번째는 한국어 콘텐츠의 빈자리입니다. 저는 한국인이고, HPC 시스템 관리, Slurm, NFS, 분산 스토리지, 클러스터 인프라에 대한 한국어 자료는 많이 없습니다. 한국 대학에도 클러스터가 있고, 한국 연구자들도 매일 그 시스템을 씁니다. 그들을 위한 실용적인 자료가 없다는 것은 바꿀 만한 일입니다.\n2. 무엇을 하려고 하나요? # 일단은 공용 HPC 클러스터를 처음 사용하는 연구자를 위한 실용 가이드를 생각하고 있습니다. \u0026ldquo;sbatch 매뉴얼 페이지를 참고하세요\u0026rdquo; 수준이 아니라, 계정이 정지되지 않고 홈 디렉토리를 날리지 않으면서 실제로 일을 처리하는 데 필요한 것들입니다.\n그 이후에는 홈 클러스터 빌드 과정을 처음부터 끝까지 기록할 계획입니다. 하드웨어 결정, 설정 파일, 실수까지 모두 담을 생각입니다. 네트워킹, 스토리지, 잡 스케줄링, Ansible 자동화, 그리고 GPU 워크로드까지입니다. 모든 것이 잘 작동한 뒤에 쓴 튜토리얼이 아니라, 실제 빌드 기록처럼 읽히는 글을 쓰고 싶습니다.\n이게 ML 인프라, 분산 학습, 클러스터 벤치마킹으로 이어질지, 아니면 다른 방향이 될지는 앞으로 천천히 계획해 보겠습니다.\n3. 어떻게 접근하려고 하나요? # 실제 하드웨어, 실제 출력 결과를 씁니다. 최종적으로 돌아가는 버전뿐 아니라 직접 겪은 문제나 트러블 슈팅 방법을 같이 업로드하고 싶습니다. 만들고 있는 클러스터는 소비자용 CPU, 소비자용 네트워킹, 게이밍 PC GPU 노드로 구성되어 있습니다. 해법도 그 조건에서 실제로 작동하는 것이어야 하고, 비슷한 제약이 있는 연구자에게도 쓸 수 있는 방식이어야 합니다.\n모든 콘텐츠는 가능하면 영어와 한국어로 함께 제공합니다. 한국어 HPC 콘텐츠는 사실상 없다시피 하고, 그 상황을 바꾸고 싶습니다.\n4. 어디에 업데이트 되나요? # 클러스터 빌드 에피소드 동영상은 The Login Node 유튜브 채널에 올라갑니다. GitHub 저장소에 공유할 자료가 있다면 올리겠습니다.\n즐거운 컴퓨팅 되세요!\n","date":"2025년 12월 16일","externalUrl":null,"permalink":"/ko/posts/hello-world/","section":"포스트","summary":"The Login Node의 첫 번째 포스트입니다. Will Paik이 블로그를 시작한 이유, 계획하는 방향, 그리고 접근 방식을 소개합니다.","title":"Hello World: 이 블로그를 왜 시작했을까","type":"posts"},{"content":"","date":"2025년 12월 16일","externalUrl":null,"permalink":"/ko/tags/introduction/","section":"Tags","summary":"","title":"Introduction","type":"tags"},{"content":"안녕하세요, Will Paik입니다. The Login Node에 오신 것을 환영합니다.\n저는 대규모 HPC 환경에서 AI/ML 모델을 확장하고 최적화하는 일을 하고 있습니다. 슈퍼컴퓨팅의 세계에는 늘 미묘한 긴장감이 흐릅니다. 시스템 관리자는 \u0026ldquo;서버가 죽으면 안 돼!\u0026ldquo;를 외치고, 연구자는 \u0026ldquo;무조건 더 빨리 돌려줘!\u0026ldquo;를 원하죠. 저는 이 둘 사이의 기술적 스윗 스팟(Sweet Spot)을 찾는 역할을 합니다.\n현재 본업은 HPC 머신러닝 성능 엔지니어입니다. 낮에는 거대 AI 모델 학습을 위해 대규모 클러스터를 최적화하고, 밤에는 그 원리를 쉽게 전해드리기 위해 방구석 미니 슈퍼컴퓨터를 직접 조립하고(가끔은 태워 먹으며) 실험합니다.\nCORE STACK: Slurm Linux Docker/Apptainer PyTorch Distributed Ansible\n이 블로그에서 다루는 것들 # The Login Node는 HPC 및 ML 인프라 엔지니어링 블로그입니다. 단순히 작업을 제출하고 기다리는 방법이 아니라, 시스템이 실제로 어떻게 동작하는지 이해하고 싶은 분들을 위한 공간입니다.\n콘텐츠는 세 가지 시리즈로 구성되어 있습니다:\n🔧 HPC From Scratch \u0026ndash; $1,300 이하의 일반 PC 부품으로 6노드 클러스터를 직접 구축합니다. 하드웨어 선택, OS 설치, 네트워크, Slurm, Ansible, GPU 워크로드까지. 여기서 시작하세요. 🎓 HPC 101 \u0026ndash; SSH, 모듈 시스템, Slurm 기초, 작업 디버깅. HPC가 처음인 연구자를 위한 시리즈. 여기서 시작하세요. 🐧 Linux 101 \u0026ndash; 터미널이 낯선 분들을 위한 명령줄 기초. 여기서 시작하세요. 홈 클러스터 # 역할 하드웨어 사양 로그인 노드 Lenovo IdeaPad 1 Ryzen 5 7520U, 8GB RAM 관리 노드 Lenovo ThinkCentre M715q Ryzen 5 2400GE, 16GB RAM 시각화 노드 Lenovo ThinkCentre M715q Ryzen 5 2400GE, 16GB RAM 워커 노드 (x2) Lenovo ThinkCentre M715q Ryzen 5 2400GE, 16GB RAM GPU 노드 HP Envy TE01 Core i7-10700F, 32GB RAM, GTX 1660 Super 스토리지 (관리 노드 경유) 1TB NVMe SSD (NFS) 네트워크 기가비트 매니지드 스위치 8포트, VLAN 지원 소프트웨어 스택: Rocky Linux 10, Slurm 25, Ansible, Apptainer, Prometheus + Grafana (구축 중)\n배경 # Penn State 대학교에서 항공우주공학 박사학위(계산과학 부전공)를 취득했으며, 그 후 8년간 500명 이상의 연구자를 지원했습니다. 현재는 Northeastern University에서 근무 중입니다. 항공우주 분야의 배경은 대규모 최적화 문제를 바라보는 시각을 형성해 주었고, 지금은 우주선 궤도 대신 GPU 클러스터에 그 방식을 적용하고 있습니다.\n전체 경력 사항은 Career 페이지에서 확인하실 수 있습니다.\n연락처 # GitHub LinkedIn YouTube ","date":"2025년 1월 1일","externalUrl":null,"permalink":"/ko/about/","section":"","summary":"","title":"소개","type":"page"},{"content":"Date: 2015–2016 Institution: Pennsylvania State University, University Park, PA\nI served as a mentor and instructor for engineering undergraduates, focusing on computational methods and programming logic.\nAerospace Analysis: Assisted students with numerical methods and engineering analysis. Programming for Engineers: Mentored students on MATLAB programming logic and algorithm development. (This entry archives past academic teaching experience at Penn State University.)\n","date":"1 1월 2015","externalUrl":null,"permalink":"/talks/teaching-psu/","section":"Talks \u0026 Workshops","summary":"","title":"Academic Teaching Experience (2015–2016)","type":"talks"},{"content":"","date":"1 1월 2015","externalUrl":null,"permalink":"/tags/teaching/","section":"Tags","summary":"","title":"Teaching","type":"tags"},{"content":"","externalUrl":null,"permalink":"/ko/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/ko/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"HPC 분야에서 9년 이상 시스템 아키텍처와 성능 최적화 경험을 보유하고 있습니다. 현재는 대규모 AI/ML 워크로드의 실제 성능을 극대화하는 데 집중하고 있으며, 대학 클러스터부터 주 단위 연구 인프라, 직접 구축한 홈 클러스터까지 다양한 환경을 다뤄왔습니다.\n경력 # HPC 머신러닝 성능 엔지니어 Research Computing, Northeastern University | 2025년 1월 – 현재\n프로덕션 HPC 클러스터에서 분산 ML 워크로드 최적화 AI/ML 애플리케이션 성능 분석 및 벤치마킹 다양한 분야의 연구자 컴퓨팅 워크플로우 최적화 지원 GPU 훈련 벤치마킹, AICR Benchmarking Group Massachusetts AI Computing Resource (AICR) | 2026년\nNortheastern University와 별개로 매사추세츠 주 AI 연구 인프라 벤치마킹 참여 멀티노드 구성에서 B200, RTX Pro 6000 클러스터의 GPU 훈련 워크로드 평가 담당 HPC 소프트웨어 컨설턴트 Institute for Computational and Data Sciences, Penn State | 2017년 1월 – 2024년 12월\n8년간 500명 이상의 연구자 지원 클러스터 성능 최적화 및 사용자 지원 재현 가능한 연구환경을 위한 컨테이너 환경 개발 (Singularity Hub 기여자) 시스템 모니터링, 자원 할당, 작업 최적화 Parallel Computing Support Application Engineer (인턴십) MathWorks | 2021년 여름\nMATLAB 병렬 컴퓨팅 툴박스 최적화 분산 컴퓨팅 성능 벤치마크 개발 HPC와 MATLAB 통합을 위한 문서 작성 기술 역량 # 분류 도구 스케줄러 Slurm, PBS (job array, 의존성 체인, 자원 최적화) 병렬 컴퓨팅 MPI (OpenMPI, Intel MPI), OpenMP, CUDA 스토리지 NFS, 병렬 파일시스템, 데이터 관리 전략 컨테이너 Singularity/Apptainer, Docker, Podman 자동화 Ansible, Bash 스크립팅, 시스템 프로비저닝 모니터링 Prometheus, Grafana, 성능 메트릭 언어 Python, C/C++, Fortran, MATLAB, Shell 버전 관리 Git, GitLab CI/CD 프로젝트 # 프로젝트 설명 HPC From Scratch 소비자용 하드웨어로 6노드 클러스터 구축. Slurm, Ansible, NFS, FreeIPA, Lmod. PyTorch DDP Benchmark HPC 클러스터용 멀티GPU/멀티노드 분산 훈련 스케일링 벤치마크. [GitHub] pkg_audit Slurm 파티션 전체 스캔 및 Ansible 수정 기능을 포함한 RPM 패키지 일관성 감사 도구. [GitHub] 4D LiDAR SLAM 최적화 ROS 2 실시간 성능을 위한 포인트 클라우드 처리 병렬화 사이드 프로젝트 (공개 예정) Game of Life 웹앱, 브라우저 포커 게임, ESP32-P4 열화상 카메라 기타 엔지니어링 프로젝트 NIST First Responder UAS Indoor Challenge (2022) 수상: 3위 + First Responder\u0026rsquo;s Choice (상금 $80,000) GPS 미작동 실내 긴급 상황을 위한 커스텀 쿼드콥터 제작. [nist.gov]\nVFS Design-Build-Vertical Flight Student Competition (2021, 2022) 수상: 3위 (2022), 예비 보고서 1위 + 최우수 전산 시뮬레이션상 (2021). [engr.psu.edu]\n제9, 10회 ESA Global Trajectory Optimization Competition (2017, 2019) 복잡한 궤도 최적화를 위한 병렬 알고리즘 개발. [psu.edu]\n학력 # Pennsylvania State University (펜실베니아 주립대학교)\n학위 연도 비고 항공우주공학 박사 2024 컴퓨터과학 부전공. 논문: Multiple Gravity-Assist Trajectory Design with Continuous-Thrust Synergetic Maneuvers 항공우주공학 석사 2015 컴퓨터과학 부전공. 논문: Optimal Orbit Raising Via Particle Swarm Optimization 항공우주공학 학사 2013 강연 및 워크샵 # 자세한 내용은 강연 페이지를 참조하세요.\n강연 장소 연도 Introduction to Parallel Computing Northeastern University 2026년 봄 Linux Essentials for HPC Researchers Northeastern University 2026년 봄 Aerospace Analysis, Programming for Engineers TA Penn State 2015–2016 ","externalUrl":null,"permalink":"/ko/cv/","section":"","summary":"","title":"이력","type":"page"}]