Newer
Older
TIASJudgeModel / docs / 04_SPEC / SPEC_03_リモート実行仕様.md

リモート実行仕様 (Remote Execution)

手元からコードを送り,研究室の複数の GPU サーバ(Newton・Archimedes)のうち空いている GPU を自動で選んでジョブを実行し,結果を回収ホワイトリストに従って持ち帰るツール tias-remote の仕様.SmTIAS-LightSim の sm-remote を移植し,複数サーバ対応と回収制限を加えたもの.

前提と制約 (Constraints)

  • 臨床データを転送しない.ツールが扱うのはコードの送信と結果の回収のみ.データは各サーバに事前配置する(ENV_03)
  • サーバに GitHub の認証情報を置かない(コードは常に手元から送る)
  • 非対話で動く(ssh は BatchMode=yes.パスワード入力を求めない)
  • 共用サーバのため,他のユーザーが使用中の GPU を勝手に使わない

サーバ設定 (Server Configuration)

configs/remote.yaml に置き,pydantic で検証する(未知のキーは拒否).

jump_host: labpc              # 踏み台.null なら直接接続
connect_timeout_sec: 15
gpu_free:
  max_memory_used_mib: 1024   # 使用中メモリがこれ未満
  max_utilization_percent: 10 # かつ使用率がこれ未満なら「空き」
  observe_sec: 10             # この秒数だけ観測してから判定する(0 なら瞬間値のみ)
  sample_interval_ms: 500     # 観測の間隔
servers:
  - name: Newton
    address: 192.168.23.14
    user: hasegawa
    priority: 1               # 小さいほど優先
    remote_dir: ~/work/TIASJudgeModel
    data_dir: ~/data/TIASJudgeModel
    uv: ~/.local/bin/uv
    data_ready: true          # 臨床データが配置済みか
  - name: Archimedes
    address: 192.168.23.15
    user: hasegawa
    priority: 2
    remote_dir: ~/work/TIASJudgeModel
    data_dir: ~/data/TIASJudgeModel
    uv: ~/.local/bin/uv
    data_ready: false
検証規則内容
servers1 件以上.name は重複不可
priority整数.同じ値のときは servers の記載順で優先
name英数字・_・- のみ(コマンド引数・表示に使うため)
閾値max_memory_used_mib は 0 以上.max_utilization_percent は 0〜100
観測observe_sec は 0〜120(0 は瞬間値のみで判定).sample_interval_ms は 100〜5000
connect_timeout_sec1 以上

環境変数による上書き:

環境変数効果
TIAS_REMOTE_JUMPjump_host を上書きする.空文字なら直接接続(研究室 LAN 内にいるとき)
TIAS_REMOTE_SSH手元の ssh 実行ファイル(Windows の既定は C:\Windows\System32\OpenSSH\ssh.exe)
TIAS_REMOTE_ALLOW_SELF1 でサーバ上実行の検出を無効化する(誤検出時の逃げ道)

接続 (Connection)

  • ssh の接続先は <user>@<address>.jump_host があれば -J <jump_host> を付ける
  • 常に -o BatchMode=yes -o ConnectTimeout=<connect_timeout_sec> を付ける
  • サーバ上のコマンドは非対話シェルで動くため,uv は設定の絶対パス(~ 展開)で呼び,PATH に ~/.local/bin を加える(非対話 ssh では ~/.local/bin が PATH に無い.ENV_02)
  • 接続できないサーバは「接続不可」として扱い,他のサーバで処理を続ける(1 台の不調で全体を止めない).すべてのサーバに接続できない場合はエラー

GPU の空き判定と選択 (GPU Selection)

取得

各サーバで次を取得する(ssh 1 回で行う).

  1. GPU の一覧(1 行 1 GPU)

    nvidia-smi --query-gpu=index,uuid,name,memory.used,memory.total,utilization.gpu --format=csv,noheader,nounits
    
  2. 観測: observe_sec 秒間,sample_interval_ms 間隔で GPU ごとの使用率と使用中メモリを繰り返し取得する(nvidia-smi --query-gpu=index,utilization.gpu,memory.used --format=csv,noheader,nounits -lms <間隔> を timeout <秒> で打ち切る等)

  3. プロセス別の観測: 同じ時間,プロセスごとの SM 使用率を取得する(nvidia-smi pmon -s u -d 1 -c <秒>.上の観測と並行して実行する)

  4. GPU 上の計算プロセス(gpu_uuid で GPU と対応付ける)

    nvidia-smi --query-compute-apps=gpu_uuid,pid,used_memory --format=csv,noheader,nounits
    
  5. 各プロセスの所有ユーザーと起動からの経過秒数(ps -o user=,etimes= -p <pid> 等)

※ --query-compute-apps と pmon の C(計算)種別のみを数え,画面描画のプロセス(G 種別.Xorg 等)は含めない.C+G のように両方を含む種別は計算として数える. ※ nvidia-smi は「最後に使った時刻」の履歴を持たないため取得しない.代わりに観測中の使用率とプロセスの経過時間で判断する.

判定

観測中の最大値で閾値を判定する(瞬間値だけでは,起動と停止を繰り返す GPU を「空き」と誤認するため.2026-09-27 に Newton で 10 秒間に 0〜74% を往復する GPU を実測).

状態条件選択
未使用観測中ずっと閾値内(使用率の最大 < max_utilization_percent かつ使用中メモリの最大 < max_memory_used_mib)で,計算プロセスが 1 つも無い最優先
空き(利用者あり)観測中ずっと閾値内だが計算プロセスがある.または計算プロセスの一覧が取得できなかった(利用者不明)未使用が無いときのみ
使用中観測中に一度でも閾値を超えた選ばない
状態不明GPU の行が解釈できない,または観測のサンプルが 1 つも得られない選ばない(取得できない GPU を使わないため)
  • 計算プロセスは所有者を問わず数える(自分の別ジョブが載っている GPU に次のジョブを重ねないため)
  • 計算プロセスの一覧の取得・解釈に失敗した場合,そのサーバの閾値内の GPU はすべて「空き(利用者あり)」とする(未使用と誤認しないため)
  • サーバ単位で GPU の一覧の取得に失敗した場合は,そのサーバの GPU をすべて候補から外す
  • observe_sec: 0(または --observe 0)のときは従来どおり 1 回の瞬間値で判定する

使い方の型 (Usage Pattern)

表示のために,観測中の使用率から GPU ごとに型を付ける(判定には使わない.判定は上の表).

型条件
常時使用使用率が 0% を超えたサンプルが全体の 80% 以上
断続的に使用使用率が 0% を超えたサンプルが 1 つ以上・80% 未満
メモリ確保のみ使用率が 0% を超えたサンプルが無く,計算プロセスがある
未使用使用率が 0% を超えたサンプルが無く,計算プロセスも無い

プロセス別の観測(pmon)が得られた場合は,プロセスごとにも観測中の SM 使用率の最大値を表示する.得られなかった場合は GPU 単位の型だけを表示する.

選択(run で GPU を指定しないとき)

  1. 候補サーバ: data_ready: true のサーバ(--no-data を付けたときは全サーバ).--server を指定したときはそのサーバのみ
  2. 全候補サーバを通して「未使用」の GPU を探し,priority 昇順(同値は記載順)→ GPU の index 昇順で最初のものを選ぶ
  3. 未使用が無ければ,同じ順で「空き(利用者あり)」の最初のものを選び,警告として利用者(ユーザー名)を表示する
  4. どちらも無ければ,各サーバの状況を表示して 0 以外の終了コードで終わる(ジョブは起動しない)

例: Newton の空き GPU がすべて利用者ありで,Archimedes に未使用の GPU があれば,Newton の方が優先順位が高くても Archimedes を選ぶ.

明示指定:

  • --server NAME --gpu I で GPU を直接指定できる.指定した GPU が「未使用」「空き(利用者あり)」なら起動する(後者は利用者を警告表示する).使用中・状態不明ならエラーにし,--force を付けたときだけ警告を出して実行する
  • --gpu だけの指定は不可(どのサーバか曖昧になるため --server を必須とする)
  • サーバに存在しない GPU 番号を指定した場合はエラー(--force でも起動しない)
  • --server に data_ready: false のサーバを指定した場合も,--no-data が無ければエラー(候補サーバの規則を明示指定にも適用する)

表示

gpus・status は各 GPU について次を表示する.

  • 状態(未使用・空き(利用者あり)・使用中・状態不明)
  • 使い方の型と,観測中の使用率の平均・最大(例: 断続的に使用・平均 21%・最大 74%)
  • 計算プロセスごとの所有ユーザー(自分のものは「自分」)・起動からの経過時間(例: 27.7 時間)・観測中の SM 使用率の最大

gpus と run は --observe SEC で観測時間を上書きできる(--observe 0 で瞬間値のみ.急ぐとき用).観測は全サーバで並行して行う.

※ 判定から起動までの間に他者が使い始める競合は防げない(既知の制約).

コマンド (Commands)

サブコマンド動作
gpus [--observe SEC]全サーバの GPU 一覧(状態・使い方の型・利用者)と,run が選ぶ GPU を表示する
push [--server NAME]コードを送る.省略時は接続できる全サーバ
run --name NAME [--server S] [--gpu I \| --cpu] [--force] [--no-data] [--no-push] [--observe SEC] [--env K=V ...] -- CMD...GPU を選び,そのサーバへコードを送り(--no-push で省略),tmux でジョブを起動する
status全サーバの実行中ジョブと GPU の状況
logs NAME [-n N] [-f]ジョブのログを表示する.ジョブのあるサーバを自動で探す
kill NAMEジョブを止める.ジョブのあるサーバを自動で探す
pull RUN_ID [--server S] [--with-checkpoints]実験結果を回収ホワイトリストに従って持ち帰る

push

  • 送るもの: リポジトリ内のコード一式(src/・scripts/・tests/・configs/・pyproject.toml・uv.lock・.python-version 等)
  • 送らないもの: .git/・.venv/・__pycache__/・.pytest_cache/・.ruff_cache/・data/・runs/・.claude/
  • 方式: tar over ssh でサーバの remote_dir に上書き展開する(Windows に rsync が無いため)
  • そのサーバでジョブが実行中なら警告を出す(上書き展開で実行中のジョブが新旧のコードを混ぜて読むおそれがある)

run

  • ジョブ名: 英数字で始まり,英数字・_・- のみ,1〜64 文字
  • いずれかのサーバで同名のジョブが実行中ならエラー
  • tmux セッション名をジョブ名とし,サーバの remote_dir で <uv> run <CMD> を実行する.接続が切れても継続する
  • ジョブに渡す環境変数: CUDA_VISIBLE_DEVICES=<選んだ GPU の index>・TIAS_DATA_ROOT=<そのサーバの data_dir>・CUDA_DEVICE_ORDER=PCI_BUS_ID(CUDA の GPU 番号を nvidia-smi の index にそろえる.そろえないと空きと判定したのと別の GPU を使いうる)・PYTHONUNBUFFERED=1・TIAS_GIT_COMMIT(手元の git rev-parse --short HEAD.未コミットの変更があれば末尾に -dirty.git が使えなければ設定しない.サーバには .git を送らないため,実験 ID の git ハッシュをここから得る.SPEC_01「実験の出力」)・--env で指定したもの.--env 以外の 5 つは --env で上書きできない(GPU の割り当てと実験の記録を迂回させないため)
  • ログ: remote_dir/runs/_remote_logs/<ジョブ名>.log に,開始行(サーバ名・GPU・日時)と終了コード行(=== [日時] exit <終了コード>)を付けて追記する
  • --dry-run を付けると GPU の選択までを行い,送信・起動はしない
  • --cpu: GPU を使わないジョブ(manifest の生成・データセットの集計等)用.GPU の観測・選択を行わず,CUDA_VISIBLE_DEVICES を空文字で渡す(GPU を誤って掴まないため).サーバは --server で指定したもの,省略時は候補サーバ(data_ready: true.--no-data で全サーバ)を priority 順に見て最初に接続できたもの.--gpu・--force・--observe とは同時に指定できない.ログの開始行の GPU は「なし(CPU)」とする
  • 起動後,選んだサーバと GPU を表示する

pull

  • 対象: サーバの remote_dir/runs/<RUN_ID>/ 配下で,SPEC_01「回収ホワイトリスト」に名前が一致するファイルのみ.checkpoints/*.pt は --with-checkpoints のときだけ
  • figures/*.png は figures/ の直下のみを対象とする(figures/sub/x.png は回収しない)
  • RUN_ID に /・\・.. を含むもの,空文字,.・- で始まるもの,制御文字・前後の空白を含むものは拒否する
  • ジョブログ(runs/_remote_logs/)は pull の対象外.logs で表示する
  • --server を省略したときは全サーバを探し,見つかったのが 1 台ならそこから回収する.複数台にあればエラー(--server の指定を求める).どこにも無ければエラー
  • 保存先: 手元の runs/<RUN_ID>/(同名ファイルは上書き)
  • 二重の防御:
    1. サーバ側で送るファイルをホワイトリストで絞り込む
    2. 手元で受け取った tar を展開前に全メンバー検査し,ホワイトリスト外の名前・パス外に出る名前(..・絶対パス・ドライブ文字・バックスラッシュ)・リンク(シンボリック・ハード)が 1 つでもあれば何も展開せずに中止する
  • 回収しなかったファイルの件数を表示する(名前は表示しない.患者情報を含む名前の可能性があるため)

サーバ上での誤実行の防止

手元のホスト名がいずれかのサーバの name と一致したら,自分自身へ ssh しようとしているとみなして止める(大文字小文字とドメイン部分は無視して比べる.TIAS_REMOTE_ALLOW_SELF=1 で無効化).

共通オプションと観測の有無 (Common Options)

  • 全サブコマンドに --config PATH(既定はプロジェクトルートの configs/remote.yaml)
  • 観測(observe_sec 秒)を行うのは gpus・run・status.push・kill・logs・pull はジョブやファイルの有無だけを見るため観測しない

テスト方針 (Testing)

  • ssh・tmux・nvidia-smi はモックする(テストが実サーバに接続しない..claude/rules/testing.md)
  • 純粋なロジックとして分離し,単体テストで固定する対象:
    • nvidia-smi 出力(GPU 状態・観測サンプル・pmon・計算プロセス)と ps 出力の解析,gpu_uuid による対応付けと 4 段階の判定(観測中の最大値で判定すること・状態不明・利用者不明・サンプル 0 件の扱いを含む)
    • 使い方の型の判定(常時・断続的・メモリ確保のみ・未使用の境界)と経過時間の表示
    • GPU の選択(未使用をサーバの優先順位より優先すること・優先順位・data_ready・--server・--gpu・--force・空き無し)
    • 接続できないサーバがあっても他のサーバで続行すること
    • ssh 引数の組み立て(踏み台の有無・BatchMode・タイムアウト)
    • 送信対象の選別(除外ディレクトリ)
    • 回収ホワイトリストの判定と tar 検査(最重要.列挙外・パス外・リンクで中止し,何も展開しないこと)
    • 設定の検証と環境変数の上書き