Docker上でnginx + unicorn + mysql環境を構築

## はじめに

RailsアプリをEC2上のdockerで動かしてみます。以下、一台のホストに必要なコンテナsを載せるシングルホスト構成での作業です。ステージング環境のような非production用途を想定しています。


![sc 2015-08-02 16.35.28.png](https://qiita-image-store.s3.amazonaws.com/0/27828/91952694-185c-8a73-76d7-38806fa2f44f.png)


ミドルウェアは以下です。

- Ruby 2.2.1
- Rails 4.2.1
- MySQL 5.6
- nginx
- unicorn

サンプルアプリのリポジトリは以下です
https://github.com/konpyu/rails-docker-sample

コンテナ構成はこんな感じです

- nginxコンテナ
- unicornコンテナ
- mysqlコンテナ
- データ共有コンテナ


![sc 2015-08-02 16.24.37.png](https://qiita-image-store.s3.amazonaws.com/0/27828/58f2962a-80e1-a65f-be42-55063dc20f28.png)



## ホストの起動と準備

EC2のインスタンスを適当に立てます。今回はCentOS7(ami-89634988)をホストOSとします。


```
# ログインしてrootになっておく
[centos@ip-10-0-x-xxx ~]$ sudo su -
[root@ip-10-0-x-xxx ~]#
```

gitとDockerをインストールしておきます

```
[root@ip-10-0-x-xxx ~]# yum -y install git docker
[root@ip-10-0-x-xxx ~]# docker -v
Docker version 1.6.2, build ba1f6c3/1.6.2
```

Dockerを起動します

```
[root@ip-10-0-x-xxx ~]# systemctl start docker
```

[サンプルアプリ](https://github.com/konpyu/rails-docker-sample)をcloneしてきます

```
[root@ip-10-0-x-xxx ~]# mkdir /app
[root@ip-10-0-x-xxx ~]# cd /app
[root@ip-10-0-x-xxx app]# git clone https://github.com/konpyu/rails-docker-sample.git .
[root@ip-10-0-x-xxx app]# ls
app  bin  config  config.ru  db  docker  docker-compose.yml  Dockerfile  Gemfile  Gemfile.lock  lib  log  public  Rakefile  README.md  README.rdoc  vendor
```

## appコンテナのビルド

Dockerfileでビルドします。RailsアプリのrootにDockefileを置いておけば、Railsのプロジェクトファイルをまるっとコンテナ内に`COPY`できます。

```Dockerfile:Dockerfile
FROM ruby:2.2.1

RUN apt-get update && apt-get install -y nodejs mysql-client --no-install-recommends

# /appをコンテナ内のRails Appのディレクトリとする
RUN mkdir -p /app
WORKDIR /app

COPY Gemfile /app/
COPY Gemfile.lock /app/
RUN bundle install -j4 --without development test

COPY . /app

EXPOSE 3000
CMD ["bundle", "exec", "unicorn", "-c", "config/unicorn.rb"]
```

```
[root@ip-10-0-x-xxx app]# docker build -t todo/app .
```

### MySQL, nginx, datastore用コンテナの起動

次にコンテナ間のファイル共有専用のデータストアコンテナと、DB用コンテナ、リバプロ用コンテナを起動します

まずは、コンテナ間でデータを共有するために、[busybox](https://registry.hub.docker.com/_/busybox/)をベースに起動します。今回は、このコンテナ内にunicorn用のソケットを置いてコンテナ間で共有します

```
[root@ip-10-0-x-xxx app]# docker run -i -t -d -P --name data -v /share busybox /bin/sh
```

次に、MySQLの公式イメージを使ってDBコンテナを立てます。サンプルアプリ内の`#{Rails.root}/docker/mysql/my.cnf`を設定ファイルとして注入しておきます


SELinuxを止めておきます ([参考](https://registry.hub.docker.com/_/mysql/))

```
chcon -Rt svirt_sandbox_file_t /app/docker
```

```
[root@ip-10-0-x-xxx app]# docker run --name mysql -e MYSQL_ROOT_PASSWORD=test -v /app/docker/mysql:/etc/mysql/conf.d -d mysql:5.6
```

my.cnfはこんな感じです

```#{Rails.root}/docker/mysql/my.cnf
[mysqld]
innodb_strict_mode
innodb_file_format = Barracuda
innodb_file_per_table
innodb_large_prefix = 1
```

リバプロ（nginx）のコンテナを公式イメージから立てます。unicornのsocketは先ほど立ててたbusyboxの中の`/share/unicorn.sock` に置くものとします

```nginx:#{Rails.root}/docker/nginx/nginx.conf
upstream unicorn_server {
   server unix:/share/unicorn.sock;
}

server {
    listen       8080;
    root         /app/public;

    location / {
        if (!-f $request_filename) {
            proxy_pass http://unicorn_server;
            break;
        }
    }
}
```

nginxのコンテナを起動します

```
[root@ip-10-0-x-xxx app]# docker run --name nginx -p 80:8080 -v /app/docker/nginx/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro --volumes-from data  -d nginx
```

ここまでの段階で、ブラウザでアクセスすると502(Bad Gateway)が返ってくるはずです

最後に、unicornを立てて、nginxに来たアクセスをunicornにproxyしましょう

```
[root@ip-10-0-x-xxx app]# docker run --name unicorn --volumes-from data -e RAILS_ENV=production -e SECRET_KEY_BASE=ageman --link mysql:db -d todo/app
```

DBを作成します

```
[root@ip-10-0-x-xxx app]# docker exec -it unicorn bundle exec rake db:create
```

これで準備完了。ブラウザで見ると、TOPに表示されるはずです


![sc 2015-08-02 14.47.47.png](https://qiita-image-store.s3.amazonaws.com/0/27828/6f715097-a304-acc8-647d-39493eb27d31.png)




## 今後の検討事項

本ポストは見通しを良くするために色々省略しています。シンプルなシングルホスト環境でも、以下にあげた事項は検討の価値/必要がアリです

- docker-composeでの起動
- Blue-Green デプロイ
- ディスクIOの最適化
- ログ収集
- rootを使わない