Docker Compose と Rails(+MySQL) で開発環境作成したときに気になったポイント

#対象
まったくのdocker初心者が[クイックスタート・ガイド：Docker Compose と Rails](http://docs.docker.jp/compose/rails.html)を一通りやっても、いまいち納得できなかった人。つまり私。

#目的
ある程度モヤモヤをスッキリさせて開発環境の雛型みたいなものまで持っていきたい。

#私の行った手順
[クイックスタート・ガイド：Docker Compose と Rails](http://docs.docker.jp/compose/rails.html)を一読したことを前提に書いていきます。

##dockerディレクトリの作成

クイックスタートを一通り実行するとアプリケーションルートにDockerfileやdocker-compose.ymlがあるのが気になります。docker-compose.ymlはここにあるのがベターに思えますが、今後Nginxを組み込んだり、MySqlの永続化を考えるとdocker関連は一か所に押し込めたくなります。
そんなわけで、以下のディレクトリ構成にしました。
[全ファイルをGitHub上に置いておきます。](https://github.com/nak1114/docker-template/tree/master/rails-mysql)

```
docker-rails/
  |-docker-compose.yml  #これはこの場にあったほうがいい
  |-Gemfile
  |-Gemfile.lock
  |-docker/
     |-mysql/
     |  |-password.yml  #MySQLのパスワードはここに記述
     |  |-volumes/       #このフォルダにMySQLのデータを永続化
     |-rails/
     |  |-Dockerfile    #Rails用のDockerfile
```


##プロジェクトの定義

###Dockerfileの作成
apt-getをMySQL用に変更。Gemfileは/tmpに置く。

```Dockerfile:./docker/rails/Dockerfile
FROM ruby:2.3.3
ENV LANG C.UTF-8
# for MySQL
RUN apt-get update -qq && apt-get install -y build-essential mysql-client nodejs

RUN gem install bundler

WORKDIR /tmp
ADD Gemfile Gemfile
ADD Gemfile.lock Gemfile.lock
RUN bundle install

ENV APP_HOME /myapp
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
ADD . $APP_HOME
```
###Gemfileを作成
変更点無し
証明書エラーで落ちる時は`http://rubygems.org`から持ってくる。

```ruby:./Gemfile
source 'http://rubygems.org'
gem 'rails', '5.0.1'
```
###Gemfile.lock(空ファイル)を作成
変更点無し
`$touch Gemfile.lock`

###docker-compose.ymlを作成
変更箇所は以下の通り。

- dbの`volumes`でデータベースを永続化
- dbの`extends`でパスワードをpassword.ymlで管理
- webの`extends`でパスワードをrailsにも教える。
- `extends`先のファイルにパスワードを設定。
- webの`build`でDockerfileの場所を指定。

```yaml:docker-compose.yml
version: '2'
services:
  db:
    image: mysql:5.7.17
    ports:
      - "3306:3306"
    volumes:
      - ./docker/mysql/volumes:/var/lib/mysql
    extends:
      file: ./docker/mysql/password.yml
      service: password
  web:
    build:
      context: .
      dockerfile: ./docker/rails/Dockerfile
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    ports:
      - "3000:3000"
    volumes:
      - .:/myapp
    environment:
      RAILS_ENV: development
    extends:
      file: ./docker/mysql/password.yml
      service: password
    depends_on:
      - db
```

```yaml:./docker/mysql/password.yml
version: '2'
services:
  password:
    environment:
      MYSQL_ROOT_PASSWORD: password
```
`MYSQL_ROOT_PASSWORD: password`の`password`の部分がMySQLのパスワードになる。

##プロジェクトの作成
特に問題なくガイド通りに進められる。
databaseはMySQLなのでそこは変更する。

```
$ docker-compose run --rm web rails new . --force --database=mysql --skip-bundle
$ docker-compose build
```

##データベースに接続

###config/database.ymlを編集
`password`を環境変数から取得するように変更。
`host`を`db`にする。

```yaml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: <%= ENV['MYSQL_ROOT_PASSWORD'] %>
  host: db

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test
```
##データベース作成
`up`が出来たのを確認したらそのまま落としても問題ないです。

```
$ docker-compose up
$ <ctrl-C>
$ docker-compose run --rm web rake db:create
```

##アプリ作成
本当にDBと連携して動いているのか確認するために簡単にアプリを作ってみる。
###みんな大好きscaffold
`docker-compose up`は`-d`オプションが良いと思う

```
$ docker-compose down
$ docker-compose run --rm web rails g scaffold item name:string amount:integer memo:text
$ docker-compose run --rm web rake db:migrate
$ docker-compose up -d
```
これで http://localhost:3000/items にアクセスして弄れることを確認。
`docker-compose down` `docker-compose up`を繰り返しても問題ないことを確認。
`docker-compose up`したままviewやcontrolerを弄ってもブラウザ上に反映されることを確認。


#Nginxも含めた環境づくり
また今度、暇になったら記事にしてみたい。

#もやもやポイントまとめ

##docker-composeコマンド編
###サービス起動したら裏に回ってくれ。
`docker-compose up -d`にする。

###サービス終了はどうするの？
`docker-compose down`する。
`docker-compose up`で回していたらCtrl+Cで強制終了。

##MySQL編
###対話的に設定したい
イメージ単独では何故か出来ません。
~~いや、対話用のMySQLクライアント作れば行けるのだろうが、railsコマンドで十分でしょ。
rootユーザーで操作しているのが気になるけど、どうやって対話以外でユーザー作成するのかわからんし。~~

`rails dbconsole`があったか。
早速、![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/109103/38c6ebbf-b699-e34d-e0f3-3bde68d723f9.png)
ん？rootがフルオープンだぞ？
ってことは開発機からもアクセスできるんじゃない？
![キャプチャ2.PNG](https://qiita-image-store.s3.amazonaws.com/0/109103/b754b576-46a7-5a20-29d9-7c91135e9f28.png)
出来た。
dockerのnetworkの外からアクセスさせたくない時は下の`ports`を消せばdocker内のrails以外からはアクセス出来なくなる。

```yaml:docker-compose.yml
version: '2'
services:
  db:
    ports:
      - "3306:3306"
```

###dbのデータってどこにあるの？
mysqlのイメージ内で勝手にdockerディレクトリに永続化されているので、以下の設定を追加して開発ディレクトリ内に持ってくる。

```yaml:docker-compose.yml
services:
  db:
    volumes:
      - ./docker/mysql/volumes:/var/lib/mysql
```

###dbのパスワードって？
以下のように書くとrootのパスワードを設定できる。

```yaml:docker-compose.yml
services:
  db:
    environment:
      MYSQL_ROOT_PASSWORD: passward
```

ただしdocker-compose.ymlにMySQLのパスワードを直接書きたくないので`extends`を使って、以下のようにパスワードファイルに追い出す。
ついでにrails環境にもパスワードを渡す。

```yaml:docker-compose.yml
services:
  db:
    extends:
      file: ./docker/mysql/password.yml
      service: password
  web:
    extends:
      file: ./docker/mysql/password.yml
      service: password
```

```yaml:./docker/mysql/password.yml
version: '2'
services:
  password:
    environment:
      MYSQL_ROOT_PASSWORD: password
```

これでpassword.ymlを.gitignoreで除外すればオールクリア。

##Rails編
###web_consoleが使えない
railsアプリに以下を追加。

```ruby:config/environments/development.rb
config.web_console.automount = true
config.web_console.whitelisted_ips = %w( 0.0.0.0/0 ::/0 )
```

#最後に
今までVirtualBoxを使っていたけど、dockerは一度設定した後は手軽かも。
最初は設定・設定・また設定でうんざりしたけど、よく考えたらプロビジョニングとそんなに変わらんなぁと。
多分VirtualBoxには戻らない気がする。