読者です 読者をやめる 読者になる 読者になる

文系プログラマによるTIPSブログ

文系プログラマ脳の私が開発現場で学んだ事やプログラミングのTIPSをまとめています。

docker for macでMySQLコンテナを生成してリモート接続する

docker mysql

ついにGAになったdocker for macとdocker for windows。これでVirtualboxやVMWareを別途インストールする事なくdockerが利用可能になりましたね!
f:id:treeapps:20160730183608p:plain
docs.docker.com

docker for macですが、既にdocker toolboxでdocker一式をインストールしている状態でも、特に何かをアンインストールする事なくdocker for macをインストールすれば、途中でマイグレーションする?と聞かれてそのままdocker-machineからdocker for macに移行できます。

という事で早速試してみたのですが、MySQLのコンテナにリモートで接続しようとして、ちょっと困った事が起きました。

MYSQLコンテナにつながらない!?

接続するためのMySQLコンテナですが、主要なmysql・mariadbのコンテナを一括生成するdocker-compose関連ファイル一式を既に用意してあります。
github.com

今回はこれを使って、今までのようなdocker-machine上のdockerホストではなく、macのローカルのdockerホストにdocker composeしてみます。

docker-machineを使ってしまうと今までのようにVirtualboxやVMWarefusion等が必要になってしまうため、docker for macでxhyveを使ってVirtualbox・VMWarefusion無しでdockerを動かします。

MySQLコンテナを起動する

まずは前述のファイル群をcloneします。

tree:test tree$ git clone https://github.com/treetips/docker-compose-all-mysql.git
Cloning into 'docker-compose-all-mysql'...
remote: Counting objects: 20, done.
remote: Total 20 (delta 0), reused 0 (delta 0), pack-reused 19
Unpacking objects: 100% (20/20), done.
Checking connectivity... done.

中身はこんな感じです。

tree:test tree$ cd docker-compose-all-mysql/
tree:docker-compose-all-mysql tree$ ll
total 56
-rw-r--r--  1 tree  wheel   2.7K  7 30 16:26 README.md
-rwxr-xr-x  1 tree  wheel   110B  7 30 16:26 connect-mariadb-10.0.sh
-rwxr-xr-x  1 tree  wheel   110B  7 30 16:26 connect-mariadb-10.1.sh
-rwxr-xr-x  1 tree  wheel   110B  7 30 16:26 connect-mysql-5-5.sh
-rwxr-xr-x  1 tree  wheel   110B  7 30 16:26 connect-mysql-5-6.sh
-rwxr-xr-x  1 tree  wheel   110B  7 30 16:26 connect-mysql-5-7.sh
-rw-r--r--  1 tree  wheel   1.3K  7 30 16:26 docker-compose.yml
drwxr-xr-x  3 tree  wheel   102B  7 30 16:26 mariadb10.0
drwxr-xr-x  3 tree  wheel   102B  7 30 16:26 mariadb10.1
drwxr-xr-x  3 tree  wheel   102B  7 30 16:26 mysql5.5
drwxr-xr-x  3 tree  wheel   102B  7 30 16:26 mysql5.6
drwxr-xr-x  3 tree  wheel   102B  7 30 16:26 mysql5.7

では全MySQLコンテナを起動します。沢山イメージをダウンローするのでちょっと時間かかります。

tree:docker-compose-all-mysql tree$ docker-compose up -d
Creating mariadb10.1
Creating mysql5.6
Creating mysql5.5
Creating mariadb10.0
Creating mysql5.7

起動したコンテナを見てみます。

tree:docker-compose-all-mysql tree$ docker-compose ps
   Name                 Command             State           Ports
--------------------------------------------------------------------------
mariadb10.0   docker-entrypoint.sh mysqld   Up      0.0.0.0:3310->3306/tcp
mariadb10.1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3311->3306/tcp
mysql5.5      docker-entrypoint.sh mysqld   Up      0.0.0.0:3355->3306/tcp
mysql5.6      docker-entrypoint.sh mysqld   Up      0.0.0.0:3356->3306/tcp
mysql5.7      docker-entrypoint.sh mysqld   Up      0.0.0.0:3357->3306/tcp

Portsに書いてある0.0.0.0は特殊な外部に全公開するIPで、localhostや127.0.0.1で接続が可能な状態です。(後述で0.0.0.0についての説明を追記しました

0.0.0.0がlocalhostで接続できるなら、一見以下で接続できそうな気がしますね。localhostであれば「-h」でホスト指定は不要な筈です。

mysql -uworker -pworker -Dwork

しかしこの結果は以下です。

tree:docker-compose-all-mysql tree$ mysql -P3357 -uworker -pworker -Dwork
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

エラーを見ると、どうやらMySQLコンテナ上のMySQLに接続しにいっているのではなく、macのローカルのMySQLに接続しようとしているようです。

ではIP指定ならどうでしょう。

tree:mysql tree$ mysql -h127.0.0.1 -P3357 -uworker -pworker -Dwork
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.13 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

いけました。MySQLコンテナ上のMySQL5.7に接続できました。
ついでにDHCPで振られたIP指定ならどうでしょう。

tree:mysql tree$ mysql -h$(ipconfig getifaddr en1) -P3357 -uworker -pworker -Dwork
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.7.13 MySQL Community Server (GPL)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

これもOKですね。どうやら localhost だけ、挙動が異なるようです。

何故localhostではダメなのか

root@5124e62261a5:/# cat /etc/mysql/my.cnf
・・・中略・・・
[mysqld]
skip-host-cache
skip-name-resolve

恐らくですが、MySQL公式Dockerfileから生成されるmy.cnfに「skip-name-resolve」がハードコードで設定されているため、DNS逆引きが行わず、localhostと127.0.0.1は別物として扱われたため、macのローカルのMySQLに接続しにいっていると思われます。

試しに以下のように変えようとしてもread onlyだと怒られてしまいました。rootユーザで行っても同様です。

mysql> set global skip_name_resolve = 'OFF';
ERROR 1238 (HY000): Variable 'skip_name_resolve' is a read only variable

mysqlの起動オプションに「--disable-skip-name-resolve」というものがあって、mysqldを起動する時に指定する事で「skip-name-resolve」をオフにできるようですが、my.cnfや環境変数で変更できないのはちょっと嫌ですね。

my.cnfを指定したmysql接続

ならば、IP・PORT・ユーザID・パスワード等を気にしなくていい接続の仕方をすればいいのです。

これには2通りあります。

解決策1) --defaults-group-suffix を使う

このオプションは、接続情報を ~/.my.cnf に記述しておくものです。具体的には以下のように使用します。

cat << EOF > ~/.my.cnf
[client_mysql5.7]
host=127.0.0.1
port=3357
database=work
user=worker
password=worker
EOF
chmod 600 ~/.my.cnf

この .my.cnf が有る状態で以下のようにコマンドを実行すると、MySQLコンテナに接続できるようになります。

mysql --defaults-group-suffix=_mysql5.7

こうすれば前述のホストにIP指定するのが面倒臭いよ問題と、mysqlコマンド実行時の以下の警告の抑制を、同時に解決する事ができます。

mysql: [Warning] Using a password on the command line interface can be insecure.

「[client_mysql5.7]」というセクションは複数記述する事ができるので、各mysqlの分だけ用意すれば、全て接続でアカウント情報無しで接続できるようになります。

解決策2) --defaults-extra-file を使う

一見すると「--defaults-group-suffix」ってホームディレクトリに配置するし、パーミッションも600で安全だし、隠しファイルだし、もしかして完璧?と思うかもしれませんが、弱点があります。

それは「ホームに配置する=git等でバージョン管理し難い」という点です。

「--defaults-extra-file」を使う場合は、my.cnfのファイルパスを指定する事で、mysqlに接続できるようになります。このmy.cnfはどこに配置しても構わないというメリットがあります。勿論ファイル名も「my.cnf」で無くても構いません。

「--defaults-extra-file」で使うmy.cnfは「--defaults-group-suffix」の場合と少し異なり、clientセクションにグループを設定する事はできず、「client」固定です。もし複数の接続が指定したい場合は、ファイルを分ける事で対応できます。

cat << EOF > ~/my5.7.cnf
[client]
host=127.0.0.1
port=3357
database=work
user=worker
password=worker
EOF
chmod 600 ~/my5.7.cnf

最終的に、以下のコマンドでMySQLコンテナに接続できるようになります。

mysql --defaults-extra-file=~/my5.7.cnf

勿論この接続方法の場合も、コマンドライン上でパスワードを指定していないので、以下の警告を抑制する事ができます。

mysql: [Warning] Using a password on the command line interface can be insecure.

--defaults-group-suffix と --defaults-extra-file の比較

--defaults-group-suffix のメリット
  • ファイルの場所は常に固定なので探さなくてもいい。
  • mysql接続時にいちいちmy.cnfのパスを指定しなくていい。(パスは長くなる事が多い)
  • my.cnfを見れば、全てのmysqlの接続先情報が書かれているため一覧性が高い。
--defaults-group-suffix のデメリット
  • my.cnfをホームディレクトリに配置するのでgit等でバージョン管理しづらくなる。
  • 複数プロジェクトでMySQLを使う場合、1ファイルに全プロジェクト、全ステージの接続情報が書かれてしまう。
--defaults-extra-file のメリット
  • my.cnfをどの場所に配置しても構わないので、既存環境に手を入れなくてよくなる。
  • my.cnfという名前でなくてもいい。
  • my.cnfの配置場所が自由なので、git等でバージョン管理し易い。
  • 複数プロジェクトでMySQLを使う場合も、フォルダ・ファイル名を分ける事で綺麗に分離できる。
--defaults-extra-file のデメリット
  • my.cnfの場所を知っていなければいけない。
  • 起動時にmy.cnfの長いパス名を入力するのが辛い。

両者ともメリット・デメリットはありますね。--defaults-group-suffixの方が手軽ではあるのですが、やはり1ファイルに全プロジェクト・全ステージの接続情報が混在するのは嫌ですね。となると--defaults-extra-fileの方がいいのですが、接続時のパス指定が辛い、と。。。。

というか、--defaults-group-suffix も --defaults-extra-file もオプション名が長くてこの時点で辛いんですが。。。

alias使ってコマンドの短縮化をする事は必須ですね。。。

追記1) MySQLを0.0.0.0で公開させない方法

docker for macでMySQLコンテナを生成してリモート接続する - 文系プログラマによるTIPSブログ

0.0.0.0 はすべて(*)なので 127.0.0.1 などと異なり外部からも繋がっちゃいますね。こちらは 127.0.0.1:3355:3306 という感じで指定するほうがいいかも。localhost に関しては https://yoku0825.blogspot.jp/2013/02/userlocalhostuser127001.html

2016/07/30 18:47
b.hatena.ne.jp
上記コメントを頂きました。(実は事前にこちらのサイトを拝見して、127.0.0.1とlocalhostが別ものになる旨を勉強しました)

今回0.0.0.0:3357でMySQLが起動してしまうのは、docker-compose.ymlでportしか指定していない点にあります。

現在の設定は↓こうです。portは3357:3306です。

mysql5.7:
  image: mysql:5.7
  container_name: mysql5.7
  ports:
    - 3357:3306
  environment:
    - MYSQL_DATABASE=work
    - MYSQL_USER=worker
    - MYSQL_PASSWORD=worker
    - MYSQL_ROOT_PASSWORD=root
  volumes:
    - ./mysql5.7/conf.d:/etc/mysql/conf.d

これを、以下のようにします。

mysql5.7:
  image: mysql:5.7
  container_name: mysql5.7
  ports:
    - 127.0.0.1:3357:3306
  environment:
    - MYSQL_DATABASE=work
    - MYSQL_USER=worker
    - MYSQL_PASSWORD=worker
    - MYSQL_ROOT_PASSWORD=root
  volumes:
    - ./mysql5.7/conf.d:/etc/mysql/conf.d

この状態でdocker-composeで起動すると、以下のように127.0.0.1:3357で待受してくれるので、全公開状態にならずに済みました。

tree:docker-compose-all-mysql tree$ docker-compose ps
   Name                 Command             State            Ports
----------------------------------------------------------------------------
mariadb10.0   docker-entrypoint.sh mysqld   Up      0.0.0.0:3310->3306/tcp
mariadb10.1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3311->3306/tcp
mysql5.5      docker-entrypoint.sh mysqld   Up      0.0.0.0:3355->3306/tcp
mysql5.6      docker-entrypoint.sh mysqld   Up      0.0.0.0:3356->3306/tcp
mysql5.7      docker-entrypoint.sh mysqld   Up      127.0.0.1:3357->3306/tcp

この件は全公開状態にならないだけであって、前述のlocalhostでdockerホスト上のMySQLに接続できない問題は解決されていません。

雑感

docker-toolboxを卒業するぞ〜 → 10分で躓く、となって早速出鼻をくじかれて悲しかったです。まあdockerの問題ではなくMySQLの公式イメージの問題なのですけどね。

docker for macを使ってみて思ったのですが、これ、docker-machineのようにdockerホストを分ける事ってできませんよね?docker-machineの場合はプロジェクトごとにdockerホストを分ける事ができるので、環境を分離できて楽だったのですよね。dockerホストが単一だとすると複数のプロジェクトのコンテナが1ホストにダラーっと並んでしまう気がするのですが、そういうものですかね。

複数ホストで運用したい場合は手軽なローカルのxhyveのdockerホストでなく、docker-machineで今までのようにVirtualboxやVMWarefusion使えって事でしょうか。一応docker-machine用のxhyveドライバを作られている方もいるのでそれを使えばVirtualboxやVMWarefusionが不要になるのですが、あまり話を聞かないのでちょっと心配だな〜、と思っています。
github.com

docker社が今後オフィシャルのxhyveのdocker-machineドライバ作ってくれると嬉しいですね(チラッ