실행 위치의 지도: 로컬·원격·클라우드
먼저 읽을 장 2.3 SSH와 원격 접속
오늘날 연구자의 하루는 여러 기계에 걸쳐 있다. 노트북에서 코드를 고치고, 연구실 GPU 서버에서 학습을 돌리고, 공유 NAS에서 데이터를 읽고, 클라우드 notebook에서 그림을 그리고, 휴대폰으로 에이전트 세션에 지시를 보낸다. 화면은 모두 내 앞에 있지만 일이 실제로 일어나는 곳은 제각각이다. 이 차이를 의식하지 않으면 "고쳤는데 반영이 안 된다", "창을 닫았더니 학습이 죽었다", "민감 데이터가 언제 클라우드로 갔는지 모른다" 같은 문제가 생긴다.
이 장에는 새 명령이 거의 없다. 대신 어떤 도구든 다섯 가지 질문으로 분해해 보는 지도를 만든다. 지도가 있으면 새 도구(새 IDE, 새 클라우드 서비스, 새 에이전트)가 나와도 그것이 어디서 무엇을 하는지 스스로 판단할 수 있다. 이 장에 나오는 제품과 서비스는 구조를 설명하기 위한 집필 시점 예시이며, 기능과 이름은 빠르게 바뀐다.
학습 목표
- 코드·연산·데이터·인증·세션의 위치를 구분한다
- 작업에 맞는 실행 구조를 선택한다
다섯 가지 질문
왜 필요한가. "원격으로 작업한다"는 말은 너무 많은 것을 뭉뚱그린다. VS Code Remote-SSH로 서버 파일을 고치는 것, SSH 터미널에서 학습을 띄우는 것, 휴대폰으로 서버의 에이전트 세션을 조종하는 것, 클라우드 notebook에서 셀을 실행하는 것은 모두 "원격"이지만, 끊겼을 때 무엇이 사라지는지, 데이터가 어디로 흘러가는지가 전혀 다르다.
정의. 어떤 작업 구성이든 다음 다섯 질문에 답할 수 있으면 그 구성을 이해한 것이다.
| 질문 | 확인할 것 | 틀렸을 때 생기는 일 |
|---|---|---|
| 1. 코드는 어디에 있는가 | 편집하는 파일의 실제 위치, 사본이 몇 개인가, 무엇이 사본을 동기화하는가 | 고친 것과 실행한 것이 다르다 |
| 2. 연산은 어디서 실행되는가 | 프로세스가 도는 기계, 그 기계의 CPU·GPU·메모리 | 노트북에서 돌 줄 알았던 것이 서버 자원을 쓰거나, 그 반대 |
| 3. 데이터는 어디에 있는가 | 데이터 파일의 저장 위치, 연산 위치와의 거리 | 대용량 이동, 느린 I/O, 반출 금지 위반 |
| 4. 누구의 자격으로 접근하는가 | 어떤 계정·키·토큰이 쓰이는가, 그 자격이 어디에 저장되는가 | 공용 계정 사용, 토큰 유출, 남의 권한으로 실행 |
| 5. 무엇이 세션을 유지하는가 | 연결이 끊기거나 창을 닫으면 무엇이 멈추는가 | 창을 닫았더니 학습이 죽었다 |
직관. 다섯 질문은 서로 독립이다. 코드는 노트북에, 연산은 GPU 서버에, 데이터는 NAS에, 자격은 내 SSH 키에, 세션은 서버의 tmux에 있을 수 있다. 문제는 대부분 우리가 이 다섯을 한 곳에 있다고 착각할 때 생긴다. 예를 들어 노트북 화면에 코드가 보이니 실행도 노트북에서 된다고 생각하는 식이다.
연구에서의 의미. 다섯 질문 중 가장 비싼 것은 보통 3번(데이터)이다. 코드는 MB 단위라 어디로든 순식간에 옮길 수 있지만, 학습 데이터는 TB 단위가 흔하다. 그래서 큰 데이터를 다루는 작업의 기본 원칙은 코드를 데이터 쪽으로 옮긴다는 것이다. 아래는 크기별 전송 시간의 하한이다. 회선 대역폭을 100% 쓴다고 가정한 계산이라 실제로는 더 걸린다.
size_bytes = {"code 50 MB": 50e6, "results 2 GB": 2e9, "dataset 2 TB": 2e12}
link_gbps = {"100 Mbps": 0.1, "1 Gbps": 1.0, "10 Gbps": 10.0}
def transfer_s(n_bytes, gbps):
return n_bytes * 8 / (gbps * 1e9) # 대역폭을 100% 쓴다고 가정한 하한
def human(t_s):
if t_s < 1:
return f"{t_s * 1000:.0f} ms"
return f"{t_s:.1f} s" if t_s < 60 else f"{t_s / 60:.1f} min" if t_s < 3600 else f"{t_s / 3600:.1f} h"
print(f"{'':10s}" + "".join(f"{name:>15s}" for name in size_bytes))
for link, gbps in link_gbps.items():
print(f"{link:10s}" + "".join(f"{human(transfer_s(b, gbps)):>15s}" for b in size_bytes.values()))code 50 MB results 2 GB dataset 2 TB 100 Mbps 4.0 s 2.7 min 44.4 h 1 Gbps 400 ms 16.0 s 4.4 h 10 Gbps 40 ms 1.6 s 26.7 min
코드는 어떤 회선으로도 몇 초면 옮기지만, 2 TB 데이터셋은 집 회선으로 이틀 가까이 걸린다. 회선 대역폭은 bits/s, 파일 크기는 bytes로 표시된다는 점에 주의한다. 둘 사이의 8배를 빠뜨리면 전송 시간을 8분의 1로 잘못 잡는다.
대표적인 실행 구조
연구에서 자주 쓰는 구성을 다섯 질문으로 분해해 본다.
로컬 편집 · 로컬 실행. 노트북에서 고치고 노트북에서 돌린다. 다섯 질문의 답이 거의 모두 "내 노트북"이라 가장 단순하다. 빠른 반복, 작은 데이터, 그림 그리기, 단위 테스트에 맞는다. 한계는 노트북의 GPU·메모리, 그리고 노트북을 덮으면 모든 것이 멈춘다는 점이다.
로컬 편집 · SSH 원격 실행. 코드는 노트북에서 편집하고 git이나 rsync로 서버에 보내거나, 원격 IDE(2.3장)로 서버의 파일을 직접 편집한다. 연산은 서버에서, 세션은 서버의 tmux나 scheduler가 유지한다. 연구실 GPU 서버를 쓰는 가장 흔한 구성이다. 주의할 점은 1번 질문이다. 원격 IDE를 쓰면 코드는 서버에 하나뿐이지만, "노트북에서 편집하고 rsync로 보내기"를 쓰면 사본이 둘이 되어 동기화를 잊기 쉽다. 사본이 둘이라면 git 커밋 해시를 실행 로그 첫 줄에 남겨, 무엇이 실행되었는지 기록한다(22.3장 실험 자동화와 하이퍼파라미터 탐색).
원격 서버에서 실행되는 에이전트 세션을 다른 기기에서 원격 제어. 코딩 에이전트(4.3장 AI 코딩 도구의 활용과 검증)를 연구실 데스크톱이나 서버에서 실행해 두고, 휴대폰·웹 브라우저 같은 다른 기기에서 그 세션에 지시를 보내고 결과를 확인하는 구성이다. 다섯 질문으로 보면 코드·연산·데이터는 모두 세션이 도는 기계에 있고, 에이전트가 실행하는 명령은 그 기계에 로그인한 계정의 권한으로 실행된다. 조종하는 기기에는 화면과 입력만 있다. 그래서 조종하는 기기를 꺼도 세션은 계속 돌 수 있고, 반대로 세션이 도는 기계가 잠들면 세션이 멈춘다.
클라우드 개발환경 · 클라우드 에이전트 · 관리형 notebook · 학습 job. 클라우드 제공자가 기계를 빌려주는 여러 형태다.
| 형태 | 예 (집필 시점) | 코드 | 연산 | 데이터 | 세션 |
|---|---|---|---|---|---|
| 클라우드 개발환경 | GitHub Codespaces 같은 브라우저 IDE | 클라우드 VM 안 저장소 사본 | 클라우드 VM | 클라우드로 옮긴 것만 | VM(유휴 시 정지될 수 있음) |
| 클라우드 에이전트 | 저장소를 받아 클라우드에서 작업하는 코딩 에이전트 | 클라우드 컨테이너 안 사본 | 클라우드 컨테이너 | 컨테이너가 접근할 수 있는 것 | 에이전트 서비스 |
| 관리형 notebook | Google Colab 같은 서비스 | notebook 파일(클라우드 저장) | 할당받은 런타임 | 업로드했거나 마운트한 것 | 런타임(유휴·시간 제한 있음) |
| 관리형 학습 job | 클라우드의 학습 작업 제출 서비스 | 제출 시점의 코드·컨테이너 이미지 | 할당받은 노드 | 클라우드 저장소 | 작업 스케줄러 |
공통점은 데이터와 자격 증명이 클라우드로 간다는 것이다. 클라우드 에이전트나 개발환경에 저장소를 열어 주면, 그 저장소에 커밋된 것과 그 환경에 설정한 토큰이 모두 그쪽에 있다. 편리함과 확장성의 대가로 4번(자격)과 3번(데이터) 질문의 답이 내 통제 밖으로 일부 넘어간다는 점을 의식하고 고른다. 서비스마다 유휴 시간 제한, 저장소 보존 기간, 데이터 처리 방침이 다르므로 쓰기 전에 해당 서비스의 문서로 확인한다.
비교 기준
실행 구조를 고를 때 다섯 가지 기준으로 비교한다.
| 기준 | 질문 | 판단을 바꾸는 상황 |
|---|---|---|
| 비용 | 기계 시간과 저장·전송 비용은 누가 얼마를 내는가 | 연구실 서버는 이미 산 자원, 클라우드는 쓴 만큼 과금(데이터를 밖으로 내보낼 때 과금되는 경우도 있다) |
| 데이터 이동량 | 연산하는 곳까지 데이터를 얼마나 옮겨야 하는가 | TB 단위면 코드를 데이터 쪽으로 |
| 보안 | 데이터·자격 증명이 어디까지 가도 되는가 | 개인정보, 협약상 반출 금지, 미공개 데이터 |
| 세션 지속성 | 몇 시간·며칠 끊김 없이 돌아야 하는가 | 장기 학습은 tmux·scheduler·관리형 job |
| 연결 제약 | 방화벽, VPN, jump host, 불안정한 회선 | 대화형 작업이 어려우면 "제출 → 회수" 방식 |
이 기준들은 서로 당긴다. 클라우드는 확장성과 세션 지속성이 좋지만 데이터 이동과 보안 비용이 크다. 노트북은 보안과 반응성이 좋지만 자원과 지속성이 약하다. 정답은 작업마다 다르고, 한 프로젝트 안에서도 단계마다 구조가 바뀐다. 예를 들어 작은 부분집합으로 노트북에서 코드를 다듬고, 전체 학습은 GPU 서버의 scheduler에 제출하고, 결과 분석은 다시 노트북에서 하는 식이다.
SSH 접속과 원격 제어의 차이
둘 다 "다른 기계를 조작한다"는 점에서 비슷해 보이지만, 세션이 어디에 묶여 있는지가 다르다.
- SSH 접속은 내 터미널을 원격에 여는 것이다. 원격에 셸 프로세스가 생기고, 그 셸은 내 SSH 연결에 묶여 있다. 그 셸에서 직접 띄운 프로세스는 연결이 끊기면 SIGHUP을 받는다(2.2장). 그래서 tmux로 세션을 연결에서 떼어 놓는다.
- 원격 제어는 원격에서 이미 돌고 있는 세션을 조종하는 것이다. 세션은 원격 기계에서 독립적으로 살아 있고, 내 기기는 메시지를 보내고 결과를 받는 창이다. 연결이 끊겨도 세션은 남고, 다시 연결하면 이어서 조종한다. tmux에 attach하는 것도 같은 구조다.
다섯 질문으로 보면 차이가 분명하다. 두 경우 모두 코드·연산·데이터는 원격에 있다. 다른 것은 5번(세션)이다. 그리고 4번(자격)도 짚어 둔다. 원격 제어로 에이전트에게 지시하면, 에이전트는 원격 기계의 계정과 그곳에 저장된 자격 증명(git 토큰, 클라우드 키)으로 명령을 실행한다. 휴대폰으로 가볍게 보낸 한 줄이 서버의 모든 권한으로 실행될 수 있다는 뜻이다. 에이전트에 허용하는 명령의 범위는 4.6장 에이전트 기반 연구 워크플로에서 다룬다.
실습
자신이 쓰는 모든 도구(IDE, notebook, 에이전트, 대시보드, 데이터 저장소)에 대해 다섯 질문의 답을 표로 정리한다. 표를 데이터로 만들면 위험한 조합을 계산으로 골라낼 수 있다. 아래는 예시 표에서 "연산하는 곳과 데이터가 있는 곳이 다른 도구"와 "창을 닫으면 멈추는 도구"를 고르는 코드다.
import pandas as pd
tools = pd.DataFrame([
# 도구, 코드, 연산, 데이터, 자격, 세션 유지
("노트북 Jupyter", "laptop", "laptop", "laptop", "나(로컬 계정)", "laptop 커널"),
("VS Code Remote-SSH", "gpu1", "gpu1", "gpu1", "내 SSH 키", "SSH 연결"),
("gpu1의 tmux 학습", "gpu1", "gpu1", "nas", "내 SSH 키", "gpu1의 tmux"),
("관리형 notebook", "cloud", "cloud", "laptop", "클라우드 계정", "브라우저 탭"),
], columns=["도구", "코드", "연산", "데이터", "자격", "세션"])
tools["데이터 이동"] = tools["데이터"] != tools["연산"] # 연산하는 곳으로 데이터를 옮겨야 하는가
tools["창 닫으면 멈춤"] = tools["세션"].isin(["SSH 연결", "브라우저 탭", "laptop 커널"])
print(tools[["도구", "연산", "데이터", "데이터 이동", "창 닫으면 멈춤"]].to_string(index=False)) 도구 연산 데이터 데이터 이동 창 닫으면 멈춤
노트북 Jupyter laptop laptop False True
VS Code Remote-SSH gpu1 gpu1 False True
gpu1의 tmux 학습 gpu1 nas True False
관리형 notebook cloud laptop True True(한글이 섞인 표는 글자 폭 때문에 열이 조금 어긋나 보인다.) "gpu1의 tmux 학습"은 세션은 안전하지만 매 epoch 데이터를 NAS에서 네트워크로 읽으므로, I/O 병목(2.2장)이나 로컬 SSD로의 복사(2.5장)를 검토할 대상이다. "관리형 notebook"은 노트북의 데이터를 클라우드로 올려야 하고 탭을 닫거나 유휴 시간이 지나면 런타임이 끊길 수 있으므로, 민감 데이터와 장기 학습 모두에 맞지 않는다.
자기 표를 만든 뒤 다음을 확인한다.
- 표의 모든 칸을 채운다. 모르는 칸이 있다면 그 도구의 문서나 설정을 찾아 확인한다(예: 원격 IDE의 확장이 어디서 실행되는지, 에이전트가 어떤 토큰을 쓰는지).
- 각 도구에 대해 "지금 노트북을 덮으면 무엇이 멈추는가"를 실제로 시험해 표의 세션 칸과 비교한다.
- 민감 데이터(개인정보, 미공개 데이터, 협약 데이터)를 다루는 프로젝트라면, 데이터 칸에 그 데이터가 나타나는 모든 행을 표시하고 반출 규정과 대조한다.
실패 사례
- 로컬 파일과 원격 파일을 혼동해 수정이 반영되지 않는 것. 노트북에서 고치고 서버에서 실행하거나, 원격 IDE 창과 로컬 IDE 창을 번갈아 쓰다 엉뚱한 쪽을 고친다. 실행 로그 첫 줄에 호스트 이름, 작업 디렉터리, git 커밋 해시, 주요 설정 값을 찍는 습관이 이 혼동을 바로 드러낸다.
- 민감 데이터를 클라우드로 무심코 옮기는 것. 관리형 notebook에 "샘플 몇 개만" 올리거나, 클라우드 에이전트에 데이터가 커밋된 저장소를 열어 주거나, 오류 메시지를 고치려고 데이터 일부가 담긴 출력을 외부 도구에 붙여 넣는다. 각 도구가 다섯 질문 중 3번과 4번에서 무엇을 어디로 보내는지 확인한 뒤 쓴다.
- 세션이 어디 묶여 있는지 모르고 장기 작업을 맡기는 것. 브라우저 탭에 묶인 notebook이나 SSH 터미널에서 며칠짜리 학습을 돌린다.
- 데이터를 연산 쪽으로 옮기는 비용을 과소평가하는 것. bits와 bytes를 혼동하거나 대역폭을 100% 쓸 수 있다고 가정해, 전송에 실제로 걸리는 시간을 몇 배 작게 잡는다.
통과 기준
- 자신이 쓰는 도구 네 개 이상에 대해 다섯 질문(코드·연산·데이터·자격·세션)의 답을 표로 정리하고, 노트북을 덮었을 때 멈추는 도구를 실제로 확인한다.
- 주어진 작업(예: 반출 금지 데이터로 1주일짜리 학습, 공개 데이터로 짧은 실험)에 대해 실행 구조를 고르고, 그 이유를 다섯 질문과 비교 기준으로 설명한다.