Docker-composeでSinatra + Nginx + MySQL を動かす時のハマりポイント

[面白法人カヤック](https://www.kayac.com/)さんのインターンで Sinatra + Nginx + MySQL を使った[簡易イ〇スタ](https://github.com/koyo-miyamura/latestgram)を作りました！後日それをdocker-composeで動かそうとしたときに、docker-compose 周りで死ぬほどハマった上にあんまり解決策出てこず苦労しました。。。

備忘録がてらシェアです！

### 最終的なdocker-compose.yml

```yml
version: "3"

services:
  web:
    build: . 
    volumes: 
      - .:/app
    ports: 
      - "4567:4567"
    depends_on:
      - db
    command: bundle exec ruby myapp.rb -o 0.0.0.0

  nginx:
    image: nginx:latest
    volumes: 
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
      - ./public:/var/www/html
    ports:
      - "8080:8080"
    depends_on:
      - web

  db:
    image: mysql:latest
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: "true"
      MYSQL_DATABASE: "latestgram"
      MYSQL_USER: "root"
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    ports:
      - "3306:3306"
```

### 1. コンテナ上でSinatraが動かない
##### 状況
ローカル環境では（dockerのホストipがlocalhostの時）`localhost:4567`でアクセスできるのですが、なぜかコンテナ上で動いている時にアクセスできない・・・
もちろん`docker-compose.yml`にポート解放の記述はあります。

##### 解決策
`docker-compose`の書き方が悪いのかと思って死ぬほどググりましたが、結局Sinatra側の仕様のようです。
[Sinatraがデフォルトでは外部から繋がらなくなってたよ - Qiita](https://qiita.com/u1_fukui/items/b86b21f6ed39f4c10d5d#environment-%E3%82%92%E5%A4%89%E3%81%88%E3%81%A6%E3%82%82%E3%81%84%E3%81%91%E3%82%8B)
によると
> なんとdevelopment環境だと、localhostからのアクセスしか受け付けないのがデフォルトらしい！！

とのこと。
じゃあdockerfileに`CMD["bundle", "exec", "ruby", "myapp.rb"`**`,"-o", "0.0.0.0"]`**とか`docker-compose.yml`に`command: bundle exec ruby myapp.rb -o 0.0.0.0`すればいいのか！
・・・できない！！なぜだああああああ

---

[Sinatra: Configuring Settings](http://sinatrarb.com/configuration.html)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/a6c3ffbe-26d0-9fe8-5b72-6eb7bbb64186.png)

myapp.rb内で`set :bind '0.0.0.0'`する方法もあるらしいので試してみる。
・・・なぜか上手くいった！
なぜ上手くいかなかったんだ・・・？

### 2. 日本語を使う場合にMySQLの文字コードエラー
##### 状況
ローカルのMacOSやWindows(WSL)では日本語を扱えるのに、docker-compose up すると
`Incorrect string value: '\xE3\x81\x82' for column ...`
とか
`Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation '='`
が出る

##### 解決策
デフォルトの文字コードを設定する必要があるそうです
[MySQLのIllegal mix of collations (latin1_swedish_ci,IMPLICIT)のエラーについて - 文系プログラマによるTIPSブログ](http://www.bunkei-programmer.net/entry/2016/01/25/001851)
docker-composeに

```yml
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
```

を追加しましょう。

そして、忘れてはいけないのが**すでに起動しているMySQLコンテナを破棄して`docker-compose up`する**こと
こうしないと、変更前のコンテナ使ってしまうので同じエラー出ます

これに気づかずに2時間くらい消費したので、みなさんは私の屍を超えていってください・・・orz

```bash
docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS                            NAMES
9d1e9aac7bd1        hoge      "bundle exec ruby my…"   10 minutes ago      Up 10 minutes               0.0.0.0:4567->4567/tcp           fugafuga
```
こんな感じで出てくるので、CONTAINER IDを参照して
`docker rm 出てきたID`でいけます！

ちなみに`docker-compose.yml`内で自分で作成した`Dockerfile`がある場合は、`Dockerfile`を編集した際にdockerイメージをbuildし直さないといけないので注意です。
`docker-compose build`でいいと思います。
### 3. nginxやmysqlにdockerのホストIPを伝えたり、コンテナ経由でアクセス
Windowsでは`Docker Quickstart Terminal`を使っていて私の環境では`192.168.99.100`でコンテナにアクセス出来ます。

しかしMacで`Docker for Mac`などを使ってターミナル上で作業する場合は`localhost`でのアクセスになります。

またnginxコンテナ→sinatraコンテナへアクセスする場合にはコンテナ通信用のipアドレスを割り当てないといけません。


##### mysql接続
`docker-compose.yml`でmysqlコンテナのサービス名をdbにしているので、それを使ってアクセスできます！

```ruby
    def db_connect()
      client = Mysql2::Client.new(
        :host     => 'db',
        :port     => '3306',
        :username => 'root',
        :password => '',
        :database => 'latestgram',
        :encoding => 'utf8mb4',
        :datatbase_timezone => :local
      )
      return client
    end
```

##### nginx.conf
同様に、Sinatraコンテナのサービス名webを使います。
また、dockerのホストipは`$host`で設定可能です。（これに気づくのに苦労しました・・・）

```nginx
worker_processes  1;

events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;

    keepalive_timeout  65;

    gzip  on;

    upstream latestgram{
        server web:4567;
    }

    server {
        listen       8080;
        server_name  $host;
        root  /var/www/html;

        proxy_set_header Host               $host:8080;
        proxy_set_header X-Forwarded-For    http://latestgram:4567;
        proxy_set_header X-Forwarded-Host   $host:8080;
        proxy_set_header X-Forwarded-Server $host:8080;

        location / {
            proxy_pass http://latestgram;
            root /var/www/html;
        }

        # redirect server error pages to the static page /50x.html
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
```

### 作ったもの
・・・というわけで作ったものはこちらです！
メンターの方がしっかりサポートしていただいて1週間で形にできました！

cloneして`docker-compose up`で一発起動しますｂ

[GitHub - koyo-miyamura/latestgram: 最新50件表示する簡易画像投稿サービス](https://github.com/koyo-miyamura/latestgram)

![image](https://user-images.githubusercontent.com/18478417/38561824-08fdb400-3d14-11e8-9fee-98ec7e1bbf17.png)