지난 11월 초에 DGX Spark 구매를 염두에 두었던 이유는 아래 두가지였다.
- 오픈소스 기반 LLM 튜닝 가내 수공업화
- 둘째 AI 프로젝트 대응
이를 위해 700만원짜리 머신을 자비로 구매하는것은 큰 고민이 필요했다. 물론 일전에 GTX 1080 GPU 두장이 들어간 머신러닝용 개인 머신을 구매했던 경험이 있고, 이 머신 덕분에 딥러닝을 업무를 시작하기 전에 스스로 학습할 수 있었다. 또한 이를 이용해 PyKoSpacing 이라는 라이브러리를 만들어 오픈할 수 있었다. 사실 1번 이유가 살짝 이 경험에 기반하고 있긴 하다.
특히 지금 1번 이유가 더 중요하다고 생각한다. 이유는 다음과 같다.
현재의 DGX 머신의 가격이 개인이 구매하기 불가능할 정도로 비싸 모델 실험 결과를 외부로 공유하거나 공개하기가 불가능하다. 이는 곧 개인의 모델링 경험을 어필할 수단이 전무하다는 것이다. 게다가 요즘엔 기업에서 기술 논문도 그렇게 장려하는 상황도 아니긴 하고… 나는 이런 시점에서 기기 성능을 떠나 개인이 개인 모델 결과를 공유할 수 있는 하나의 수단이 DGX Spark이라는 생각이 들었다. 이 기기를 통해서 가볍게 튜닝된 여러 재미난 모델이 대중에 내 이름으로 공유될 수 있기를 희망하고 있다.
이런 비슷한 이야기는 틈틈히 다음 포스팅들을 통해서 공유해볼 생각이다.
DGX Spark 엔비디아 머신이 나온뒤 곧 여러 벤더들에서 동일한 사양이지만 여러 옵션이 있는 머신들이 슬슬 나오기 시작했고, MSI Edgexpert의 1TB 머신을 구매하게 되었다(4개월 할부ㅜㅜ).

4TB 머신 가격 대비 1TB 가격이 150만원 가량 차이가 나는데 3TB 저장 용량 차이 가격치고는 너무 비싸다.
그러나 1TB 머신으로 DGX Spark를 온전히 사용하기엔 불가능한게 사실이다. 도커와 모델 이미지 몇개만 다운 받으면 금방 1TB가 채워지기 때문이다. 따라서 해당 머신 구매전 어떻게 저장 공간을 확장할지 전략이 필요하다.
1TB 구매를 맘에 두고, 내 저장공간 확장 전략은 아래 두가지였다.
- 머신 개봉 후 직접 SSD를 4TB로 업그레이드 하는것 (워런티 void 이슈 있음)
- USB 3.2 Type C 의 속도를 극대화 할 수 있는 외장형 SSD 구성
일단 1번 옵션은 SSD 가격이 좀 떨어지거나 혹은 1년 warranty가 끝난 다음에 시도하는걸로 했고, 2번옵션을 시도했다.
지난 11월 중순 알리 익스프레스에서 M.2 nvme ssd 2TB를 13만원 정도에 구매(글을 쓰는 지금은 41만원이다. ㅜㅜ)했고, USB 3.2 Gen 2×2 (20Gbps) 지원하는 SSD 인클로저도 함께 구매했다. 당시 생각보다 USB 3.2 Gen 2×2 (20Gbps)를 지원하는 케이스가 국내에는 없었는데, 알리에서 3만원 정도에 구할 수 있었다(국내 제품은 5만원 짜리 단일 상품만 존재했음). 16만원 정도로 추가로 2TB를 확장했고 지금은 SSD가 더 비싸졌으니 돈 번 것이다. ㅎㅎ 또한 필요하다면 추가 구매해서 확장 구성할 수도 있을 것이고.
약 2주정도 기다려 제품을 받을 수 있었고, 바로 SSD 속도 측정을 해봤다.
$ lsusb -t
/: Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 480M
/: Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 20000M/x2
|__ Port 001: Dev 002, If 0, Class=Mass Storage, Driver=uas, 20000M/x2
/: Bus 003.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 480M
/: Bus 004.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 20000M/x2
/: Bus 005.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 480M
/: Bus 006.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 20000M/x2
|__ Port 001: Dev 002, If 0, Class=Mass Storage, Driver=uas, 20000M/x2
/: Bus 007.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 480M
/: Bus 008.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 20000M/x2
/: Bus 009.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 480M
/: Bus 010.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 20000M/x2
/: Bus 011.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/2p, 480M
|__ Port 002: Dev 002, If 0, Class=Wireless, Driver=btusb, 480M
|__ Port 002: Dev 002, If 1, Class=Wireless, Driver=btusb, 480M
|__ Port 002: Dev 002, If 2, Class=Wireless, Driver=btusb, 480M
/: Bus 012.Port 001: Dev 001, Class=root_hub, Driver=xhci-hcd/1p, 20000M/x2
Bus 002, 006에 이론상 최대 2,500MB/s까지 나올 수 있게 연결된 것을 확인 할 수 있다. (002는 기존 가지고 있던 SSD)
그러면 fio로 성능을 테스트 해본다. 참고로 해당 테스트는 실제 GPU가 활발하게 쓰이는 상황에서 측정되었다.
#쓰기
$fio --name=write_test --filename=/path/to/ssd/testfile --size=4G --rw=write --bs=1M --direct=1 --ioengine=libaio --iodepth=32 --group_reporting
#읽기
$ fio --name=read_test --filename=/path/to/ssd/testfile --rw=read --bs=1M --direct=1 --ioengine=libaio --iodepth=32 --group_reporting
| 항목 | 순차 읽기 (Sequential Read) | 순차 쓰기 (Sequential Write) |
| 테스트 대상 | /dev/sda1 (장치 직접 접근) | testfile.fio (파일 시스템 기반) |
| 작업 수 (Jobs) | 4개 (numjobs=4) | 1개 (numjobs=1) |
| 전송 속도 (MiB/s) | 1,023 MiB/s | 1,100 MiB/s |
| 전송 속도 (MB/s) | 1,073 MB/s | 1,154 MB/s |
| IOPS | 1,022 | 1,100 |
| 평균 지연 시간 | 약 120.92 ms | 약 28.15 ms |
| 최종 평가 | 10Gbps 대역폭 한계 도달 | 10Gbps 대역폭 한계 도달 |
이론대비 절반정도의 성능인데, 현재 구동중인 GPU 기반 학습 작업 때문에 머신온도가 올라가서 인 것으로 추정된다(평상시는 최대로 나오는것 확인). 그럼에도 불구하고 초당 1G 정도 쓰기 읽기는 모델 로딩,쓰기에 큰 지장이 없는 속도이긴 하다. 단, 이 머신으로 pretraining을 할 경우는 이것보다 더 읽기 쓰기가 소요될 것으로 보이나 그렇게 해당 머신이 쓰일 가능성은 거의 0에 가깝다고 본다(해당 용도가 아니기 때문에.. ). 무엇보다 이런 퍼포먼스 정도의 환경에서 튜닝/인퍼런스를 해본 결과 불편함이 전혀 없는 상황이다. 왜냐면 큰 사이즈 모델 읽고, 쓰는 경우가 그렇게 빈번하게 발생하지 않고, 컴펙트한 데이터만으로 퀵하게 LoRA 튜닝을 주로 하기 때문이다.
이것을 구매하고 바로 반드시 해야 될 일은 docker 이미지나 컨테이너들을 이 SSD로 옮기는 작업일 것이다.
아래 스크립트는 새로운 SSD로 도커 이미지 경로를 교체하는 스크립트이다. 적절히 바꿔서 사용하면 된다.
#!/bin/bash
# 1. 기존 Docker Root Dir 확인
echo "현재 Docker Root Dir:"
docker info | grep "Docker Root Dir"
# 2. Docker 서비스 중지
echo "Stopping Docker service..."
sudo systemctl stop docker.service
sudo systemctl stop docker.socket
# 3. 기존 데이터 복사 (rsync 사용)
# {NEW_PATH} 부분을 원하는 새 경로로 변경하세요. 예: /data/docker
NEW_PATH="/mnt/fastssd"
echo "Copying Docker data to $NEW_PATH ..."
sudo rsync -aP /var/lib/docker $NEW_PATH
# 4. 새 경로 생성
echo "Creating new Docker directory..."
sudo mkdir -p $NEW_PATH
# 5. Docker 데몬 설정 변경
echo "Updating daemon.json..."
sudo bash -c "cat > /etc/docker/daemon.json <<EOF
{
\"data-root\": \"$NEW_PATH\"
}
EOF"
# 6. Docker 서비스 재시작
echo "Restarting Docker service..."
sudo systemctl start docker
# 7. 새 경로 확인
echo "New Docker Root Dir contents:"
ls $NEW_PATH
참고로 기존에 가지고 있던 SSD 외장 하드를 사용하려 하신다면 정말 말리고 싶다. 구형 외장 하드하고 최신 외장 하드 속도 차이가 어마어마 해서 도커 사용하는데만 해도 큰 (기다림의)고통이 따를 것이기 때문이다.
지금까지 왜 DGX Spark를 구매했는지와 SSD 확장해서 속도 측정 및 도커 이미지 이동 등 초기 셋업 작업을 해봤다.
다음 포스팅에서는 튜닝 및 인퍼런스, 그러니까 unsloth RL 튜닝 실험과 vllm을 모두 사용할 수 있는 내가 직접 구성하고 사용중인 도커 이미지를 공유하고 RL 학습 경험이라든지 vllm 인퍼런스 예시를 소개할 예정이다. (참고로 vllm이 잘 동작하는 unsloth 도커 이미지는 시중엔 없는 상황이다.)