원격 GPU 서버 사용 입문
목표: “서버 들어가서 3번 GPU로 돌려”, “로그인 노드에서 학습 돌리지 마세요”, “sbatch로 올려 두세요”, “홈 쿼터 찼어요” 같은 말을 듣고 지금 어느 컴퓨터의 어느 자원을 쓰고 있는지 알고, 남에게 피해 없이 실험을 돌리고 지켜볼 수 있기.
SSH 기초는 컴퓨터 기본 도구와 개발 환경 입문 §9, 포트와 IP는 네트워크 기초 입문, 프로세스와 메모리는 운영체제 기초 입문에서 먼저 다룬다. GPU와 CUDA 자체는 GPU와 CUDA 입문에 맡기고, 이 노트는 서버를 쓰는 법에 집중한다.
한 줄로 먼저
원격 GPU 서버는 “여러 사람이 함께 쓰는 남의 컴퓨터”다. 그래서 접속(SSH), 작업 유지(tmux·스케줄러), 자원 확인(nvidia-smi·df), 공유 매너가 한 묶음이다.
내 Mac (터미널, VS Code)
│ ssh (§1~3: 접속, 포트 포워딩, 파일 전송)
▼
[로그인 노드] ← 접속·편집·작업 제출만. 무거운 계산 금지
│ sbatch / srun (§8: Slurm이 자리 배정)
▼
[계산 노드 node01, node02 ...] ← GPU가 실제로 달린 곳 (§5: nvidia-smi)
│
▼
[공유 스토리지] /home (작고 안전), /scratch (크고 빠르지만 임시) (§6)0. 서버는 어떻게 생겼나
연구실에서 만나는 GPU 서버는 대체로 두 가지 모양이다.
| 구분 | (A) 연구실 단일 서버 | (B) 클러스터 (HPC) |
|---|---|---|
| 생김새 | GPU 4~8장 달린 컴퓨터 한두 대 | 로그인 노드 + 계산 노드 여러 대 + 공유 스토리지 |
| 작업 실행 | 접속한 그 컴퓨터에서 바로 python train.py | 스케줄러에 작업을 제출하면 계산 노드에서 실행 |
| GPU 배정 | 사람끼리 약속 (메신저, 예약 시트) | 스케줄러가 자동 배정 |
- 노드(node): 클러스터를 이루는 컴퓨터 한 대.
- 로그인 노드(login node): SSH로 처음 들어가는 컴퓨터. 모든 사용자가 함께 쓴다. 편집, 파일 정리, 작업 제출, 짧은 테스트만 한다.
- 계산 노드(compute node): GPU가 달린 컴퓨터. 스케줄러가 배정해 줄 때만 쓴다.
- 스케줄러(scheduler): 누가 어느 노드의 GPU를 몇 시간 쓸지 줄을 세우는 프로그램. 대학 클러스터에서는 Slurm이 흔하다.
로그인 노드에서 학습을 돌리면 모든 사람의 터미널이 느려지고, 관리자가 그 프로세스를 끄기도 한다. 서버의 CPU, 메모리, GPU, 디스크는 전부 남과 나눠 쓴다. 노트북에서 괜찮던 습관(GPU 전부 쓰기, Jupyter 켜 두고 퇴근)이 서버에서는 남의 실험을 막는다. 규칙은 서버마다 다르니 관리자 안내나 연구실 위키를 먼저 본다.
1. SSH 제대로 쓰기
~/.ssh/config로 별명 만들기
Mac의 ~/.ssh/config에 적어 두면 ssh gpu, scp file gpu:~/, rsync ... gpu:~/가 모두 이 별명으로 된다. VS Code Remote-SSH도 이 파일을 읽는다.
Host gpu
HostName gpu-server.example.ac.kr
User user
Port 2222
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
ServerAliveCountMax 3ServerAliveInterval 60: 60초마다 “살아 있나” 신호를 보낸다. 가만히 있는 연결을 공유기나 방화벽이 끊는 문제를 줄인다.ServerAliveCountMax 3은 3번 연속 응답이 없으면 끝낸다는 뜻이다.- SSH는 같은 설정이 여러 번 나오면 먼저 나온 값을 쓴다. 공통 설정인
Host *블록은 맨 아래에 둔다.
키 관리
ssh-keygen -t ed25519 -C "laptop" # 키 만들기, -C는 메모
ssh-copy-id -i ~/.ssh/id_ed25519.pub gpu # 공개키를 서버 ~/.ssh/authorized_keys에 추가- 키에 암호(passphrase) 를 걸면 노트북을 잃어버려도 키만으로는 접속할 수 없다.
- 암호를 매번 치기 싫으면 ssh-agent(키를 메모리에 잠시 들고 있는 프로그램)에 맡긴다. macOS에서는 config에
AddKeysToAgent yes,UseKeychain yes를 넣으면 키체인에 저장된다.UseKeychain은 macOS 전용이다. - 서버의
~/.ssh권한이 너무 열려 있으면 SSH가 키를 거부한다.chmod 700 ~/.ssh,chmod 600 ~/.ssh/authorized_keys로 맞춘다.
점프 호스트(ProxyJump)
학교 밖에서는 GPU 서버에 바로 못 들어가고, 먼저 점프 호스트(bastion, 게이트웨이 서버)를 거쳐야 하는 경우가 많다. 정문을 지나야 안쪽 건물에 갈 수 있는 구조다.
Host gateway
HostName gateway.example.ac.kr
User user
Host gpu
HostName gpu-server
User user
ProxyJump gatewaygpu-server는 gateway 안쪽에서만 보이는 이름이다. 이제 ssh gpu 한 번이면 gateway를 거쳐 들어간다. config 없이 한 번만 할 때는 ssh -J user@gateway.example.ac.kr user@gpu-server다. VPN을 켜야만 되는 서버도 있으니 안내를 따른다.
OTP(일회용 비밀번호)를 쓰는 서버라면 ControlMaster auto, ControlPath ~/.ssh/cm-%r@%h:%p, ControlPersist 10m을 config에 넣어 연결 하나를 나눠 쓰게 할 수 있다. 그러면 scp나 VS Code가 붙을 때마다 번호를 다시 치지 않는다.
2. 포트 포워딩과 VS Code Remote-SSH
로컬 포트 포워딩 (-L)
서버에서 띄운 Jupyter(8888)나 TensorBoard(6006)는 서버의 localhost에서만 기다린다. Mac 브라우저로 보려면 SSH 연결 안에 통로를 판다.
ssh -N -L 8888:localhost:8888 gpu
# 내Mac포트:목적지주소:목적지포트-N: 셸은 열지 않고 통로만 유지한다.- 이제 Mac에서
http://localhost:8888을 열면 서버의 Jupyter가 보인다. - 가운데 목적지 주소는 SSH 서버 쪽에서 본 주소다. 그래서 이
localhost는 Mac이 아니라 서버 자신이다.
클러스터에서 Jupyter가 계산 노드(node01)에서 돈다면 ssh -N -L 8888:node01:8888 cluster처럼 로그인 노드를 거쳐 통로를 낸다. 이 방식이 허용되는지, 웹 포털(Open OnDemand 등)이 따로 있는지는 기관마다 다르다.
VS Code Remote-SSH
Remote-SSH 확장을 쓰면 서버의 폴더를 내 폴더처럼 열고, 편집·터미널·디버거·Jupyter를 그대로 쓴다.
- 처음 접속하면 서버의
~/.vscode-server에 VS Code 서버와 확장을 깐다. 이 폴더가 커져 홈 쿼터를 잡아먹기도 한다. - 서버에서 뜬 포트를 감지해 자동으로 포워딩해 주기도 한다(PORTS 탭).
- 클러스터에서는 보통 로그인 노드에 붙는다. 꽤 무거워서 사용을 제한하는 기관도 있다.
- VS Code 터미널에서 돌린 학습은 창을 닫으면 같이 죽을 수 있다. 긴 작업은
tmux나 스케줄러로 돌린다.
3. 파일 옮기기: scp, rsync, sshfs
| 도구 | 특징 | 언제 |
|---|---|---|
scp | 단순 복사. 끊기면 처음부터 | 파일 몇 개 |
rsync | 바뀐 부분만 전송, 이어받기 가능 | 폴더, 데이터셋, 체크포인트, 반복 동기화 |
sshfs | 서버 폴더를 Mac에 디스크처럼 연결 | 결과 파일을 Finder에서 가끔 열어 볼 때 |
scp results.csv gpu:~/project/ # Mac → 서버
scp gpu:~/project/fig.png . # 서버 → Mac
rsync -avhP data/ gpu:~/project/data/ # 폴더 동기화
rsync -avhP --exclude '.git' ./ gpu:~/project/
rsync -avhn --delete src/ gpu:~/src/ # -n: 무엇을 할지 보여 주기만 함-a: 권한·시간을 보존하며 하위 폴더까지.-v: 자세히.-h: 읽기 쉬운 단위.-P: 진행률 + 끊겨도 이어받기.- 끝의 슬래시가 뜻을 바꾼다.
rsync data/ dst/는data안의 내용을dst에 넣고,rsync data dst/는dst/data를 만든다. --delete는 원본에 없는 파일을 목적지에서 지운다. 방향을 거꾸로 쓰면 서버의 결과가 날아간다. 처음에는 꼭-n으로 미리 본다.
sshfs는 작은 파일을 많이 읽으면 매우 느리고, Mac에서는 macFUSE를 따로 깔아야 한다. 대부분은 VS Code Remote-SSH + rsync가 더 편하다. 큰 데이터셋은 Mac을 거치지 말고 서버에서 직접 받는다.
4. 접속이 끊겨도 돌리기: tmux, nohup, 로그
SSH로 실행한 프로그램은 그 SSH 세션의 자식이다. 연결이 끊기면 셸이 SIGHUP(연결이 끊겼다는 신호)을 받고, 자식 프로그램도 대개 같이 끝난다.
tmux
tmux(terminal multiplexer)는 서버 안에 셸을 띄워 두는 프로그램이다. 내가 나가도 그 안의 프로그램은 계속 돈다.
tmux new -s train # "train" 세션 만들기
tmux ls # 세션 목록
tmux attach -t train # 다시 붙기 (줄여서 tmux a -t train)
tmux kill-session -t train # 세션 끝내기tmux 안에서는 접두키 Ctrl+B 를 눌렀다 뗀 뒤 명령 키를 누른다.
키 (Ctrl+B 다음) | 동작 |
|---|---|
d | 빠져나오기 (detach). 프로그램은 계속 돎 |
c / n / p | 새 창(window) / 다음 창 / 이전 창 |
0~9 | 그 번호 창으로 |
% / " | 화면을 좌우 / 위아래로 나누기 (pane) |
| 방향키 | 나눈 칸 사이 이동 |
z | 지금 칸 크게 보기 / 되돌리기 |
[ | 스크롤 모드 (방향키·PageUp, q로 끝) |
s | 세션 목록에서 골라 이동 |
흔한 배치는 한 칸에 학습, 옆 칸에 watch nvidia-smi다. 마우스 스크롤을 쓰려면 ~/.tmux.conf에 set -g mouse on을 넣는다. tmux 세션은 서버가 재부팅되면 사라진다. 클러스터의 긴 작업은 스케줄러(§8)에 맡긴다.
nohup
tmux가 없을 때 쓰는 간단한 방법이다.
nohup python -u train.py > train.log 2>&1 &nohup: SIGHUP을 무시해서 연결이 끊겨도 계속 돈다.> train.log 2>&1: 일반 출력과 에러를 모두 파일로. 끝의&: 백그라운드 실행.- 이미 돌고 있는 프로그램은
Ctrl+Z(일시 정지) →bg(백그라운드에서 계속) →disown(셸과 연 끊기)으로 살릴 수 있다.
로그 남기기
python -u train.py 2>&1 | tee logs/run1.log # 화면에도 보고 파일에도 저장
tail -f logs/run1.log # 로그 끝을 실시간으로 따라 보기Python은 출력이 파일이나 파이프로 갈 때 버퍼(buffer) 에 모았다가 한꺼번에 쓴다. 그래서 로그가 한참 비어 보인다. python -u나 PYTHONUNBUFFERED=1로 바로바로 쓰게 한다. 로그 맨 앞에는 Git 커밋 해시와 설정값을 찍어 둔다.
5. nvidia-smi 읽기와 GPU 고르기
출력 읽는 법
nvidia-smi는 NVIDIA 드라이버에 딸린 GPU 상태 도구다. 버전마다 모양이 조금 다르지만 대략 이렇다(숫자는 예시).
| NVIDIA-SMI 550.xx Driver Version: 550.xx CUDA Version: 12.4 |
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| 0 NVIDIA A100 ... On | 00000000:07:00.0 Off | 0 |
| N/A 34C P0 62W / 400W | 38000MiB / 81920MiB | 97% Default |
| 1 NVIDIA A100 ... On | 00000000:0B:00.0 Off | 0 |
| N/A 29C P0 45W / 400W | 4MiB / 81920MiB | 0% Default |
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| 0 N/A N/A 41235 C python 37990MiB || 칸 | 뜻 |
|---|---|
CUDA Version (맨 위) | 이 드라이버가 지원하는 최고 CUDA 버전. 설치된 CUDA 툴킷 버전이 아니다 |
GPU | GPU 번호(0부터). CUDA_VISIBLE_DEVICES에 쓰는 번호 |
Memory-Usage | 사용 중 / 전체 GPU 메모리. 남이 쓰는지 보는 첫 단서 |
GPU-Util | 최근 짧은 구간에서 GPU가 일하던 시간의 비율. 100%여도 연산 능력을 다 쓴다는 뜻은 아님 |
Temp, Pwr:Usage/Cap, Perf | 온도, 전력 사용/한도, 성능 상태(P0가 최고) |
| Processes 표 | 그 GPU를 쓰는 PID와 메모리. C는 계산, G는 그래픽 |
메모리는 차 있는데 Util이 0% 인 GPU는 누군가 Jupyter 커널이나 멈춘 프로그램으로 메모리만 잡고 있는 경우가 많다. 놀고 있어 보여도 남의 것이다. 컨테이너 안이거나 권한 설정 때문에 Processes 표가 비어 보이는데 메모리는 차 있는 경우도 있다.
지켜보기
watch -n 1 nvidia-smi # 1초마다 다시 실행 (Ctrl+C로 끝)
gpustat -i 1 # 한 줄에 GPU 하나, 사용자 이름까지 (pip install gpustat)
nvtop # htop처럼 생긴 GPU 모니터 (서버에 없을 수 있음)CUDA_VISIBLE_DEVICES로 GPU 고르기
PyTorch는 따로 정하지 않으면 보이는 GPU 중 0번부터 쓴다. 단일 서버에서 모두가 0번에 몰리지 않도록 쓸 GPU를 정한다.
CUDA_VISIBLE_DEVICES=2 python train.py
CUDA_VISIBLE_DEVICES=2,3 torchrun --nproc_per_node=2 train.py- 이 변수는 프로그램에 보이는 GPU 목록 자체를 바꾼다.
2,3이면 프로그램 안에서는 실제 2번이cuda:0, 3번이cuda:1이다. - 번호 순서가
nvidia-smi와 CUDA 프로그램 사이에 다를 수 있다. 맞추려면CUDA_DEVICE_ORDER=PCI_BUS_ID도 함께 설정한다. - 프로그램이 GPU를 처음 쓰기 전에 정해져 있어야 한다. 코드 안에서 바꾸려면
torchimport보다 앞에서 바꾼다. - Slurm에서
--gres=gpu:1로 GPU를 받으면 스케줄러가 배정한 GPU만 보이게 대개 알아서 설정한다. 직접 덮어쓰지 않는다.
6. 자원 확인: CPU, 메모리, 디스크
CPU와 메모리
htop # 프로세스별 CPU·메모리 (q 종료, u 사용자 필터)
nproc # 쓸 수 있는 CPU 코어 수
free -h # 메모리. available 칸이 새로 쓸 수 있는 양에 가까움
ps -u $USER -f # 내 프로세스 목록데이터로더 num_workers를 너무 크게 잡으면 CPU와 메모리를 독차지한다.
디스크와 쿼터
df -h ~ # 내 홈이 속한 디스크의 전체/사용/남은 용량
du -sh ~/project # 이 폴더의 용량
du -h --max-depth=1 ~ | sort -h # 홈 아래 폴더별 용량, 작은 것부터
quota -s # 내 쿼터 (서버에 따라 명령이 다름)쿼터(quota) 는 사용자 한 명이 쓸 수 있는 상한이다. 디스크 전체가 비어 있어도 내 쿼터가 차면 더 못 쓴다. 용량뿐 아니라 파일 개수(inode) 쿼터도 있어서, 작은 파일 수백만 개짜리 데이터셋은 용량이 작아도 걸린다. 확인 명령은 파일 시스템마다 다르다(예: Lustre는 lfs quota).
스토리지 구분
| 위치 (이름은 기관마다 다름) | 성격 | 둘 것 |
|---|---|---|
/home/user | 작다. 백업되는 경우가 많음 | 코드, 설정 |
/scratch, /data, /work | 크고 빠름. 백업 없음, 오래된 파일 자동 삭제가 있기도 함 | 데이터셋, 체크포인트, 캐시, 환경 |
노드 로컬 디스크 (/tmp, $TMPDIR) | 그 노드에만 있음, 가장 빠름, 작업 뒤 지워질 수 있음 | 작업 중 임시 파일 |
홈을 조용히 채우는 주범은 캐시다. ~/.bashrc에서 위치를 큰 저장소로 옮긴다(경로는 서버에 맞게).
export HF_HOME=/scratch/$USER/hf_cache # Hugging Face 모델·데이터셋 캐시
export PIP_CACHE_DIR=/scratch/$USER/pip_cache
export UV_CACHE_DIR=/scratch/$USER/uv_cache이미 쌓인 캐시는 conda clean --all, pip cache purge, uv cache clean으로 지운다.
7. 권한과 공유 매너
sudo는 없다고 생각한다
공용 서버의 일반 사용자는 관리자 권한(sudo)이 없어서 sudo apt install을 못 한다. 대신 Python 패키지와 CUDA 런타임은 conda·uv로 홈에 깔고(§9), 시스템 프로그램은 conda-forge에서 찾고, 클러스터라면 module avail을 본다. 그래도 없으면 관리자에게 요청한다.
GPU 매너
- 쓰기 전에 본다.
nvidia-smi와 연구실 규칙(예약 시트, 채널)을 확인한다. - 남이 쓰는 GPU에 올리지 않는다. 메모리가 남아 보여도 둘 다 느려지고, 상대 작업이 메모리 부족으로 죽을 수 있다.
- 필요한 만큼만 쓰고, 다 쓰면 놓는다. 끝난 Jupyter 커널은 Shutdown한다. 탭만 닫으면 커널은 GPU 메모리를 계속 잡는다.
- 로그인 노드에서 무거운 일을 하지 않는다. 큰 압축 풀기와 전처리도 계산 노드에서 한다.
- 계정을 남과 같이 쓰지 않는다. 대부분 기관 규정 위반이다.
남은 프로세스 정리
Ctrl+C로 끊었는데 GPU 메모리가 안 풀릴 때가 있다. 대개 데이터로더 워커나 분산 학습의 다른 프로세스가 고아(orphan) 로 남아 메모리를 잡고 있다. 흔히 “좀비”라고 부르지만, 운영체제 용어의 좀비(이미 끝나고 기록만 남은 Z 상태)는 메모리를 잡지 않는다. 치울 대상은 살아 있는 남은 프로세스다.
nvidia-smi # GPU를 잡은 PID 확인
ps -fp 41235 # 그 PID가 무엇이고 누구 것인지
kill 41235 # 정상 종료 요청 (SIGTERM)
kill -9 41235 # 그래도 안 죽으면 강제 종료 (SIGKILL)
pkill -u $USER -f train.py # 내 것 중 명령줄에 train.py가 든 것 전부kill은 내 프로세스에만 쓴다. 남의 프로세스가 GPU를 잡고 있으면 본인이나 관리자에게 알린다.
8. Slurm 기초
클러스터에서는 작업을 직접 실행하지 않고 Slurm에 제출한다. 요청한 자원(GPU, CPU, 메모리, 시간)이 비는 노드가 생기면 Slurm이 그곳에서 실행한다.
- 작업(job): 제출한 실행 단위. Job ID를 받는다.
- 파티션(partition): 노드를 묶은 줄.
gpu,debug처럼 나뉘고 줄마다 최대 시간과 GPU 종류가 다르다. 이름은 기관마다 다르다. - gres (generic resource): GPU 같은 추가 자원.
--gres=gpu:1은 “GPU 1장”이다.--gres=gpu:a100:1처럼 종류를 정하는 곳도 있다.
배치 작업: sbatch
#SBATCH로 시작하는 줄은 셸에게는 주석이지만, Slurm은 자원 요청으로 읽는다.
#!/bin/bash
#SBATCH --job-name=train-llm
#SBATCH --partition=gpu
#SBATCH --gres=gpu:1
#SBATCH --cpus-per-task=8
#SBATCH --mem=64G
#SBATCH --time=12:00:00
#SBATCH --output=logs/%x-%j.out
echo "job $SLURM_JOB_ID on $(hostname), GPUs: $CUDA_VISIBLE_DEVICES"
module load cuda/12.1 # 클러스터에 따라 필요 없거나 이름이 다름
source ~/miniforge3/etc/profile.d/conda.sh
conda activate myproj
cd ~/project
python -u train.py --config configs/base.yamlmkdir -p logs # 로그 폴더는 미리 만든다 (없으면 로그를 못 남기고 실패할 수 있음)
sbatch train.sh # → "Submitted batch job 123456"%x는 작업 이름,%j는 Job ID로 바뀐다.--error를 따로 안 주면 에러도 같은 파일에 쌓인다.--time은 최대 실행 시간이다. 넘으면 Slurm이 작업을 끈다. 그래서 체크포인트를 주기적으로 저장하고 이어서 학습할 수 있게 만든다.- 스크립트 안에서 환경을 명시적으로 활성화한다. 로그인 셸의 상태를 그대로 믿지 않는다.
대화형 작업: srun
디버깅은 계산 노드의 셸을 직접 받아서 한다.
srun --partition=gpu --gres=gpu:1 --cpus-per-task=4 --mem=32G --time=1:00:00 --pty bash
nvidia-smi # 배정된 GPU만 보임
exit # 끝나면 반드시 나와서 반납대화형 작업을 열어 둔 채 자리를 비우면 그만큼 GPU를 놀리는 셈이다.
상태 보기와 취소
squeue -u $USER # 내 작업 목록
sinfo # 파티션·노드 상태 (idle, mix, alloc, down)
scancel 123456 # 작업 취소
sacct -j 123456 # 끝난 작업의 기록squeue의 ST 칸에서 PD는 대기, R은 실행 중이다. 대기 중이면 NODELIST(REASON) 칸에 이유가 나온다. (Resources)는 자원을 기다리는 중, (Priority)는 앞 순서 작업이 있다는 뜻이다.
9. 환경 구성: conda/uv, module, Apptainer
conda나 uv를 홈에 설치
# Miniforge (conda-forge 기본 conda 배포판)
wget https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh
bash Miniforge3-Linux-x86_64.sh
# uv
curl -LsSf https://astral.sh/uv/install.sh | sh- 서버는 대개
x86_64다(uname -m으로 확인). Mac의arm64용 설치 파일이나 Mac에서 만든 환경 폴더를 가져오지 말고,environment.yml·uv.lock으로 서버에서 다시 만든다. - conda 환경은 하나에 수 GB가 되기도 한다. 홈이 작으면
conda create -p /scratch/$USER/envs/myproj python=3.11처럼 큰 저장소에 만든다. - PyTorch는 서버 드라이버가 지원하는 CUDA 버전에 맞는 빌드를 깐다. 설치 뒤
python -c "import torch; print(torch.cuda.is_available())"로 확인한다. 버전 맞추는 원리는 GPU와 CUDA 입문을 본다.
module load
클러스터는 여러 버전의 CUDA·컴파일러를 미리 깔아 두고 환경 모듈(Environment Modules, Lmod)로 고르게 한다. 불러오면 PATH 같은 환경 변수가 그 버전을 가리킨다.
module avail cuda # 쓸 수 있는 cuda 모듈
module load cuda/12.1 # 불러오기 (이름·버전은 클러스터마다 다름)
module list # 지금 불러온 것
module purge # 전부 내리기pip·conda로 받은 PyTorch는 CUDA 런타임을 대개 자체적으로 들고 와서, 단순 학습에는 module load cuda가 필요 없을 때도 있다.
컨테이너는 Apptainer
공용 클러스터는 보안 문제로 Docker를 허용하지 않는 경우가 많다(이유는 Docker 입문 §9). 대신 일반 사용자 권한으로 도는 Apptainer(옛 이름 Singularity)를 쓴다. Docker 이미지를 받아 .sif 파일 하나로 바꾼다.
apptainer pull pytorch.sif docker://nvcr.io/nvidia/pytorch:24.05-py3
apptainer exec --nv pytorch.sif python train.py--nv: 호스트의 NVIDIA 드라이버와 GPU를 컨테이너 안에서 쓰게 한다. 빼먹으면 GPU가 안 보인다.- 홈과 현재 폴더는 기본으로 연결된다. 다른 경로는
--bind /scratch/$USER처럼 연결한다. .sif와 변환 캐시는 수 GB다. 캐시 위치는APPTAINER_CACHEDIR로 큰 저장소에 둔다.
10. 자주 나는 문제
| 증상 | 흔한 원인 | 해결 |
|---|---|---|
Broken pipe, 접속이 자꾸 끊김 | 가만히 있는 연결을 네트워크 장비가 끊음 | ServerAliveInterval 60, 작업은 tmux·Slurm 안에서 |
Permission denied (publickey) | 공개키 미등록, 키 파일 권한, 다른 키 사용 | ssh -v gpu로 시도하는 키 확인, 권한 700/600 |
Disk quota exceeded | 내 쿼터 초과 (용량 또는 파일 개수) | du로 큰 폴더 찾기, 캐시를 /scratch로 |
No space left on device | 디스크 자체가 가득 (/tmp 포함) | df -h로 어느 디스크인지 확인, 공용이면 관리자에게 |
bind [127.0.0.1]:8888: Address already in use | Mac의 8888번을 이전 터널 등이 이미 씀 | lsof -i :8888로 찾아 끄거나 -L 8889:localhost:8888 |
torch.cuda.is_available()이 False | CPU용 PyTorch, 로그인 노드에서 실행, Apptainer --nv 누락 | nvidia-smi가 되는 곳인지 확인, CUDA 빌드 재설치 |
CUDA out of memory
- 먼저
nvidia-smi로 남이 같은 GPU를 쓰는지 본다. 그렇다면 내 코드 문제가 아니다. - 내 이전 실행이나 Jupyter 커널이 메모리를 잡고 있지 않은지 본다(§7).
- 배치를 줄이고, 원하는 유효 배치는 gradient accumulation(작은 배치 여러 번의 기울기를 모아 한 번에 업데이트)으로 맞춘다.
- bf16 혼합 정밀도, gradient checkpointing(중간 결과를 버렸다가 역전파 때 다시 계산해 메모리를 아끼는 방법)을 쓴다.
- 평가는
torch.no_grad()안에서 돌린다.
torch.cuda.empty_cache()는 PyTorch가 쥐고만 있던 빈 메모리를 돌려줄 뿐이다. 아직 쓰는 텐서의 메모리는 줄이지 못한다.
11. 명령 치트시트
| 하고 싶은 일 | 명령 |
|---|---|
| 접속 / 점프 호스트 경유 | ssh gpu / ssh -J user@gateway user@gpu-server |
| 서버 포트를 Mac으로 | ssh -N -L 8888:localhost:8888 gpu |
| 폴더 동기화 | rsync -avhP src/ gpu:~/dst/ (--delete는 -n 먼저) |
| tmux 만들기 / 붙기 / 목록 | tmux new -s name / tmux a -t name / tmux ls |
| tmux 빠지기 / 나누기 / 스크롤 | Ctrl+B 뒤 d / % " / [ |
| 끊겨도 돌리기 + 로그 | nohup python -u train.py > train.log 2>&1 & |
| 로그 따라 보기 | tail -f train.log |
| GPU 지켜보기 | watch -n 1 nvidia-smi, gpustat -i 1 |
| GPU 고르기 | CUDA_VISIBLE_DEVICES=1 python train.py |
| CPU·메모리 | htop, free -h, nproc |
| 디스크·쿼터 | df -h ~, du -sh dir, quota -s |
| 내 프로세스 / 끄기 | ps -u $USER -f / kill PID, kill -9 PID |
| Slurm 제출 / 목록 / 취소 | sbatch job.sh / squeue -u $USER / scancel JOBID |
| Slurm 노드 상태 / 기록 | sinfo / sacct -j JOBID |
| Slurm 대화형 GPU 셸 | srun -p gpu --gres=gpu:1 --time=1:00:00 --pty bash |
| 모듈 | module avail, module load cuda/12.1, module purge |
| 컨테이너로 실행 | apptainer exec --nv image.sif python train.py |
12. 아주 짧은 암기
- 서버는 남과 함께 쓰는 컴퓨터다. 쓰기 전에 보고, 다 쓰면 놓는다.
-L 내포트:목적지:포트의 목적지는 서버 쪽에서 본 주소다.- rsync는 끝 슬래시가 뜻을 바꾸고,
--delete는-n으로 먼저. - 긴 작업은 tmux나 Slurm 안에서, 로그는 파일로,
python -u. nvidia-smi의 CUDA Version은 드라이버 한계, GPU-Util은 바쁜 시간 비율.- 메모리만 차 있고 Util 0%인 GPU도 남의 것이다.
CUDA_VISIBLE_DEVICES=2,3이면 안에서는 0, 1번이 된다.- 홈은 작게, 데이터·캐시·체크포인트는
/scratch로./scratch는 백업 없음, 중요한 결과는 따로 보관. - Slurm:
sbatch로 올리고,squeue로 보고,scancel로 끈다.
입문자가 조심할 점
- 규칙은 서버마다 다르다. 파티션 이름, 쿼터, 스토리지 경로, 삭제 정책, 계산 노드 접속 허용 여부는 기관과 연구실마다 다르다. 이 노트의 예시보다 관리자 안내가 우선이다.
- 서버에는 휴지통이 없다.
rm -rf와rsync --delete의 경로를 두 번 확인한다.$VAR가 비어 있으면rm -rf $VAR/가 엉뚱한 곳을 지운다. - API 키를 공용 서버에 흩어 두지 않는다. 홈 권한이 남에게 열린 서버도 있다.
ls -ld ~로 확인하고 키 파일은chmod 600으로 둔다. - 긴 작업은 작게 먼저 돌린다. 몇 스텝만 돌려 로그·체크포인트 저장·평가가 끝까지 되는지 확인한 뒤 올린다. 12시간 뒤 저장 경로 오타로 죽는 일이 흔하다.
출처
- OpenSSH manual pages,
ssh(1),ssh_config(5),ssh-keygen(1),ssh-agent(1). https://man.openbsd.org/ssh_config - Visual Studio Code Docs, Remote Development using SSH. https://code.visualstudio.com/docs/remote/ssh
- rsync manual page. https://download.samba.org/pub/rsync/rsync.1
- tmux wiki and manual. https://github.com/tmux/tmux/wiki
- NVIDIA, nvidia-smi (System Management Interface) documentation. https://docs.nvidia.com/deploy/nvidia-smi/
- NVIDIA, CUDA C++ Programming Guide — CUDA Environment Variables (
CUDA_VISIBLE_DEVICES,CUDA_DEVICE_ORDER). https://docs.nvidia.com/cuda/ - PyTorch Docs, CUDA semantics — memory management. https://pytorch.org/docs/stable/notes/cuda.html
- SchedMD, Slurm Workload Manager Documentation —
sbatch,srun,squeue,scancel,sinfo,sacct, GRES Scheduling. https://slurm.schedmd.com/documentation.html - Lmod Documentation. https://lmod.readthedocs.io
- Apptainer Documentation — GPU support (
--nv), bind paths. https://apptainer.org/docs/ - conda-forge, Miniforge. https://github.com/conda-forge/miniforge
- Astral, uv documentation. https://docs.astral.sh/uv/
- Linux man pages,
nohup(1),df(1),du(1),free(1),quota(1),kill(1)