#macOS#運用#MLインフラ#シェル

macOSでバックグラウンド常駐推論を安定運用する小ワザ集


Linux サーバの感覚で macOS 上に推論サービスを常駐させようとすると、細かいところで足を掬われます。「Linux では動いたワンライナーが Mac では動かない」「ログを grep したら文字化けエラーで落ちる」——地味だけど時間を溶かすやつです。

この記事は、Seed-VC ベースの音声変換サービス(FastAPI + uvicorn、ローカルの 127.0.0.1:8770)を macOS 上でバックグラウンド運用するなかで溜まった、短い TIPS 集です。Linux 前提の記事には載っていない Mac 特有のハマりどころを中心にまとめます。

TIP 1: setsid / timeout は macOS に無い

まず前提として、macOS(BSD 系)には GNU coreutils の setsidtimeout が標準では入っていません。Linux で常駐化やタイムアウト付き実行に使うこれらを、そのまま Mac のスクリプトに書くと command not found で転びます。

対処は2択です。

  • Homebrew で coreutils を入れて gsetsidgtimeout を使う
  • 標準ツールだけで代替する(次の TIP)

外部依存を増やしたくない常駐スクリプトでは、標準ツールでの代替が無難でした。

TIP 2: 常駐は nohup + disown

setsid が無い環境で「シェルを閉じてもプロセスを生かす」には、nohupdisown の組み合わせが確実です。

# 推論サービスをバックグラウンド常駐させる
nohup bash scripts/start-backend.sh > backend.log 2>&1 &
disown
  • nohup … ハングアップシグナル(SIGHUP)を無視させ、端末が切れても死なないようにする
  • > backend.log 2>&1 … 標準出力と標準エラーをログファイルへ
  • & … バックグラウンド実行
  • disown … このジョブをシェルのジョブテーブルから外す。これでシェルを閉じても道連れにならない

nohup だけでもだいたい生き残りますが、disown まで打っておくと「ターミナルを閉じたら止まった」という事故を確実に防げます。

TIP 3: バイナリ混じりのログで tr / grep が死ぬ → LC_ALL=C

これが Mac でいちばんハマりました。推論のログには、進捗バーの制御文字や、まれにマルチバイトの化けたバイト列が混じることがあります。それを macOS の trgrep に食わせると、

tr: Illegal byte sequence

で処理ごと落ちます。ロケールが UTF-8 前提だと、不正なバイト列を「不正な文字」と見なして例外にするためです。

対処は、そのコマンドだけロケールを C(バイト列として素通し) に落とすこと。

# ログから不要な制御文字を除去する(Illegal byte sequence 回避)
LC_ALL=C tr -d '\r' < backend.log > backend.clean.log
LC_ALL=C grep "ERROR" backend.log

LC_ALL=C にすると、テキストを「文字」ではなく「バイト」として扱うので、不正シーケンスで死ななくなります。ログを機械的に加工・検索するパイプラインでは、これを付けておくのが安全です。

TIP 4: 起動完了は health エンドポイントで待つ

モデルの読み込みには時間がかかるので、nohup で起動した直後にリクエストを投げると「まだ準備中」で失敗します。「起動したはず」を sleep 10 で決め打ちするのは応急処置で、遅いマシンでは早すぎ、速いマシンでは待ちすぎます。

正攻法は、サービスの health エンドポイントを叩いて 200 が返るまで待つことです。この構成では起動時に http://127.0.0.1:8770/health を案内しているので、これをポーリングします。

# health が通るまで待ってから次に進む
until curl -sf http://127.0.0.1:8770/health >/dev/null; do
  sleep 1
done
echo "backend ready"

curl -sf は失敗時に非ゼロ終了するので、until と組み合わせれば「準備できるまで待つ」がそのまま書けます。固定待機ではなく状態で待つ——地味ですが、起動スクリプトの信頼性が段違いになります。

TIP 5: 停止は pkill でパターン指定

PID をファイルに残していない常駐プロセスは、pkill でコマンドラインのパターンから止めるのが手軽です。

# uvicorn で立てた推論サービスを止める
pkill -f "uvicorn server:app"

-f はコマンドライン全体にマッチするので、ポートやアプリ名まで含めた具体的なパターンにすると、無関係なプロセスの巻き添えを避けられます。より丁寧にやるなら、起動時に PID を控えておいてそれを狙い撃つ運用にします。

まとめ

  • macOS には setsidtimeout が無い。Homebrew の coreutilsgsetsid/gtimeout)を入れるか、標準ツールで代替する
  • 常駐は nohup ... & disown。SIGHUP を無視させ、ジョブテーブルから外して端末切断の道連れを防ぐ
  • ログにバイナリが混じると tr/grepIllegal byte sequence で死ぬ → その場だけ LC_ALL=C でバイト列として素通しする
  • 起動完了は sleep の決め打ちでなく health エンドポイントを until curl -sf でポーリングして状態で待つ
  • 停止は pkill -f "具体的なパターン"。巻き添えを避けるため、マッチ文字列は十分に限定する
← 記事一覧へ