Docker ComposeによるMySQL, Redis, nginx, Rails構成の設定例

## 導入

Docker Composeを利用して、複数のdocker containerを使った環境の設定例を紹介します。なお、Docker ComposeはWindowsでは使えません。Max OSX or 64bit Linuxのみ対象となっています。

### 準備

Docker Toolboxをインストールできれば環境はすべて整います。
https://www.docker.com/products/docker-toolbox
docker, docker-machine, docker-composeといったツールとVirtualBoxがインストールされるはずです。

次にmachineの準備
https://docs.docker.com/machine/get-started/

```
$ docker-machine create --driver virtualbox default
$ docker-machine env default

export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/user/.docker/machine/machines/default"
export DOCKER_MACHINE_NAME="default"
# Run this command to configure your shell: 
# eval $(docker-machine env default)

$ eval $(docker-machine env default)
```

これでdefault machineを使ってdocker containerを起動することができるようになりました。
`docker-machine ls`で確認できます

```
$ docker-machine ls
NAME         ACTIVE   DRIVER       STATE     URL                          SWARM   DOCKER    ERRORS
default      *        virtualbox   Running   tcp://192.168.99.100:2376            v1.10.1   
prod         -        google       Running   tcp://104.155.xxx.xxx:2376           v1.9.1    
prod2        -        google       Running   tcp://104.199.xxx.xxx:2376           v1.10.1 
```

### Version

今回利用したdockerツール類のversionを記載しておきます。

```
$ docker-machine -v
docker-machine version 0.6.0, build e27fb87
$ docker-compose -v
docker-compose version 1.6.0, build d99cad6
$ docker -v        
Docker version 1.10.1, build 9e83765
```

## Docker Compose 1.6からの書式変更

docker-composeは1.6からconfigurationの書式が変更になっているのでご注意ください。
以下のように構造が変わっています。

### 1.5系まで(version 1)

```
db:
  image: postgres
web:
  build: .
  command: bundle exec rails s -p 3000 -b '0.0.0.0'
  volumes:
    - .:/myapp
  ports:
    - "3000:3000"
  links:
    - db
```

### 1.6以降

以下のようにversionを明示し, 各serviceの設定を束ねるservicesが追加されました。

```
version: '2'
services:
  db:
    image: postgres
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    links:
      - db
```

1.6でもversion1の書式を受け付けてくれますが、サポートは1.6系までのため新規のものはversion 2で設定してください。

- https://docs.docker.com/compose/compose-file/#versioning

# Docker Compose 設定例

`rails new myapp`で作成し、myappは以下の構成となっていることを前提とします。

```
$ ls -1F 
Dockerfile
Dockerfile.prod
Gemfile
Gemfile.lock
README.rdoc
Rakefile
app/
bin/
config/
config.ru
db/
doc/
docker-compose.prod.yml
docker-compose.yml
lib/
log/
nginx/
public/
script/
test/
tmp/
vendor/
```

- docker-compose.ymlがcurrent dirにあると`docker-compose up -d`のみでbuildからcontainer起動まですみます。
- docker-compose.prod.ymlのようにdefault以外の名前をつけた場合、-fオプションで指定する必要があります
  - 例: `docker-compose -f docker-compose.prod.yml up -d`

以下のページを参考に設定してきます。
https://docs.docker.com/compose/compose-file/

## development(docker-compose.yml)

```yaml
version: '2'
services:
  db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: $MYAPP_DATABASE_PASSWORD
    volumes:
      - db-data:/var/lib/mysql
  redis:
    image: redis:3.0.5
  app:
    build: .
    environment:
      MYAPP_DATABASE_USERNAME: root
      MYAPP_DATABASE_PASSWORD: $MYAPP_DATABASE_PASSWORD
      MYAPP_DATABASE_HOST: db
      MYAPP_SLACK_API_TOKEN: $MYAPP_SLACK_API_TOKEN
  web:
    extends:
      service: app
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    links:
      - db
      - redis
  worker:
    extends:
      service: app
    command: bundle exec sidekiq -q notification
    volumes:
      - .:/myapp
    links:
      - db
      - redis

volumes:
  db-data:
    driver: local
```

### image

`image: mysql`は`image: mysql:latest`省略であり、2016/02/17現在は`image: mysql:5.7.11`を意味します
buildのたびにバージョンがばらけてしまうことになるので、`image: mysql:5.7`や`image: mysql:5.7.11`というようにversion指定したほうがよいです。

基本的なものは https://hub.docker.com/ から探すことができます。
- https://hub.docker.com/_/mysql/
- https://hub.docker.com/_/redis/
- https://hub.docker.com/_/nginx/

### environment

containerに渡す環境変数の設定ですが、password類をyamlに書いてrepositoryに突っ込むわけにもいかないので、以下のようにshellの環境変数を渡します。

```
    environment:
      MYSQL_ROOT_PASSWORD: $MYAPP_DATABASE_PASSWORD
```

### volumes

databaseのように永続的なストレージが欲しい場合に必要な設定です。

```
    volumes:
      - db-data:/var/lib/mysql

volumes:
  db-data:
    driver: local
```

https://docs.docker.com/compose/compose-file/#volume-configuration-reference

### extends

今回の例のようにwebとworker containerがあり、設定が共通している場合にはextendsを利用して共通化することができます。

```
  app:
    build: .
  web:
    extends:
      service: app
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
  worker:
    extends:
      service: app
    command: bundle exec sidekiq -q notification
```

設定ファイルを分けて管理することもできます。
https://docs.docker.com/compose/compose-file/#extends

### links

linksを設定すると、container内で名前が引けるようになります。今回の例ではwebがdbとredisに接続するため、設定しています。

```
    links:
      - db
      - redis
```

## production(docker-compose.prod.yml)


```yaml
version: '2'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
    environment:
      RAILS_SERVE_STATIC_FILES: 'true'
      MYAPP_SECRET_KEY_BASE: $MYAPP_SECRET_KEY_BASE
      MYAPP_DATABASE_USERNAME: app
      MYAPP_DATABASE_PASSWORD: $MYAPP_DATABASE_PASSWORD
      MYAPP_DATABASE_HOST: db
      MYAPP_SLACK_API_TOKEN: $MYAPP_SLACK_API_TOKEN
    logging:
      driver: syslog
      options:
        syslog-address: "udp://logs3.papertrailapp.com:xxx"
  web:
    extends:
      service: app
    command: bundle exec puma -C config/puma.rb
    links:
      - db
      - redis
  worker:
    extends:
      service: app
    command: bundle exec sidekiq -q notification
    links:
      - db
      - redis

  nginx:
    build: ./nginx
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt
    links:
      - web
    logging:
      driver: syslog
      options:
        syslog-address: "udp://logs3.papertrailapp.com:xxx"

  db:
    image: mysql
    volumes:
      - db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: $MYAPP_DATABASE_PASSWORD
      MYSQL_DATABASE: myapp_production
      MYSQL_USER: driver
      MYSQL_PASSWORD: $MYAPP_DATABASE_PASSWORD
    logging:
      driver: syslog
      options:
        syslog-address: "udp://logs3.papertrailapp.com:xxx"

  redis:
    image: redis:3.0.5
    logging:
      driver: syslog
      options:
        syslog-address: "udp://logs3.papertrailapp.com:xxx"

volumes:
  db-data:
    driver: local
```

### build

`build: .`であれば./Dockerfileを使ってbuildしますが、別のdirectoryやfileの場合にはcontext,dockerfileで指定してください。

```
  app:
    build:
      context: .
      dockerfile: Dockerfile.prod
```

以下のように書くと、./nginx/Dockerfileを使ってbuildします。

```
  nginx:
    build: ./nginx
```

### logging

特に設定しない場合は`driver: json-file`となり、docker machine上の`/var/lib/docker/containers/*/*-json.log`にログが出力されています。
ログを何処かに集約したい場合は、fluentd containerを用意してあれこれしたり、以下の様なlogging設定をする方法などが考えられます。

```
    logging:
      driver: syslog
      options:
        syslog-address: "udp://logs3.papertrailapp.com:xxx"
```
https://papertrailapp.com/ に転送する例です。実際にはxxxに指定されたport番号を書いておく必要があります。

Docker本体に関わることですが、log周りは以下が片付くとまたかわってくると思います。
- [Logging driver plugins](https://github.com/docker/docker/issues/18604)
- [Proposal: New logging driver that logs to tcp, udp and unix domain sockets](https://github.com/docker/docker/pull/18001)


## 補足

### Dockerfile

```
$ cat Dockerfile
FROM ruby:2.2.4
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential
RUN mkdir /myapp
WORKDIR /myapp
ADD Gemfile Gemfile.lock /myapp/
RUN bundle install
ADD . /myapp
```

### Dockerfile.prod

```
FROM ruby:2.2.4
ENV LANG C.UTF-8
RUN apt-get update -qq && apt-get install -y build-essential
RUN mkdir /myapp
WORKDIR /myapp

ADD Gemfile Gemfile.lock /myapp/
RUN bundle install --jobs 20 --retry 5 --without development test

ENV RAILS_ENV production
ADD . /myapp
RUN bundle exec rake tmp:create
RUN bundle exec rake assets:precompile
```

### .dockerignore

.dockerignoreファイルを用意することで余計なものをcontainerに載せずにすみます。
https://docs.docker.com/engine/reference/builder/#dockerignore-file

```
.git
log
tmp
```