tmuxはクライアント-サーバだ
一言でいうと
tmuxでコマンドを実際に実行しているのは、自分のターミナルではなくサーバープロセスです。自分のターミナルは、そのサーバーにつながっている画面にすぎないので、画面が消えても作業は動き続けます。
なぜ必要なのか
リモートサーバーでデプロイスクリプトを実行している最中に、Wi-Fiが切れました。再接続してみると、スクリプトは途中で死んでおり、半分だけ適用された状態が残っていました。一度くらいは、誰でも経験します。
なぜ死んだのでしょうか。SSH接続が切れると、そのセッションの制御端末が消え、カーネルはセッションリーダーにSIGHUPを送ります。シェルはそれを自分のジョブに伝播します。デプロイスクリプトは自分のシェルの子だったので、一緒に死にます。
nohupやsetsidで、この連鎖を断ち切ることもできます。しかしそうすると、作業は生き残りますが、画面をもう一度見ることはできません。進捗を見るにはログファイルを別に見る必要があり、途中で入力を与えることもできません。
tmuxは、別の方向で解決します。コマンドを自分のシェルの子ではなく、tmuxサーバーの子にするのです。tmuxサーバーは、自分のSSHセッションとは無関係の別のプロセスで、各ウィンドウの疑似端末(pty)を所有します。自分のターミナルは、その画面を受け取って見るクライアントにすぎません。そのため、クライアントが消えても、サーバーとその子たちには何も起こりません。再接続してtmux attachすれば、画面までそのまま戻ってきます。
どう動くのか
構造は4階層で、それぞれに身近な例えがあります。
| 階層 | 例え | コマンド |
|---|---|---|
| Server | オペレーティングシステム | 最初のセッションを作るときに自動で起動します |
| Session | デスクトップ / 作業スペース | tmux new -s dev |
| Window | ブラウザーのタブ | prefix + c |
| Pane | 画面分割 | prefix + % / " |
すべてのキー入力は、プレフィックスキーを先に押して手を離してから、コマンドのキーを押す2段階です。デフォルトはCtrl-bで、多くの人が手に楽なCtrl-aに変更します。このとき、シェルの「行頭へ」のショートカットと重なるので、bind C-a send-prefixを一緒に入れて、2回押すと元のキーが渡されるようにします。
セッション操作の核心となるコマンドは5つだけ知っていれば、初日は十分です(以下のコマンドのプレースホルダーは、すべてセッション名です)。
tmux new -s 이름: 作成してアタッチtmux new -d -s 이름: 作成するがアタッチしない(スクリプトでは必須)tmux ls: 一覧tmux attach -t 이름: アタッチ- prefix +
d: 切り離し(detach)
そして、実務で最も役に立つ慣用句が1つあります。tmux new -A -s devというコマンドは、セッションがあればアタッチし、なければ作成します。シェルのaliasに登録しておけば、毎回存在するかどうかを確認する必要がありません。
現場での姿
夜間マイグレーション。DBサーバーに接続して、tmux new -s migrationでセッションを作り、マイグレーションを開始したらデタッチして、帰宅します。翌日アタッチすれば、進捗がそのまま見えます。nohupと違い、画面と入力が生きているという点が異なります。
開発環境の3分割。左にエディター、右上に開発サーバー、右下にテスト監視。この配置を毎回手で作る代わりに、スクリプト1行で再現します。ここでよく抜けるのが-c "#{pane_current_path}"です。これがないと、新しく作ったペインがホームディレクトリで開くため、毎回cdを打ち直す必要があります。
複数サーバーの同時運用。4台のサーバーにそれぞれsshでつないだペインを4つ作ってsynchronize-panesを有効にすると、入力したすべてのコマンドが4か所で同時に実行されます。危険な分だけ強力なので、有効/無効の状態をステータスバーに表示しておくとよいです。
用語の落とし穴が1つあります。tmuxのsplit-window -hは「横に分ける」ではなく、左右に分けるという意味です(水平方向に配置)。-vは上下です。紛らわしいので、会話するときは「左右分割・上下分割」と言い、フラグは別に覚えるほうがよいです。
最初の日に変えるべき設定が2つあります。base-index 1とpane-base-index 1です。キー0がキーボードの右端にあって不便だからです。そしてhistory-limitはデフォルトが2000行なので、ログを少し見ただけで切れてしまいます。5万行くらいが、実用的な上限です。
切れても生き残る作業を作る
tmuxを使う最大の理由は、画面分割ではなく、接続が切れても作業が続くことです。その性質をきちんと使うには、いくつか決めておく必要があります。
セッションに名前を付けます。番号は、アタッチやデタッチを繰り返すと変わります。名前があれば、「あればアタッチし、なければ作成する」1行が可能になります。
tmux new-session -A -s deploy # 있으면 attach, 없으면 새로
この1行をスクリプトに入れておけば、接続するたびに同じ場所に戻れます。
長い作業には、専用のウィンドウを1つ与えます。マイグレーションや大容量のコピーは、そのウィンドウでだけ実行し、そのウィンドウの名前を作業名に変えておきます。数時間後に戻ってきても、どのウィンドウが何かがわかります。
Ctrl-b , 창 이름 바꾸기
Ctrl-b w 창 목록에서 고르기
Ctrl-b d 떼어 내기(작업은 계속 돈다)
このコードブロックの韓国語コメントは、上から順に、ウィンドウ名の変更、ウィンドウ一覧から選択、切り離し(作業は動き続けます)という意味です。
スクロールの代わりに、コピーモードを使います。ターミナルのスクロールバーは、tmuxの中ではうまく動作しません。Ctrl-b [でコピーモードに入ると、上にさかのぼりながら検索までできます。デフォルトの履歴が2,000行なので、長いログは切れてしまうため、増やしておきます。
set -g history-limit 50000
入れ子になる場合に備えます。リモートサーバーでさらにtmuxを起動すると、プレフィックスキーが重なります。内側に送るには、Ctrl-bを2回押します。頻繁にそうするなら、内側のセッションのプレフィックスキーを別のキーに変えるほうがよいです。
複数人が同じセッションを見られます。障害対応で、画面共有の代わりに使えます。ただし、同じセッションにアタッチすると、ウィンドウサイズが最も小さい画面に合わせられます。それぞれが別のウィンドウを見たいときは、セッションを分けて作ります。
tmux new-session -t deploy -s deploy-2 # 창은 공유, 보기는 따로
tmuxが死ぬと、すべて消えます。サーバーが再起動すると、セッションは残りません。本当に生き残らなければならない作業は、tmuxではなくsystemdユニットやバッチジョブとして作ります。tmuxは、人がアタッチしている作業のためのツールです。
次のラボですること
2つのラボが続きます。tmux-basicsでは、セッションとウィンドウを作ってデタッチ状態を確認し、設定ファイルが実際にサーバーに反映されるかを検証します。tmux-panesでは、ペインの分割・タイトル・拡大・同期を扱い、最後に開発レイアウトを再現するスクリプトを作ります。どちらのラボも最後のステップが自動化スクリプトである理由は、手で作った配置は翌日消えますが、スクリプトは残るからです。