Making Ghost run forever by Systemd

TL;DR

ghost.service設置

$ cat /etc/systemd/system/ghost.service
[Unit]
Description = Ghost blog  
After = network.target

[Service]
Type = simple  
ExecStart = /usr/bin/node /var/www/ghost/index.js  
WorkingDirectory = /var/www/ghost  
KillMode = process  
Restart = always  
User = root  
Group = root  
Environment = "NODE_ENV=production"

[Install]
WantedBy = multi-user.target  

service設定をsystemdに反映させる

sudo systemctl daemon-reload  

自動起動ON

sudo systemctl enable ghost  

スタート

sudo systemctl start ghost  

SystemdでGhostを永続的に動かす

このブログはGhostというblogプラットフォームで動いていて、GhostはNode.jsで書かれている。Node.jsなのでblogのような、起動し続ける必要のあるアプリの場合は永続化しなければならない。そこで、今まではGhostドキュメントにもあるforeverを使って永続化していた。

が、foreverを使った運用がいろいろ面倒になってきた。具体的には

  • 起動コマンドが長い、覚えられない(NODE_ENV=production forever --minUptime=1000 --spinSleepTime=1000 -a -l /var/log/forever/forever.log --pidFile /var/run/forever/forever.pid restart /var/www/ghost/index.js)
    • 起動シェルスクリプト作れば良い?このcentos7時代に!?
  • 自動起動、プロセス落ちたときの対応は別途用意しないといけない
  • ログの吐き出し先とか、ログのローテートをお世話しないといけない
  • というかSystemdという仕組みが初めからあるんだからそれ使うべきな気がする

と思い始めた。

そこでh2o.serviceとかsystemd.serviceとかSystemd入門(1) - Unitの概念を理解する - めもめもとかを参考にとりあえず以下のようなghost.serviceを書いた。

[Unit]
Description = Ghost blog  
After = network.target

[Service]
Type = simple  
ExecStart = /usr/bin/node /var/www/ghost/index.js  
WorkingDirectory = /var/www/ghost  
KillMode = process  
Restart = always  
User = root  
Group = root  
Environment = "NODE_ENV=production"

[Install]
WantedBy = multi-user.target  

Unitセクションでは

  • Description: このUnitの説明
  • After: 依存関係の設定(networkサービス起動後に起動)

Serviceセクションでは

  • Type: 起動完了の条件の指定(simpleはフォアグラウンドでコマンドの実行を継続する場合使う)
  • ExecStart: 起動したいコマンド
  • WorkingDirectory: 実行する場所の指定(上の場合ExecStartをフルパスで書いているので実行場所の指定いらないかも)
  • KillMode: stop実行した時残ったプロセスの処理方法(processだとSIGTERM/SIGKILLで停止させる)
  • Restart: プロセスが何らかの要因で落ちた場合の挙動の指定(alwaysの場合どのような場合でも再起動を試みる)
  • User/Group: そのまんま。指定したユーザー、グループで実行する
  • Environment: 環境変数の指定

Installセクションでは

  • WantedBy: 自動起動の設定(multi-user.targetはランレベル3に相当)

みたいな感じ。まだあんまりちゃんと理解していない部分が多いので後でドキュメント読む。systemdとっつきにくくないっすか!?ちょっと好きになってはきたけど...。

Ansibleで書く

やり方が分かったところで、ansibleで構成管理しているのでansibleに起こした。こんな感じ。

roles/common/tasks/main.yml

- name: ghost systemd service file added
  copy: src="ghost.service" dest="/etc/systemd/system/ghost.service" owner=root group=root mode=0644
  notify:
    - reload daemon
  tags: ghost

- name: ghost is enabled
  service: name=ghost state=running enabled=yes
  tags: ghost

roles/common/handlers/main.yml

- name: reload daemon
  shell: systemctl daemon-reload

roles/common/files/ghost.service

[Unit]
Description = Ghost blog  
After = network.target

[Service]
Type = simple  
ExecStart = /usr/bin/node /var/www/ghost/index.js  
WorkingDirectory = /var/www/ghost  
KillMode = process  
Restart = always  
User = root  
Group = root  
Environment = "NODE_ENV=production"

[Install]
WantedBy = multi-user.target  

適用&掃除

本番適用前、念のためforever stop /var/www/ghost/index.jsした後ansible-playbookコマンド実行。うまく動いているのを確認して移行は完了!

そして忘れないうちに使わなくなったファイルを掃除!ansibleのコードから消すのは簡単だけど、サーバーに残った残骸はしょうがないので手で掃除してく...。

$ sudo npm uninstall -g forever
unbuild forever@0.15.2  
$ sudo rm /etc/logrotate.d/forever
$ sudo rmdir /var/run/ghost/
$ sudo rm -rf /var/log/forever/

ほかにも生き残りファイルがあるかもしれない。見つけ次第、しばく。

感想

  • Ghostの管理がSystemd任せになってすごい楽になった!
  • なにげにSysyemdの起動スクリプト1から書いたの初だった...
  • 起動スクリプト、SysVinitよりだいぶ書きやすいナリ
  • Systemdは機能多すぎィなのでもっと使い込んで慣れていくぞ!!
  • Systemdはなんでもやりすぎという意見もある。私も最初はあまり好きじゃなかったけど今はそうでもない。迎合していくぞ!!

参考リンク