概要
Dockerで起動したPythonのコンテナから
MySQLのコンテナを利用する際に色々ハマったのでまとめます。
環境
ホスト
- Docker for Macのパブリックベータ版
- docker-composeを使用して以下のゲストOSを起動
ゲスト
- MySQLの公式イメージ
-
Pythonの公式イメージ(
3.5.2-alpine
を使用) - Flaskアプリケーションで、SQLAlchemyを通してMySQLにアクセス
ハマったこと
1. mysql
コマンドが見当たらず、コマンドラインで接続できない
Dockerを利用して開発を行う場合
コンテナの軽量化のためにAlpine Linuxを使うケースは多いと思います。
軽量で起動が早い一方で、多くのコマンドを自身でインストールする必要があります。
Alpine Linux製のコンテナからコマンドラインでMySQLコンテナに接続する際に
もちろんmysql
コマンドは入っていないので
パッケージ管理ツールのapkを使って以下のようにインストールします。
$ apk update
$ apk add mariadb-client
mariadb
となっていますが、MySQLでも問題なく利用できます。
DBサーバはlocalhostには保持しないため、クライアントのみインストールしました。
これで以下のようにコマンドを入力すればMySQLをコマンドラインで操作できます。
$ mysql -h {host} -u {user} -p
2. MySQLdb
が使用できず、スクリプトからDBにアクセスできない
PythonでWebアプリケーションを開発する際に
SQLAlchemyをORMとして利用してDBにアクセスすることが多いと思います。
(Djangoの場合は独自のORMを持っているため例外ですが)
Flask-SQLAlchemyのドキュメントに従って、最初は以下のように記載しました。
SQLALCHEMY_DATABASE_URI = 'mysql://username:password@server/db'
この設定でDBへのアクセスを行うとエラーがおきます。
$ ImportError: No module named 'MySQLdb'
そこで以下のようにインストールを試してみますが、うまくいきません。
$ pip install MySQL-python
$ ImportError: No module named 'ConfigParser'
この時点でザッと調べたところ、MySQLdbはPython3では利用できないようです。
いくつかの記事でなんとかして使うための方法も見かけましたが
今回MySQLdbにこだわる理由はないので、他のライブラリを検討します。
SQLAlchemyのドキュメントを見た感じだとPyMySQLが次点で
(英語の読解が曖昧ですが)互換性も問題なさそうなのでこちらを選びました。
以下のようにインストールとコンフィグの変更を行い、正常な動作を確認しました。
$ pip install PyMySQL
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://username:password@server/db'
3. 日本語文字列の処理で失敗する
Pythonを使ってると大体日本語処理で詰まります。
今回は日本語文字列をDBにインサートする際に以下のようなエラーがおきました。
sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1366, "Incorrect string value: '\\xE9\\x98\\xBF\\xE4\\xBA\\x95...' for column 'name' at row 1")
使用するスクリプトのshebang行には以下の指定をしています。
# -*- coding: utf-8 -*-
それ以外に問題があるとすればMySQL側の文字コード設定かと思い
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で文字コードを指定する
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
に追加します。
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
改めて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点紹介しましたが
今後使っていく上で増えていく可能性は往々にしてあります。