MySQL
docker
docker-compose

DockerのMySQLコンテナに外部からアクセスする方法まとめ改

以前「コンテナ上のMariaDBに外部から接続する方法まとめ」というタイトルで、アプリやSQLクライアントから、DockerのMariaDBコンテナにアクセスすべく悪戦苦闘した時の備忘録を記事にしていました。
コンテナ上のMariaDBに外部から接続する方法まとめ - Qiita

ですが、Macに移ってきたのを機に改めてやってみたら こんな面倒なことをする必要は全く無かった ので、改めて備忘録を残しておきます。

※2018/05/27 コメント頂いた内容を適宜反映致しました。

  • IPではなく、コンテナ名でそのままアクセス出来ること
  • MYSQL_DATABASEに記載したスキーマを文字コードを指定しながら作成出来ること

試行環境

  • macOS 10.13 High Sierra
  • Docker for Mac

アプリコンテナとDBコンテナが別になっている環境を想定しています。
なお、今回はMariaDBではなく、MySQL5.7なので、そこだけ前提が違うことをご了承ください。

Docker立ち上げるまで

docker-compose.yml

※2018/05/27 内容を更新しました。

version: '2'
services:
  db:
    build: ./db/
    environment:
      MYSQL_ROOT_PASSWORD: your_password
      MYSQL_DATABASE: your_schema
    ports:
      - "3306:3306"
  app:
    build: ./app/
    ports:
      - "8080:80"
    links:
      - db
    volumes:
      - ./src:/var/www/html
    stdin_open: true
    tty: true

db/Dockerfile

FROM mysql:5.7
COPY ./mysql.conf.d /etc/mysql/mysql.conf.d

db/mysql.conf.d/mysqld.cnf

[mysqld]
〜中略〜
character-set-server=utf8mb4 // mysqldの最後に追加

[client]
default-character-set=utf8mb4 // clientのセクションそのものを追加

ポイントは、

  • environmentとして最低限MYSQL_ROOT_PASSWORDMYSQL_DATABASEを設定しておく
    • 文字コードはmysqld.cnfの中で定義すれば、MYSQL_DATABASEで作成されるスキーマに反映される
  • コンテナ側の3306番ポートに繋がるようにする(ローカル側は何番でも良いが、同じく3306にするのが吉)
    • exposeではなく、portsで。

※appコンテナ側は今回の話題と直接は関係しないので、説明を省きます。

立ち上げ

何も難しいことはありません。普通に立ち上げます。

$ docker-compose up -d

※2018/05/27 必ずしもコンテナの中に入る必要はなくなりました。入り方のメモとして残しておきます。

立ち上がったら、docker execしてコンテナの中に一度入ります。
入る前には、docker psでidを調べておきましょう。
コンテナが特定出来れば良いので、 上3桁ぐらいで十分 です。

$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                    NAMES
97c4d1095fab        dirname_app         "/bin/bash"              14 minutes ago      Up 14 minutes       0.0.0.0:8080->80/tcp     dirname_app_1
d47f748e436c        dirname_db          "docker-entrypoint.s…"   14 minutes ago      Up 14 minutes       0.0.0.0:3306->3306/tcp   dirname_db_1

DBコンテナd47f748e436cdocker execします。

$ docker exec -it d47 /bin/bash

ホストを調べる

実は、アプリコンテナからDBコンテナにはlocalhostではアクセス出来ません(当たり前)
じゃあどうするかというと、DBコンテナのIPが分かっている必要があります。(※)


※2018/05/27追記

kazuyoshikitahara様、otolab様からのご指摘コメントの内容を反映してなかったので追記。

IP分かっている必要はありません。 docker-compose.yml に記載したコンテナ名でアクセス出来ます。
/etc/hosts に記載されている、ということは、 そもそもそのホスト名でアクセス出来る ということ。
なので、下記の様に /etc/hosts をわざわざ見る必要はありません。


Dockerの場合はホストを調べれば分かるので、先程docker execしたDBコンテナの中の/etc/hostsを調べます。

ifconfigを使わないのは、コンテナの中にifconfigコマンドがインストールされていないため。

root@d47f748e436c:/# ifconfig
bash: ifconfig: command not found

/etc/hostscatします。

root@d47f748e436c:/# cat /etc/hosts
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.18.0.2  d47f748e436c

先程調べたDBコンテナのidd47f748e436cが、172.18.0.2のホスト名になっています。
ここから172.18.0.2がこのDBコンテナのIPアドレスである、と判断することが出来ます。

これで準備完了です。

SQLクライアントからのアクセス

MacならSequel Pro、WindowsならA5:SQL mk2などのSQLクライアントからアクセスするのは、以下のように設定すればOKです。

  • Host: 127.0.0.1
  • User: root
  • Password: your_password
  • Port: 3306
    • ローカル側のポートを変更した方は、ここをその番号に

Sequel Proの場合は「標準」接続でOKです。

先に調べたIPアドレスではなく、127.0.0.1であるというのがミソです。
localhostでもありません。

127.0.0.1にしないといけない理由(推測)

  • 172.18.0.2というのはDockerのネットワーク上でのローカルIPであるため、その外にあるSQLクライアントからは参照出来ない
    • おそらくではありますが、あながち間違いでも無いかと思ってます。
  • localhostの場合、ソケットの指定が必要になり、どれ使ったら良いのか分からない。
  • docker-compose.ymlportsの設定をしており、ローカルの3306番ポートがコンテナの3306番ポートとリンクしているため、127.0.0.1を指定した時、そのままコンテナの中までリンク出来る。

アプリからのアクセス

アプリからアクセスするには、先程調べたDBコンテナのIPを使用します。 docker-compose.ymlに記載したコンテナ名でアクセスします。

  • Host: 172.18.0.2 db
    • 上記方法で調べたIPを使用してください。今回はこれ。
    • 2018/05/27 上述追記の通り、 docker-compose.yml に記載のコンテナ名で。今回はdbという名前を付けていたので、それを使います。
  • User: root
  • Password: your_password
  • Port: 3306

先述の通り、SQLクライアントの時とは逆で、127.0.0.1localhostではNGです。
当然ながら、アプリコンテナの中にMySQLがあるわけではありませんからね。

例えばPHPで、PDO使って接続するなら、こんな感じで書けばOKです。

$pdo = new PDO('mysql:dbname=your_schema;host=db;', 'root', 'your_password');

TODO

現状試せていないものを一応控えておきます。

root以外のユーザーでも同様に出来るようにする

実はここまでのやり方では、root以外のユーザーでうまくいきません。
docker-compose.ymlenvironmentで、MYSQL_USERを指定した時、などです。
理由は、mysql.userテーブルを覗いてみると、Select_privすら与えられてないため。
MYSQL_USERappというユーザーを追加した時の、mysql.userを見るとこうなってます。

mysql> select host, user, Select_priv from mysql.user;
+-----------+---------------+-------------+
| host      | user          | Select_priv |
+-----------+---------------+-------------+
| localhost | root          | Y           |
| localhost | mysql.session | N           |
| localhost | mysql.sys     | N           |
| %         | root          | Y           |
| %         | app           | N           |
+-----------+---------------+-------------+
5 rows in set (0.00 sec)

どこかで、MYSQL_USERに対しても権限を与えないといけません。
これはTODOとして、改めて調べないといけませんね。。

文字コードを指定しながらスキーマを作成する

2018/05/27追記。

docker-compose.ymlにて、MYSQL_DATABASEを定義するとコンテナ作成時にスキーマを作成してくれます。
ただ問題は、設定をしてあげないと文字コードがlatin1になってしまうこと。
utf8mb4などにするには、コンテナ内/etc/mysql/mysql.conf.dに下記の内容を含んだmysqld.cnfを配置すればOKです。

[mysqld]
〜中略〜
character-set-server=utf8mb4 // mysqldの最後に追加

[client]
default-character-set=utf8mb4 // clientのセクションそのものを追加

おわりに

以前Docker for Windowsでやっていた時にはドハマリしていたところが、なぜか楽に出来ていたのは謎ですが。。。
Sequel ProなどのSQLクライアントからアクセス出来ないのは地味にダルいので、あまり手間掛けずアクセス出来るようになってよかったです。