DockerMachineとDockerComposeでRailsを構築してみる


# TL;DR

DockerMachineとDockerComposeが発表されたので、早速Railsが起動できるところまで作ってみました。
開発環境として利用する想定で書いています。


# Enviroment

- OSX v10.10.2
- docker-machine v0.1.0
- docker-compose v1.1.0
- Virtualbox v4.3.20
- boot2docker v1.5.0
- ruby v2.2.0p0
- rails v4.2.0

# Install


## Virtualbox

https://www.virtualbox.org/ からInstallerをDLしてインストールします。

## docker-machine

URLにバージョンが含まれているので、適宜最新のバージョンに読み替えてください。

```
$ curl -L https://github.com/docker/machine/releases/download/v0.1.0/docker-machine_darwin-amd64 > /usr/local/bin/docker-machine
$ chmod +x /usr/local/bin/docker-machine
```

## docker-compose

URLにバージョンが含まれているので、適宜最新のバージョンに読み替えてください。

```
$ curl -L https://github.com/docker/compose/releases/download/1.1.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
$ chmod +x /usr/local/bin/docker-compose
```


# Docker Machineでローカルに開発環境のDockerを構築する

## Docker Machineの作成

`boot2docker`も自動的にダウンロードされます。

```
$ docker-machine create --driver virtualbox dev
INFO[0000] Creating SSH key...
INFO[0000] Creating VirtualBox VM...
INFO[0007] Starting VirtualBox VM...
INFO[0007] Waiting for VM to start...
INFO[0038] "dev" has been created and is now the active machine
INFO[0038] To connect: docker $(docker-machine config dev) ps
```

Dockerの環境変数を`shell`に設定します。

```
$ $(docker-machine env dev)
export DOCKER_TLS_VERIFY=yes
export DOCKER_CERT_PATH=$HOME/.docker/machines/.client
export DOCKER_HOST=tcp://192.168.99.100:2376
```

## Docker Machineの停止


```
$ docker-machine stop dev
$ docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL   SWARM
dev    *        virtualbox   Stopped
```


## Docker Machineの起動

```
$ docker-machine start dev
INFO[0000] Waiting for VM to start...
$ docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL                         SWARM
dev    *        virtualbox   Running   tcp://192.168.99.100:2376
```


# Docker Machine上にDockerComposeでRailsを構築する

## Setup

Railsを作成します。Databaseは`mysql`にしてあります。`--skip-bundle`オプションでgemをインストールしないようにしてます。`DockerImage`作成時にインストールするためです。

```
$ gem install rails
$ rails new docker-compose-rails --database=mysql --skip-bundle
$ cd docker-compose-rails
```

### config/database.yml

Dockerで立ち上げたMysqlに接続する情報を設定します。
起動時はDockerが自動的に環境変数を設定してくれます。

```yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: <%= ENV['DB_ENV_MYSQL_ROOT_PASSWORD'] %>
  host: <%= ENV['DATABASE_URL'] %>
  port: <%= ENV['DB_PORT_3306_TCP_PORT'] %>

development:
  <<: *default
  database: project_development
```
  
### Dockerfile

```
FROM rails:onbuild
```

### docker-compose.yml

```yml
db:
  image: mysql:5.6.21
  environment:
    - MYSQL_ROOT_PASSWORD=supersecretpass
  ports:
    - 3306
  volumes:
    - ./mysql:/var/lib/mysql
web:
  build: .
  command: bundle exec rails server -p 3000 --bind=0.0.0.0

  volumes:
    - .:/app
  ports:
    - 3000:3000
  links:
    - db
  environment:
    DATABASE_URL: mysql2://root:supersecretpass@db:3306    
```

## Build

`Gemfile.lock`を作成します。`gem`はインストールしないので`--no-deployment`オプションを付与して実行します。

```
$ bundle install --no-deployment
```

Railsの`DockerImage`を作成します。

```
$ docker-compose build
db uses an image, skipping
Building web...
Step 0 : FROM rails:onbuild
# Executing 4 build triggers
Trigger 0, COPY Gemfile /usr/src/app/
Step 0 : COPY Gemfile /usr/src/app/
 ---> Using cache
Trigger 1, COPY Gemfile.lock /usr/src/app/
Step 0 : COPY Gemfile.lock /usr/src/app/
Trigger 2, RUN bundle install
Step 0 : RUN bundle install
 ---> Running in 9995d0e1cb36
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
installing your bundle as root will break this application for all non-root
users on this machine.
Fetching gem metadata from https://rubygems.org/...........
Using rake 10.4.2
Installing i18n 0.7.0
Installing json 1.8.2
Installing minitest 5.5.1
Installing thread_safe 0.3.4
Installing tzinfo 1.2.2
Installing activesupport 4.2.0
Installing builder 3.2.2
Installing erubis 2.7.0
Installing mini_portile 0.6.2
Installing nokogiri 1.6.6.2
Installing rails-deprecated_sanitizer 1.0.3
Installing rails-dom-testing 1.0.5
Installing loofah 2.0.1
Installing rails-html-sanitizer 1.0.1
Installing actionview 4.2.0
Installing rack 1.6.0
Installing rack-test 0.6.3
Installing actionpack 4.2.0
Installing globalid 0.3.3
Installing activejob 4.2.0
Installing mime-types 2.4.3
Installing mail 2.6.3
Installing actionmailer 4.2.0
Installing activemodel 4.2.0
Installing arel 6.0.0
Installing activerecord 4.2.0
Installing debug_inspector 0.0.2
Installing binding_of_caller 0.7.2
Installing columnize 0.9.0
Installing debugger-linecache 1.2.0
Installing slop 3.6.0
Installing byebug 3.5.1
Installing coffee-script-source 1.9.1
Installing execjs 2.3.0
Installing coffee-script 2.3.0
Installing thor 0.19.1
Installing railties 4.2.0
Installing coffee-rails 4.1.0
Installing hike 1.2.3
Installing multi_json 1.10.1
Installing jbuilder 2.2.8
Installing jquery-rails 4.0.3
Installing mysql2 0.3.18
Using bundler 1.7.12
Installing tilt 1.4.1
Installing sprockets 2.12.3
Installing sprockets-rails 2.2.4
Installing rails 4.2.0
Using rdoc 4.2.0
Installing sass 3.4.13
Installing sass-rails 5.0.1
Installing sdoc 0.4.1
Installing spring 1.3.3
Installing turbolinks 2.5.3
Installing uglifier 2.7.1
Installing web-console 2.1.0
Your bundle is complete!
It was installed into /usr/local/bundle
Trigger 3, COPY . /usr/src/app
Step 0 : COPY . /usr/src/app
 ---> 2ede4b3a1b8a
Removing intermediate container 5e18d2e37320
Removing intermediate container 9995d0e1cb36
Removing intermediate container d12157b6a196
Successfully built 2ede4b3a1b8a
```

作成された`DockerImage`を確認します。

```
$ docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
dockercomposerails_web       latest              2ede4b3a1b8a        2 minutes ago       939.2 MB
rails                        onbuild             fc33f5385339        4 weeks ago         828.5 MB
ruby                         2.2.0               51473a2975de        4 weeks ago         774.7 MB
```

## UP

RailsとMysqlをDocker上で同時に立ち上げます。

```
$ docker-compose up
Recreating dockercomposerails_db_1...
Recreating dockercomposerails_web_1...
Attaching to dockercomposerails_db_1, dockercomposerails_web_1
db_1  | 2015-03-01 16:04:44 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
db_1  | 2015-03-01 16:04:44 1 [Note] Plugin 'FEDERATED' is disabled.
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Using atomics to ref count buffer pool pages
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: The InnoDB memory heap is disabled
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Memory barrier is not used
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Compressed tables use zlib 1.2.3
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Using Linux native AIO
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Not using CPU crc32 instructions
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Initializing buffer pool, size = 128.0M
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Completed initialization of buffer pool
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Highest supported file format is Barracuda.
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: 128 rollback segment(s) are active.
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: Waiting for purge to start
db_1  | 2015-03-01 16:04:44 1 [Note] InnoDB: 5.6.21 started; log sequence number 1625997
db_1  | 2015-03-01 16:04:44 1 [Note] Server hostname (bind-address): '*'; port: 3306
db_1  | 2015-03-01 16:04:44 1 [Note] IPv6 is available.
db_1  | 2015-03-01 16:04:44 1 [Note]   - '::' resolves to '::';
db_1  | 2015-03-01 16:04:44 1 [Note] Server socket created on IP: '::'.
db_1  | 2015-03-01 16:04:44 1 [Note] Event Scheduler: Loaded 0 events
db_1  | 2015-03-01 16:04:44 1 [Note] mysqld: ready for connections.
db_1  | Version: '5.6.21'  socket: '/tmp/mysql.sock'  port: 3306  MySQL Community Server (GPL)
web_1 | => Booting WEBrick
web_1 | => Rails 4.2.0 application starting in development on http://0.0.0.0:3000
web_1 | => Run `rails server -h` for more startup options
web_1 | => Ctrl-C to shutdown server
web_1 | [2015-03-01 16:04:47] INFO  WEBrick 1.3.1
web_1 | [2015-03-01 16:04:47] INFO  ruby 2.2.0 (2014-12-25) [x86_64-linux]
web_1 | [2015-03-01 16:04:47] INFO  WEBrick::HTTPServer#start: pid=1 port=3000
```

別のコンソールで`db:create`を行います。

```
$ docker-compose run web rake db:create
```

## Open Browser

ブラウザを開いて動作確認をします。
接続先のIPアドレスは`docker-machine ip`で取得できます。

```
$ open "http://$(docker-machine ip):3000"
```

![Ruby_on_Rails__Welcome_aboard.jpg](https://qiita-image-store.s3.amazonaws.com/0/4943/0db5a303-fa30-e0cc-6b12-061330970b3a.jpeg "Ruby_on_Rails__Welcome_aboard.jpg")



# REF Dockerfile ONBUILD and FROM

[Docker Official Image packaging for Ruby on Rails](https://github.com/docker-library/rails)

```https://github.com/docker-library/rails/blob/master/onbuild/Dockerfile
FROM ruby:2.2.0

# throw errors if Gemfile has been modified since Gemfile.lock
RUN bundle config --global frozen 1

RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

ONBUILD COPY Gemfile /usr/src/app/
ONBUILD COPY Gemfile.lock /usr/src/app/
ONBUILD RUN bundle install

ONBUILD COPY . /usr/src/app

RUN apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y mysql-client postgresql-client sqlite3 --no-install-recommends && rm -rf /var/lib/apt/lists/*

EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
```

[Docker Official Image packaging for Ruby](https://github.com/docker-library/ruby)

```https://github.com/docker-library/ruby/blob/master/2.2/Dockerfile
FROM buildpack-deps:jessie

ENV RUBY_MAJOR 2.2
ENV RUBY_VERSION 2.2.0

# some of ruby's build scripts are written in ruby
# we purge this later to make sure our final image uses what we just built
RUN apt-get update \
	&& apt-get install -y bison libgdbm-dev ruby \
	&& rm -rf /var/lib/apt/lists/* \
	&& mkdir -p /usr/src/ruby \
	&& curl -SL "http://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR/ruby-$RUBY_VERSION.tar.bz2" \
		| tar -xjC /usr/src/ruby --strip-components=1 \
	&& cd /usr/src/ruby \
	&& autoconf \
	&& ./configure --disable-install-doc \
	&& make -j"$(nproc)" \
	&& make install \
	&& apt-get purge -y --auto-remove bison libgdbm-dev ruby \
	&& rm -r /usr/src/ruby

# skip installing gem documentation
RUN echo 'gem: --no-rdoc --no-ri' >> "$HOME/.gemrc"

# install things globally, for great justice
ENV GEM_HOME /usr/local/bundle
ENV PATH $GEM_HOME/bin:$PATH
RUN gem install bundler \
	&& bundle config --global path "$GEM_HOME" \
	&& bundle config --global bin "$GEM_HOME/bin"

# don't create ".bundle" in all our apps
ENV BUNDLE_APP_CONFIG $GEM_HOME

CMD [ "irb" ]
```

# REF

## Qiita

- [Docker Machine リファレンス](http://qiita.com/spesnova/items/073dd64a8a1d580d9ae9)
- [Docker Machine を使って VirtualBox に Dockerホストを立てる](http://qiita.com/spesnova/items/038af6a8a4e401e3d2aa)
- [Docker Machine を使って DigitalOcean に Dockerホストを立てる](http://qiita.com/spesnova/items/c1a8b7c1a079834aed2d)
- [Docker Machine を使って EC2 に Dockerホストを立てる](http://qiita.com/spesnova/items/4d3738627997ce135deb)
- [Docker Swarm リファレンス](http://qiita.com/spesnova/items/f4b5f7ee57c9271c9dbd)
- [Docker Swarm を使ってマルチホスト環境下で Hello World コンテナを動かす](http://qiita.com/spesnova/items/47fa9ba3e8aa3721384c)
- [Docker Compose リファレンス](http://qiita.com/spesnova/items/6e8919b34e763f27c34e)
- [Docker Compose を使って Sinatra と Redis コンテナを 1 コマンドで立ち上げる](http://qiita.com/spesnova/items/40b49b9faa5faa1f98bf)
- [Swarm と組み合わせて Compose(Fig) をマルチホスト環境で使う](http://qiita.com/spesnova/items/bd64b84368370391eb0c)

## Official

- [Docker Machine](https://docs.docker.com/machine/)
- [Docker Compose](https://docs.docker.com/compose)
- [Getting started with Compose and Rails](https://docs.docker.com/compose/rails/)
- [Getting started with Fig and Rails](http://www.fig.sh/rails.html)
- [boot2docker/boot2docker](https://github.com/boot2docker/boot2docker)

## Others

- [Use Fig and Docker to run a Rails app... without installing Rails](https://www.orchardup.com/blog/use-fig-to-run-a-rails-app)