Docker: MySQLコンテナのポートをMac側に公開せずに、MacからそのMySQLに接続する小技

DockerやDocker Composeで、MySQLコンテナを起動するとき、どうしてもポートをホスト側に公開できないが、ホスト側からはDBにはアクセスしたいことがあります。

この投稿では、Mutagenを利用して、ポートを開けずとも、ホスト側からMySQLコンテナにアクセスする方法を紹介します。

## この投稿が解決したいこと

この投稿が解決するのは、下記のような制約とやりたいことの板ばさみ状態です。

__制約__

* MySQLコンテナのポートを開けるのが面倒。
    * 例えば、複数のプロジェクトに携わっていて、複数のdocker-compose.ymlがあり、それぞれにMySQLコンテナがある。
    * それぞれに3306, 3307, 3308, ...と割り当てていくのは、重複を意識したり管理が面倒
* 勝手にはMySQLのポートをホスト側に公開できない。
    * 例えば、MySQLのポートが空いていないdocker-compose.ymlがチームで共有されていて、設定を変更したくない。
* ポートを公開するのに消極的。
    * デバッグのために、ポートを開けるのはあまり気が進まない。

__やりたいこと__

* Mac(ホストマシン)側からMySQLのコンテナのDBをいじりたい。
* MacのSequel ProからDBを参照したい。
* PhpStormなどのIDEから、Docker上のMySQLを参照したい。

__この投稿で紹介する解決策が意味ないケース__

* 普通に、`-p`, `--publish`オプションでコンテナのポートが公開できる。
    * シンプルにこれができるなら、これがベスト。

## 解決策の概要

解決策の概要は、[Mutagen]のForward機能を使って、Macの3306ポートをコンテナの3306ポートにネットワーク転送する、というものです。

[Mutagen]: https://mutagen.io/

## Mac側にポートを公開せずに、Docker上のMySQLに接続する方法

### Mutagenをインストールする

Homebrewでインストールします:

```
brew install mutagen-io/mutagen/mutagen
```

### 普通にMySQLコンテナを立ち上げる

MySQLコンテナは、ポートを公開しない設定で普通に起動すればOK:


```
docker-compose up -d
```


```docker-compose.yml
version: '3.1'

services:
  db:
    image: mysql
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' # 便宜上rootのパスワードは空欄にしときます
      MYSQL_DATABASE: project1_db # データベース名の設定
```

### 起動したMySQLコンテナの名前をひかえる

Mutagenでネットワーク転送をかけるには、Dockerコンテナの名前を知っておく必要があるので、それを調べておきます:

```
$ docker-compose ps
    Name                   Command               State          Ports
----------------------------------------------------------------------------
project1_db_1   docker-entrypoint.sh --def ...   Up      3306/tcp, 33060/tcp
```

この例だと、`project1_db_1`がコンテナの名前です。

### Mutagenでネットワーク転送をかける

`mutagen`の`foward create`コマンドでネットワーク転送をしかけます:

```
mutagen forward create --name project1-mysql tcp::3306 docker://project1_db_1:tcp::3306
```

コマンドのオプションと引数を説明すると:

* `--name project1-mysql`: ネットワーク転送管理用の名称です。ネットワーク転送を解除するときに使います。好きに決めてOK。
* `tcp::3306`: ひとつめの引数のこれは、ホスト側、つまりMac側のポートです。ここでは、3306を指定しているので、Macからは`localhost:3306`でMySQLコンテナに繋がります。
* `docker://project1_db_1:tcp::3306`: ふたつめの引数のこれは、MySQLコンテナがEXPOSEしているポートです。Docker Hubに公開されている`mysql`イメージの場合、3306がEXPOSEされているので、ここではそれを指定しています。


### Mac側のMySQLクライアントで接続する

ここまでやれば、Mac側のMySQLクライアントで接続することができます:

```
$ mysql -uroot -h 127.0.0.1 -e 'SHOW DATABASES'
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| project1_db        |
| sys                |
+--------------------+
```

先程の`docker-compose.yml`で設定したデータベース名の`project1_db`が表示されているので、ちゃんとコンテナのDBに接続できていることが分かります。

## ちなみに

Mutagenで作ったネットワーク転送は明示的に削除しない限り、残留します。

ネットワーク転送の一覧と削除は以下のコマンドでできます。

### Mutagenでネットワーク転送の一覧を表示する方法

```
$ mutagen forward list
--------------------------------------------------------------------------------
Name: project1-mysql
Identifier: fwrd_UOiwU9MsAHe4NxTZR0iwxxCypNyL9ca2AscBhrhirfX
Labels: None
Source:
	URL: tcp::3306
	Connection state: Connected
Destination:
	URL: docker://project1_db_1:tcp::3306
		DOCKER_HOST=
		DOCKER_TLS_VERIFY=
		DOCKER_CERT_PATH=
	Connection state: Connected
Status: Forwarding connections
--------------------------------------------------------------------------------
```

### Mutagenでネットワーク転送を削除する方法

```
mutagen forward terminate project1-mysql
```

---

最後までお読みくださりありがとうございました。Twitterでは、Qiitaに書かない技術ネタなどもツイートしているので、よかったらフォローしてもらえると嬉しいです:relieved:→[Twitter@suin](https://twitter.com/suin)