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

  • 15
    Like
  • 0
    Comment

概要

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

環境

ホスト

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

ゲスト

ハマったこと

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のドキュメントに従って、最初は以下のように記載しました。

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

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

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

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

MySQLdbのインストール
$ pip install MySQL-python
エラーログ
$ ImportError: No module named 'ConfigParser'

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

SQLAlchemyのドキュメントを見た感じだとPyMySQLが次点で
(英語の読解が曖昧ですが)互換性も問題なさそうなのでこちらを選びました。
以下のようにインストールとコンフィグの変更を行い、正常な動作を確認しました。

PyMySQLのインストール
$ pip install PyMySQL
config.py
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行には以下の指定をしています。

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

それ以外に問題があるとすればMySQL側の文字コード設定かと思い
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に追加します。

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

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

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_databasecharacter_set_serverutf-8に変化しました。
この状態で無事に日本語をインサートすることができました。

まとめ

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

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