開発環境を「落ちない」ものにする — tmuxとsystemdによる永続セッション設計
TL;DRSSHが切れても作業を失わないためにtmuxでセッションを永続化し、再起動してもサービスが自動復帰するようにsystemdのユニットを書く。ユーザー権限で常駐させるならユーザーサービスとloginctlのlinger設定が鍵。tmuxは「作業の永続化」、systemdは「サービスの永続化」と役割を分けて使う。
リモートの開発サーバーで作業していて、SSHが切れた瞬間に実行中のプロセスごと作業が消えた。サーバーを再起動したら、手動で起動していた開発用サービスが止まったまま数日気づかなかった。この2つは初学者が必ず踏む問題ですが、性質が違います。前者は「対話的な作業の永続化」の問題で、tmuxが解決します。後者は「サービスの永続化」の問題で、systemdが解決します。この役割分担を理解すると、開発環境は「落ちない」ものに変わります。
tmux:SSHセッションと作業を切り離す
tmuxはターミナルマルチプレクサと呼ばれるツールで、本質は「ターミナルのセッションをSSH接続から独立させる」ことにあります。SSH経由で普通にコマンドを実行すると、そのプロセスはSSHセッションの子として生きているため、接続が切れると一緒に死にます。tmuxのセッション内で実行すれば、プロセスの親はtmuxサーバーになるため、SSHが切れても処理は走り続け、再接続後にアタッチし直せば切断前の画面がそのまま戻ってきます。
terminal# 名前付きセッションを作る
tmux new -s dev
# (作業する。SSHが切れても中の処理は生き続ける)
# 再接続後にセッションへ戻る
tmux attach -t dev
# セッション一覧の確認
tmux ls
機械学習の長時間学習やパッケージの大量ビルドなど、数時間かかる処理を流すときは、tmux内で実行するのを習慣にしておくと事故が消えます。ウィンドウ分割やペイン操作などの多機能は後から覚えればよく、まずは「new・attach・detach(Ctrl-b d)」の3つだけで実用になります。なお類似ツールにGNU screenがありますが、現在の情報量と開発の活発さからtmuxを選んでおけば困りません。
systemd:サービスとして起動を宣言する
一方、Webアプリの開発サーバーやOllamaのような常駐プロセスを「ログインしていなくても、再起動後も動いている」状態にしたいなら、それはもう対話的な作業ではなくサービスです。サービスの管理はtmuxの仕事ではなく、systemdの仕事です。root権限を使わず自分のユーザー権限で管理したい場合は、ユーザーサービスとしてユニットファイルを置きます。
~/.config/systemd/user/myapp.service[Unit]
Description=My development app server
After=network.target
[Service]
Type=simple
WorkingDirectory=%h/projects/myapp
ExecStart=/usr/bin/npm run start
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
terminal
systemctl --user daemon-reload
systemctl --user enable --now myapp.service
systemctl --user status myapp.service
journalctl --user -u myapp.service -f
Restart=on-failureを書いておけば、プロセスが異常終了しても5秒後に自動で立ち上がります。ログはjournalctlで一元的に追えるため、nohupでログファイルをまき散らす運用より圧倒的に管理しやすくなります。
落とし穴:lingerを設定しないと「ログアウトで死ぬ」
ユーザーサービスには重大な落とし穴があります。デフォルトでは、ユーザーサービスは「そのユーザーがログインしている間」しか動きません。SSHのセッションが全部切れると、systemdはユーザーインスタンスごとサービスを終了させます。これを防ぐのがlinger(居残り)の設定です。
terminalsudo loginctl enable-linger $USER
loginctl show-user $USER | grep Linger
これでユーザーのsystemdインスタンスがブート時から起動し、ログアウト後もサービスが生き続けます。「tmuxの中でサービスを起動して永続化した気になっていたが、サーバー再起動で全部止まっていた」という状態から卒業するには、この一行が必要です。
使い分けの指針
整理すると、人間が対話的に触る作業はtmux、人間がいなくても動き続けるべきものはsystemd、という分担になります。tmuxでサービスを飼うのは再起動に弱く、ログ管理も自前になるためアンチパターンです。逆にエディタでの編集作業をサービス化する意味はありません。この区別は、クラウド上でEC2を運用するときも、コンテナのrestartポリシーを考えるときも、そのまま通用する基礎概念です。開発環境を放置できる状態にしておくと、学習の再開コストが下がり、結果として継続しやすくなります。インフラの設計が学習習慣まで支える、というのが実感です。