어느 클라우드에서 돌리든 저장은 허깅페이스에
SkyPilot으로 멀티클라우드 GPU를 오가며 Hugging Face Hub에 데이터와 체크포인트를 두는 제로 이그레스 패턴을 따라가 봅니다.
원문 논문 보기
오늘 다룰 글은 Hugging Face 블로그에 올라온 Run AI workloads on any cloud, store on Hugging Face: zero-egress storage with SkyPilot입니다. SkyPilot 측과의 협업으로 쓰인 이 포스트는 같은 학습 작업을 여러 클라우드에서 돌리면서 데이터와 결과물은 Hugging Face Hub에 두자고 제안하는데, 원문 링크는 제목 아래에 붙어 있습니다.
제가 이 글을 고른 이유는 주장이 짧아서입니다. 컴퓨트는 어디서든 빌리고 스토리지는 한곳에 두면 이그레스 요금과 락인을 함께 줄일 수 있다는 것인데, 말은 쉬워도 막상 해보면 스토리지 쪽에서 꼭 무언가 걸리니까요. 그래서 이번 글에서는 그 짧은 주장을 따라가면서 어디가 편해지고 어디가 여전히 아픈지 하나씩 짚어보겠습니다.
왜 좋은 GPU를 찾아다니면 스토리지가 발목을 잡을까
안녕하세요, 패트릭입니다.
학습을 돌려본 분들이라면 다들 비슷한 밤을 겪어보셨을 겁니다. 낮에는 어느 리전의 어느 GPU가 비어 있는지 가격표를 뒤지다가, 밤이 되면 갑자기 스토리지가 말을 안 듣기 시작하죠. 버킷 이름은 리전에 묶여 있고 복사 스크립트는 특정 클라우드 명령어에 기대고 있으며, 체크포인트 경로는 하드코딩되어 있기 때문입니다. 그래서 GPU가 옆 클라우드에 남아도 선뜻 옮기지 못합니다.
생각해보면 이 모습은 이사와 비슷합니다. 컴퓨트는 시간 단위로 빌리는 몸이고 데이터와 체크포인트는 쌓아둔 짐인데, 짐이 특정 집 창고에 들어가 있으면 그 집 근처에서만 살아야 하니까요. 그렇다면 짐을 중립적인 창고에 두면 어떨까요. 이번 포스트가 던지는 질문도 바로 그겁니다.
저는 이 대목에서 고개를 끄덕이면서도 한 가지를 조심했습니다. 스토리지 중립이라는 말이 듣기에는 시원하지만, 실제로는 속도랑 권한이랑 버전 관리가 함께 와야 하니까요. 글은 그 답으로 Hugging Face Hub를 꺼내 드는데, 모델과 데이터셋이 이미 모여 있는 곳이니 창고이자 도서관처럼 쓰자는 제안입니다.
같은 YAML을 어느 클라우드에서나 돌린다는 말의 실제 의미
SkyPilot을 처음 보면 문법부터 눈에 들어옵니다. 리소스와 실행 단계를 YAML 하나로 적고, 클라우드를 바꾸더라도 그 파일을 그대로 쓴다는 것이죠. 뒤에서는 옵티마이저가 가격과 GPU 재고를 보고 어디에 띄울지 고르는데, 오늘은 AWS에 자리가 있으면 AWS에, 내일은 GCP가 싸지면 GCP에 올리는 식입니다.
여기서 재미있는 질문이 생깁니다. 파일 하나를 그대로 쓴다는 게 정말 아무것도 안 바꿔도 된다는 뜻일까요. 제 경험으로는 아닙니다. 코드는 같아도 데이터 경로와 인증과 아웃풋 복사가 클라우드마다 조금씩 달라지거든요. 그런데 이 글의 접근은 그 다른 부분을 줄이는 쪽입니다. 컴퓨트 쪽은 SkyPilot이 추상화하고, 스토리지 쪽은 Hub라는 바깥 좌표를 쓰니, S3 경로도 아니고 GCS 경로도 아닌 허브 리포지토리 이름 하나면 어디서든 같은 주소를 가리키니까요.
물론 클라우드마다 네트워크 성능이나 디스크 옵션은 여전히 다릅니다. 다만 적어도 데이터가 어디 있는지를 두고 싸울 일은 줄어드는데, 밤새 복사 작업을 돌리다 아침에 요금 고지서를 보는 장면이 사라지는 것만으로도 운영자 입장에서는 숨이 트입니다. 이 차이는 생각보다 큽니다. 이사를 갈 때마다 짐 목록을 새로 쓰지 않아도 되기 때문입니다.

그렇다면 스토리지를 클라우드 바깥에 두면 어떻게 달라질까
앞에서 컴퓨트는 빌리고 짐은 따로 둔다고 했는데, 그 짐을 구체적으로 어디에 두는지가 이번 이야기의 갈림길입니다. 글은 데이터셋과 체크포인트와 최종 산출물을 Hub 리포지토리에 두자고 말하는데, 학습용 데이터는 Hub 데이터셋으로, 중간 체크포인트는 모델 리포지토리에 버전별로, 마지막 가중치도 같은 자리에 올리는 식이죠.
이 방식이 편한 이유는 주소가 하나이기 때문입니다. 어느 클라우드 워커에서 돌리든 같은 리포지토리 이름을 읽고 쓰면 되니, 어제는 비어 있던 클라우드에서 돌린 실험을 오늘은 다른 클라우드에서 이어서 돌려도 버킷을 복사하거나 경로를 고칠 필요가 없습니다. 실험 기록과 파일이 같은 리포지토리 히스토리에 남으니, 나중에 누가 돌려도 재현 경로가 단순해집니다.
반면 이런 말을 들으면 바로 드는 의심도 있습니다. 오브젝트 스토리지 대신 Git 기반 허브에 큰 파일을 계속 올리면 속도가 괜찮을까 하는 것이죠. 글도 이 점을 숨기지 않는데, 작은 체크포인트와 빈번한 푸시는 편하지만 수백 기가바이트를 매 스텝마다 쏟아내는 대형 학습에서는 처리량과 속도 제한을 따져봐야 하니까요. 저는 이 솔직함이 마음에 들었습니다. 만능이라고 말하는 대신 어디까지 편하고 어디부터 따져봐야 하는지 선을 그어주거든요.

HF_TOKEN 하나로 워커가 허브에 붙는 흐름
이제 실제로 붙이는 장면을 따라가 보겠습니다. 흐름 자체는 단순합니다. SkyPilot 워커가 뜰 때 Hugging Face 토큰을 환경 변수로 넣어주고, 학습 코드에서는 huggingface_hub 클라이언트로 로그인한 뒤 필요한 리포지토리를 읽고 쓰면 되니까요. HF_TOKEN이라는 이름 하나로 인증이 끝나니 클라우드마다 다른 IAM 역할을 엮을 필요가 없습니다.
개인적으로 인상 깊었던 점은, 이 단순함이 디버깅을 쉽게 만든다는 것입니다. 로컬 노트북에서 되던 토큰 읽기 쓰기가 클라우드 워커에서도 거의 그대로 되는데, S3라면 리전 엔드포인트를 확인하고 역할 정책을 뜯어봐야 할 일이 여기서는 토큰 권한이 읽기인지 쓰기인지, 리포지토리가 비공개인지 공개인지 정도로 좁혀지니까요. 밤에 장애가 났을 때 확인할 목록이 짧다는 건 운영자에게는 큰 위로입니다.
문제는 그다음입니다. 토큰 하나면 편하지만, 그만큼 토큰 관리가 중요해지니까요. 누가 읽기 토큰을 가졌는지, 쓰기 토큰은 어디까지 허용하는지, 팀 리포지토리마다 권한을 어떻게 나눌지 정해야 합니다. 글도 비공개 데이터셋의 거버넌스를 따로 언급하는데, 편리한 만큼 토큰이 새면 파장이 크니 짧게 쓰고 자주 돌리는 운영 습관이 필요합니다. 편하다고 아무 토큰이나 워커에 넣어두면 나중에 꼭 탈이 나거든요.
학습 코드는 생각보다 단순해지는 이유
코드 쪽으로 내려가면 이야기가 더 구체적이 됩니다. 데이터는 datasets 라이브러리의 load_dataset으로 스트리밍하거나 내려받고, 체크포인트는 학습 중간에 push_to_hub로 올리면 되니까요. 클라우드별 복사 명령어를 외울 필요가 없고, 학습 스크립트가 어느 클라우드에서 돌아도 같은 허브 주소를 바라보게 됩니다.
같은 모델에 같은 질문을 던져도 결과가 크게 달라질 수 있다는 것을 경험으로 알고 있습니다. 코드 이식도 비슷합니다. 경로가 바뀌면 꼭 어딘가에서 깨지는데, 허브 주소를 쓰면 그 깨지는 지점이 줄어드니까요. 어제 GCP에서 돌린 파인튜닝을 오늘 Azure 스팟에서 이어서 돌려도 스크립트가 가리키는 데이터셋 이름은 그대로입니다. (참고로 여기서 말하는 스트리밍은 매번 처음부터 끝까지 내려받는 것과 다르며, 필요할 때 필요한 만큼 가져오는 방식에 가깝습니다.)
저는 이 대목보다 그 뒤의 구조가 더 중요하다고 봅니다. 코드가 단순해졌다는 말은 곧 실험을 옮기는 비용이 낮아졌다는 뜻이고, 실험을 옮기는 비용이 낮아지면 GPU 가격을 보고 클라우드를 고르는 일이 현실이 되니까요. SkyPilot의 옵티마이저가 빛나는 지점도 바로 여기입니다. 옮기는 게 어려우면 최적 가격은 그림의 떡이지만, 옮기는 게 쉬우면 가격표가 바로 협상 카드가 됩니다.

제로 이그레스는 무료라는 뜻이 아니다
여기서 용어를 한 번 짚고 넘어가야 합니다. 제로 이그레스라는 말을 들으면 뭔가 공짜 전송로가 열린 것처럼 느껴지는데요. 실제로는 그렇지 않습니다. 클라우드 사업자가 버킷 사이 복사나 외부 전송에 매기는 이그레스 요금을 피한다는 뜻에 가깝습니다. 허브의 스토리지와 CDN에서 직접 읽으니 클라우드 바깥으로 데이터를 빼낼 때 붙던 요금이 줄어드는 것이죠.
이 차이는 생각보다 중요합니다. S3에 있는 수십 테라바이트를 다른 클라우드로 옮기면 요금이 눈에 띄게 불어나는데, 처음부터 허브에서 읽으면 그 복사가 필요 없습니다. 특히 여러 클라우드를 오가며 실험하는 팀에게는 매번 복사본을 만들고 지우는 수고까지 줄어듭니다. 돈도 돈이지만, 기다리는 시간이 줄어드는 게 체감이 큽니다.
그런데 여기서는 조금 조심해서 읽을 필요가 있습니다. 허브에서 읽는다고 모든 전송이 무료가 되는 건 아니니까요. 클라우드 쪽 인바운드 정책이나 리전별 네트워크 비용, 빈번한 체크포인트 푸시에 따르는 시간 비용은 여전히 남습니다. 제가 앞으로 보고 싶은 것은 이 부분의 실측입니다. 같은 체크포인트 크기를 S3에 쓸 때와 허브에 올릴 때 wall-clock 시간과 비용이 리전별로 어떻게 갈리는지 숫자로 보고 싶습니다. 방향은 분명하지만, 그 폭은 워크로드마다 다를 테니까요.

컴퓨트는 빌리고 스토리지는 쌓는 분리가 주는 재현성
앞에서 돈 이야기를 했으니, 이제는 기록 이야기를 해보겠습니다. 컴퓨트는 휘발성이고 스토리지는 영속적이라는 구분은 말로는 쉬운데 막상 지키기는 어렵습니다. 급하게 돌리다 보면 체크포인트가 어느 VM 디스크에 있는지, 그 VM이 벌써 반납됐는지부터 헷갈리거든요.
허브에 두면 이 풍경이 바뀝니다. 데이터셋 버전과 코드 커밋과 체크포인트 리비전이 같은 주소 체계 안에 남으니, 누가 언제 어떤 데이터로 어떤 가중치를 만들었는지 허브 히스토리를 따라가면 됩니다. 나중에 논문 리뷰어가 물어봐도 당황하지 않죠. 저희가 새로운 AI 아키텍처를 볼 때도 숫자만 보지는 않습니다. 실제로 무엇이 바뀌었고, 그 변화가 시스템에서 어떤 비용 구조를 만드는지를 함께 보는데요. 재현성도 같은 맥락입니다. 숫자 뒤에 그 숫자를 만든 경로가 있어야 믿을 수 있으니까요.
그렇다면 이 흐름은 어디서 가장 빛날까요. 저는 여러 클라우드를 오가는 팀 실험에서라고 봅니다. 오늘은 남은 크레딧 때문에 A 클라우드에서 돌리고, 다음 주에는 GPU 재고 때문에 B 클라우드에서 돌려도 결과물이 같은 리포지토리에 쌓이면 비교가 쉬워집니다. 실험 관리 도구가 따로 있어도 파일 자체가 한곳에 모여 있다는 감각은 꽤 든든합니다. 흩어진 체크포인트를 찾아다니던 밤이 줄어드는 것만으로도 연구 속도가 달라집니다.

그래도 S3를 완전히 버릴 수는 없는 이유
여기까지 읽으면 허브 하나로 다 될 것 같지만, 현실은 그렇게 단순하지 않습니다. 글도 절충점을 비교적 솔직하게 적어두었는데요. 저도 이 대목에서는 기대를 조금 낮추는 편이 맞다고 봅니다.
먼저 처리량 문제가 있습니다. 대형 모델을 분산 학습하면서 매번 큰 샤드를 자주 올리면 업로드 대역폭이 병목이 될 수 있습니다. 로컬 NVMe나 리전 내 오브젝트 스토리지처럼 옆에 붙은 저장소와 비교하면, 바깥 허브까지 왕복하는 시간은 무시하기 어렵습니다. 체크포인트 빈도를 낮추거나 비동기로 올리는 식의 타협이 필요해집니다.
다음으로 속도 제한과 거버넌스가 있습니다. 팀 단위로 비공개 데이터셋을 다룰 때는 누가 무엇을 읽고 썼는지, 외부 반출 규칙은 어떻게 되는지, 보존 기한은 어떻게 둘지를 정해야 합니다. 네이티브 오브젝트 스토리지에는 이미 익숙한 감사와 정책 도구가 있지만, 허브에서는 그 운영법을 새로 맞춰야 합니다. 작은 팀에게는 허브가 간편하지만, 규제가 촘촘한 조직에서는 오히려 결재 라인이 늘어날 수도 있습니다.
그래서 제 결론은 분리 사용에 가깝습니다. 자주 옮겨 다니는 실험과 공유용 가중치와 재현용 기록은 허브에 두고, 초대형 체크포인트의 빈번한 중간 저장은 리전 내 스토리지에 두는 식이죠. 다 허브에 넣겠다는 욕심보다 어디까지 허브에 둘지를 정하는 게 먼저입니다.
실제로 어디서 막히는지 미리 점검한다면
좋은 건 알겠는데, 이걸 실제로 어떻게 올릴까요. 이 질문에 답하려면 개념보다 체크리스트가 필요합니다. 제가 엔지니어 관점에서 보면 다음 질문이 바로 생기는데, 토큰은 어디에 두고 속도는 어떻게 재고 실패하면 어디로 떨어질지 정했는가 하는 것이죠.
먼저 토큰과 권한을 나눠야 합니다. 읽기 전용 토큰과 쓰기 토큰을 분리하고 리포지토리마다 접근자를 다르게 두는 겁니다. CI에서 돌리는 실험과 사람이 손으로 올리는 릴리스가 같은 토큰을 쓰면 언젠가 사고가 납니다. 토큰을 환경 변수로 넣을 때도 로그에 찍히지 않는지, 스냅샷에 남지 않는지 확인해야 하죠.
다음으로 데이터 크기와 빈도를 재야 합니다. 데이터셋 전체를 매번 내려받을지 스트리밍할지, 체크포인트를 몇 스텝마다 올릴지, 실패한 푸시는 어떻게 재시도할지를 정해야 합니다. 한 번은 작은 파인튜닝으로 왕복 시간을 재보고, 그다음에 큰 학습으로 넓혀가는 순서가 안전합니다. 여기서 숫자를 하나만 꼽자면 체크포인트 하나의 크기와 업로드에 걸리는 시간을 먼저 재는 게 좋습니다. 그 두 숫자가 정해져야 빈도를 정할 수 있으니까요.
마지막으로 떨어질 곳을 마련해야 합니다. 허브 업로드가 막히면 로컬 디스크에 임시로 쌓아둘지, 리전 버킷에 우회 저장할지 미리 정해두는 것이죠. 네트워크가 흔들리는 밤에 백업 경로가 없으면 학습이 멈춥니다. 운영 측면에서 보면 이 예비 경로가 생각보다 중요합니다. 잘될 때가 아니라 안 될 때를 대비하는 게 운영이니까요.

다음에 GPU를 옮길 때 무엇이 남을까
이야기를 정리할 시간이 됐습니다. 처음 질문으로 돌아가면, 왜 GPU를 찾아다니면 스토리지가 발목을 잡았을까요. 답은 짐이 특정 집에 묶여 있었기 때문이고, 이번 글이 제안한 답은 짐을 중립 창고에 두자는 것이었습니다. SkyPilot이 몸을 가볍게 만들고, Hub가 짐을 한곳에 모아주는 그림이죠.
저는 이 조합을 만능 해법이라기보다 좋은 기본값으로 봅니다. 여기까지 일반화하기에는 워크로드별 실측이 아직 부족하지만, 여러 클라우드를 오가는 팀이라면 한 번 깔아둘 만한 기본값이라는 뜻입니다. 특히 모델과 데이터셋을 이미 허브에서 주고받는 팀에게는 추가로 들일 공구가 적습니다. 토큰 하나와 리포지토리 규칙 몇 개면 시작할 수 있으니까요.
그렇다면 사람의 역할은 무엇일까요. 저는 옮기는 일을 자동화할수록 남는 일은 고르는 일이라고 봅니다. 무엇을 허브에 둘지, 무엇을 리전 안에 둘지, 체크포인트를 얼마나 자주 남길지, 그 결정을 워크로드마다 내리는 일이죠. 다음에 GPU 가격이 바뀌어 클라우드를 옮길 때, 여러분의 체크포인트는 어디에서 여러분을 기다리고 있을까요. 그 답이 허브라면, 이 글에서 소개한 패턴은 이미 절반쯤 와 있는 셈입니다.
참고 자료
- Run AI workloads on any cloud, store on Hugging Face: zero-egress storage with SkyPilot · huggingface.co
리뷰 원문