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


面白法人カヤックさんのインターンで Sinatra + Nginx + MySQL を使った簡易イ〇スタを作りました!後日それをdocker-composeで動かそうとしたときに、docker-compose 周りで死ぬほどハマった上にあんまり解決策出てこず苦労しました。。。

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

最終的なdocker-compose.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
によると

なんとdevelopment環境だと、localhostからのアクセスしか受け付けないのがデフォルトらしい!!

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


Sinatra: Configuring Settings

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ブログ
docker-composeに

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

を追加しましょう。

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

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

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にしているので、それを使ってアクセスできます!

    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で設定可能です。(これに気づくのに苦労しました・・・)

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で一発起動しますb

GitHub - koyo-miyamura/latestgram: 最新50件表示する簡易画像投稿サービス