Webアプリケーションを書いていると、しばしば計算負荷を軽減する必要が出てきます。
Webアプリケーションを書いていると、計算の多いタスクを非同期ワーカースクリプトにオフロードしたり、タスクを後でスケジューリングしたり、あるいは、クライアントと直接通信するためにソケットをリッスンするデーモンを書いたりする必要がよくあります。
時にはもっと良いツールがあるかもしれませんが (タスク キュー サーバーのような既存のソフトウェアを最初に使うことを常に検討してください)、独自のサービスを書くことで、サード パーティ製ソフトウェアの制約に縛られていたときには決して得られないレベルの柔軟性を得ることができます。
PHPを使って小さなサーバーを作ってみましょう。眉をひそめる人もいるかもしれませんが、意外とうまくいきますよ。 UDPポート10000をリッスンし、受け取ったメッセージをROT13変換して返します:
さて、起動してみましょう。
$ php server.php
そして、別のターミナルでテストしてみましょう:
$ nc -u 127.0.0.1 10000
Hello, world!
Uryyb, jbeyq!
クール、動きました。 次に、このスクリプトを常に実行し、失敗(予期せぬ終了)の際には再起動し、さらにサーバーの再起動にも耐えられるようにしたいと思います。 ここで、systemdの出番です。
サービスにする
/etc/systemd/system/rot13.service
というファイルを作成しましょう:
Description=ROT13 demo service
After=network.target
StartLimitIntervalSec=0
Type=simple
Restart=always
RestartSec=1
User=centos
ExecStart=/usr/bin/env php /path/to/server.php
WantedBy=multi-user.target
必要なものは以下のとおりです。
-
User=
-
ExecStart=
以上です。 これでサービスを開始することができます。
$ systemctl start rot13
さらに、起動時に自動的に開始するようにすることもできます
$ systemctl enable rot13
さらに進む
サービスが (うまくいけば) 動作するようになったので、構成オプションをもう少し詳しく調べて、常に期待通りに動作するようにすることが重要かもしれません。
正しい順序で開始する
After=
ディレクティブが何をしているのか不思議に思われたかもしれません。 これは単に、ネットワークの準備が整ってからサービスを開始しなければならないことを意味します。 プログラムが MySQL サーバーの起動を期待している場合は、次のように追加します。
After=mysqld.service
終了時の再起動
デフォルトでは、systemd はプログラムが何らかの理由で終了してもサービスを再起動しません。
Restart=always
on-failure
0
でない場合にのみ再起動するようにすることもできます。
デフォルトでは、systemdは100ms後に再起動を試みます。 再起動を試みるまでの秒数は、次のようにして指定できます:
RestartSec=1
Avoiding the trap: the start limit
個人的には何度もこれに陥りました。 デフォルトでは、私たちのように Restart=always
を設定すると、systemd は 10 秒間隔で 5 回以上起動に失敗すると、サービスの再起動を諦めます。
これには 2 つの 設定オプションが関係しています:
StartLimitBurst=5
StartLimitIntervalSec=10
RestartSec
ディレクティブも結果に影響を与えます: 3 秒後に再起動するように設定すると、10 秒以内に 5 回の再試行に失敗することはありません。
いつもうまくいく簡単な修正方法は、StartLimitIntervalSec=0
を設定することです。
物事がうまくいかなくなったときにサーバーに大きなストレスをかけないために、RestartSec
を少なくとも 1 秒に設定することをお勧めします。
別の方法として、デフォルトの設定のままで、StartLimitAction=reboot
を使用して、開始制限に達した場合にsystemdにサーバーを再起動させることもできます。