Skip to content

workerの導入と常駐 ​

workerは、API serverからJobを受け取り、コンピュータへSSHで接続してコードを実行するプロセスです。実行中のログ、メトリクス、出力ファイルをAPI serverへ送り、終わったらJobの結果を報告します。

workerはAPI serverとは別のマシンに置けます。SSHの秘密鍵はworkerのマシンだけに置き、API serverには持たせません。導入、更新、状態の確認は、workerのマシンでmado-tracking-workerコマンドを使って行います。

text
API server ← worker(SSH鍵・known_hosts・worker token) → Compute target(GPUマシン)

こんなときに向いています ​

  • GPUマシンでJobを動かすworkerを、ログアウトや再起動の後も動かし続けたい
  • workerを更新しても、実行中の学習を止めたくない
  • tokenや鍵の権限の設定ミスを、Jobを流す前に見つけたい

必要なもの ​

  • Linux(Ubuntuで確認しています)
  • Python 3.11以上、venv、pip、OpenSSH client、Git
  • workerのマシンからAPI serverへ届くこと
  • コンピュータからもAPI serverへ届くこと。実行中のコードがメトリクスを記録するためです。SSHの接続先でworkerのマシンの127.0.0.1を指定しても、そのAPIには届きません

Ubuntuでは次のコマンドで入れられます。

bash
sudo apt-get update
sudo apt-get install -y python3 python3-venv python3-pip openssh-client git
python3 --version

1. worker用のtokenを用意する ​

workerのtokenは、人に紐付かないService Accountで発行します。発行した人がProjectを離れても止まりません。作業はProject adminがWebの画面で行います。

  1. [プロジェクト設定]を開き、[Service Accounts]の[Service Accountを作成]を押します。
  2. [名前]にgpu-host-1-workerのような名前、[Role]にAdminを指定して保存します。worker:execute scopeは、RoleがAdminのService Accountにだけ発行できます。
  3. 作成した行の[API tokenを発行]を押し、scopeと有効期限を選びます。
scope必要な場面
read必須。入力データセットの一覧の取得などに使います
worker:execute必須。Jobの受け取りと結果の報告に使います
artifacts:write必須。実行前のコードと、出力ファイルの保存に使います
registry:writeresult.json(version 2)で出力モデル・データセットを宣言するJobがある場合

有効期限の上限は365日です。

  1. 表示されたtokenを、次の手順の入力に使います。tokenは一度だけ表示されます。

Service AccountとAPI tokenの詳細はAPI tokenとService Accountを参照してください。

workerをインストールする ​

workerのマシンでターミナルを開き、worker専用のvenvへPythonパッケージmado-trackingを入れます。ここではGitHubのリポジトリから入れる例を示します。社内の配布先やwheelファイルがある場合は、pip installの引数をそれに置き換えてください。

bash
git clone https://github.com/aida0710/mado-ml-tracking.git ~/mado-ml-tracking
python3 -m venv ~/.local/share/mado-tracking-worker/venv
~/.local/share/mado-tracking-worker/venv/bin/pip install "$HOME/mado-ml-tracking/python[telemetry]"
~/.local/share/mado-tracking-worker/venv/bin/mado-tracking-worker --help

telemetryを付けると、GPU使用率などのsystem metricsも採取します。

2. systemdで常駐させる ​

installは、設定ファイルとsystemdのunitを書き、workerを起動します。

bash
~/.local/share/mado-tracking-worker/venv/bin/mado-tracking-worker install \
  --api-url https://tracking.example.internal \
  --worker-id gpu-host-1 \
  --target-ids "<targetのID>"
loginctl enable-linger "$USER"
引数入力例内容
--api-urlhttps://tracking.example.internalAPI serverのURL
--worker-idgpu-host-1workerの名前。再起動や更新の後も同じ値を使います。英数字、.、_、-で64文字まで
--target-idsコンピュータのIDこのworkerが担当するコンピュータのID。複数あればカンマで区切ります。省略すると、tokenとAPIの設定で許可されたすべてのコンピュータのJobを処理します
--token-file/path/to/tokentokenをファイルから読むとき。省略するとターミナルで入力します
--state-dir実行中のJobの記録を置くディレクトリ。通常は省略します

実行すると、tokenの入力を求められます。入力した文字は表示されません。tokenをコマンドの引数に書かないでください。

--target-idsは省略できます。コンピュータを足す前にworkerを入れるときは、省略してかまいません。省略したworkerも、[コンピュータを追加]の[接続に使うworker]で選べば、接続確認とディレクトリの読み取りを受け取ります(SSH・Localのコンピュータ)。workerを選んでいない確認を受け取るのは、--target-idsにそのコンピュータを含むworkerだけです。コンピュータのIDは、全体設定の「コンピュータ」でそのコンピュータの名前を押すと、詳細のコンピュータIDに[コピー]付きで出ます。その下の「workerの設定」には、このページのURLとそのIDを入れた、この節のコマンド(--worker-idはworkerのマシンのホスト名)と、systemdを使わずに環境変数で動かす例があります。「workerの設定」は、そのコンピュータの所有者と全体管理者にだけ出ます。ほかの人には、概要の「workerの設定」の行に、誰が設定するか(所有者か全体管理者)だけを出します。

loginctl enable-lingerは、ログアウトした後もuserのunitを動かし続けるための設定です。

installが行うことは次のとおりです。

  • ~/.config/mado-tracking-worker/<worker-id>.envを権限600で書きます。API URL、worker ID、コンピュータ、state directory、tokenが入ります。
  • ~/.config/systemd/user/mado-tracking-worker@.serviceを書き、mado-tracking-worker@<worker-id>.serviceを有効にして起動します。
  • state directoryは~/.local/state/mado-tracking-worker/の下に作ります。手でworkerを起動していた場合も同じ場所を使うので、実行中のJobの記録を引き継ぎます。

system unitにする場合 ​

専用のユーザーで動かすときは、rootで--systemd-system --service-user <ユーザー名>を付けます。設定ファイルは/etc/mado-tracking-worker/<worker-id>.env(root、権限600)、state directoryは/var/lib/mado-tracking-worker/<worker-id>(そのユーザーの所有、権限700)になります。手で置く場合のunitの雛形は、リポジトリのdeploy/worker/mado-tracking-worker@.serviceとdeploy/worker/worker.env.exampleです。

3. 動いていることを確かめる ​

bash
WORKER=~/.local/share/mado-tracking-worker/venv/bin/mado-tracking-worker
$WORKER doctor --worker-id gpu-host-1 --ssh-key ~/.ssh/mmt_worker_ed25519 --known-hosts ~/.ssh/mmt_known_hosts
$WORKER status --worker-id gpu-host-1
journalctl --user -u mado-tracking-worker@gpu-host-1 -f
コマンド確かめること
doctor設定ファイルとstate directoryの権限、APIへの到達、tokenのscope、~/.ssh・秘密鍵・known_hostsの権限。問題があれば終了コード1
statusunitの状態、workerのlock、保持しているJob。止まっていれば終了コード3
journalctlworkerのログ

最後に、WebのComputeの[Workers]に、workerのバージョンとホスト名が「オンライン」で表示されることを確かめます。全体管理者は、全体設定の「コンピュータ」の[コンピュータを追加]を開くと、[接続に使うworker]にこのworkerが出ることも確かめられます。

4. 更新する ​

bash
git -C ~/mado-ml-tracking pull
~/.local/share/mado-tracking-worker/venv/bin/mado-tracking-worker upgrade \
  --worker-id gpu-host-1 --package-spec "$HOME/mado-ml-tracking/python[telemetry]"

社内の配布先からバージョンを指定して入れる場合は、--package-specの代わりに--version 0.2.0のように指定します。

upgradeは、unitが使っているvenvへパッケージを入れてから、unitを再起動します。

  • 実行中のJobは止まりません。unitはKillMode=processなので、workerの再起動・停止・更新では実行中のコードを止めず、再起動したworkerが記録から引き継ぎます。
  • pipが失敗したら、再起動しません。
  • unitが止まっているのに、手で起動したworkerがstate directoryを使っている場合は、何もせずに止まります。

workerはバージョン・ホスト名・並列数・SSHファイル候補・対応する機能をAPIへ報告します。必須項目が欠けると、Jobの取得・再開は422で拒否されます。

ワークフローのステップのJobは、データの用途と抽出条件を実行コードへ渡せるworkerだけが受け取ります。対応する機能を報告するworkerがいないコンピュータでは、ワークフローのJobは待機したままになります。

tokenを差し替える ​

期限が近づいたら新しいtokenを発行し、ファイルに保存してinstallをもう一度実行します。

bash
install -m 600 /dev/null ~/worker-token
printf 'Worker API token: '; read -r -s TOKEN && echo && printf '%s\n' "$TOKEN" > ~/worker-token && unset TOKEN
~/.local/share/mado-tracking-worker/venv/bin/mado-tracking-worker install \
  --api-url https://tracking.example.internal --worker-id gpu-host-1 \
  --target-ids "<targetのID>" --token-file ~/worker-token
rm ~/worker-token

古いtokenは、Projectの[Projectのtoken一覧]で失効させます。Service Accountを[無効化]すると、そのService Accountのtokenは次の要求からすべて使えなくなります。

SSH鍵の候補を報告する ​

workerは、自分のマシンにあるSSHの秘密鍵とknown_hostsのパスを、API serverへ報告します。報告したパスは、[コンピュータを追加]の[SSH鍵のパス][known_hostsのパス]の候補に出ます(SSH・Localのコンピュータ)。

  • 報告するのはパスだけです。鍵とknown_hostsの中身や指紋は読まず、送りません。
  • 探すのは~/.sshの直下です。環境変数MMT_WORKER_SSH_DIRにディレクトリを指定すると、その直下も探します。
  • 秘密鍵の候補は、名前がid_*・*.pem・*.keyのファイルと、所有者だけが読めるファイル(権限600など)です。.pub、known_hosts*、authorized_keys*、configなどは除きます。known_hostsの候補は、名前がknown_hostsで始まるファイルです。
  • それぞれ50件までです。60秒ごとに探し直すので、鍵を足すと1分ほどで候補に出ます。

鍵を~/.ssh以外に置くときは、installで書いた設定ファイルにMMT_WORKER_SSH_DIRの行を足して、workerを再起動します。/srv/mmt-keysは、鍵を置いたディレクトリに置き換えてください。

bash
echo 'MMT_WORKER_SSH_DIR=/srv/mmt-keys' >> ~/.config/mado-tracking-worker/gpu-host-1.env
systemctl --user restart mado-tracking-worker@gpu-host-1

system unitでは/etc/mado-tracking-worker/<worker-id>.envに足し、sudo systemctl restart mado-tracking-worker@<worker-id>で再起動します。installをもう一度実行すると設定ファイルを書き直すので、この行は足し直してください。

Docker ComposeのMMT_WORKER_SSH_DIRは意味が違い、コンテナの~/.sshへmountするホストのディレクトリを指します(Docker Composeで動かす)。

手で起動して試す ​

常駐させる前に試すときは、ターミナルで直接起動できます。

bash
export MMT_API_URL=https://tracking.example.internal
export MMT_WORKER_ID=gpu-host-1
export MMT_WORKER_TARGET_IDS="<targetのID>"
export MMT_WORKER_STATE_DIR="$HOME/.local/state/mmt-worker-manual"
printf 'Worker API token: '; read -r -s MMT_API_TOKEN; echo
export MMT_API_TOKEN
~/.local/share/mado-tracking-worker/venv/bin/mado-tracking-worker run

--onceを付けると、Jobを1件処理して終了します。同じstate directoryでworkerを2つ起動すると、後から起動した方を拒否します。

Docker Composeで動かす ​

SSHのコンピュータだけを使うworkerは、リポジトリのDocker Composeでも動かせます。

bash
docker compose --profile worker up -d worker
docker compose --profile worker run --rm worker doctor
  • token: ./var/worker-token(MMT_WORKER_TOKEN_FILEで変更)をDocker secretとして渡します。
  • 鍵とknown_hosts: ./var/worker-ssh(MMT_WORKER_SSH_DIR)をコンテナの/home/worker/.sshへ読み取り専用でmountします。コンピュータの[SSH鍵のパス][known_hostsのパス]は、コンテナ内のパスで登録します。workerが報告する候補も、コンテナ内のパス(/home/worker/.ssh/...)で出ます。
  • state: volumeworker-stateです。消すと実行中のJobを引き継げなくなります。

実行コードにはJob限定tokenを渡す ​

workerは、自分のtokenを実行コードに渡しません。代わりにAPI serverがJobごとに発行するJob限定token(mado_mtj_で始まる)を、実行コードのMMT_API_TOKENとMLFLOW_TRACKING_TOKENに入れます。実行コードは、Python SDKもMLflow 3 SDKも、そのままこのtokenで記録できます。

  • 権限は、Runを作った人の現在の権限です。その人をProjectから外すと、実行中のコードからの記録も拒否されます。
  • 書き込めるのは、そのJobのRun(メトリクス、パラメータ、タグ、ログ、入力データセット、Artifact)と、そのRunを生成元とするモデル・データセットのバージョンだけです。
  • 同じProjectのほかのRunへの書き込み、tokenの発行、Projectの設定、自動実行ルールの変更はできません。読み取りは同じProjectの中ならできます(上流RunのArtifactの取得など)。
  • Jobが終わる(完了、失敗、中止)と、そのtokenは使えなくなります。

tokenはworkerのstate directory(権限700、ファイルは600)に保存します。workerを再起動しても、実行中のJobは同じtokenを使い続けます。

実行コードに渡すもの ​

workerは、実行コードに次の環境変数を渡します。コンテナでは、パスもコンテナ内のものになります。

環境変数・パス内容
MMT_API_URL、MMT_API_TOKENAPI serverのURLとJob限定token
MLFLOW_TRACKING_URI、MLFLOW_TRACKING_TOKENMLflow 3 SDK用の接続先とJob限定token
MMT_RUN_IDこのJobのRun。Python SDKのstart_run()を引数なしで呼ぶと、このRunへ記録します
/mmt/inputs、MMT_MODEL_FILE取得済みのモデルの重み(読み取り専用)
MMT_PARAMETERS_FILEパラメータのJSON
MMT_INPUT_DATASET_DIRS入力データセットバージョンごとの、取得済みの本体のディレクトリ(JSON)
MMT_DATASET_VERSIONS_FILE入力データセットバージョンの情報
MMT_UPSTREAM_RUN_ID、MMT_UPSTREAM_RUN_FILE上流Run(ルールの連鎖のとき)
MMT_DATASET_USAGE_FILE入力データの用途・重み・抽出条件({"items": [...]}。ワークフローのステップのとき)
MMT_WORKFLOW_EXECUTION_IDワークフローの実行のID(ワークフローのステップのとき)
MMT_RESUME_CHECKPOINT_DIRなど再開元のcheckpoint(学習の途中再開のとき)
/mmt/source追加のソース(コンテナで、ソースを指定したとき。読み取り専用)
/mmt/outputs、MMT_OUTPUTS_DIR出力ファイルを書く場所
MMT_RESULT_FILESDKを使わない実行の完了の宣言(result.json)を書くパス

SDKを使わずに結果を返す ​

SDKを入れていないコンテナでは、出力を/mmt/outputsに書き、最後にresult.jsonを書きます。workerは終了後に宣言とファイルを照合し、ファイルをRunのArtifactcontainer/<path>として保存します。

json
{
  "version": 2,
  "complete": true,
  "artifacts": [{"path": "model/weights.bin", "sha256": "<64桁のhex>", "size": 1048576}],
  "metrics": [{"name": "train.loss", "value": 0.12, "step": 100}],
  "models": [{"path": "model/weights.bin"}]
}
  • sha256とsizeは実際のファイルと一致させます。すべての出力を書き終えてからresult.jsonを書きます。
  • modelsで宣言した重みは、モデルのバージョンとして登録します。宣言できるのは学習とファインチューニングのRunだけで、workerのtokenにregistry:writeが必要です。
  • path指定ではdigestを送らず、サーバーがファイル一覧から計算します。uri指定ではdigestが必須です。
  • datasetsには{datasetId, path}を書くと、出力をデータセットバージョンとして登録します。推論の出力を評価へ渡すときに使います。pathにディレクトリ(例: "prepared")を書くと、その下の出力ファイルすべてを1つのバージョンにします。前処理で数千の音声を1つのデータセットにするときに使います。バージョンの中のパスは、そのディレクトリからの相対パスです。
  • 出力ファイルが数千件あるときは、result.jsonに並べず、1行に1件{"path","sha256","size"}を書いたJSON Linesのファイルを"artifactsManifest": "artifacts.jsonl"で指します。
上限値
出力ファイル数10000(workerのMMT_WORKER_MAX_OUTPUT_FILESで変更)
result.json1MiB
メトリクス1000点
models、datasets16件、64件
datasetsのディレクトリの下のファイル100000件

接続が切れたとき、止めたとき ​

  • workerとコンピュータのSSHが切れても、コンピュータの上でコードは動き続けます。workerは再接続して同じJobに戻り、続きのログを送ります。同じJobを二重に起動することはありません。
  • workerを再起動しても、state directoryの記録から実行中のJobを引き継ぎます。state directoryを消さないでください。
  • Jobを止めるのは、Jobsの[停止を要求]だけです。確認のダイアログにRun名・作成者・実行先と何が起きるかが出るので、確かめてから[停止する]を押します。止められるのはJobの作成者(JobのRunを作った人)とProjectのAdminだけです(一覧の[作成者]の列で確かめられます。ほかの人のJobの[操作]には、ボタンの代わりにその理由が出ます)。workerは実行中のプロセスにSIGTERMを送り、10秒で終わらなければSIGKILLで止めてから、Jobを「中止」で終えます。
  • Jobのheartbeatが60秒途絶えると、Jobsに「応答なし」を表示します。Jobの状態とGPUの予約は変えません。workerのマシンで状態を確かめてから、停止や再実行を手で行ってください。
  • workerの応答が120秒途絶えると、Computeの[Workers]で「オフライン」になります。通知で受け取るには、Projectの通知ルールでworker.offlineを選びます(通知)。