サーバーで動くプログラムなら、一般的なエンドユーザー用のプログラムとはその挙動が違うということは明らかでしょう。どんなデータを持ち、どんな処理をするかの問題でもありますが、今回は簡単に「実行」の面で書いてみたいと思います。簡単にいうと、誰かが止めるまではずっと実行され続けるようなプログラムをLinuxではどう実現したらいいかの話です。
CentOSやRHELでは、service
というコマンドがあります。yum
のようなパッケージ管理プログラムを使って、様々なプログラムをインストールしていくとその中には一回だけの実行で終わらず、メモリー常に常住させる必要のあるものもありますね。以前このブログで紹介したことのあるJenkins
もまたそのようなプログラムの一つです。
そのようなプログラムは一回インストールしたら自動的にservice
コマンドで動かしたり止めたりすることができるようになりますが、例えば自作したプログラムをそのように動作させることはできるのでしょうか?ユーザーがなんでもできるLinuxならできそうですが。
そう思っていたら、ちょうど仕事でJavaアプリケーションをサービスとして登録し、管理する必要があることになりました。私はJenkinsのタスクを管理していたため、実際はJavaアプリケーションがGitにコミットされたらPull→ビルド→実行されているJavaアプリケーションを止めて新しくビルとした物を実行するというタスクが必要となったのです。実際はサービスに触れる必要はなく、ビルドの後にサービスを止めたり再開させたりするシェルコマンドを実行するように仕組んだだけだったのですが、一体これはどう動いてるのか気になりましたので調べてみました。
Service(Daemon)を作ること
サービスになるものは、Demon
とも呼ぶらしいです。少し調べてみると「システムに常住しながら、とある状態になると自動動作するプログラム」「周期的なサービス要請を処理するために実行され続けるプログラム」「バックグラウンドで動いている」などという定義がありました。これでだいたいどんな性格のプログラムのことを指しているかがわかりますね。Spring
やNode.js
などで作られたWebサーバプログラムとかがこの定義に該当するでしょう。
それでは早速、そのサービス(もしくはデーモン)を実現するためにはどうしたらいいかを説明していきたいと思います。準備するものは大きくサービスとして登録したいプログラム、そのプログラムをどんなサービスにするかを書いたサービスの設定ファイル、コマンドでのサービス登録と実行などがあります。仕事ではSpring Boot
で作られたプログラムをサービスにしていますが、これの場合は実行するためのシェルスクリプトの設定や外部ファイルの参照なども必要となるので比較的簡単な、Node.js
を使ったプログラムを例として使います。
Node.js
の場合、$ node /node/index.js
みたいな簡単なコマンドで実行できますね。これをサービスに登録する場合は以下のようになります。
serviceファイルを作成する
まずはサービスとしてどんな動きをしてほしいか、どんな名前でサービス化するかなどを記述したファイルを作ります。
vi /etc/systemd/system/NodeServer.service
それでは以下のような形で中を書いていきます。
# サービスとしての設定
[Unit]
Description = NodeServer # この名称でサービスが登録される
After = syslog.target network.target # システム起動時の実行の優先順位(syslogとnetworkの後に実行する)
# 実行するプログラムの設定
[Service]
Type = simple # 動作の様子を決める・デフォルトはsimple
ExecStart = /usr/bin/node /node/index.js # node /node/index.jsと同じだがシンボリックリンクなしで記述する
Restart = on-failure # 起動に失敗すると再実行
User = nodeservice # 実行するユーザー、権限に注意!
# シンボリックリンクや別名などの設定
[Install]
WantedBy = multi-user.target # どこのフォルダにシンボリックリンクを作るかを指定、これが一般的らしい
他にも様々なオプションがありますが、基礎的な情報はこのくらいかと。テストのために作成しておいたNode.jsでのWebサーバーはこれで動きました。(と言ってもHello Node.js!
を出力するだけの簡単なものですが…)
実際仕事で扱っていたJavaアプリケーションは、PID1を管理するスクリプトの指定や、止める時実行するシェルスクリプトの指定もありました。例えばExecStop
を使うとサービスを修理するために実行したいコマンドを、ExecReload
を使うとリロードする時に実行したいコマンドを書くことができるらしいです。シェルスクリプトから実行する場合や状態が変わった時何かの措置が必要な場合に考えられるオプションですね。
このようにserviceファイルを作成したら、次はプロセスをシステムサービスに登録して実行します。
Enable & Start Service
先にservice
というコマンドを言及しましたが、CentOS7からはsystemctl
を使うらしいです。2実際、CentOS7ではservice
だけではシステムサービスの登録は不可能なのでsystemctl
を使うことになります。このコマンドでできることはサービスの登録や解除、実行と停止、再実行などがあります。まずサービスをシステムサービスに登録すると、システムが起動する時に実行されるようになります。
Windowsに例えると、「スタートアップ」にプログラムを登録したり解除することと、タスクマネージャーによってプロセスを管理するような機能が一緒になっている感じですね。
こちらはコマンドだけなので、簡単に紹介します。
# システムサービスとして登録
$ systemctl enable NodeServer
$ systemctl start NodeServer
# 実行されているサービスの中で検索する
$ systemctl list-units | grep NodeServer
# serviceファイルを修正した場合はリロードする
$ systemctl daemon-reload
# 停止と再起動
$ systemctl stop NodeServer
$ systemctl restart NodeServer
# サービスの状態を確認する
$ systemctl status NodeServer
# システムサービスから解除
$ systemctl enable NodeServer
# 実行に問題があった場合は以下のコマンドでサービスのログを参照できる
$ journalctl -xe
サービスの登録や解除はできなくても、service
コマンドでの制御もできます。ただいずれは消えるかもしれないコマンドなので、なるべくsystemctl
に慣れた方が良いかもしれませんね。
# serviceでできること
$ service NodeServer start
$ service NodeServer status
$ service NodeServer stop
$ service NodeServer restart
これで私の作った簡単なNode.jsのWebサーバーがシステムサービスとなって、止まることなく動くようになりました。めでたしめでたし。他にも何か自動化のプログラムを作ることになったら有効活用できそうな知識ではないのかと思います。
それでは今回のポストはここまで。また会いましょう!