DockerでPythonとMySQLを使うときにハマった3つのこと


## 概要

Dockerで起動したPythonのコンテナから
MySQLのコンテナを利用する際に色々ハマったのでまとめます。

## 環境

### ホスト
* Docker for Macのパブリックベータ版
* docker-composeを使用して以下のゲストOSを起動

### ゲスト
* [MySQLの公式イメージ](https://hub.docker.com/_/mysql/)
* [Pythonの公式イメージ](https://hub.docker.com/_/python/)(`3.5.2-alpine`を使用)
* Flaskアプリケーションで、SQLAlchemyを通してMySQLにアクセス

## ハマったこと

### 1. `mysql`コマンドが見当たらず、コマンドラインで接続できない

Dockerを利用して開発を行う場合
コンテナの軽量化のためにAlpine Linuxを使うケースは多いと思います。
軽量で起動が早い一方で、多くのコマンドを自身でインストールする必要があります。

Alpine Linux製のコンテナからコマンドラインでMySQLコンテナに接続する際に
もちろん`mysql`コマンドは入っていないので
パッケージ管理ツールのapkを使って以下のようにインストールします。

```bash:クライアントをインストール
$ apk update
$ apk add mariadb-client
```

`mariadb`となっていますが、MySQLでも問題なく利用できます。
DBサーバはlocalhostには保持しないため、クライアントのみインストールしました。

これで以下のようにコマンドを入力すればMySQLをコマンドラインで操作できます。

```bash:接続コマンド（ホスト名とユーザ名は読み替えてください）
$ mysql -h {host} -u {user} -p
```

### 2. `MySQLdb`が使用できず、スクリプトからDBにアクセスできない

PythonでWebアプリケーションを開発する際に
SQLAlchemyをORMとして利用してDBにアクセスすることが多いと思います。
（Djangoの場合は独自のORMを持っているため例外ですが）

[Flask-SQLAlchemyのドキュメント](http://flask-sqlalchemy.pocoo.org/2.1/config/#configuration-keys)に従って、最初は以下のように記載しました。

```python:config.py
SQLALCHEMY_DATABASE_URI = 'mysql://username:password@server/db' 
```

この設定でDBへのアクセスを行うとエラーがおきます。

```bash:エラーログ
$ ImportError: No module named 'MySQLdb'
```

そこで以下のようにインストールを試してみますが、うまくいきません。

```bash:MySQLdbのインストール
$ pip install MySQL-python
```

```bash:エラーログ
$ ImportError: No module named 'ConfigParser'
```

この時点でザッと調べたところ、MySQLdbはPython3では利用できないようです。
いくつかの記事でなんとかして使うための方法も見かけましたが
今回MySQLdbにこだわる理由はないので、他のライブラリを検討します。

[SQLAlchemyのドキュメント](http://docs.sqlalchemy.org/en/latest/dialects/mysql.html#module-sqlalchemy.dialects.mysql.pymysql)を見た感じだと[PyMySQL](https://github.com/PyMySQL/PyMySQL)が次点で
（英語の読解が曖昧ですが）互換性も問題なさそうなのでこちらを選びました。
以下のようにインストールとコンフィグの変更を行い、正常な動作を確認しました。

```bash:PyMySQLのインストール
$ pip install PyMySQL
```

```python:config.py
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://username:password@server/db' 
```

### 3. 日本語文字列の処理で失敗する

Pythonを使ってると大体日本語処理で詰まります。
今回は日本語文字列をDBにインサートする際に以下のようなエラーがおきました。

```bash:エラー
sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1366, "Incorrect string value: '\\xE9\\x98\\xBF\\xE4\\xBA\\x95...' for column 'name' at row 1")
```

使用するスクリプトのshebang行には以下の指定をしています。

```python:xxx.py
# -*- coding: utf-8 -*- 
```

それ以外に問題があるとすればMySQL側の文字コード設定かと思い
`mysql`のコマンドラインで以下のように設定を調べます。

```bash:MySQLの設定を確認
MySQL [(none)]> SHOW VARIABLES LIKE 'chara%'; 
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)
```

`latin1`が混ざってるのがいかにもで怪しいですね。
このままでも特にメリットがないので変更したいと思います。

一番まともな方法は`my.cnf`の設定だと思いますが
今回はコンテナとしてMySQLサーバを起動しているため、変更が厄介です。
そこで、そもそもコンテナを立てるときにこれを設定することを考えました。

調査の結果、以下のページが非常に参考になりました。
[Docker公式イメージのMySQLで文字コードを指定する](http://qiita.com/neko-neko/items/de8ea13bbad32140de87)
[MySQLの公式イメージ](https://hub.docker.com/_/mysql/)

> Many configuration options can be passed as flags to mysqld. This will give you the flexibility to customize the container without needing a cnf file.

（和訳）
mysqldに対して多くのオプションをフラグとして渡すことができます。
これにより`cnf`ファイルを必要とせずにコンテナを柔軟にカスタマイズできます。

筆者の環境ではdocker-composeを利用しているため
以下の記載を`docker-compose.yml`に追加します。

```bash:docker-compose.yml
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
```

改めてMySQLの設定を確認します。

```bash:MySQLの設定を確認
MySQL [(none)]> SHOW VARIABLES LIKE 'chara%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)
```

`character_set_database`と`character_set_server`が`utf-8`に変化しました。
この状態で無事に日本語をインサートすることができました。

## まとめ

コンテナでDBサーバを作成しているがゆえの問題もいくつか起きますが
`docker-compose`コマンドでポチポチやるだけで環境ができてしまうので
慣れてしまえば開発のスピードは上げられると思います。

ちなみに、今回ハマりどころを3点紹介しましたが
今後使っていく上で増えていく可能性は往々にしてあります。