はじめに
開発中のコンテナ内で動いている開発サーバー(ポート5174)に、別のホストからアクセスするために、次のコマンドでポートを中継しました。
socat TCP-LISTEN:5174,fork,reuseaddr TCP:claude-wt-bristleworm:5174
このとき付けている fork オプションが何をしているのか、付けないとどうなるのか、逆に付けないほうがよいのはどんなときかを整理しました。
forkなしの場合
socat(ネットワーク用語:2つの通信経路の間でデータを中継するコマンド)は、fork を付けないと 最初の1本の接続を受け付けた時点で待ち受けをやめます。
そのため、
- 2本目以降の接続は拒否される
- 1本目の接続が終わると、socat自体も終了する
という動きになります。
ブラウザは1つのページを開くだけでも、HTMLやJS、CSSの取得やHMR(フロントエンド開発用語:ページを再読み込みせずに変更を反映する仕組み)のWebSocketなど、複数の接続を同時に張ります。そのため fork なしでは開発サーバーをまともに使えません。
一方で、fork を付ければ何でも解決というわけでもありません。
forkをつけた場合
forkで接続ごとに子プロセスを作る
fork を付けると、接続が来るたびに中継用の子プロセスを作ります。親プロセスは待ち受けを続けるので、複数の接続を同時に扱えます。
socat TCP-LISTEN:5174,fork,reuseaddr TCP:claude-wt-bristleworm:5174
プロセスを作りすぎる問題に注意する
socatのプロセスは1つあたり数MB程度と軽いため、開発用途で数十本程度の接続なら問題になりません。
問題になるのは、デフォルトでは接続数に上限がないことです。大量の接続が来ると、次のようなことが起きます。
- メモリとファイルディスクリプタ(OS用語:プロセスが開いている通信やファイルを指す番号)を消費し、上限(
ulimit、Linuxのリソース上限設定)に達すると新しい接続を受けられなくなる - コンテナ内では、pids制限(cgroup、Linuxの資源管理機能によるプロセス数の上限)に達し、socat以外のプロセスも起動できなくなることがある
- 外部に公開している場合は、DoS(セキュリティ用語:大量のリクエストでサービスを止める攻撃)に弱くなる
対策として、max-children で子プロセス数の上限を指定できます。
socat TCP-LISTEN:5174,fork,reuseaddr,max-children=20 TCP:claude-wt-bristleworm:5174
forkを使わないほうがよいケース
fork なしの「1回つないだら終わる」動きのほうが適している場面もあります。
| ケース | 理由 |
|---|---|
| 相手が同時に1つしか開けない | シリアルポートなどのデバイスへ中継する場合、複数の子プロセスが同じデバイスを取り合って壊れる |
| 1回だけ使えばよい | ファイルを1回送る、動作確認で1回だけつなぐ場合は、終わったら自動で終了するほうが後片付けが要らない |
| 接続を1クライアントに限定したい | 他の人に割り込まれたくない場合 |
おわりに
fork は「複数の接続を同時に受けるためのオプション」です。ブラウザから開発サーバーへ中継するような用途では必須ですが、接続数に上限がない点には注意が必要です。
- 複数接続を受けたい →
fork(必要ならmax-childrenで上限を付ける) - 1回きり・1クライアント限定・デバイスへの中継 →
forkなし
と使い分けるとよさそうです。
参考
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼
https://projisou.jp
