hidemium's blog

日々学んだことをアウトプットする。

ChefからDockerコンテナ内の起動プロセスを追加する

Dockerはコンテナの起動時に、initプロセスではなく、CMDで指定したプロセスから実行されるため、通常は1つしかプロセスを起動できない制約があります。しかし、プロセス管理ツールのsupervisorを使用すれば、コンテナ内で複数のプロセスを起動することができます。

前回、Dockerfileを使ってsupervisorからsshdとMySQLのプロセスを起動させることを確認できました。そこで、Chefを使ってsupervisorに起動プロセスの定義を追加することで、コンテナ内の起動プロセスを追加できないか試してみました。

構成

Ubuntu 12.04: サーバ構築対象
Ubuntu 12.04はDocker 0.10上で動作しています。

Dockerfileの作成

Dockerfileの構成は以下の通りです。

sshd
├──Dockerfile
├──sources.list      ... ミラーサイト一覧
├──id_rsa.pub        ... Chef workstationの公開鍵
└──supervisord.conf  ... supervisorの定義ファイル

Dockerfileを以下のように作成します。
後でMySQLを起動できるように、「RUN dpkg-divert~」を設定しています。
supervisorの定義ファイルは、/etc/supervisor/conf.d/配下にも置くことができるので、「RUN mkdir -p /etc/supervisor/conf.d/」でディレクトリを作成しています。

$ vi Dockerfile
FROM ubuntu:12.04

MAINTAINER hidemium

# Ubuntu update
ADD sources.list /etc/apt/sources.list
RUN apt-get -y update

# Hack for initctl not being available in Ubuntu
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -sf /bin/true /sbin/initctl

# ssh install
RUN apt-get -y install openssh-server
RUN apt-get -y install python-setuptools
RUN apt-get clean
RUN easy_install supervisor

RUN mkdir /var/run/sshd
RUN echo 'root:root' | chpasswd

# sshd config
ADD id_rsa.pub /root/id_rsa.pub
RUN mkdir /root/.ssh/
RUN mv /root/id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 700 /root/.ssh
RUN chmod 600 /root/.ssh/authorized_keys
RUN sed -i -e '/^UsePAM\s\+yes/d' /etc/ssh/sshd_config

# supervisor config
RUN mkdir -p /var/log/supervisor
RUN mkdir -p /etc/supervisor/conf.d/
ADD supervisord.conf /etc/supervisord.conf

# Expose ports.
EXPOSE 22

# Define default command.
CMD ["supervisord", "-n"]

supervisorの定義ファイルは、デフォルトの定義にsshdの起動を追加したものにしています。

$ vi supervisord.conf
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10           ; (num of main logfile rotation backups;default 10)
loglevel=info                ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false               ; (start in foreground if true;default false)
minfds=1024                  ; (min. avail startup file descriptors;default 1024)
minprocs=200                 ; (min. avail process descriptors;default 200)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[include]
files = /etc/supervisor/conf.d/*.conf

[program:sshd]
command=/usr/sbin/sshd -D
autostart=true
autorestart=true

Chefレシピの作成例

それでは、Chef側について見ていきます。
今回、apache2とMySQLについて試してみました。

apache2

cookbookの構成は以下の通りです。

site-cookbook
└──apache2
      └──recipes
      │    └──default.rb
      └──templates
            └──default
                  └──apache2.conf.erb ... supervisorの定義ファイル

recipesファイル(抜粋)は以下の通りです。

Chef側では、templateファイルとしてsupervisorの定義ファイルを用意します。
定義ファイルを配置後、supervisorctl reloadコマンドによりプロセスを起動します。
プログラムによっては、supervisorctl <サービス名> startコマンドでも起動できない場合があるため、supervisorctl reloadコマンドでsupervisorに定義されたプロセスをすべて再読み込みさせています。
ChefはSSH接続で実行していますが、supervisorの再読み込みでSSHが切断されることはありませんでした。

$ vi site-cookbooks/apache2/recipes/default.rb
:
package "apache2" do
    action :install
end

template "apache2.conf" do
    path "/etc/supervisor/conf.d/apache2.conf"
    owner "root"
    group "root"
    mode "0644"
    source "apache2.conf.erb"
end

bash "supervisorctl reload" do
    code   "supervisorctl reload"
    action :run
end
:

templateファイルは以下の通りです。

$ vi site-cookbooks/apache2/templates/default/apache2.conf.erb
[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && exec /usr/sbin/apache2 -DFOREGROUND"

MySQL

cookbookの構成は以下の通りです。

site-cookbook
└──mysql
      └──recipes
      │    └──default.rb
      └──templates
            └──default
                  └──mysqld.conf.erb ... supervisorの定義ファイル

MySQLを追加する場合のrecipesファイル(抜粋)は以下の通りです。

上記と同じ構成となっています。
supervisorctl reloadコマンド後に、sleepを入れいるのは、mysqldの起動に時間がかかるため、mysqlコマンドを実行しても接続できないためです。

$ vi site-cookbooks/mysql/recipes/default.rb
:
package "mysql-server" do
    action :install
end

template "mysqld.conf" do
    path "/etc/supervisor/conf.d/mysqld.conf"
    owner "root"
    group "root"
    mode "0644"
    source "mysqld.conf.erb"
end

bash "supervisorctl reload" do
    code   "supervisorctl reload; sleep 3"
    action :run
end
:

templateファイルは以下の通りです。

$ vi site-cookbooks/mysql/templates/default/mysqld.conf.erb
[program:mysqld]
command=/usr/bin/mysqld_safe

おわりに

supervisorを使って複数のプロセスを起動させる場合、プロセスごとにDockerfileを用意する必要があり、効率が良くないなと考えていました。今回、Chefからコンテナの起動プロセスを追加することができたため、SSH接続ができるところまでをDockerfile、それ以降をChefといったプロビジョニングツールでといった役割分担ができそうです。