Dockerでは、基本的に内部で動かせるサービスは一つだけです。そして、そのサービスの実行がとまった瞬間に、コンテナは動作を停止します。
そのため、複数のサービスを実行する場合は、SupervisorやMonitなどのツールを使い、それをコンテナとすることで、複数のサービスを動かします。
イメージとしては、以下のサイトに書かれている図が詳しい。
本来Monitは、その名の通りさまざまなサービスの生死状態やファイルの存在、プロセスの状態などを監視して状況をメールやWeb UIで通知するツールです。定義ファイルにメールの宛先やメール送信条件、Web UIの表示ポート番号などを指定すると、監視ツールとして機能するようになっています。
今回はその機能は使わず、それぞれのサービスを動かすためのコンテナとして動作をしてもらいます。
Monit定義ファイルディレクトリ
とはいえ、こちらでやることはMonitのサービス定義ファイルを設定ディレクトリに配置するだけです。今回は前回紹介したNginxとPHP(hhvm)、MySQLを利用します。それぞれのインストール方法については追々。
Monitのサービス定義ファイルは、/etc/monit/
ディレクトリ配下に配置します。たぶん直下のmonitrcに直接書いても良いのでしょうが、conf.d
ディレクトリの配下(/etc/monit/conf.d/
)に配置するのが一般的っぽいです。
Nginx
Nginxについては、以下のような感じ。必須なのは最初の三行です。他、Monitにサーバの生死確認などの機能を持たせたいときは、四行目以降にいろいろ書いていく感じ。
check process nginx with pidfile /var/run/nginx.pid
start program = "/etc/init.d/nginx start" with timeout 60 seconds
stop program = "/etc/init.d/nginx stop"
if failed port 80 protocol http
then restart
group www-data
hhvm
HHVMについては次のような感じです。ただ、実際この設定でMonitのログファイルを見ると、プロセスの監視に失敗している模様です。動作はしているのでとりあえず無視していますがちゃんと使うなら直した方がよさげ。
check process hhvm with pidfile /var/run/hhvm/pid
group hhvm
start program = "/usr/sbin/service hhvm start" with timeout 60 seconds
stop program = "/usr/sbin/service hhvm stop"
if failed unixsocket /var/run/hhvm/hhvm.sock then restart
if mem > 400.0 MB for 1 cycles then restart
if 5 restarts with 5 cycles then timeout
MySQL
引き続きMySQLも。
check process mysql with pidfile /var/run/mysqld/mysqld.pid
start program = "/etc/init.d/mysql start"
stop program = "/etc/init.d/mysql stop"
if failed unixsocket /var/lib/mysql/mysql.sock then restart
if 5 restarts within 5 cycles then timeout
Dockerfile
そして最後にDockerfile。関連してるところだけの抜粋です。
# monit
RUN apt-get -y install monit
・・・
# monit
ADD monit/*.conf /etc/monit/conf.d/
・・・
CMD ["/usr/bin/monit", "-I", "-c", "/etc/monit/monitrc"]
とりあえずこれで動作します。最後monitの-Iオプションは、monitをフォアグラウンドで起動させるためのオプションです。このオプションを使わないと、monitがバックグラウンドで起動する→起動直後、フォアグラウンドなプロセスが全部終了したとみなされて、コンテナが終了してしまいます。
Monitの監視状態をモニタする
今回はMonitのWeb UI機能を使いませんので、それぞれのサーバの状況をMonitを使って閲覧する場合、CUIからの操作となります。
DockerでMonitを動かしている場合、以下記事で紹介されている方法でコンテナのシェルにログインして、コマンドを実行します。
で、そのコマンドは、そのままmonit
です。-v
オプションを使えばひとまず監視中のサービスの状態が表示されます。参考までに自分の表示結果を。
> monit -v
Runtime constants:
Control file = /etc/monit/monitrc
Log file = /var/log/outlog/monit.log
Pid file = /run/monit.pid
Id file = /var/lib/monit/id
State file = /var/lib/monit/state
Debug = True
Log = True
Use syslog = False
Is Daemon = True
Use process engine = True
Poll time = 120 seconds with start delay 0 seconds
Expect buffer = 256 bytes
Event queue = base directory /var/lib/monit/events with 100 slots
Mail from = (not defined)
Mail subject = (not defined)
Mail message = (not defined)
Start monit httpd = False
The service list contains the following entries:
Process Name = nginx
Group = www-data
Pid file = /var/run/nginx.pid
Monitoring mode = active
Start program = '/etc/init.d/nginx start' timeout 60 second(s)
Stop program = '/etc/init.d/nginx stop' timeout 30 second(s)
Existence = if does not exist 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Pid = if changed 1 times within 1 cycle(s) then alert
Ppid = if changed 1 times within 1 cycle(s) then alert
Port = if failed [localhost:80 [HTTP via TCP] with timeout 5 seconds and retry 0 time(s) 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Process Name = mysql
Pid file = /var/run/mysqld/mysqld.pid
Monitoring mode = active
Start program = '/etc/init.d/mysql start' timeout 30 second(s)
Stop program = '/etc/init.d/mysql stop' timeout 30 second(s)
Existence = if does not exist 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Pid = if changed 1 times within 1 cycle(s) then alert
Ppid = if changed 1 times within 1 cycle(s) then alert
Unix Socket = if failed [/var/lib/mysql/mysql.sock [protocol DEFAULT] with timeout 5 seconds and retry 0 time(s) 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Timeout = If restarted 5 times within 5 cycle(s) then unmonitor
Process Name = hhvm
Group = hhvm
Pid file = /var/run/hhvm/pid
Monitoring mode = active
Start program = '/usr/sbin/service hhvm start' timeout 60 second(s)
Stop program = '/usr/sbin/service hhvm stop' timeout 30 second(s)
Existence = if does not exist 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Pid = if changed 1 times within 1 cycle(s) then alert
Ppid = if changed 1 times within 1 cycle(s) then alert
Unix Socket = if failed [/var/run/hhvm/hhvm.sock [protocol DEFAULT] with timeout 5 seconds and retry 0 time(s) 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Memory amount limit = if greater than 409601kB 1 times within 1 cycle(s) then restart else if succeeded 1 times within 1 cycle(s) then alert
Timeout = If restarted 5 times within 5 cycle(s) then unmonitor
System Name = bbe50316866a
Monitoring mode = active
Dockerとのつきあい方
あちこち見ると、Dockerの使い方には二種類あるように感じます
- 一つのコンテナに全てのサービスを突っ込んで運用する
- 複数のコンテナに一つ一つサービスを突っ込んで、それを連携させて一つのシステムをなす
個人的には後者の方がコンテナ一つあたりの構成はシンプルになるし扱いやすくなるとは思います(複数のコンテナ間連携には、dockerの--linkオプションなどが利用できます)が、複数のコンテナを協働させる仕組みが必要になるため、総合的な運用コストが高まります。
また、あくまで主役はコンテナではなくその中で動くアプリですので、環境を維持しつつアプリをテストしていくというDocker本来の用途から離れてしまうように感じます。
そのため、今回は前者の方法でコンテナを作成・運用する方法として、Monitを使う方法をためしてみました。