長らくブランクが空きましたが当エントリはcfn-initはUbuntuのどの起動システムにも対応しているのか?(其の一) の続きです。今回はservices
キーの調査であることがより明確であるエントリ名に変更いたしました。
前回のおさらいと今回の目的
CloudFormationには便利なツールcfn-init
が用意されています。これを利用することでservices
キーに指定したサービスの停止などの制御を行うことができます。ドキュメント によるとこれは起動システムsysvinit
を前提としたものとのこと。しかし、Ubuntu14でもUbuntu16でもcronデーモンを停止することができました。起動システムとして、Ubuntu14はUpstart
、Ubuntu16はsystemd
が採用されているにも関わらず、です。前回はその仕組みについては棚上げにしていましたので今回はそこを調査したいと思います。
調査方法
今回は Ubuntu14.04 および Ubuntu16.04 の各々において以下の流れで調査を行いたいと思います。
- EC2インスタンスを起動
- 自作のデーモンを作成&設定
- このインスタンスのAMIを作成
- 3.で作成したAMIを指定してCloudFormationでインスタンス作成。この際にサービスの停止を定義する。
- 検証
Ubuntu14.04
1. EC2インスタンスを起動
ami-db7311bd を起動します。
2. 自作のデーモンを作成&設定
起動サービスで実行されるデーモンスクリプトを作成します。
#!/usr/bin/env bash
filename=$1
while (true); do
sleep 1
date >> /opt/daemon_test/${filename}.log
done
実行結果は以下となります。
$ sudo ./hello.sh foo
$ cat /opt/daemon_test/foo.log
Tue Jan 9 10:25:59 UTC 2018
Tue Jan 9 10:26:00 UTC 2018
Tue Jan 9 10:26:01 UTC 2018
Tue Jan 9 10:26:02 UTC 2018
Upstart
準拠の起動設定を行います。/etc/init/hello-upstart.conf
として以下のようなファイルを設置します。
description "hello-upstart"
start on runlevel [2345]
stop on runlevel [016]
chdir /opt/daemon_test
exec ./hello.sh upstart
respawn
次にデーモンの設定を反映します。
$ sudo initctl reload-configuration
$ sudo initctl list | grep hello
hello-upstart stop/waiting
インスタンスを再起動してデーモンが起動していることを確認します。
$ sudo initctl list | grep hello
hello-upstart start/running, process 958
$ tail -f /opt/daemon_test/upstart.log
Wed Jan 10 06:29:12 UTC 2018
Wed Jan 10 06:29:13 UTC 2018
Wed Jan 10 06:29:14 UTC 2018
Wed Jan 10 06:29:15 UTC 2018
Wed Jan 10 06:29:16 UTC 2018
3. このインスタンスを基にAMIを作成
Webコンソールから行いました。
4. 3.で作成したAMIを基にインスタンスをCloudFormationで作成。この際にcronデーモンと自作デーモンを停止するように定義。
実行したところ、cronデーモンは停止しましたが、自作デーモンは停止しませんでした。原因を調査してみます。
5. 検証
/var/log/cfn-init.log
を見ると/etc/init.d/hello-upstart
が無いという理由で落ちているようです。
:
2018-01-26 08:30:03,817 [DEBUG] Setting service hello-upstart to disabled
2018-01-26 08:30:03,824 [ERROR] update-rc.d failed with error 1. Output: update-rc.d: /etc/init.d/hello-upstart: file does not exist
2018-01-26 08:30:03,824 [ERROR] Error encountered during build of config: Could not disable service hello-upstart (return code 1)
:
ToolError: Could not disable service hello-upstart (return code 1)
参考にcronデーモンを見てみます。
/etc/init/cron.conf
が存在しますし、以下の実行結果からもUpstart
で起動管理されているように見えます。
$ initctl list | grep cron
cron start/running, process 1073
しかし、sysvinit
スタイルの定義の置き場/etc/init.d
にも設定は存在していました。というかシンボリックリンクが設置されているようですね。
$ ls -la /etc/init.d/cron
lrwxrwxrwx 1 root root 21 Feb 9 2013 /etc/init.d/cron -> /lib/init/upstart-job
/lib/init/upstart-job
のコメントにはSymlink target for initscripts that have been converted to Upstart.
と記してあります。どうやらこれがキモのようですね。自作デーモンにおいてもこれを真似して設置してみました。
$ cd /etc/init.d
$ sudo ln -s /lib/init/upstart-job hello-upstart
$ ls -la hello-upstart
lrwxrwxrwx 1 root root 21 Jan 28 10:05 hello-upstart -> /lib/init/upstart-job
これを基にAMIを作成しました。
そして、このAMIを基にインスタンスをCloudFormationで作成。この際にcronデーモンと自作デーモンを停止するように定義しました。
結果、cronデーモンも自作デーモンも停止しました。
Ubuntu16.04
1. EC2インスタンスを起動
ami-a07012c6 を起動します。
2. 自作のデーモンを作成&設定
起動サービスで実行されるデーモンスクリプトを作成します。Ubuntu14.04 の場合と同じですね。
#!/usr/bin/env bash
filename=$1
while (true); do
sleep 1
date >> /opt/daemon_test/${filename}.log
done
systemd
準拠の起動設定を行います。/etc/systemd/system/hello.service
として以下のようなファイルを設置します。
[Unit]
Description = hello systemd
[Service]
ExecStart = /opt/daemon_test/hello.sh systemd
Restart = always
Type = simple
[Install]
WantedBy = multi-user.target
次にデーモンの設定反映します。
# 状態確認
$ systemctl list-unit-files --type=service | grep hello
hello.service disabled
# 有効化
$ sudo systemctl enable hello
Created symlink from /etc/systemd/system/multi-user.target.wants/hello.service to /etc/systemd/system/hello.service.
$ sudo systemctl status hello
● hello.service - hello systemd
Loaded: loaded (/etc/systemd/system/hello.service; enabled; vendor preset: enabled)
Active: inactive (dead)
$ systemctl list-unit-files --type=service | grep hello
hello.service enabled
# 起動
$ sudo systemctl start hello
$ sudo systemctl status hello
● hello.service - hello systemd
Loaded: loaded (/etc/systemd/system/hello.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2018-01-16 09:22:38 UTC; 3s ago
Main PID: 6463 (bash)
Tasks: 2
Memory: 348.0K
CPU: 8ms
CGroup: /system.slice/hello.service
├─6463 bash /opt/daemon_test/hello.sh
└─6475 sleep 1
Jan 16 09:22:38 ubuntu-xenial systemd[1]: Started hello systemd.
インスタンスを再起動してデーモンが起動していることを確認します。
$ sudo systemctl status hello
● hello.service - hello systemd
Loaded: loaded (/etc/systemd/system/hello.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2018-01-16 09:22:38 UTC; 3s ago
Main PID: 6463 (bash)
Tasks: 2
Memory: 348.0K
CPU: 8ms
CGroup: /system.slice/hello.service
├─6463 bash /opt/daemon_test/hello.sh
└─6475 sleep 1
Jan 16 09:22:38 ubuntu-xenial systemd[1]: Started hello systemd.
$ tail -f /opt/daemon_test/systemd.log
Wed Jan 10 06:31:12 UTC 2018
Wed Jan 10 06:31:13 UTC 2018
Wed Jan 10 06:31:14 UTC 2018
Wed Jan 10 06:31:15 UTC 2018
Wed Jan 10 06:31:16 UTC 2018
3. このインスタンスを基にAMIを作成
Webコンソールから行いました。
4. 3.で作成したAMIを基にインスタンスをCloudFormationで作成。この際にcronデーモンと自作デーモンを停止するように定義。
実行したところ、cronデーモンは停止しましたが、自作デーモンは停止しませんでした。原因を調査してみます。
5. 検証
/var/log/cfn-init.log
を見るとcannot find a LSB script for hello
という理由で落ちているようです。
2018-01-28 10:41:34,511 [DEBUG] Setting service hello to disabled
2018-01-28 10:41:34,520 [ERROR] update-rc.d failed with error 1. Output: update-rc.d: error: cannot find a LSB script for hello
2018-01-28 10:41:34,520 [ERROR] Error encountered during build of config: Could not disable service hello (return code 1)
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/cfnbootstrap/construction.py", line 542, in run_config
CloudFormationCarpenter(config, self._auth_config).build(worklog)
File "/usr/local/lib/python2.7/dist-packages/cfnbootstrap/construction.py", line 270, in build
CloudFormationCarpenter._serviceTools[manager]().apply(services, changes)
File "/usr/local/lib/python2.7/dist-packages/cfnbootstrap/service_tools.py", line 155, in apply
参考にcronデーモンを見てみます。
以下の実行結果からもsystemd
で起動管理されているように見えます。
$ sudo systemctl status cron
● cron.service - Regular background program processing daemon
Loaded: loaded (/lib/systemd/system/cron.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:cron(8)
しかし、sysvinit
スタイルの定義の置き場/etc/init.d
にも設定は存在していました。ただ、Ubuntu14.04とは違ってシンボリックリンクではなさそうです。
$ ls -la /etc/init.d | grep cron
-rwxr-xr-x 1 root root 3049 Apr 5 2016 cron
内容を見るといわゆる「普通の」sysvinit用の設定ファイルのように見えます。
自作デーモンにおいてもこれを真似して設置してみました。
スケルトンを基にコピーして編集。実行権限を付与します。
$ sudo cp -iv /etc/init.d/skeleton /etc/init.d/hello-sysvinit
'/etc/init.d/skeleton' -> '/etc/init.d/hello-sysvinit'
$ sudo chmod +x hello-sysvinit
スケルトンとの差分はこんな感じ。
### BEGIN INIT INFO
-# Provides: skeleton
+# Provides: hello-sysvinit
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
DESC=“Description of the service”
-DAEMON=/usr/sbin/daemonexecutablename
+NAME=hello-sysvinit
+DAEMON=/opt/daemon_test/hello.sh
+DAEMON_ARGS=“sysvinit”
sysvinit
の起動設定と同様の作業をすることで
$ sudo update-rc.d hello-sysvinit defaults
以下のようにsystemd
の起動設定が作られました。
$ ls -la /run/systemd/generator.late/hello-sysvinit.service
-rw-r--r-- 1 root root 592 Jan 29 15:45 /run/systemd/generator.late/hello-sysvinit.service
これを基にAMIを作成しました。
そして、このAMIを基にインスタンスをCloudFormationで作成。この際にcronデーモンと自作デーモンを停止するように定義しました。
結果、cronデーモンは無事に停止しましたが自作デーモンは停止しませんでした。状態は以下の通り。Active
がfailed
となっています。
$ systemctl status hello-sysvinit
● hello-sysvinit.service - LSB: Example initscript
Loaded: loaded (/etc/init.d/hello-sysvinit; bad; vendor preset: enabled)
Active: failed (Result: timeout) since Tue 2018-01-30 08:45:22 UTC; 4min 6s ago
Docs: man:systemd-sysv-generator(8)
CGroup: /system.slice/hello-sysvinit.service
├─ 1266 bash /opt/daemon_test/hello.sh sysvinit
└─11182 sleep 1
Jan 30 08:40:22 ip-172-31-20-228 systemd[1]: Starting LSB: Example initscript...
Jan 30 08:45:22 ip-172-31-20-228 systemd[1]: hello-sysvinit.service: Start operation timed out. Terminating.
Jan 30 08:45:22 ip-172-31-20-228 systemd[1]: Failed to start LSB: Example initscript.
Jan 30 08:45:22 ip-172-31-20-228 systemd[1]: hello-sysvinit.service: Unit entered failed state.
Jan 30 08:45:22 ip-172-31-20-228 systemd[1]: hello-sysvinit.service: Failed with result ‘timeout’.
/var/log/cfn-init.log
を見ると当該サービスを停止する必要がないとみなされたようです。
2018-01-30 08:45:54,198 [DEBUG] Setting service hello-sysvinit to disabled
2018-01-30 08:45:54,295 [INFO] disabled service hello-sysvinit
2018-01-30 08:45:54,311 [DEBUG] No need to modify running state of service hello-sysvinit
これはデーモン稼働時のステータスがactivating (start)
になっていることに起因しているのではないかと考えられます。
$ systemctl status hello-sysvinit
● hello-sysvinit.service - LSB: Example initscript
Loaded: loaded (/etc/init.d/hello-sysvinit; bad; vendor preset: enabled)
Active: activating (start) since Tue 2018-01-30 05:09:55 UTC; 1min 6s ago
Docs: man:systemd-sysv-generator(8)
Control: 1210 (hello-sysvinit)
Tasks: 3
Memory: 560.0K
CPU: 118ms
CGroup: /system.slice/hello-sysvinit.service
├─1210 /bin/sh /etc/init.d/hello-sysvinit start
├─1252 bash /opt/daemon_test/hello.sh sysvinit
└─1762 sleep 1
Jan 30 05:09:55 ip-172-31-29-245 systemd[1]: Starting LSB: Example initscript...
ちなみにcronデーモンだと以下のように active (running)
になっています。/etc/init.d/hello-sysvinit
の書き方に一工夫必要なように感じられます。
$ systemctl status cron
● cron.service - Regular background program processing daemon
Loaded: loaded (/lib/systemd/system/cron.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2018-01-30 05:09:55 UTC; 57s ago
Docs: man:cron(8)
Main PID: 1099 (cron)
Tasks: 1
Memory: 356.0K
CPU: 1ms
CGroup: /system.slice/cron.service
└─1099 /usr/sbin/cron -f
Jan 30 05:09:55 ip-172-31-29-245 cron[1099]: (CRON) INFO (pidfile fd = 3)
Jan 30 05:09:55 ip-172-31-29-245 cron[1099]: (CRON) INFO (Running @reboot jobs)
Jan 30 05:09:55 ip-172-31-29-245 systemd[1]: Started Regular background program processing daemon.
systemd
と正しくコンバーチブルなsysvinit
設定ができていなかったようですね…
今回はこれ以上は深追いしないでおきます。
まとめ
今回の結果から確実に言えるのは、プレーンなUpstart
およびsystemd
の設定のみから起動されるデーモンはcfn-init
のservices
キーでは制御できないということです。cronデーモンはsysvinit
とのコンバーチブルな設定が配置されていたためcfn-init
のservices
キーで制御できていたのですね。もし制御したいサービスがsysvinit
コンバーチブルな設定が配置されていない場合は自前で準備してやる必要がありそうです。少々面倒ですね。
くわえて、今回の実験においてcfn-init
のインストールに非常に時間がかかるという事実がありました。インストール完了までは各種デーモンが稼働してしまっているのが確認できました。リソースのスペックによるとは思うのですが、この点は要注意です。
これらを総合して考えると、cfn-init
のservices
キーを使ってのサービスの停止が有用な場面は限られてくると思われます。「sysvinit
設定が配置されていて、稼働時間が完全にゼロにならなくても実害がないサービス」のみで使えそうですね。cfn-init
の使用を前提とした場合は、OSの起動システムでの起動設定は行わずcfn-init
にて所望のデーモンを起動するのが良さそうです。
何ごとも適材適所ですね。