6 min read
AI assisted

colab CLI로 무료 GPU 원격 조종

google-colab-cli로 랩탑에서 무료 Colab GPU를 원격 조종하기

Colab은 원래 대화형이다. 셀에 코드 붙여넣고, 돌리고, 결과 보고, 다음 셀. 그런데 최근에 google-colab-cli가 나왔다. 이게 뭐냐면, 그 대화형 커널을 로컬 셸에서 원격 조종하게 해주는 도구다. colab new로 세션을 잡고, colab exec로 스크립트를 돌리고, colab stop으로 반납한다. 무료 티어 GCE 스팟 인스턴스를 gcloud로 다루는 감각이라고 하면 얼추 맞는다. 카드 등록도, 청구서도, GCP 콘솔도 없이. 개인적으로 정말 반가운 도구다. 트레이드오프도 실제 스팟과 닮았다. 싸거나 공짜지만 선점형이다. 쿼터가 있고, 유휴에 잘리고, 12시간 하드 캡이 걸린다. 인바운드 방화벽, 쿼터, 세션 휘발성 같은 제약은 그대로지만, CLI가 붙으면 셸에서 다룰 수 있는 대상이 된다. 이 글은 그 셋업이 어떻게 도는지 실제 세션 하나로 훑는다.


이 도구가 실제로 하는 것

google-colab-cli는 파이썬으로 설치되는(pip install google-colab-cli) 클라이언트로, Google 계정 인증 후 Colab 커널을 원격에서 조종한다. 동사는 몇 개뿐이다. new, sessions, exec, upload, download, stop. 각각이 노트북 UI가 쓰는 것과 같은 백엔드에 붙는다. 도는 커널도 같은 커널. 같은 GPU, 같은 디스크, 같은 유휴 타임아웃. 이제 에디터·버전 관리·CI가 브라우저 없이 그 커널을 만진다는 점이 달라진다.

"노트북 없는 노트북" 이상으로 유용해지는 대목 두 가지:

  • 스크립트가 셀이 아니라 저장소에 산다. git에 두고 로컬에서 편집하고, 원격 커널에 exec한다. 커널이 죽으면 같은 파일을 다시 돌리면 그만.
  • CLI가 조합된다. exec는 스크립트가 끝나면 stdout/stderr을 돌려준다. 그 위에서 셸 스크립트가 단계를 잇거나, 상태를 폴링하거나, 출력에 따라 다음 단계를 결정하는 게 자연스럽다. 다른 원격 러너 다루듯이.

세션과 쿼터

GPU는 자원이 아니라 슬롯이고, 이 CLI가 그 슬롯을 눈에 띄게 드러낸다.

colab new --gpu t4        # e87c7c 같은 세션 id 반환
colab sessions            # 활성 세션 목록
colab stop -s <SESS>      # 슬롯 반납

기억해 둘 두 가지:

  • TooManyAssignmentsError(HTTP 412)는 쿼터이지 버그가 아니다. 이미 슬롯을 하나 잡고 있으면 Google 배정기가 두 번째 GPU 슬롯을 거부한다. 유휴 세션도 슬롯을 계속 점유한다. 한 시간 전에 sleep 1000 걸어놓은 것 하나면 다음 new가 실패하기에 충분하다. 처방: sessions로 뭐가 살아 있는지 보고, 남은 걸 stop하고, 다시 시도.
  • 세션은 휘발성이다. 커널은 유휴 90분쯤에 해체되고 총 12시간쯤에 하드 캡이 걸린다. 살아남게 하고 싶은 건 뭐든 커널 밖으로 나가야 한다. 저장소에 push하든, 다른 곳에 upload하든, 터널로 뽑아내든.

이 휘발성이 결국 워크플로의 이유가 된다. 진실의 원천은 저장소에 두고, 커널은 잠깐 쓰는 컴퓨트로 취급한다.

실행 모델

세션 안 거의 모든 단계가 이 모양이다:

colab exec -s <SESS> -f scripts/NN_step.py --timeout N

스크립트는 VM 안에서 돈다. 그 /content는 VM의 /content, 그 localhost:8000은 VM의 localhost. 이 구분은 노트북 워크플로에서 처음 넘어올 때 놓치기 쉬운데, 스크립트 안 경로나 포트는 랩탑이 아니라 커널 시점에서만 의미가 있다.

비트리비얼한 실행이면 거의 매번 걸리는 두 가지 귀결:

  • 백그라운드 프로세스는 nohup + 로그 파일 패턴이 필요하다. exec는 스크립트가 리턴하면 같이 리턴한다. 서버 하나를 Popen하고 스크립트가 종료돼 버리면 서버는 커널 안에서 계속 돌지만, 출력을 보려면 미리 로그 파일에 써 두고 다음 exectail해야 한다. 실전 Colab CLI 셋업이 이런 모양인 이유가 여기 있다. 설치·서빙은 한 exec에서, 폴링은 다음 exec에서.
  • 환경변수는 뒤로 전파되지 않는다. 스크립트 안에서 os.environ["X"] = "y"를 해봐야 그 스크립트에서만 유효하다. 이후 단계가 그 값을 필요로 하면, 이전 단계에 심을 게 아니라 그 단계 명령의 셸 prefix에 넣어야 한다. LD_LIBRARY_PATH, PYTHONPATH 등 모두 같은 규칙.

파일 주고받기

Colab VM은 공인 IP 없이 Google 엣지망 뒤에 있고, 인바운드는 필터링된다. scp, ssh, iptables로 포트를 열어봐야 VM엔 안 닿는다. 통제 밖에서 막기 때문. CLI의 답은 exec와 같은 인증 터널을 타는 동사 두 개다:

colab upload  -s <SESS> local.png /content/local.png
colab download -s <SESS> /content/out.json ./out.json

파일은 이걸로 해결된다. 이걸로 해결되지 않는 건 임의 TCP다. 커널의 스크립트가 소켓을 외부에 열어 서빙하려면 VM이 밖으로 나가는 아웃바운드-개시 터널이 필요하다. cloudflared Quick Tunnel이 가장 짧게 통하는 옵션이고(다음 절), ngrok이나 Tailscale funnel이 같은 부류다.

서버를 외부에 노출: 아웃바운드 터널

패턴의 이유를 깔끔히 짚으면 이렇다:

  • VM은 새로 들어오는 인바운드 커넥션을 받을 수 없다.
  • VM은 나가는 아웃바운드 커넥션은 자유롭게 뚫는다.
  • cloudflared, ngrok, tailscale funnel 셋 다 자기 엣지에 지속적인 아웃바운드 커넥션을 열어 두고, 그 엣지가 공개 HTTPS를 받아 터널로 밀어 넣는다.
  • 결국 요청은 안에서 열린 리턴 패스로 VM에 닿는다.

cloudflared Quick Tunnel은 zero-config다:

curl -sL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \
  -o /content/cloudflared && chmod +x /content/cloudflared
nohup /content/cloudflared tunnel --url http://localhost:8000 --no-autoupdate \
  >> /content/cf_tunnel.log 2>&1 &

몇 초 후 로그에 https://<random>.trycloudflare.com가 찍힌다. 이 URL은 공개된 데다 무인증이다. Cloudflare가 아무의 HTTPS 요청이든 터널로 밀어 넣는다. 데모보다 오래 살릴 거라면 서버 쪽에 API 키를 하나 걸어라 (vllm serve ... --api-key <KEY>, 서빙하는 것에 맞는 등가물). 호스트명을 고정하고 싶으면 Cloudflare 계정 + 소유 도메인(무료 티어)으로 Named Tunnel을 쓴다.

실전: T4에서 1B VLM 서빙

워크플로가 실제로 흔들려 본 예. PaddleOCR-VL(~1B, ERNIE-4.5 + NaViT)을 무료 T4에서 서빙하고, 랩탑에서 공개 URL로 인보이스 이미지를 OCR한다.

CLI 쪽은 위 패턴만 잡히면 조용하다:

colab exec -s $SESS -f scripts/03_start_install.py --timeout 60      # pip install vllm
colab exec -s $SESS -f scripts/04_poll_install.py  --timeout 200     # 설치 로그 tail
colab exec -s $SESS -f scripts/08_start_server.py  --timeout 60      # nohup vllm serve
colab exec -s $SESS -f scripts/09_poll_server.py   --timeout 120     # /v1/models 폴링
colab exec -s $SESS -f scripts/10_test_ocr.py      --timeout 180     # 로컬 OCR 왕복
colab exec -s $SESS -f scripts/11_start_tunnel.py  --timeout 200     # cloudflared
colab exec -s $SESS -f scripts/12_get_url.py       --timeout 60      # trycloudflare URL 추출

Colab에서 실제 서빙이 밟는 T4-특정 함정 세 개는 CLI 문제가 아니라, 스크립트가 Turing 카드에서 vLLM stable-libtorch ABI로 실제 실행될 때 발견되는 것들이다:

  • vLLM 0.24.0은 CUDA 13에 링크돼 있고 torch는 CUDA 12를 유지한다. 서빙 전에 LD_LIBRARY_PATHnvidia-cu13-* 경로를 추가한다.
  • T4에는 bf16 텐서코어가 없다. --dtype float16을 넘겨 vLLM이 캐스팅하게 둔다.
  • 131072 토큰 기본 KV 창은 OOM 난다. --max-model-len 16384 --max-num-batched-tokens 16384 --gpu-memory-utilization 0.85 --enforce-eager 조합이면 서버가 예측 가능하게 뜬다.

FA2와 FlashInfer는 sm75에서 안 되고 vLLM이 자동으로 TORCH_SDPA / TRITON_ATTN로 폴백한다. 별도 override는 필요 없다. 각 함정의 전체 스택 트레이스와 스크립트는 원본 저장소에 있다. 여기선 워크플로가 어디서 걸리는지만 짚는다.

서버가 뜨고 터널이 나갔으면 클라이언트는 stdlib-only다:

python client/ocr_client.py \
  --url https://<random>.trycloudflare.com \
  --image sample.png --task "OCR:"

뒷정리와 쿼터 습관

세션이 끝날 때 매번 같은 마무리가 필요하다:

colab exec -s $SESS -f scripts/14_cleanup.py --timeout 60   # 터널·서버 종료, 로그 삭제
colab stop -s $SESS                                         # GPU 슬롯 반납

stop이 중요한 쪽이다. 유휴 세션도 슬롯을 계속 점유하고, 슬롯을 그냥 태워 버리면 Google이 늘 곧바로 새로 주지는 않는다. 자리를 뜰 때 stop하는 습관을 들이는 게 낫다.

이 도구가 어울리는 자리 (그리고 안 어울리는 자리)

CLI가 잘 어울리는 경우:

  • 에디터가 노트북보다 나은, 스크립트 모양의 짧은 GPU 실험.
  • 한 시간짜리 공개 URL이 필요한 일회성 데모.
  • 전용 카드까지 쓰지 않고 이따금 GPU가 필요한 CI/스케줄 잡.
  • 셀이 아니라 저장소가 진실의 원천이어야 하는 모든 것.

어울리지 않는 경우:

  • 지속적으로 도는 서비스. 12시간 캡과 유휴 해체가 하드 리밋이다.
  • 앞단에 인증을 두지 않은 공개 URL로 민감한 데이터를 다루는 것.
  • 긴 트레이닝 런에서 GPU를 잡고 있어야 하는 워크로드. 총합으로 보면 렌탈 카드가 싸다.

유료 스팟 GPU VM은 gcloud compute instances create ... --provisioning-model=SPOT으로 얻고, 무료 티어 대응은 colab new --gpu t4로 얻는다. 선점형 프로파일은 같고, 청구서만 없다. 그 두 번째를 실제 dev 대상으로 다루게 해주는 게 이 CLI다.

전체 소스와 14개 단계별 스크립트, 재사용 OCR 클라이언트는 github.com/ysys143/test_colab_cli에 있다.