xinetd はスーパーサーバと呼ばれるプログラムで、あるポートに対してアクセスがあった際に設定ファイルを元にして特定のサービスを起動することができる。 以前はプログラムを常駐させないことでメモリの節約などに使うことが主な用途だったようだが、メモリを潤沢に使える今となってはその目的で使われることは減ってきたはず。 最近ではその用途に替わって、あるポートに対して簡易的なサービスを提供できることから監視を目的として使われることが増えてきたようだ。
まずは軽く触ってみる
本題に入る前に xinetd 自体がどういったものなのか見ていくことにする。 環境には CentOS7 を使った。
$ cat /etc/redhat-release CentOS Linux release 7.1.1503 (Core) $ uname -r 3.10.0-229.el7.x86_64
まずは xinetd を yum でインストールする。
$ sudo yum -y install xinetd
前述した通り xinetd はあるポートにアクセスがあった際にそれに登録されたサービスを起動する。 その設定ファイルは /etc/xinetd.d ディレクトリ以下にある。 最初から幾つかのサービスがサンプルとして用意されているが、すべて無効な状態になっている。
$ sudo grep -r "disable" /etc/xinetd.d /etc/xinetd.d/chargen-dgram: disable = yes /etc/xinetd.d/chargen-stream: disable = yes /etc/xinetd.d/daytime-dgram: disable = yes /etc/xinetd.d/daytime-stream: disable = yes /etc/xinetd.d/discard-dgram: disable = yes /etc/xinetd.d/discard-stream: disable = yes /etc/xinetd.d/echo-dgram: disable = yes /etc/xinetd.d/echo-stream: disable = yes /etc/xinetd.d/tcpmux-server: disable = yes /etc/xinetd.d/time-dgram: disable = yes /etc/xinetd.d/time-stream: disable = yes
試しにその中の一つを有効にして使ってみよう。 echo-stream サービスは TCP でエコーサーバを提供するための設定だ。
$ sudo sed -i -e "s:\(disable.*=.*\)yes:\1no:" /etc/xinetd.d/echo-stream $ sudo grep "disable" /etc/xinetd.d/echo-stream disable = no
echo-stream サービスを有効にしたら xinetd を開始する。
$ sudo systemctl start xinetd
$ sudo systemctl enable xinetd
echo-stream サービスが有効になったことで TCP の 7 番ポートが Listen される。
$ ss -ant | grep ::7 LISTEN 0 64 :::7 :::*
nc (netcat) を使って動作を確認してみよう。 エコーサーバなので入力した内容がそのまま出力として返ってくる。
$ sudo yum -y install nc
$ nc localhost 7
Hello, World
Hello, World
^C
自分でサービスを作ってみる
先ほどは組み込みのサービスを使ってみたので、次は自分で設定ファイルを書いてサービスを作ってみよう。 内容は先ほどと同じエコーサーバにする。
xinetd の設定ファイルは以下の通り。 'disable' から 'socket_type' までは設定が必須となる項目だ。 Web 上を見ると 'id' や 'type' を省略している場合が多いものの、設定ファイルを見るとこれらは Mandatory と書かれていた。 ポイントは 'server' に対してシェルスクリプトのパスを指定しているところだ。 このシェルスクリプトが提供するサービス本体になる。
$ cat << EOF | sudo tee /etc/xinetd.d/myecho-stream > /dev/null service myecho-stream { disable = no id = myecho-stream type = UNLISTED wait = no socket_type = stream user = root server = /var/tmp/myecho.sh port = 37564 flags = REUSE log_on_failure += USERID per_source = UNLIMITED } EOF
以下が上記の設定ファイルで指定したシェルスクリプト。 read で読み込んだ入力内容をそのまま echo で返している。
$ cat << EOF > /var/tmp/myecho.sh #!/usr/bin/env sh while read i ; do echo -e \${i} done EOF
シェルスクリプトに実行権限を付けたら設定を読み込み直すために xinetd を再起動する。
$ chmod +x /var/tmp/myecho.sh $ sudo systemctl restart xinetd
上手くいけば TCP の 37564 番ポートを Listen し始める。 何かおかしいときは /var/log/messages にエラーが出るはずなので確認しよう。
$ ss -ant | grep 37564 LISTEN 0 64 :::37564 :::*
先ほどと同様に nc を使って動作確認する。
$ nc localhost 37564
Hello, World
Hello, World
^C
ばっちり。
HTTP を喋るようにする
特定ポートでシェルスクリプトを使った入出力ができるようになったので、汎用性を考えて次は HTTP を喋らせてみよう。
xinetd の設定ファイルは先ほどとほとんど変わらない。
$ cat << EOF | sudo tee /etc/xinetd.d/http-stream > /dev/null service http-stream { disable = no id = http-stream type = UNLISTED wait = no socket_type = stream user = root server = /var/tmp/http.sh port = 8080 flags = REUSE log_on_failure += USERID per_source = UNLIMITED } EOF
シェルスクリプトも出力内容が HTTP になっただけ。
$ cat << EOF > /var/tmp/http.sh #!/usr/bin/env sh MSG="Hello, World" MSG_LEN=\$(( \$(echo \${MSG} | wc -c) + 2 )) /bin/echo -en "HTTP/1.1 200 OK\r\n" /bin/echo -en "Content-Type: text/plain\r\n" /bin/echo -en "Content-Length: \${MSG_LEN}\r\n" /bin/echo -en "\r\n" /bin/echo -en "\${MSG}\r\n" /bin/echo -en "\r\n" EOF
スクリプトに実行権限を付けて xinetd を再起動する。
$ chmod +x /var/tmp/http.sh $ sudo systemctl restart xinetd $ ss -ant | grep 8080 LISTEN 0 64 :::8080 :::*
今度は nc の代わりに curl を使って動作確認する。
$ sudo yum -y install curl
$ curl http://localhost:8080
Hello, World
上手くいった。
MariaDB の状態を xinetd で公開する
次はいよいよプログラムの状態を xinetd を使って外部に公開してみる。
対象は MariaDB にしよう。
$ sudo yum -y install mariadb-server
xinetd の設定ファイルは先ほどと同様。
$ cat << EOF | sudo tee /etc/xinetd.d/mariadb-healthcheck > /dev/null service mariadb-healthcheck { disable = no id = mariadb-healthcheck type = UNLISTED wait = no socket_type = stream user = root server = /var/tmp/mariadb-healthcheck.sh port = 33060 flags = REUSE log_on_failure += USERID per_source = UNLIMITED } EOF
実行するシェルスクリプトでは mysqladmin ping コマンドの結果によって出力内容を変えている。 mysqladmin ping コマンドの実行が成功 (返り値が 0) であればステータスコードを 200 にしているが、失敗 (返り値が非 0) のときは 503 だ。 Content-Body についてもステータスコード毎に変えている。 今回はテスト用途なので内容がおおざっぱ。 実際には HA の状態とか他にも色々と確認した方がいいはず。
$ cat << EOF > /var/tmp/mariadb-healthcheck.sh #!/usr/bin/env sh function _result() { STATUS=\$1 MSG=\$2 MSG_LEN=\$(( \$(echo \${MSG} | wc -c) + 2 )) /bin/echo -en "HTTP/1.1 \${STATUS}\r\n" /bin/echo -en "Content-Type: text/plain\r\n" /bin/echo -en "Content-Length: \${MSG_LEN}\r\n" /bin/echo -en "\r\n" /bin/echo -en "\${MSG}\r\n" /bin/echo -en "\r\n" } function _ok () { _result "200 OK" "\$1" } function _ng() { _result "503 Service Unavailable" "\$1" } mysqladmin ping >/dev/null 2>/dev/null if [ \$? -eq 0 ]; then _ok "MariaDB is alive" else _ng "MariaDB is dead" fi EOF
スクリプトに実行権限をつけたら MariaDB を起動して xinetd を再起動する。
$ chmod +x /var/tmp/mariadb-healthcheck.sh $ sudo systemctl start mariadb $ sudo systemctl restart xinetd $ ss -ant | grep 33060 LISTEN 0 64 :::33060 :::*
先ほどと同様 curl を使って動作を確認する。 MariaDB のサービスが動作しているため alive と表示される。
$ curl http://localhost:33060
MariaDB is alive
次に MariaDB を停止した状態で確認する。
$ sudo systemctl stop mariadb
今度はサービスが停止しているので dead と表示された。
$ curl http://localhost:33060
MariaDB is dead
あとは上記のサービスを外部から監視する。
まとめ
今回は xinetd を使ってホストの状態を外部に公開する方法について書いた。 ヘルスチェック用のスクリプトを書いて、それを xinetd 経由で外部に公開することでホストの状態を監視できる。 ちなみに、このやり方は RDB の HA 状態を監視するのによく使われているようだ。