hidemium's blog

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

Figで複数のDockerコンテナをまとめて起動して、Redmineを構築する

以前sameersbn/redmineというRedmineのイメージを試してみましたが、アプリケーションとMySQLでコンテナを分離した場合、複数のコンテナ起動や、DBの作成、マイグレーションの実行が個別に必要となり手間がかかっていました。

そこで今回は、マルチコンテナ構成ツールであるFigを用いて、アプリケーションとMySQLでコンテナを分離した場合でも、Redmineを一括起動するDockerfileを書いてみました。

構成

  • Ubuntu 14.04
  • Docker 1.0.0
  • Fig 0.5.2

コンテナとDockerfileの割り振り

Redmineの起動のためには、DBサーバ、アプリケーションサーバ、Webサーバが必要となります。これらを1つのコンテナの中で動かす事も出来ますが、安定性等を考え、以下のように2つのコンテナで動かす事にします。

また、Dockerfileのメンテナンス性を考え、APコンテナはRubyまでを導入したイメージと、そのイメージをベースにRedmineを導入したイメージに分離させています。Redmineの設定を変更したい場合、Rubyのインストールを省くことができます。

  • DBイメージ  : 共通設定、MySQL
  • Rubyイメージ : 共通設定、Ruby
  • APイメージ  : Redmine、Nginx、Unicorn (Rubyイメージをベースに差分ビルド)

Dockerfile

Dockerfileを以下のように作成します。
ソースコードこちら@githubで公開しています。

Dockerfileの構成

Dockerfileの構成は以下の通りです。
このほかにもコンテナに追加する定義ファイルもいくつかあります。

fig-redmine 
├──fig.yml           ... Figのコンテナ起動定義ファイル
├──mysql
│ └──Dockerfile      ... MySQL用のDockerfile
├──ruby     
│ └──Dockerfile      ... Ruby用のDockerfile 
└──redmine     
   └──Dockerfile      ... Redmine用のDockerfile 

MySQL

$ vi mysql/Dockerfile
FROM ubuntu:14.04

MAINTAINER hidemium

# install basic package
ADD sources.list /etc/apt/sources.list
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && \
    apt-get -y install \
      mysql-server \
      openssh-server \
      supervisor && \
      apt-get clean

# root user
RUN echo 'root:root' | chpasswd

# install sshd
RUN mkdir -p /root/.ssh /var/run/sshd
ADD id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 700 /root/.ssh
RUN chmod 600 /root/.ssh/authorized_keys
RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config
RUN sed -ri 's/#UsePAM no/UsePAM no/g' /etc/ssh/sshd_config
RUN sed -ri 's/PermitRootLogin without-password/PermitRootLogin yes/g' /etc/ssh/sshd_config

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

# install MySQL
RUN rm -rf /var/lib/mysql/mysql
RUN rm -rf /var/lib/apt/lists/*
ADD my.cnf /etc/mysql/my.cnf
ADD mysql-listen.cnf /etc/mysql/conf.d/mysql-listen.cnf
RUN chown -R mysql:mysql /var/lib/mysql
RUN chmod 700 /var/lib/mysql

# initialize MySQL 
RUN mysql_install_db --user mysql > /dev/null
RUN /usr/bin/mysqld_safe & \
    sleep 3 && \
    mysqladmin -u root password "root" && \
    mysql -u root -proot -e "GRANT ALL ON *.* TO 'root'@'172.17.%.%' IDENTIFIED BY '' WITH GRANT OPTION;" && \
    mysql -u root -proot -e "CREATE USER 'redmine'@'%.%.%.%' IDENTIFIED BY 'redmine';" && \
    mysql -u root -proot -e "CREATE DATABASE IF NOT EXISTS redmine_production DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;" && \
    mysql -u root -proot -e "GRANT ALL PRIVILEGES ON redmine_production.* TO 'redmine'@'%.%.%.%';" && \
    mysql -u root -proot -e "FLUSH PRIVILEGES;"

# expose ports
EXPOSE 22 3306 

# define default command.
CMD ["supervisord", "-n"]
  • MySQLのDockerfile内でRedmine用のユーザ作成、データベース作成を定義しています。

Ruby

$ vi ruby/Dockerfile
FROM ubuntu:14.04

MAINTAINER hidemium

# install basic package
ADD sources.list /etc/apt/sources.list
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && \
    apt-get -y install \
    openssh-server \
    supervisor \
    build-essential \
    curl \
    unzip \
    git-core \
    subversion \
    mercurial \
    libcurl4-openssl-dev \
    libreadline-dev \
    libssl-dev \
    libxml2-dev \
    libxslt1-dev \
    libyaml-dev \
    zlib1g-dev && \
    apt-get clean && \
    curl -O http://ftp.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz && \
    tar -zxvf ruby-2.1.2.tar.gz && \
    cd ruby-2.1.2 && \
    ./configure --disable-install-doc && \
    make && \
    make install && \
    cd .. && \
    rm -r ruby-2.1.2 ruby-2.1.2.tar.gz && \
    echo 'gem: --no-document' > /usr/local/etc/gemrc

# root user
RUN echo 'root:root' | chpasswd

# install sshd
RUN mkdir -p /root/.ssh /var/run/sshd
ADD id_rsa.pub /root/.ssh/authorized_keys
RUN chmod 700 /root/.ssh
RUN chmod 600 /root/.ssh/authorized_keys
RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config
RUN sed -ri 's/#UsePAM no/UsePAM no/g' /etc/ssh/sshd_config
RUN sed -ri 's/PermitRootLogin without-password/PermitRootLogin yes/g' /etc/ssh/sshd_config

# install supervisor
RUN mkdir -p /var/log/supervisor /etc/supervisor/conf.d
ADD sshd.conf /etc/supervisor/conf.d/sshd.conf

# install bundler
RUN \
  echo 'gem: --no-rdoc --no-ri' >> /.gemrc && \
  gem install bundler
  • rbenvを使ってRubyをインストールする方法もありますが、複数バージョンのRubyを使い分ける必要はないため、ソースからのビルドしています。DockerはImmutableなので、異なるバージョンのRubyを使用したい場合は、コンテナを作り直せばいいという考えです。
  • Redmineに必要なパッケージもこちらでインストールしておきます。

Redmine

$ vi redmine/Dockerfile
FROM ubuntu-ruby:14.04

MAINTAINER hidemium

ENV REDMINE_VERSION 2.5-stable
ENV REDMINE_HOME /var/redmine
ENV SETUP_DIR /app/setup
ENV RAILS_ENV production

# install basic package
RUN apt-get update && \
    apt-get -y install \
      nginx \
      imagemagick \
      libmagickcore-dev \
      libmagickwand-dev \
      libmysqlclient-dev

# install Redmine
RUN git clone -b ${REDMINE_VERSION} https://github.com/redmine/redmine.git ${REDMINE_HOME}
WORKDIR /var/redmine 
RUN echo 'gem "mysql2", "~> 0.3.11"' >> Gemfile
RUN echo 'gem "unicorn"' >> Gemfile
RUN bash -l -c 'bundle install --without development test'

# install plugins
RUN mkdir -p ${SETUP_DIR}
ADD setup/themes_install ${SETUP_DIR}/themes_install
ADD setup/plugins_install ${SETUP_DIR}/plugins_install
RUN chmod 755 ${SETUP_DIR}/*
RUN ${SETUP_DIR}/themes_install
RUN ${SETUP_DIR}/plugins_install

# add config file
ADD config/database.yml ${REDMINE_HOME}/config/database.yml
ADD config/configuration.yml ${REDMINE_HOME}/config/configuration.yml
ADD config/unicorn.rb ${REDMINE_HOME}/config/unicorn.rb
RUN mkdir ${REDMINE_HOME}/public/plugin_assets

# install nginx
ADD redmine.conf /etc/nginx/conf.d/redmine.conf
RUN rm -f /etc/nginx/sites-enabled/default
RUN ln -s /etc/nginx/sites-available/redmine /etc/nginx/sites-enabled/redmine
RUN echo "daemon off;" >> /etc/nginx/nginx.conf

# add supervisor config file 
ADD unicorn.conf /etc/supervisor/conf.d/unicorn.conf
ADD nginx.conf /etc/supervisor/conf.d/nginx.conf

# expose ports
EXPOSE 22 80

# define default command
CMD bash -l -c 'bundle install --without development test' && \
    bash -l -c 'bundle exec rake generate_secret_token' && \
    mv plugins plugins-1 && \
    bash -l -c 'bundle exec rake db:migrate RAILS_ENV=${RAILS_ENV}' && \
    mv plugins-1 plugins && \
    bash -l -c 'bundle exec rake redmine:plugins:migrate RAILS_ENV=${RAILS_ENV}' && \
    supervisord -n
  • 「FROM ubuntu-ruby:14.04」で先に作成したRubyイメージをベースにしています。
  • RedmineソースコードGitHubリポジトリから取得しています。
  • 一度bundle installすれば、Dockerのキャッシュが使われるので再実行が短縮できます。そのため、最初にbundle installを実行しています。ただし、RedmineのGemfileは、database.yml内の情報からデータベースの種類を判別し、必要なgemを選択しています。database.ymlを先に追加すると処理が失敗するため、「'gem "mysql2", "~> 0.3.11"' >> Gemfile」で追加しています。
  • テーマとプラグインのインストールは、「themes_install」と「plugins_install」というシェルから実行しています。
  • RedmineをNginx + Unicornで動作させる設定を行っています。
  • CMDでマイグレーションコマンドを定義することで、Redmineコンテナ起動時にマイグレーションを自動で行うことができます。

Dockerfileのビルド

以下のコマンドにより、Dockerfileの取得とDockerイメージのビルドを行います。

$ git clone https://github.com/hideakihal/docker-fig-redmine.git
$ cd docker-fig-redmine
$ sudo docker build -t ubuntu-mysql:14.04 mysql
$ sudo docker build -t ubuntu-ruby:14.04 ruby
$ sudo docker build -t ubuntu-redmine:14.04 redmine

Fig

Dockerイメージの用意ができたため、ここからはFigを使用方法を説明します。

インストール

FigはPythonで書かれているので、パッケージ管理をするpipをインストールしておきます。pipを使ってFigをインストールします。

$ sudo apt-get install -y python-pip python2.7-dev
$ sudo pip install -U fig 

fig.yml

複数のコンテナをどのように起動するかはfig.ymlというファイルで定義します。
fig.ymlは、以下のような構成になり、起動するDockerイメージやポート番号、コンテナ間通信の設定などが行えます。
fig.ymlのリファレンスは公式サイトにあります。

$ vi fig.yml
db:
  image: ubuntu-mysql:14.04
  ports:
    - 22
    - 3306
web:
  image: ubuntu-redmine:14.04
  ports:
    - 22
    - 80
  environment:
    - DB_HOST=db_1
  links:
    - db

上記の設定を通常のDockerコマンドで表現すると以下のようになります。
毎回以下のようなコマンドを打つのは大変なので、とても簡単できるようになります。

$ sudo docker run -d -p 22 -p 3306 --name figredmine_db_1 ubuntu-mysql:14.04
$ sudo docker run -d -p 22 -p 80 -e "DB_HOST=db_1" --link figredmine_db_1:db_1 --name figredmine_web_1 ubuntu-redmine:14.04

環境変数について

fig.yml内で、DB_HOSTという環境変数を定義しています。これは、APコンテナのdatabase.ymlにDBコンテナをホスト名を渡すために行っています。
当初は、以下のようにlinkオプションの起動でDBコンテナのIPアドレスを取得していましたが、DBコンテナ名が変わるとDockerイメージのリビルドが必要でした。

$ vi /var/redmine/config/database.yml
production:
  adapter: mysql2
  database: redmine_production
  host: <%= ENV.fetch('DB_1_PORT_3306_TCP_ADDR') %>
  port: <%= ENV.fetch('DB_1_PORT_3306_TCP_PORT') %>

Figのissueを見てみると、linkオプションにより/etc/hostsも変更されることが分かり、以下のように設定を修正しました。

$ vi /var/redmine/config/database.yml
production:
  adapter: mysql2
  database: redmine_production
  host: <%= ENV.fetch('DB_HOST') %>
  port: 3306
...

Dockerの公式ドキュメントを見てみると、コンテナのポート番号やIPをもった環境変数と/etc/hostsが追加されることが記載されています。
実際のコンテナでも確認したところ、確かにコンテナの環境変数と/etc/hostsが更新されていることが分かりました。

root@be9520e48d64:~# env
DB_1_PORT_3306_TCP_ADDR=172.17.3.117
DB_1_PORT_3306_TCP_PORT=3306
...

ホスト名が、db_1のように「コンテナ名_数字」となっていますが、これはFigの仕様で同じDockerイメージをスケールさせる場合に末尾の数字がカウントアップされていきます。今回はスケールさせないため、末尾の数字は1として表示されます。

root@be9520e48d64:~# cat /etc/hosts
172.17.3.117    db_1
172.17.3.117    figredmine_db_1
...

複数コンテナの一括起動

以下のコマンドを実行し、複数コンテナを一括起動します。

$ sudo fig up -d

起動の進捗を確認する場合は、以下のコマンドを使用できます。

$ sudo fig logs
Attaching to figredmine_db_1, figredmine_web_1
web_1 | Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
web_1 | installing your bundle as root will break this application for all non-root
web_1 | users on this machine.
web_1 | Your Gemfile lists the gem vcard (~> 0.2.8) more than once.
web_1 | You should probably keep only one of them.
web_1 | While it's not a problem now, it could cause errors if you change the version of just one of them later.
web_1 | Your Gemfile lists the gem capybara (~> 2.1.0) more than once.
web_1 | You should probably keep only one of them.
web_1 | While it's not a problem now, it could cause errors if you change the version of just one of them later.
...

コンテナの起動を確認する場合、以下のコマンドを使用できます。APとDBのコンテナが起動していることが分かります。
docker psコマンドより情報がまとまってシンプルに表示できます。

$ sudo fig ps
    Name                   Command               State               Ports
---------------------------------------------------------------------------------------
figredmine_db_1    supervisord -n                Up      49177->22/tcp, 49178->3306/tcp
figredmine_web_1   /bin/sh -c bash -l -c '...    Up      49179->22/tcp, 49180->80/tcp

上記で、80番ポートにフォワードされているポート番号を確認し、ブラウザから、「http://localhost:ポート番号」にアクセスし、Redmineのログイン画面が表示されることを確認します。

本番環境で動作させる場合

fig upコマンドを再実行すると、既存のコンテナは削除され、コンテナが再作成されます。開発中は不要なコンテナの削除を自動的に行ってくれるため便利ですが、本番環境で現行のコンテナを残して、Blue-Green Deploymentを行いたい場合は不便です。また、開発環境と本番環境でコンテナの起動設定を変えたい場合などがあるかと思います。
そこで、fig upコマンドでは、-fオプションによりデフォルトパスのfig.yml以外のfig.ymlを指定することができます。

以下のように、fig-production.yml(任意)を作成します。
上記のfig.ymlとは異なり、コンテナ名の変更や、データの永続性のため共有ディレクトリの設定を追加しています。また、DB_HOSTのコンテナ名も合わせて変更します。

$ vi fig-production.yml
dbBlue:
  image: ubuntu-mysql:14.04
  ports:
    - 22
    - 3306
  volumes:
    - /path/in/host:/var/lib/mysql
webBlue:
  image: ubuntu-redmine:14.04
  ports:
    - 22
    - 80
  environment:
    - DB_HOST=dbBlue_1
  volumes:
    - /path/in/host:/var/redmine/files
  links:
    - dbBlue
  • コンテナ名は記号は不可で、英数字しか使用できないようです。

以下のコマンドを実行し、本番環境用のコンテナを起動します。

$ sudo fig -f fig-production.yml up -d

おわりに

Figによって、複雑になったdocker runコマンドを定義ファイルにまとめることができ、複数コンテナが連携するDockerfileの開発がとても楽になりました。さらに、複数コンテナのDockerfileとFigの定義ファイルを1つのフォルダで管理できるため、リポジトリもとても見やすくなったかと思います。

Figを開発したOrchard社は、Docker社は買収されたようなので、今後の展開に注目しています。

DockerでPlay Frameworkアプリケーションをデプロイする

DockerによるWebアプリのデプロイ方法をいくつか調べてみると、ビルド時間の短縮やメンテナンス性から、インフラレイヤーとアプリケーションレイヤーに分けて差分ビルドする方法が良さそうだったので、Play Frameworkアプリでデプロイを試してみました。

構成

  • Ubuntu 12.04
  • Docker 1.0.0
  • Play Framework 2.2.3

インフラレイヤー

Java及びPlay Frameworkの実行環境までを構築します。

Dockerfileの構成

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

java
├──Dockerfile
├──id_rsa.pub            ... 公開鍵
├──play.sh               ... 環境変数の定義ファイル
├──sources.list          ... ミラーサイト一覧
└──supervisord.conf    ... supervisorの定義ファイル

Dockerfile

Dockerfileを以下のように作成します。

$ cd java
$ vi Dockerfile
FROM ubuntu:12.04

MAINTAINER hidemium

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

# 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

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

# Install Java
RUN apt-get install -y software-properties-common python-software-properties
RUN add-apt-repository ppa:webupd8team/java
RUN apt-get update
RUN echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y oracle-java7-installer

# Install Play Framework
ENV PLAY_VERSION 2.2.3
RUN apt-get install -y unzip
RUN wget http://downloads.typesafe.com/play/$PLAY_VERSION/play-$PLAY_VERSION.zip
RUN unzip play-$PLAY_VERSION.zip
RUN mv play-$PLAY_VERSION /usr/local
ADD play.sh /etc/profile.d/play.sh
RUN sh /etc/profile.d/play.sh

# Expose ports
EXPOSE 22 9000 9999

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

Dockerfileの中で、読み込みを行っているファイルは以下の通りです。

sources.list

日本のミラーサイトを指定しています。

$ cat sources.list
deb http://jp.archive.ubuntu.com/ubuntu precise main restricted
deb-src http://jp.archive.ubuntu.com/ubuntu precise main restricted
deb http://jp.archive.ubuntu.com/ubuntu precise-updates main restricted
deb-src http://jp.archive.ubuntu.com/ubuntu precise-updates main restricted

play.sh

Java及びPlay Frameworkの環境変数を設定しています。

export JAVA_HOME=/usr/lib/jvm/java-7-oracle
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
export PLAY_HOME=/usr/local/play-2.2.3
export PATH=$PATH:$PLAY_HOME

supervisord.conf

supervisorの設定とsshdの起動を定義しています。

$ cat 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

Dockerイメージのビルド

以下のコマンドで、Dockerfileからビルドし、ベースとなるDockerイメージを作成します。

$ sudo docker build -t ubuntu-java:12.04 .

アプリケーションレイヤー

上記のDockerイメージをベースに、Play Frameworkのアプリケーションをコンテナに追加し、本番モードで起動します。
なお、データベースはPlay Frameworkに内蔵しているH2 databaseを使用する想定です。

Dockerfileの構成

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

play
├──Dockerfile
└──myapp           ... Play Frameworkアプリケーションを格納したディレクトリ

上記の「myapp」には、git cloneなどでデプロイするPlay Frameworkアプリケーションをコピーしておきます。手元にPlay Frameworkアプリケーションがない場合は、ダウンロードしたPlay Frameworkのパッケージ内にsampleプログラムが格納されているため、こちらを使用することも可能です。

Dockerfile

Play Frameworkは、Play Frameworkのバージョンに依存しないように、単独で起動が可能なdistと呼ばれる配布パッケージを作成することができます。*1
以下では、stage向けとdist向けのDockerfileを用意しました。

stageデプロイ用

Dockerfileを以下のように作成します。

$ cd play
$ vi Dockerfile
FROM ubuntu-java:12.04

MAINTAINER hidemium

RUN mkdir /myapp
WORKDIR /myapp
ADD myapp /myapp
RUN bash -l -c 'play clean compile stage'
RUN rm target/universal/stage/bin/*.bat

EXPOSE 22 9000
CMD target/universal/stage/bin/$(ls target/universal/stage/bin) -DapplyEvolutions.default=true & \
    supervisord -n
  • 「FROM ubuntu-java:12.04」では、インフラレイヤーの箇所で作成したDockerイメージをベースにしています。
  • 「ADD myapp /myapp」では、Play Frameworkアプリケーションをコンテナに追加します。
  • 「ENTRYPOINT target/universal/stage/bin/$(ls target/universal/stage/bin) -DapplyEvolutions.default=true」では、Play Frameworkアプリケーションを本番モードで実行します。また、Evolutionsが自動実行されるようにオプションを指定しています。
distデプロイ用

Dockerfileを以下のように作成します。
distファイル(zipファイル)は、あらかじめ解凍し、myappをいうディレクトリにリネームしておきます。

$ cd play
$ vi Dockerfile
FROM ubuntu-java:12.04

MAINTAINER hidemium

RUN mkdir /myapp
WORKDIR /myapp
ADD myapp /myapp
RUN rm bin/*.bat

EXPOSE 22 9000
CMD bin/$(ls bin) -DapplyEvolutions.default=true & \
    supervisord -n
  • 「FROM ubuntu-java:12.04」では、インフラレイヤーの箇所で作成したDockerイメージをベースにしています。
  • 「ADD myapp /myapp」では、Play Frameworkアプリケーションをコンテナに追加します。
  • 「ENTRYPOINT bin/$(ls bin) -DapplyEvolutions.default=true」では、Play Frameworkアプリケーションを本番モードで実行します。distファイルは、stage用と比べてパスがシンプルになっています。

Dockerイメージのビルド

以下のコマンドで、Dockerfileからビルドし、Play FrameworkアプリケーションのDockerイメージを作成します。Play Frameworkの実行環境をベースに差分ビルドを行っているため、ビルド時間を短縮できます。

$ sudo docker build -t ubuntu-play:12.04 .

Play Frameworkアプリケーションのデプロイ

以下のコマンドで、Play Frameworkアプリケーションのコンテナを起動します。

$ sudo docker run -d -p 22 -p 9000 ubuntu-play:12.04

Dockerコンテナの9000番ポートにフォワードしているポート番号を確認します。

$ sudo docker port <コンテナID> 9000
0.0.0.0:<ポート番号>

「http://<サーバのIPアドレス>:<ポート番号>」にアクセスし、Play Frameworkアプリケーションに接続できることを確認します。

*1:distを使用する場合は、インフラレイヤーからPlay Frameworkをインストールしている箇所を削除してしまっても問題ありません。

Fluentd + Elasticsearch + Kibanaでログを可視化する

FluetndからElasticsearchへログを転送し、Kibanaでログを可視化できるか試してみました。転送するログはSensu Serverのログを使用しました。

構成

Ubuntu 12.04: Sensu Server インストール対象
Ubuntu 12.04: Elasticsearch
※上記はDocker 1.0.0上で動作しています。

Fluentdのインストール

Fluentdをインストールします。
Fluentdの安定版であり、Rubyのインタープリンタを含んだtd-agentを使用します。また、td-agentの必須パッケージのlibssl0.9.8が入っていなかったので合わせてインストールしています。

$ curl http://packages.treasuredata.com/GPG-KEY-td-agent | apt-key add -
$ echo "deb http://packages.treasuredata.com/precise/ precise contrib" > /etc/apt/sources.list.d/treasure-data.list
$ apt-get update
$ apt-get install -y libssl0.9.8
$ apt-get install -y ntp td-agent

Fluentdのバージョンを確認します。

$ /usr/lib/fluent/ruby/bin/fluentd --version
fluentd 0.10.50

supervisorでFluentdを起動する設定を追加します。

$ vi /etc/supervisor/conf.d/td-agent.conf
[program:td-agent]
command=/etc/init.d/td-agent start
autostart=true
autorestart=true

Elasticsearchへの連携

td-agent.confに以下の内容を追加し、転送するログとElasticsearchサーバの指定を行います。
今回はSensu Serverのログを転送する設定をしています。

$ vi /etc/td-agent/td-agent.conf
<source>
  type tail
  format json
  path /var/log/sensu/server.log
  tag ubuntu.sensu.server
  pos_file /tmp/sensu.pos
</source>

<match *.sensu.*>
  type elasticsearch
  include_tag_key true
  tag_key @log_name
  host <IPアドレス>
  port 9200
  logstash_format true
  flush_interval 10s
</match>

supervisorをリロードして、Fluentdを起動します。

$ supervisorctl reload

Kibanaの確認

「http://<サーバのIPアドレス>:<ポート番号>」からKibanaにアクセスし、トップページの「Logstash Dashboard」をクリックすると、Sensu Serverのログが転送されているのが確認できます。

KibanaでDockerコンテナのメトリクスを可視化する

前回、Sensuで取得したDockerコンテナのメトリクスをElasticsearchに転送することができたので、今回はメトリクスを可視化するためにKibanaの設定を行ってみました。なお、Dockerコンテナ内からはコンテナごとのリソース情報は見えず、ホスト全体のリソース情報しか見えないので、あくまで可視化の試みとなります。

構成

Ubuntu 12.04: Sensu Server、Sensu Client
Ubuntu 12.04: Elasticsearch
※上記はDocker 1.0.0上で動作しています。

Kibanaの設定

「http://<サーバのIPアドレス>:<ポート番号>」からKibanaにアクセスし、トップページの「Blank Dashboard」をクリックすると、まっさらダッシュボードが表示されるので、ここからスタートします。もちろん「Sample Dashboard」をベースに設定するのもよいかと思います。

QUERY

ダッシュボードの上部には「QUERY」というものが表示されています。ここにプロットしたいデータの抽出条件を定義します。
Sensuで取得したメトリクスは以下の形式となっていました。

Field Value
@timestamp 2014-07-15T17:03:20+00:00
_id a7f9a093d4945aab2be5b0d912193491
_index vmstat_metrics
_type vmstat_metrics
address 127.0.0.1
check_name vmstat_metrics
client elasticsearch
command /etc/sensu/plugins/system/vmstat-metrics.rb
key 62b7f582ef86.vmstat.cpu.system
occurrences 1
status 0
value 1

そこで、各コンテナのリソースごとのデータが見たかったので、以下のようにクエリを設定しました。

key:"62b7f582ef86.vmstat.cpu.system"

他のクエリの設定を含めると以下のようになりました。

f:id:hidemium:20140718135507p:plain

histogram

データの棒グラフや折れ線グラフをプロットするには、「histogram」というパネルを使用します。PanelタブのChart valueを「max」、Value Fieldを「value」に変更します。また、QueriesタブのQueriesを「seletcted」に変更し、上記で設定したクエリを選択します。
以下はhistogramの設定画面になります。

f:id:hidemium:20140718140234p:plain

histogramでプロットした画面が以下になります。
各リソースごとに、3つのDockerコンテナのリアルタイム24hデータをプロットしてみましたが、コンテナ内から取得したため、全てほぼ同じ値になっていることが分かります。

f:id:hidemium:20140718141153p:plain

設定の保存

パネルの設定は、ブラウザを閉じると消えてしまうので、ある程度パネルの設定ができたところで、右上のフロッピーアイコンをクリックしてセーブします。

パネルの紹介

他にも面白そうなパネルがあったので、いくつか紹介してみます。

table

データの内容を表示するパネルです。データの中身を確認することができます。

f:id:hidemium:20140718142654p:plain

terms

データ数をカウントしてくれるパネルです。円グラフ、表、棒グラフで表示することができます。

f:id:hidemium:20140718143616p:plain

sparklines

クエリごとの傾向をプロットしてくれるパネルです。全体をぱっと俯瞰したいときに便利かもしれません。

f:id:hidemium:20140718144120p:plain

stats

データの総数、最大値、最小値を表示するパネルです。リソースの最大値、最小値を見るのにいいかもしれません。

f:id:hidemium:20140718144852p:plain

おわりに

KibanaでSensuから取得したDockerコンテナのメトリクスを可視化することができました。
後は、ホストのcgroupからコンテナごとのリソース情報の取得ができれば、Dockerコンテナの監視の可視化が出来上がるかと思います。
また、Elasticsearchにメトリクスを転送できたので、現在リソースの負荷が高いコンテナだけを自動的にKibanaで表示することができれば便利そうだなと思っています。(できる方法ありましたら、どなたか教えてください。)

SensuからElasticsearchにメトリクスを転送する

前回、Elasticsearchのインストールができたため、今回はinokappa/sensu-handler-metrics-elasticsearchを用いて、SensuからElasticsearchにDockerコンテナのメトリクスを転送できるか試してみました。*1
なお、Sensuのメトリクスの可視化にはGraphiteがよく使われていますが、FluentdではElasticsearchが使われており、サーチエンジンを使い分けるのは大変そうだったので、Elasticsearchにしてみました。

構成

Ubuntu 12.04: Sensu Server、Sensu Client
Ubuntu 12.04: Elasticsearch
※上記はDocker 1.0.0上で動作しています。

Sensu Serverの設定

Sensu Server側でElasticsearchと連携するための設定を行います。
config.jsonに以下の設定を追加します。

$ vi /etc/sensu/config.json
{
  "rabbitmq": {
    "port": 5672,
    "host": "localhost",
    "user": "admin",
    "password": "admin",
    "vhost": "/"
  },
  "redis": {
    "host": "localhost",
    "port": 6379
  },
  "api": {
    "host": "localhost",
    "port": 4567
  },
  "dashboard": {
    "host": "localhost",
    "port": 8080,
    "user": "admin",
    "password": "admin"
  },
  "handlers": {
    "default": {
      "type": "pipe",
      "command": "true"
+   },
+   "elasticsearch-metrics": {
+     "type": "pipe",
+     "command": "/etc/sensu/handlers/metrics/elasticsearch-metrics.rb"
    }
  }
}

今回はvmstatの情報を転送するため、vmstatの監視設定を追加します。
typeは「metric」、handlersは「elasticsearch-metrics」とします。

$ vi /etc/sensu/conf.d/metrics/metric_vmstat.json
{
  "checks": {
    "vmstat_metrics": {
      "type": "metric",
      "handlers": ["elasticsearch-metrics"],
      "command": "/etc/sensu/plugins/system/vmstat-metrics.rb",
      "subscribers": [
        "default","sensu"
      ],
      "interval": 60
    }
  }
}

SensuのメトリクスをElasticsearchに転送するスクリプトを取得します。

$ cd /etc/sensu/handlers/metrics
$ wget https://raw.githubusercontent.com/inokappa/sensu-handler-metrics-elasticsearch/master/elasticsearch-metrics.rb
$ chmod 755 elasticsearch-metrics.rb

転送先のElasticsearchサーバを指定するファイルを取得します。
ファイルを取得後、ElasticsearchサーバのIPアドレスとポート番号を設定します。

$ cd /etc/sensu/conf.d
$ wget https://github.com/inokappa/sensu-handler-metrics-elasticsearch/blob/master/elasticsearch-metrics.json
$ vi elasticsearch-metrics.json
{
  "elasticsearch-metrics": {
    "host": "<IPアドレス>",
    "port": <ポート番号>,
    "index": "sensu-metrics",
    "type": "metrics"
  }
}

Elasticsearchは、データをインポートすると、ルールに従って自動的に各フィールドの型を識別し、マッピング(フィールドと型の組み合わせ)が自動で設定されます。
ただ、思ったとおりにマッピングが設定されない場合もあるため、Mapping Templateという機能を使い手動でマッピングを定義します。

$ curl -XPUT <ElasticsearchのIPアドレス>:<ポート番号>/_template/vmstat_metrics -d '
{
    "template" : "sensu-metrics*",
    "mappings" : {
      "vmstat_metrics" : {
        "numeric_detection" : true,
        "properties" : {
          "@timestamp" : { "type":"date", "format":"dateOptionalTime" },
          "client" : {type: "string"},
          "status": {type: "long"},
          "address": {type: "string"},
          "check_name": {type: "string"},
          "occurrences": {type: "long"},
          "value": {type: "long"},
          "command": {type: "string"},
          "key": {type: "string"}
          }
        }
      }
    }
  }
'

今回、手動マッピングでvalueフィールドをlong型で定義しているにも関わらず、valueフィールドに転送したデータの型により、valueフィールドがString型で定義される場合がありました。
この問題をつぶやいていたところ、以下のようなコメントを頂き、「numeric_detection」を使うことで問題を解決することができました。

最後に、Sensu Serverの再起動を行い、Elasticsearchとの連携設定を反映させます。
supervisorでSensu Serverのautorestartをオンにしているため、stopコマンドだけ実行しています。

$ service sensu-server stop

Elasticsearchの確認

マッピングが思ったようにできているか確認するため、「http://<サーバのIPアドレス>:9200/_plugin/head」にアクセスし、 Overviewタブ>インデックス(vmstat_metrics)>infoボタン>Index Metadataをクリックします。
valueフィールドのtypeを見るとlongになっていることが確認できます。

f:id:hidemium:20140716005458p:plain

「http://<サーバのIPアドレス>:<ポート番号>」からKibanaにアクセスし、トップページの「Sample Dashboard」をクリックすると、Sensuのメトリクスが転送されているのが確認できます。

f:id:hidemium:20140716005827p:plain

おわりに

SensuからElasticsearchにメトリクスを転送することができ、Dockerコンテナの監視の可視化に向けて一歩進んだかと思います。

今回の記事は、Sensuのスクリプトを作成された@inokaraさんのブログを参考にさせて頂きました。また、マッピングの問題では@johtaniさんにコメントを頂き助かりました。
@inokaraさん、@johtaniさんありがとうございした。

*1:Dockerコンテナ内からはコンテナごとのリソース情報は見えず、ホスト全体のリソース情報しか見えないので、今回はメトリクスの転送をすることを目的としています。

Elasticsearch + Kibanaを起動するDockerfileを書いてみた

前回、sensuでDockerコンテナを監視することができたため、今回はDockerコンテナの監視を可視化するための前段階として、最近注目されているサーチエンジンであるElasticsearchを起動するDockerfileを書いてみました。また、Elasticsearchのフロントエンドを提供するKibanaも合わせてインストールします。

構成

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

Dockerfile

Dockerfileの構成

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

elasticsearch
├──Dockerfile
├──id_rsa.pub            ... 公開鍵
├──sources.list          ... ミラーサイト一覧
├──config.js             ... Kibanaの定義ファイル
├──default.conf          ... nginxの定義ファイル
├──elasticsearch.conf    ... supervisorの定義ファイル(Elasticsearch用)
├──nginx.conf       ... supervisorの定義ファイル(nginx用)
└──supervisord.conf    ... supervisorの定義ファイル

Dockerfile

Dockerfileを以下のように作成します。

$ mkdir -p docker/elasticsearch
$ cd docker/elasticsearch
$ vi Dockerfile
FROM ubuntu:12.04

MAINTAINER hidemium

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

# Install ssh
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

# config sshd
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

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

# Install Java
RUN apt-get install -y python-software-properties debconf-utils
RUN add-apt-repository ppa:webupd8team/java
RUN apt-get update
RUN echo "oracle-java7-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y oracle-java7-installer

# Install Elasticsearch
RUN apt-get install -y curl
RUN curl http://packages.elasticsearch.org/GPG-KEY-elasticsearch | apt-key add -
RUN echo "deb http://packages.elasticsearch.org/elasticsearch/1.2/debian stable main" >> /etc/apt/sources.list.d/elasticsearch.list
RUN apt-get update
RUN apt-get install -y elasticsearch
RUN /usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head
ADD elasticsearch.conf /etc/supervisor/conf.d/elasticsearch.conf

# Install Kibana
RUN apt-get install -y unzip
RUN wget http://download.elasticsearch.org/kibana/kibana/kibana-latest.zip
RUN unzip kibana-latest.zip
RUN mv kibana-latest /usr/local/kibana
ADD config.js /usr/local/kibana/config.js

# Install Nginx
RUN echo "deb http://nginx.org/packages/ubuntu/ precise nginx" >> /etc/apt/sources.list.d/nginx.list
RUN echo "deb-src http://nginx.org/packages/ubuntu/ precise nginx" >> /etc/apt/sources.list.d/nginx.list
RUN curl http://nginx.org/keys/nginx_signing.key | apt-key add -
RUN apt-get update
RUN apt-get install -y nginx
ADD nginx.conf /etc/supervisor/conf.d/nginx.conf
ADD default.conf /etc/nginx/conf.d/default.conf

# Expose ports.
EXPOSE 22 80 9200 9300

# Define default command.
CMD ["supervisord", "-n"]
  • 「RUN echo "oracle-java7-installer shared/accepted-oracle-..」と「ENV DEBIAN_FRONTEND noninteractive」は、Javaのライセンス承諾画面を飛ばすためにやっています。
  • 「RUN /usr/share/elasticsearch/bin/plugin -install mobz/elasticsearch-head」は、 elasticsearch-headを使用できるようにプラグインをインストールしています。

Dockerfileの中で、読み込みを行っているファイルは以下の通りです。

sources.list

日本のミラーサイトを指定しています。

$ cat sources.list
deb http://jp.archive.ubuntu.com/ubuntu precise main restricted
deb-src http://jp.archive.ubuntu.com/ubuntu precise main restricted
deb http://jp.archive.ubuntu.com/ubuntu precise-updates main restricted
deb-src http://jp.archive.ubuntu.com/ubuntu precise-updates main restricted

config.js

Kibanaのconfig.jsを環境に合わせて修正しています。

$ cat config.js
:
    /*
    elasticsearch: "http://"+window.location.hostname+":9200",
     */
    elasticsearch: "http://<IPアドレス>:9200",
:

default.conf

kibanaにアクセスできるように、nginxのdefault.confを修正しています。

$ cat default.conf
:
    #location / {
    #    root   /usr/share/nginx/html;
    #    index  index.html index.htm;
    #}

    location / {
        root   /usr/local/kibana/;
        index  index.html index.htm;
    }
:

supervisord.conf

supervisorの設定とsshdの起動を定義しています。

$ cat 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

elasticsearch.conf

Elasticsearchの起動を定義しています。

$ cat elasticsearch.conf
[program:elasticsearch]
command=/usr/share/elasticsearch/bin/elasticsearch -f
autostart=true
autorestart=true

nginx.conf

Nginxの起動を定義しています。

$ cat nginx.conf
[program:nginx]
command=/usr/sbin/nginx -c /etc/nginx/nginx.conf
autostart=true
autorestart=tru

Elasticsearchコンテナの起動

以下のコマンドで、Dockerfileからビルドし、Dockerコンテナの起動を行います。

$ sudo docker build -t ubuntu-elasticsearch:12.04 .
$ sudo docker run -d -p 22 -p 80 -p 9200:9200 -p 9300:9300 ubuntu-elasticsearch:12.04

Dockerコンテナの80番ポートにフォワードしているポート番号を確認します。

$ sudo docker port <コンテナID> 80
0.0.0.0:<ポート番号>

「http://<サーバのIPアドレス>:<ポート番号>」にアクセスし、Kibanaに接続できることを確認します。

f:id:hidemium:20140714023026p:plain

また、「http://<サーバのIPアドレス>:9200/_plugin/head」にアクセスし、 elasticsearch-headに接続できることを確認します。

f:id:hidemium:20140714023139p:plain

ChefからDockerコンテナにSensu Clientをインストールする

前回、ChefからDockerコンテナにSensu Serverをインストールすることができました。そこで、今回はChefからDockerコンテナにSensu Clientをインストールして、Dockerコンテナが自動的に監視対象として追加されるか試してみました。
また、Clientの監視定義として、CPU使用率監視、メモリ使用率監視、ログ監視が設定されるようにしてみました。

構成

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

Dockerコンテナ

前回作成したDockerコンテナを使用します。

Sensu ClientのChefレシピ

それでは、Sensu Clientをインストーするレシピについて見ていきます。

cookbookの構成

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

site-cookbook
└──sensu-client
      └──recipes
      │    └──default.rb
      └──templates
            └──default
                  ├──client.json.erb ... Sensu Clientの監視定義ファイル
                  ├──config.json.erb ... Sensu Serverの定義ファイル
                  ├──sensu.list.erb  ... リポジトリの定義ファイル
                  └──sensu-client.conf.erb ... supervisorの定義ファイル

recipesファイル

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

$ vi site-cookbooks/sensu-client/recipes/default.rb
%w{wget}.each do | pkg |
    package pkg do
        action :install
    end
end

template "/etc/apt/sources.list.d/sensu.list" do
    owner "root"
    mode 0644
    source "sensu.list.erb"
end

bash "add sensu repos" do
    code "wget -q http://repos.sensuapp.org/apt/pubkey.gpg -O- | apt-key add -"
    action :run
end

bash "apt-get update" do
    code "apt-get update"
    action :run
end

%w{sensu git-core bc rsyslog}.each do | pkg |
    package pkg do
        action :install
    end
end

git "/tmp/sensu_plugins" do
    repository "git://github.com/sensu/sensu-community-plugins.git"
    reference "master"
    action :sync
end

bash "copy sensu plugins" do
    code "cp -Rf /tmp/sensu_plugins/plugins /etc/sensu/"
    action :run
end

bash "chmod sensu plugins" do
    code "find /etc/sensu/plugins/ -name *.rb -exec chmod +x {} \\;"
    action :run
end

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

template "config.json" do
    path "/etc/sensu/config.json"
    owner "root"
    group "root"
    mode "0644"
    source "config.json.erb"
end

template "client.json" do
    path "/etc/sensu/conf.d/client.json"
    owner "root"
    group "root"
    mode "0644"
    source "client.json.erb"
end

bash "ln ruby path" do
    code   "ln -s /opt/sensu/embedded/bin/ruby /usr/bin/ruby"
    action :run
end

bash "supervisorctl reload" do
    code   "supervisorctl reload"
    action :run
end
  • Sensu Clientをインストールするために、Sensuのリポジトリを指定しています。
  • GitHubからSensuのCommunity Pluginを取得しています。
  • Sensu Clientを起動するため、supervisorの定義ファイルであるsensu-client.confを配置しています。

templateファイル

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

client.json

client.jsonは監視対象の情報や監視定義を設定するファイルであり、Sensu Client側に配置します。
checks_*.jsonの"subscribers"に定義した名称をsubscriptionsに指定することで、監視定義を設定することができます。
"sensu"はSensu Server用にしているので、"default"のみを設定します。

$ vi site-cookbooks/sensu-client/templates/default/client.json.erb
{
    "client": {
      "name": "sensu-client",
      "address": "127.0.0.1",
      "subscriptions": [ "default" ]
    }
}
config.json

config.jsonは接続先のSensu Serverの定義ファイルになります。
"rabbitmq"には、追加したユーザ名を指定しています。

$ vi site-cookbooks/sensu-client/templates/default/config.json.erb
{
  "rabbitmq": {
    "port": 5672,
    "host": "<IPアドレス>",
    "user": "admin",
    "password": "admin",
    "vhost": "/"
  }
}
sensu.list

sensu.listには、Sensuのリポジトリを指定しています。

$ vi site-cookbooks/sensu-client/templates/default/sensu.list.erb
deb http://repos.sensuapp.org/apt sensu main
sensu-client.conf

sensu-client.confには、supervisorからSensu Clientを起動する設定をしています。
シスログ監視のサンプルのため、rsyslogの起動も設定しています。

$ vi site-cookbooks/sensu-client/templates/default/sensu-client.conf.erb
[program:sensu-client]
priority=40
directory=/tmp
command=/opt/sensu/bin/sensu-client -c /etc/sensu/config.json -d /etc/sensu -e /etc/sensu/extensions -v -l /var/log/sensu/client.log
user=root
autostart=true
autorestart=true

[program:rsyslogd]
command=/usr/sbin/rsyslogd
autostart=true
autorestart=true

Chefレシピの実行

Dockerコンテナを起動します。
22のポートを外部からアクセスできるようにしておきます。

$ sudo docker run -d -p 22 ubuntu-supervisor:12.04
$ sudo docker ps -a
CONTAINER ID        IMAGE                                 COMMAND                CREATED             STATUS                  PORTS                                                                                                                        NAMES
365a226d1a9d        ubuntu-supervisor:12.04               supervisord -n         3 days ago          Up 3 days               0.0.0.0:[ポート番号]->22/tcp                                                                                                        high_elion

Sensu ClientのChefレシピを流します。
Chefを実行する時は、22番ポートにフォワードされているポート番号を指定します。

$ vi nodes/<IPアドレス>.json
{
  "run_list": [
    "recipe[sensu-client]"
  ]
}
$ knife solo bootstrap root@<IPアドレス> -p [ポート番号]

Sensu Serverの管理画面

Sensu Clientが追加されたことを確認するため、「http://<サーバのIPアドレス>:8080」にアクセスし、Sensuのダッシュボードに接続します。ダッシュボードの「Clients」をクリックすると、先ほど追加したDockerコンテナがいるのが分かります。

f:id:hidemium:20140708031053p:plain

また、「http://<サーバのIPアドレス>:15672」にアクセスし、RabbitMQの管理画面に接続すると、接続数が1つ増えていることが分かります。

f:id:hidemium:20140708031106p:plain

おわりに

ChefからDockerコンテナにSensu Clientをインストールすることができました。また、Sensuの特徴として、クライアント(監視対象)の自動登録がありますが、これらを確認することができました。

Sensuの監視の仕組みとして、Sensu Server側で監視の動作を定義して、Sensu Client側はsubscriptionsを使ってその監視定義の中から必要なものだけを選択する仕組みとなっているため、監視を共通化できれば監視設定を簡単にスケールさせることができそうです。