init スクリプトを systemd ユニットファイルに変換する方法
Environment
- Red Hat Enterprise Linux 7
- Red Hat Enterprise Linux 8
- Red Hat Enterprise Linux 9
Issue
- Red Hat Enterprise Linux 6 以前のバージョン用の init スクリプトがあります。これを systemd ユニットファイルに移植して、Red Hat Enterprise Linux 7 以降で動作するようにしたいと考えています。
- init スクリプトを systemd ユニットファイルに変換するにはどうしたらよいですか?
- カスタム init スクリプトを systemd と互換性があるように変更したいと考えています。
Resolution
以下の情報は一例として提示しており、状況を問わず保証されるものではありません。Red Hat サポートは、ユニットファイルの設定と構文に関する質問には対応できますが、移植されたスクリプトはサポート対象外であるため作成しません。
Red Hat が保守するすべての Red Hat Enterprise Linux 7、8、および 9 用パッケージは、すでに systemd が動作している状態でインストールされます。Red Hat Enterprise Linux 7、8、または 9 での実行が認定されたソフトウェアを提供するサードパーティーには、Red Hat Enterprise Linux 7、8、または 9 用の systemd ユニットファイルがすでに含まれているはずです。サードパーティーの init スクリプトの移植を試みる前に、サードパーティーソフトウェアプロバイダーに連絡して関連するユニットファイルの提供を依頼する必要があります。
以下の例では、単純なデーモンをコンパイルし、その init スクリプトを作成します。次に、その init スクリプトを systemd ユニットファイルに移植します。
I. Hello World デーモン
例として (init スクリプトのみを使用する古いシステムの使用方法を示すために)、 まず、RHEL6 システムから始めて、次のように gcc、lsof、wget、および telnet クライアントをインストールします。
yum install gcc lsof wget telnet
次に、以下から取得する簡単なデーモンをそこにビルドします。以下に示すのは、サポートされていないデーモンの例にすぎません。任意のデーモンを使用できます。
http://www.catb.org/esr/cookbook/helloserver.c
このデーモンはポート 30153 をリッスンし、入力された内容をエコーバックし、接続を閉じて実行を続けます。次のコマンドでデーモンをビルドします。
wget http://www.catb.org/esr/cookbook/helloserver.c
gcc -o hellod helloserver.c
sudo mv hellod /usr/local/bin/
デーモンを起動します。
/usr/local/bin/hellod
実行中であることを検証し、PID を取得します。
[root@rhel6 hello]# ps axu | grep hellod
root 2893 0.0 0.1 6164 584 ? S 14:21 0:00 /usr/local/bin/hellod
root 2895 0.0 0.1 103244 828 pts/0 S+ 14:21 0:00 grep hellod
[root@rhel6 hello]#
開いているファイルを確認します。
[root@rhel6 hello]# lsof -p 2893
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
hellod 2893 root cwd DIR 253,0 4096 2 /
hellod 2893 root rtd DIR 253,0 4096 2 /
hellod 2893 root txt REG 253,0 17468 145414 /usr/local/bin/hellod
hellod 2893 root mem REG 253,0 65928 133364 /lib64/libnss_files-2.12.so
hellod 2893 root mem REG 253,0 1921216 133348 /lib64/libc-2.12.so
hellod 2893 root mem REG 253,0 154520 133336 /lib64/ld-2.12.so
hellod 2893 root 0u CHR 1,3 0t0 3782 /dev/null
hellod 2893 root 1u CHR 1,3 0t0 3782 /dev/null
hellod 2893 root 2u CHR 1,3 0t0 3782 /dev/null
hellod 2893 root 3u REG 253,0 5 80 /var/run/helloserver.pid
hellod 2893 root 4u unix 0xffff88001de616c0 0t0 18774 socket
hellod 2893 root 5u IPv4 18775 0t0 TCP *:30153 (LISTEN)
[root@rhel6 hello]#
デーモンをテストします。
[root@rhel6 hello]# telnet localhost 30153
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
hello
Connection closed by foreign host.
[root@rhel6 hello]#
デーモンを強制終了し、デーモンが自身でクリーンアップすることを確認します。
[root@rhel6 hello]# ls /var/run/ | grep hello
helloserver.pid
[root@rhel6 hello]# kill 2893
[root@rhel6 hello]# ls /var/run/ | grep hello
[root@rhel6 hello]#
これで、init スクリプトを作成するために動作するデーモンができました。
II. RHEL6 での Hello World 用の System V Init スクリプト
今度は、hellod の init スクリプトを作成します。
touch /etc/init.d/hello
chmod 755 /etc/init.d/hello
以下の 4 行を /etc/init.d/hello に追加します。
#!/bin/bash
#
# chkconfig: 35 90 12
# description: Hello Daemon
上記のコマンドは以下のようになります。
- chkconfig 行により、chckconfig コマンドでスクリプトを管理できるようになります。
- '35' は、実行レベル 3 および 5 で起動することを意味します。
- '90' は起動優先度です (最後に起動されるサービスの 1 つ)。
- '12' は強制終了の優先度です (最初に強制終了されるサービスの 1 つ)。
続いて、次の行に以下を追加して、init スクリプトに含まれる残りの 800 行の関数のファイルを含めます。
. /etc/init.d/functions
上記の後に、hellod サービスを起動する次の 8 行を追加します。success 関数は /etc/init.d/functions で定義されています。
start() {
echo -n "Starting Hello Server: "
/usr/local/bin/hellod
# touch the lock file
touch /var/lock/subsys/hellod
success $"Hello Server startup"
echo
}
上記の後に、hellod サービスを停止する次の 7 行を追加します。
stop() {
echo -n "Stopping Hello Server: "
killproc hellod
# Remove the lock file
rm -f /var/lock/subsys/hellod
echo
}
最後に、次の 18 行を追加して、入力を正しく処理し、start 関数と stop 関数を呼び出すようにします。killproc 関数は /etc/init.d/functions で定義されています。
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status servicename
;;
restart|reload|condrestart)
stop
start
;;
*)
echo $"Usage: $0 {start|stop|restart|reload|status}"
exit 1
esac
新しい init スクリプトを保存し、動作することを検証します。
[root@rhel6 ~]# /etc/init.d/hello status
hellod is stopped
[root@rhel6 ~]# /etc/init.d/hello start
Starting Hello Server: [ OK ]
[root@rhel6 ~]# /etc/init.d/hello status
hellod (pid 3780) is running...
[root@rhel6 ~]# telnet localhost 30153
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
hello
Connection closed by foreign host.
[root@rhel6 ~]# /etc/init.d/hello stop
Stopping Hello Server: [ OK ]
[root@rhel6 ~]# /etc/init.d/hello status
hellod is stopped
[root@rhel6 ~]#
これで、動作する init スクリプトができました。次に、init スクリプトを systemd に移植して、RHEL7 で動作するようにします。
III. RHEL7 での Hello World 用の Systemd ユニットファイル
/usr/local/bin/hellod のバイナリーを RHEL6 から RHEL7 に移動し、動作するかどうかをテストします。
ユニットファイルを保持する /etc/systemd/system/hellod.service を作成します。パッケージメンテナーユニットファイルは /usr/lib/systemd/system/ に存在するはずですが、変更する場合はこれを /etc/systemd/system/ にコピーする必要があります。これは systemd が /etc を優先するためです。
touch /etc/systemd/system/hellod.service
chmod 644 /etc/systemd/system/hellod.service
次の 10 行を /etc/systemd/system/hellod.service に追加します。
[Unit]
Description=Hello Service
After=network.target
[Service]
ExecStart=/usr/local/bin/hellod
Type=forking
PIDFile=/var/run/helloserver.pid
[Install]
WantedBy=multi-user.target
上記の 10 行を保存したら、ユニットファイルは完成です。その動作を説明します。
最初の [Unit] セクションには、サービスの説明が含まれています。また、After= タイプの依存関係も設定します。これは、ネットワークがサーバーで実行されるまで、このネットワークサービスが起動しないことを意味します。
2 番目の [Service] セクションでは、ExecStart= を使用して、サービスの起動時に実行するバイナリーへのパスを定義します。元のプログラムはフォークデーモンなので、Type=forking を設定します。その他のタイプは、systemd.service の man ページで説明されています。フォークを使用する場合は、systemd がデーモンのメインプロセスを識別できるように、PIDFile= オプションを使用することが推奨されます。上記の lsof
の例では、デーモンによって作成された pid ファイルは /var/run/helloserver.pid でした。そこで、これを PIDFile= に明示的に設定しました。
3 番目の [Install] セクションでは、WantedBy= を使用して、サービスが RHEL6 の実行レベル 3 のようなマルチユーザーモードで起動することを指定します。
systemd サービスファイルが作成されたので、次のコマンドを使用して systemd にその存在を通知する必要があります。
systemctl daemon-reload
新しいサービスを起動します。
systemctl start hellod
これをテストします。
[root@rhel7 ~]# telnet localhost 30153
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hey
hey
Connection closed by foreign host.
[root@rhel7 ~]#
ステータスを確認します。
[root@rhel7 ~]# systemctl status hellod
hellod.service - Hello Daemon
Loaded: loaded (/etc/systemd/system/hellod.service; disabled)
Active: active (running) since Mon 2014-06-16 17:15:57 EDT; 1min 38s ago
Process: 11812 ExecStart=/usr/local/bin/hellod (code=exited, status=0/SUCCESS)
Main PID: 11814 (hellod)
CGroup: /system.slice/hellod.service
└─11814 /usr/local/bin/hellod
Jun 16 17:15:57 rhel7.example.com systemd[1]: Starting Hello Daemon...
Jun 16 17:15:57 rhel7.example.com systemd[1]: Started Hello Daemon.
[root@rhel7 ~]#
再起動すると、PID が変更されていることがわかります。
[root@rhel7 ~]# systemctl restart hellod
[root@rhel7 ~]# systemctl status hellod
hellod.service - Hello Daemon
Loaded: loaded (/etc/systemd/system/hellod.service; disabled)
Active: active (running) since Mon 2014-06-16 17:23:54 EDT; 2s ago
Process: 11898 ExecStart=/usr/local/bin/hellod (code=exited, status=0/SUCCESS)
Main PID: 11900 (hellod)
CGroup: /system.slice/hellod.service
└─11900 /usr/local/bin/hellod
Jun 16 17:23:54 rhel7.example.com systemd[1]: Starting Hello Daemon...
Jun 16 17:23:54 rhel7.example.com systemd[1]: Started Hello Daemon.
[root@rhel7 ~]#
デーモンを停止します。
systemctl stop hellod
基本的な init スクリプトを systemd に移植し、40 行のコードを 10 行のコードに置き換えました。サービス用の systemd ユニットファイルの作成に関する詳細は、systemd.service の man ページを参照してください。
Root Cause
Diagnostic Steps
This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.
Comments