Dockerで手っ取り早くRails環境を構築

# 方針
* Dockerを使ってRails環境を構築する
* base OSとしてCentOSを利用[^0]
* Rubyは2.3.4を利用[^1]
* railsをはじめとするbundler gemは全てシステム(docker Container内)へインストール[^2]
* Rails projectのソースはコンテナ外部（ホストOSとの共有領域）に作成

[^0]: 2017/06/19時点での最新版は7.3.1611

[^1]: Rails4と5の両方をカバーするには最低でもruby 2.2.2が必要となります。なお、Ruby 2.3.4とRails 4との相性については検証不足です。すみません。

[^2]: 一般には、`bundle install --path vendor/bundle`などとしてrails projectディレクトリ以下にbundler gemをインストールすることが多いのですが、その方法だとVagrantやVirtualBoxといった仮想マシンとのファイル共有下で開発を行う場合（筆者の環境がそれです）、rakeとかrails serverの起動とかがいちいち遅くて本当に酷いです。これは共有ファイルへのI/O速度が低いのが原因です。ですので、ここではbundler gemをシステム側(docker container内)へインストールすることにしました。なお、プロダクトをデプロイする時に`bundle install --deployment`としてrails projectディレクトリ以下にbunder gemをインストールしてやれば特に問題はないかなと思います。

# 環境構築
## Docker imageの作成

次のDockerfileを用意します。

```bash:Dockerfile
FROM centos
MAINTAINER takaeda

# set ruby version to be installed
ENV ruby_ver="2.3.4"

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y install git make autoconf curl wget
RUN yum -y install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel sqlite-devel bzip2
RUN yum clean all

# install ruby & bundler
RUN git clone https://github.com/sstephenson/rbenv.git /usr/local/rbenv
RUN git clone https://github.com/sstephenson/ruby-build.git /usr/local/rbenv/plugins/ruby-build

RUN echo 'export RBENV_ROOT="/usr/local/rbenv"' >> /etc/profile.d/rbenv.sh
RUN echo 'export PATH="${RBENV_ROOT}/bin:${PATH}"' >> /etc/profile.d/rbenv.sh
RUN echo 'eval "$(rbenv init --no-rehash -)"' >> /etc/profile.d/rbenv.sh

RUN source /etc/profile.d/rbenv.sh; rbenv install ${ruby_ver}; rbenv global ${ruby_ver}
RUN source /etc/profile.d/rbenv.sh; gem update --system; gem install --no-ri --no-rdoc rails; gem install bundler

# make workdir for rails projects
RUN mkdir -p /work
```

以下の作業はこのDockerfileを置いたディレクトリで実行することを想定しています。

```shell-session:
[host]$ ls
Dockerfile
[host]$
```
docker buildでイメージを生成。

```shell-session
[host]$ docker build -t takaeda/rails .
Sending build context to Docker daemon  3.072kB
Step 1/15 : FROM centos
.
.
.
Successfully built 62b21f9aa104
Successfully tagged takaeda/rails:latest
[host]$
```

ここで、`-t`オプションで指定した`takaeda/rails`というタグ名は適当に変更してください。

## Docker Containerの起動

Docker Containerを起動します。

```shell-session
[host]$ docker run -i -d -p 8080:3000 -v "${PWD}:/work" --name 'rails' takaeda/rails
214542b4f2755d1d8f01c7f856cfc208f0e67b775d113e37cffa5b56e3e4a264
[host]$
```

<details><summary>docker runのオプションの説明</summary>`-i` =>  attachされなくてもSTDINを維持
`-d` => バックグラウンド実行
`-p HostPort:ContainerPort` => ContainerPortをHostPortへ割り当て
`-v HostVolume:ContainerVolume` => ホストのディレクトリ(HostVolume)とコンテナ内のディレクトリ(ContainerVolume)を共有
`--name name` => コンテナ名(適当に)</details>

ここでは、ホストポートを8080に[^3]、ホストボリュームを現在のディレクトリ${PWD}に割り当てましたが、これらは手元の条件に合わせて適宜変更してください。

[^3]: MacやMS WindowsでVirtualBox等VMを介してDockerを利用している場合、このホストポートはVMのポートとなります。手元のブラウザから`http://127.0.0.1:8080`や`http://localhost:8080`などとしてRailsプロジェクトへアクセスする場合は、VMとHost OS (Mac, MS Windows)間でのポートフォーワードの設定が必要となります。

以上で環境構築は完了です。

# 新しくRails projectを作成する場合

前節で`rails'と名付けて起動したDocker Container内に入ります。

```shell-session
[host]$ docker exec -it rails /bin/bash
[root@214542b4f275 /]#
```

`/work`ディレクトリへ移動して`rails new project`などと実行して新しいRails projectを作成してください。

```shell-session
[root@214542b4f275 /]# cd /work
[root@214542b4f275 work]# rails new project
      create  
      create  README.md
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
.
.
.
Bundle complete! 16 Gemfile dependencies, 70 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
         run  bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted
[root@214542b4f275 work]# 
```

ここでいつもどおり:-) `project/Gemfile`を編集して`# gem 'therubyracer', platforms: :ruby`の行をコメントアウトします。

```shell-session
[root@214542b4f275 work]# vi project/Gemfile
```

```bash:project/Gemfile
.
.

# See https://github.com/rails/execjs#readme for more supported runtimes
gem 'therubyracer', platforms: :ruby  # この行をコメントアウト

.
.

```

あとは`project`ディレクトリに移って`rails server`を起動するだけです。

```shell-session
[root@214542b4f275 work]# cd project
[root@214542b4f275 project]# rails s
=> Booting Puma
=> Rails 5.1.1 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.9.1 (ruby 2.3.4-p301), codename: Private Caller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
```

Host環境から`curl -i --head http://localhost:8080`などとして接続を確認してください。

```shell-session
[host]$ curl -i --head http://localhost:8080
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
ETag: W/"9af74e9d7d13613de8018a96f1a1658e"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 8f71bbdd-dcc4-4c03-a6cd-a348d6884ee1
X-Runtime: 0.028164
```

# 既存のRails projectを使う場合

先ほどと同様、まずはDocker Containerへ入ります。

```shell-session
[host]$ docker exec -it rails /bin/bash
[root@214542b4f275 /]#
```

ここでは、既存のRails 4.0.5のprojectが`/work/project`に展開されているとします。
`/work/project`ディレクトリへ移動して`bundle install`を実行します。

```shell-session
[root@214542b4f275 /]# cd /work/project
[root@214542b4f275 project]# bundle install
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/..........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Fetching rake 12.0.0
.
.
.
Fetching rails 4.0.5
Installing rails 4.0.5
Bundle complete! 10 Gemfile dependencies, 47 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
[root@214542b4f275 project]# 
```

もしここで次のようなエラーが表示された場合は[こちら](#rails-projectで利用するrubyのバージョンに制約がある場合)へ。

```
rbenv: version `2.2.2' is not installed (set by /work/project/.ruby-version)
```

エラーがなければ`rails server`を起動して問題がないことを確認します。

```shell-session
[root@214542b4f275 project]# rails s
/usr/local/rbenv/versions/2.3.4/lib/ruby/gems/2.3.0/gems/activesupport-4.0.5/lib/active_support/values/time_zone.rb:283: warning: circular argument reference - now
=> Booting WEBrick
=> Rails 4.0.5 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2017-06-09 07:16:45] INFO  WEBrick 1.3.1
[2017-06-09 07:16:45] INFO  ruby 2.3.4 (2017-03-30) [x86_64-linux]
[2017-06-09 07:16:45] INFO  WEBrick::HTTPServer#start: pid=581 port=3000
```

サーバがうまく起動しない場合は自力でトラブルシュートしてください。:cry:


# Rails projectで利用するRubyのバージョンに制約がある場合

既存のRails pojectディレクトリで`bundle install`したときに次のようなエラーが表示される場合があります。

```
rbenv: version `2.2.2' is not installed (set by /work/project/.ruby-version)
```

これは`rbenv local`で「このRails projectで使われるRubyのバージョンが特定のバージョンに限定されている」ケースと考えられます。ここで`.ruby-version`ファイルを消して`bundle install`を実行するのはいささか乱暴です。
素直にこの制約に従うべく、Docker Containerの中で次の作業をおこなってください。

```shell-session
[root@214542b4f275 project]# rbenv install `cat .ruby-version`
sha256sum: the --quiet option is meaningful only when verifying checksums
Try 'sha256sum --help' for more information.
Downloading ruby-2.2.2.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.2.tar.bz2
Installing ruby-2.2.2...
Installed ruby-2.2.2 to /usr/local/rbenv/versions/2.2.2

[root@214542b4f275 project]# gem install bundler
Fetching: bundler-1.15.1.gem (100%)
Successfully installed bundler-1.15.1
Parsing documentation for bundler-1.15.1
Installing ri documentation for bundler-1.15.1
Done installing documentation for bundler after 4 seconds
1 gem installed

[root@214542b4f275 project]# bundle install
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/..........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Fetching rake 12.0.0
.
.
.
Installing rails 4.0.5
Bundle complete! 10 Gemfile dependencies, 47 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
```

ここまでエラーが出なければ、あとはサーバーを起動してください。

```shell-session
[root@214542b4f275 project]# rails s
/usr/local/rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.0.5/lib/active_support/values/time_zone.rb:283: warning: circular argument reference - now
=> Booting WEBrick
=> Rails 4.0.5 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2017-06-09 07:48:27] INFO  WEBrick 1.3.1
[2017-06-09 07:48:27] INFO  ruby 2.2.2 (2015-04-13) [x86_64-linux]
[2017-06-09 07:48:27] INFO  WEBrick::HTTPServer#start: pid=15572 port=3000
```

---