Dockerの構成管理「Fig」で開発環境を整備しよう

Dockerに対する注目がどんどん高まっています。GoogleやMicrosoftもDockerを積極的に採用して自社クラウドサービスに組み込んでいますし、Dockerを使ったコンテナベースのクラウドサービスも増えています。

Dockerでは各コンテナではあまり複雑な構成をせず、1コンテナ1アプリケーションとして、コンテナ間をリンクして使うのがお勧めです。それによってコンテナ間の依存度を下げつつ保守性を維持できるのがメリットになります。

逆にデメリットとしては構成が複雑になるためにアプリケーションをまとめあげ、構成する仕組みが必要になります。今回はそのためのツールとしてリリースされたFigを紹介します。元々Orchard Labsが開発していましたがDocker社に買収され、2014年10月にリリースされたツールになります。Docker本体が買収したとあって、需要は高いツールと言えるでしょう。

Figは開発環境を柔軟に管理、運用するツールになります(開発環境用オーケストレーションツールです)。Linux上またはMac OSX上(boot2docker利用)が想定されているようです。今回はこのFigの使い方を紹介します。なおOSはUbuntu 14.04 LTSを使っています。

Figのインストール

FigはDockerの最新版である1.3以上で動作します。CoreOSや通常のaptなどでインストールできるバージョンは低いので最新版にします。こちらのコマンドを実行します。

$ curl -sSL https://get.docker.io/ubuntu/ | sudo sh
$ docker -v
Docker version 1.3.1, build 4e9bbfa

Dockerをインストールしたら、設定ファイルを編集した後でサービスとして起動します。

$ cat /etc/default/docker
  :
# DOCKER_OPTSをこのように編集します。
#DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4" (元)
# ↓
DOCKER_OPTS="-H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock --dns 8.8.8.8 --dns 8.8.4.4"
  :

設定を編集したらサービスとして起動し、DOCKER_HOSTを環境変数に設定します。

$ sudo restart docker # Dockerをサービス起動します
$ export DOCKER_HOST=tcp://localhost:4243 # DockerのURIを設定します

次にFigのインストールです。次のコマンドでインストールできます。

# curl -L https://github.com/docker/fig/releases/download/1.0.1/fig-`uname -s`-`uname -m` > /usr/local/bin/fig; chmod +x /usr/local/bin/fig

バイナリがGitHubで公開されていますので、それを /usr/local/bin/fig としてコピーしています。なおMac OSX版もあるのですが、こちらはboot2dockerをインストールすれば同梱されるようです。

$ fig --version
fig 1.0.1

これで準備は完了です。

Ruby on Rails環境を構築する

まずRuby on Railsの開発環境を構築する例になります。構成としては、Web(Ruby on Rails)とDB(PostgreSQL)という構成になります。

作業用ディレクトリを作成します。

$ mkdir rails
$ cd rails

Dockerfileを次の内容で作成します。

$ cat Dockerfile
FROM ruby
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev
RUN mkdir /myapp
WORKDIR /myapp
ADD Gemfile /myapp/Gemfile
RUN bundle install
ADD . /myapp

Rubyがあらかじめ入ったコンテナイメージをベースに、aptで必要なものをインストールしています。さらに myapp というディレクトリを作成し、そこにGemfileを入れています。とてもシンプルで、PostgreSQLに関する記述はありません。

次に同じディレクトリにGemfileを作成します。こちらはまず最低限です。

$ cat Gemfile
source 'https://rubygems.org'
gem 'rails', '4.0.2'

最後に fig.yml を作成します。これがFigが使う構成管理に関する設定ファイルです。

$ cat fig.yml
db:
  image: postgres
  ports:
    - "5432"
web:
  build: .
  command: bundle exec rackup -p 3000
  volumes:
    - .:/myapp
  ports:
    - "3000:3000"
  links:
    - db

リンクするデータベース(db)のイメージファイルやポート番号、さらにWebアプリケーション(web)の実行コマンドやポート、リンクに関する設定が記述されています。これまでDockerfile内で記述されていたような内容ですが、分離することでfig.ymlが構成管理に関する記述だけにまとめられるようになっています。

ここまでの記述が終わったら、figコマンドを実行します。

$ fig run web rails new . --force --database=postgresql --skip-bundle

fig run というコマンドで指定したコンテナ(ここではweb)に対して任意のコマンドが実行できます。ここでは rails new . --force --database=postgresql --skip-bundle が実行されると言うことで、Gemfileに記述した内容と同じくRuby on Railsがインストールされます。このときPostgreSQLのコンテナイメージも取得するのでしばらくかかります。

終わると次のようにRailsアプリケーションの基本構成がカレントディレクトリに展開されます。

$ ls
app  config     db          fig.yml  Gemfile.lock  log     Rakefile     test  vendor
bin  config.ru  Dockerfile  Gemfile  lib           public  README.rdoc  tmp

今後、GemfileやDockerfileをアップデートした際には fig build コマンドを実行する必要があるようです。

最後にRailsアプリケーションのデータベース設定を編集します。

$ cat config/database.yml
  :
development:
  adapter: postgresql
  encoding: unicode
  database: postgres
  pool: 5
  username: postgres
  password:
  host: db

hostがdbと簡単な表記になっているのが特徴です。これでFigで設定しているdbと連携してくれるようになります。

ではRailsアプリケーションを実行します。

$ fig up
Recreating rails_db_1...
Recreating rails_web_1...
Attaching to rails_db_1, rails_web_1
db_1  | LOG:  database system was interrupted; last known up at 2014-11-07 08:25:16 UTC
db_1  | LOG:  database system was not properly shut down; automatic recovery in progress
db_1  | LOG:  record with zero length at 0/1782A80
db_1  | LOG:  redo is not required
db_1  | LOG:  database system is ready to accept connections
db_1  | LOG:  autovacuum launcher started
web_1 | [2014-11-07 12:42:49] INFO  WEBrick 1.3.1
web_1 | [2014-11-07 12:42:49] INFO  ruby 2.1.4 (2014-10-27) [x86_64-linux]
web_1 | [2014-11-07 12:42:49] INFO  WEBrick::HTTPServer#start: pid=1 port=3000
Ruby on Rails実行結果

コンソールに入る場合は次のようになります。

$ fig run web rails c
Loading development environment (Rails 4.0.2)
irb(main):001:0>

figというコマンドを介しているものの、ローカルで開発しているのと変わらないくらいの感覚でDockerが使えるようになるのではないでしょうか。仮想化がされているので開発環境の可搬性があがり、メンテナンスはとてもしやすいでしょう。

WordPressを立ち上げる

次にWordPressをFigを使って立ち上げてみます。

$ mkdir wordpress
$ cd wordpress

まず、WordPressをダウンロードして解凍します。

$ curl https://wordpress.org/latest.tar.gz | tar -xvzf -

そうするとwordpressというディレクトリができます。

続いてDockerfile、fig.ymlを作成します。

$ cat Dockerfile
FROM orchardup/php5
ADD . /code
$ cat fig.yml
web:
  build: .
  command: php -S 0.0.0.0:8000 -t /code
  ports:
    - "8000:8000"
  links:
    - db
  volumes:
    - .:/code
db:
  image: orchardup/mysql
  environment:
    MYSQL_DATABASE: wordpress

こちらもまたDockerfileはとてもシンプルに保たれていることが分かるかと思います。

そして wordpress/wp-config-sample.phpをベースに設定ファイルを作成します。

define('DB_NAME', 'wordpress');
define('DB_USER', 'root');
define('DB_PASSWORD', '');
define('DB_HOST', "db:3306");
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

データベースの接続情報について編集します。こちらもホスト設定がdb:3306とシンプルなものになっているのが分かるかと思います。

最後にPHPのビルトインWebサーバ用にファイルを作成します。

$ cat router.php
<?php

$root = $_SERVER['DOCUMENT_ROOT'];
chdir($root);
$path = '/'.ltrim(parse_url($_SERVER['REQUEST_URI'])['path'],'/');
set_include_path(get_include_path().':'.__DIR__);
if(file_exists($root.$path))
{
    if(is_dir($root.$path) && substr($path,strlen($path) - 1, 1) !== '/')
        $path = rtrim($path,'/').'/index.php';
    if(strpos($path,'.php') === false) return false;
    else {
        chdir(dirname($root.$path));
        require_once $root.$path;
    }
}else include_once 'index.php';

これで準備は完了です。Figを実行します。初回はイメージをとってくるので時間がかかります。

$ fig up
WordPress実行画面

Dockerfileを使って設定を記述するとはいっても、実際には単体では難しく、別途Shellスクリプトを実行するケースはとても多かったです。さらに複雑化すれば記述内容が増えたり、メンテナンス性が悪くなります。それではDockerの持ち味が活かせなくなるでしょう。

Figは構成を分かりやすくできるほか、例えば

$ fig scale web=2 worker=3

のように実行することでWebというコンテナの数を増やすこともできます。Dockerを使った運用が柔軟になりそうです。開発環境でこういった複数構成を試せるのが良いですね。これまでVagrantで構築していたのをboot2docker + Figにすることで複数のプロジェクトを切り替えたり、多くのVMイメージファイル管理から解放されるのではないでしょうか。

ぜひFigを使って開発、運用負荷を軽減してください!

Fig | Fast, isolated development environments using Docker