systemd - .socket 編

f:id:masm11:20201202230535p:plain

こんにちは、masm11 です。

systemd シリーズの第4回です。

初回:

前回:

今回は .socket ファイルについて説明します。

.socket ファイルとは

以前から Linux/UNIX を管理している方には、inetd の代わり、と言うと 話が早いかと思います。

この unit ファイル中に指定されているポートでリクエストを待ち受け、 リクエストがあったら指定されているサービスに接続を渡すのです。

サービス自身がポートを開いて待ち受けるのに比べると、メリットもあります。 サービスがポートを開いて待ち受ける場合、そのサービスを restart すると、 短い時間とは思いますが、リクエストを受け付けられない時間ができます。 .socket ファイルを使うと、サービスが動いていない間も systemd が 待ち受け続けていますので、その時間をなくすことができます。

この機能は socket activation と呼ばれることもあります。

.socket ファイルの書き方

[Unit] セクション

.socket ファイルも unit ファイルの一種なので、[Unit] セクションが あります。

[Unit]
Description=ZFS snapshot receiver socket

こんな感じで良いかと思います。.service の場合と同様、Description は ログ出力の際などに使われます。

[Socket] セクション

.socket ファイル特有のセクションです。

まず、ポートを指定します。

ListenStream=1234

このように ListenStream に数字で指定すると、TCP になります。 この例では TCP のポート 1234 で待ち受けます。

ListenStream=/tmp/foo.sock

このようにファイル名を指定した場合は、stream 型 unix domain socket になります。

datagram タイプもあります。

ListenDatagram=1234

このように ListenStream の代わりに ListenDatagram を使い、数字で指定すると、 UDP になります。この例では UDP のポート 1234 で待ち受けます。

ListenDatagram=/tmp/foo.sock

同様に datagram 型 unix domain socket です。

次に、unix domain socket の場合は、socket の所有者やパーミッションを指定します。

SocketUser=root
SocketGroup=root
SocketMode=0600

例えばこのように指定すると、所有者は root、グループも root で、パーミッションは 0600 になります。

また、stream 型の場合は、backlog を指定できます。

Backlog=5

これは何かと言うと、接続待ちの最大本数です。 接続リクエストがたくさん同時に来た場合に、すぐに接続完了を返せれば良いのですが、 忙しくて待たせてしまう場合があります。 上のように設定すると、最大5本までは待たせておくことができます。

この数字を大きくすると、たくさん待たせておくことができます。 逆に小さくすると、待たせることができなかった接続については、 接続拒否になってしまいます。

C言語的に言うと、

listen(sock, 5);

の、5 です。

そして、かなりの昔から 5 にしておくことが多いようですが、 .socket のデフォルトとしては 128 だそうです。

更に、stream の場合には、accept するかどうかも指定できます。

Accept=true

これを true にすると、接続リクエストがあるたびに、 systemd が接続を完了させた後、その接続をサービスに引き渡します。

false にすると、最初に接続リクエストがあった時に、 接続とともに、待ち受けている socket もサービスに引き渡します。 そしてそれ以後サービスが待ち受けます。

さて、Accept=false の場合には、サービスの指定が可能です。

Service=foo.service

この指定をしなかった場合は、foo.socket のデフォルトは foo.service に なりますので、大抵の場合は指定しなくて良いと思います。

[Install] セクション

このセクションを書いておくと、systemctl enable できるようになります。

[Install]
WantedBy=sockets.target

こう書いておくことが多いと思います。

.service ファイルの書き方

そして、引き渡す先のサービスも定義しておく必要があります。 サービスですので .service ファイルに書きます。

今回は、.socket ファイルで TCP で Accept=true を指定した場合の例を 挙げます。

[Unit]
Description=ZFS snapshot receiver service

[Service]
ExecStart=/home/service/zfsrecvsnap/zfsrecvsnap.sh
StandardInput=socket
StandardOutput=socket
StandardError=journal

ExecStart にはサービスのプログラムを指定します。

StandardInputStandardOutput で、標準入力と標準出力を socket に つないでいます。 また、プログラムでエラーが起きた場合の標準エラー出力は journal へ投げることに しました。journal へ投げると、journalctl で確認できます。

そして、この .service ファイルには [Install] セクションがありません。 .socket から起動されるので、systemctl enable する必要がないからです。

以上で、sudo systemctl start foo.socket すると待ち受けが始まり、 接続するたびにサービスが起動します。

注意

.socket ファイルはプログラムの仕様と密接な関係にあります。

既存の .socket ファイルを編集してポート番号を変更するくらいは 問題ないと思いますが、 Accept を変更してサービスが問題なく動作してくれるとは思えません。

変更の際には必ずドキュメントに当たってください。

まとめ

今回は .socket ファイルの内容について説明しました。

.socket ファイルは、実は FIFO にもキャラクタ型デバイスにも使えるんですね。 socket だけだと思っていました。

ではまた!