workerの導入と常駐
workerは、API serverからJobを受け取り、コンピュータへSSHで接続してコードを実行するプロセスです。実行中のログ、メトリクス、出力ファイルをAPI serverへ送り、終わったらJobの結果を報告します。
workerはAPI serverとは別のマシンに置けます。SSHの秘密鍵はworkerのマシンだけに置き、API serverには持たせません。導入、更新、状態の確認は、workerのマシンでmado-tracking-workerコマンドを使って行います。
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では次のコマンドで入れられます。
sudo apt-get update
sudo apt-get install -y python3 python3-venv python3-pip openssh-client git
python3 --version1. worker用のtokenを用意する
workerのtokenは、人に紐付かないService Accountで発行します。発行した人がProjectを離れても止まりません。作業はProject adminがWebの画面で行います。
- [プロジェクト設定]を開き、[Service Accounts]の[Service Accountを作成]を押します。
- [名前]に
gpu-host-1-workerのような名前、[Role]にAdminを指定して保存します。worker:executescopeは、RoleがAdminのService Accountにだけ発行できます。 - 作成した行の[API tokenを発行]を押し、scopeと有効期限を選びます。
| scope | 必要な場面 |
|---|---|
read | 必須。入力データセットの一覧の取得などに使います |
worker:execute | 必須。Jobの受け取りと結果の報告に使います |
artifacts:write | 必須。実行前のコードと、出力ファイルの保存に使います |
registry:write | result.json(version 2)で出力モデル・データセットを宣言するJobがある場合 |
有効期限の上限は365日です。
- 表示されたtokenを、次の手順の入力に使います。tokenは一度だけ表示されます。
Service AccountとAPI tokenの詳細はAPI tokenとService Accountを参照してください。
workerをインストールする
workerのマシンでターミナルを開き、worker専用のvenvへPythonパッケージmado-trackingを入れます。ここではGitHubのリポジトリから入れる例を示します。社内の配布先やwheelファイルがある場合は、pip installの引数をそれに置き換えてください。
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 --helptelemetryを付けると、GPU使用率などのsystem metricsも採取します。
2. systemdで常駐させる
installは、設定ファイルとsystemdのunitを書き、workerを起動します。
~/.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-url | https://tracking.example.internal | API serverのURL |
--worker-id | gpu-host-1 | workerの名前。再起動や更新の後も同じ値を使います。英数字、.、_、-で64文字まで |
--target-ids | コンピュータのID | このworkerが担当するコンピュータのID。複数あればカンマで区切ります。省略すると、tokenとAPIの設定で許可されたすべてのコンピュータのJobを処理します |
--token-file | /path/to/token | tokenをファイルから読むとき。省略するとターミナルで入力します |
--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. 動いていることを確かめる
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 |
status | unitの状態、workerのlock、保持しているJob。止まっていれば終了コード3 |
journalctl | workerのログ |
最後に、WebのComputeの[Workers]に、workerのバージョンとホスト名が「オンライン」で表示されることを確かめます。全体管理者は、全体設定の「コンピュータ」の[コンピュータを追加]を開くと、[接続に使うworker]にこのworkerが出ることも確かめられます。
4. 更新する
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をもう一度実行します。
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は、鍵を置いたディレクトリに置き換えてください。
echo 'MMT_WORKER_SSH_DIR=/srv/mmt-keys' >> ~/.config/mado-tracking-worker/gpu-host-1.env
systemctl --user restart mado-tracking-worker@gpu-host-1system 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で動かす)。
手で起動して試す
常駐させる前に試すときは、ターミナルで直接起動できます。
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でも動かせます。
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: volume
worker-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_TOKEN | API serverのURLとJob限定token |
MLFLOW_TRACKING_URI、MLFLOW_TRACKING_TOKEN | MLflow 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_FILE | SDKを使わない実行の完了の宣言(result.json)を書くパス |
SDKを使わずに結果を返す
SDKを入れていないコンテナでは、出力を/mmt/outputsに書き、最後にresult.jsonを書きます。workerは終了後に宣言とファイルを照合し、ファイルをRunのArtifactcontainer/<path>として保存します。
{
"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.json | 1MiB |
| メトリクス | 1000点 |
models、datasets | 16件、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を選びます(通知)。