nginx + Rails (unicorn) + MySQL 環境を Docker で構築する

こちらの記事をアップデートしたものを、Zennで公開しています。こちらの記事は古くなってきているので[こちら](https://zenn.dev/utahka/articles/d473fc6d84678d)を参照ください。

# <i class="fa fa-files-o" aria-hidden="true"></i> はじめに
まず，本記事では次の図のような構成で nginx + unicorn + MySQL な環境を構築します．リバースプロキシを異なるコンテナ間でやるのがはじめてだったので，とりあえず記事書いて覚え書きにでもしようかなと言うモチベーションです．

<center><img width="600" alt="docker-compose.png" src="https://qiita-image-store.s3.amazonaws.com/0/76596/2c3da445-3a71-c10b-2f7f-25485d946341.png"></center>


また，本記事におけるワークディレクトリの構成は以下のようになっています．ファイル名などはこちらを参考にしてください．いくつかのメインではない設定ファイルに関しては，記事が冗長になることを避けて掲載していませんので適宜 Github のリポジトリを参照してください．

```
.
├── Gemfile
├── Gemfile.lock
├── docker
│   ├── gitignore
│   ├── mysql
│   │   ├── Dockerfile
│   │   ├── charset.cnf
│   │   └── password.yml
│   ├── nginx
│   │   ├── Dockerfile
│   │   ├── nginx.conf
│   │   └── rails.conf
│   └── rails
│       ├── Dockerfile
│       └── unicorn.rb
└── docker-compose.yml
```

Github リポジトリは，[こちら](https://github.com/utahkaA/rails-docker) になっております．

# <i class="fa fa-square-o" aria-hidden="true"></i> nginx コンテナ
nginx コンテナの Dockerfile は以下のように書きます．

```dockerfile:docker/nginx/Dockerfile
FROM nginx:1.11
RUN apt-get update && \
    apt-get install -y apt-utils \
                       locales && \
    sed -i -e 's/# ja_JP.UTF-8/ja_JP.UTF-8/g' /etc/locale.gen && \
    locale-gen ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LC_TIME C
ADD ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
ADD ./docker/nginx/rails.conf /etc/nginx/conf.d/default.conf
```

# <i class="fa fa-square-o" aria-hidden="true"></i> Rails コンテナ
Rails コンテナの Dockerfile は以下のように書きます．

```dockerfile:docker/rails/Dockerfile
FROM ruby:2.3.0
RUN apt-get update -qq && \
    apt-get install -y apt-utils \
                       build-essential \
                       libpq-dev \
                       nodejs \
                       mysql-client
RUN mkdir /app
WORKDIR /app
ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock
RUN bundle install -j4
ADD . /app

EXPOSE 3000
```

また，Gemfile は以下のように書いておき，Gemfile.lock は空のままにしておきます．

```ruby:Gemfile
source 'https://rubygems.org'
gem 'rails', '5.0.0'
```

最後に，config/unicorn.rb として配置する unicorn.rb も用意しておきます．

```ruby:docker/rails/unicorn.rb
worker_processes 8

pid "/var/run/unicorn.pid"
listen "/share/unicorn.sock"

stdout_path "./log/unicorn.stdout.log"
stderr_path "./log/unicorn.stderr.log"
```

# <i class="fa fa-square-o" aria-hidden="true"></i> MySQL コンテナ
MySQL コンテナの Dockerfile は以下のように書きます．(`charset.cnf` の内容は，Github のリポジトリから参照してください．)

```dockerfile:docker/mysql/Dockerfile
FROM mysql:5.7

RUN apt-get update && \
    apt-get install -y apt-utils \
                       locales && \
    rm -rf /var/lib/apt/lists/* && \
    echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
    locale-gen ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ADD ./docker/mysql/charset.cnf /etc/mysql/conf.d/charset.cnf
```

# <i class="fa fa-th-large" aria-hidden="true"></i> Docker Compose
3 つの主要コンテナの Dockerfile が出揃ったので，docker-compose.yml を作ることでコンテナ同士を連携させていきます．

```yaml:docker-compose.yml
version: '2'
services:
  datastore:
    image: busybox
    volumes:
      - /share
      - ./docker/mysql/volumes:/var/lib/mysql
      - /etc/localtime:/etc/localtime:ro
  server:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    ports:
      - '80:80'
    volumes_from:
      - datastore
    depends_on:
      - datastore
  web:
    build:
      context: .
      dockerfile: ./docker/rails/Dockerfile
    command: bundle exec unicorn -p 3000 -c /app/config/unicorn.rb
    ports:
      - '3000:3000'
    volumes:
      - .:/app
      - /etc/localtime:/etc/localtime:ro
    volumes_from:
      - datastore
    depends_on:
      - db
    links:
      - db
      - db:database
      - db:mysql
    extends:
      file: ./docker/mysql/password.yml
      service: password
  db:
    build:
      context: .
      dockerfile: ./docker/mysql/Dockerfile
    ports:
      - '3306:3306'
    volumes_from:
      - datastore
    depends_on:
      - datastore
    extends:
      file: ./docker/mysql/password.yml
      service: password
```

ここで，`docker/mysql/password.yml` を以下のように書いて作っておきます．

```
version: '2'
services:
  password:
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: hoge
      MYSQL_PASSWORD: hogepassword
      TZ: "Asia/Tokyo"
```

各パスワードやユーザ名は，ご自身のものを記入ください．この password.yml にこれらパスワード類を隔離することで docker-compose.yml に直書きしなくて済みます．

## ビルドと Rails アプリケーションの作成
docker-compose.yml が用意できたら，`docker-compose build` でビルドします．`Successfully built ...` と最後に出力されたらビルド成功です．

次に，`docker-compose` コマンド経由で `rails new` してアプリケーションを作成します．

```bash
docker-compose run web rails new . --force --database=mysql --skip-bundle
```

これによってアプリケーションディレクトリが作られたかと思います．このとき，Gemfile が更新されているので `gem 'unicorn'` という行を追加して再度 `docker-compose build` します．ただ単に，`docker-compose run web bundle install` してもコンテナが終了すると状態は保存されないからです．

次に，事前に作っておいた `unicorn.rb` をコピーするのと，`config/database.yml` の該当箇所を忘れずに変更します．

```bash
cp docker/rails/unicorn.rb config/
```

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

最後に，MySQL 上に `rake db:create` してデータベースを作れば完了です．

## おわりに
ネットワーク関連には疎いので，nginx で詰まりました．まあ，何でもできてしまうと大したことではなく...
(不具合，指摘等ありましたら，コメント頂ければ幸いです．)