MySQL道普請便り

第122回 DockerでMySQLをもっと便利に活用してみる

この記事を読むのに必要な時間:およそ 4 分

本連載でも,何回かMySQLをDockerで扱う方法に関して簡単に説明をしてきました。今回は,DockerでMySQLを使う上でもっと便利に扱う方法を紹介してみたいと思います。特に,初期値のロードやコンテナ内にあるmysqlクライアントを使った時に文字化けする問題に対応していきます。

検証環境

今回はMacでDocker for Macを利用して検証を行っております。また,MySQLのDockerイメージは8.0.20で確認を行います。検証のデータには,第2回 MySQLにはじめてのデータを入れてみるで紹介されたKEN_ALL.CSVを利用します。

初期値のロードを自動化したい

第113回 anemoeaterを使ってスローログを可視化してみるでは,以下のように--secure-file-privを起動時に設定して起動していました。--secure-file-privは,指定されたディレクトリの中にあるファイルに対してLoad Data Infileの実行を認めるオプションです。これを利用して,指定したディレクトリ中にあるファイルを読み込むことで作業を行いました。

以下の起動コマンドでは--secure-file-priv/tmpを指定しているので,/tmp配下にマウントされたファイルを任意にロードすることができるようになります。

docker run --name michibushin -v "/tmp:/tmp" -v "$PWD/config:/etc/mysql/conf.d" -v "$PWD/logs/mysql:/var/log/mysql"  -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 3306:3306  -d mysql:8.0.18 --secure-file-priv=/tmp

起動後にMySQLにログインして,Load Data Infileでデータをロードしていました。

この方法でも,もちろんデータのロードを行うことはできるのですが,実はMySQL公式のコンテナでは,SQLを特定のディレクトリに特定の命名規則でマウントすることで,自動でSQLを起動時に実行する機能があります。

対象となるディレクトリは/docker-entrypoint-initdb.dで,この中に入っている拡張子が.sh.sql.sql.gz.sql.gzのファイルが実行されます。該当するファイルが複数ある場合の実行の順番はアルファベット順で実行されるので,実行に順序が必要な場合には,わかりやすくするためにファイル名に番号を付けておくと良いかもしれません。

前回までと同様に,郵便局のデータを読み込む場合どうなるかを考えてみます。/docker-entrypoint-initdb.dにマウントするディレクトリを以下のように作成します。

$ ls docker-entrypoint-initdb.d
1_ddl.sql               2_load_data_infile.sql  KEN_ALL_UTF8.CSV

続いて,それぞれのファイルの中身について紹介します。

$ cat 1_ddl.sql
DROP SCHEMA IF EXISTS zipcode;
CREATE DATABASE zipcode CHARACTER SET utf8mb4;
use zipcode;

CREATE TABLE zipcode.zipcode(
       code varchar(12) NOT NULL,
       old_zipcode varchar(5) NOT NULL,
       zip_code varchar(7) NOT NULL,
       prefecture_kana varchar(255) NOT NULL,
       city_kana varchar(255) NOT NULL,
       town_kana varchar(255) NOT NULL,
       prefecture varchar(128) NOT NULL,
       city varchar(128) NOT NULL,
       town varchar(128) NOT NULL
     ) DEFAULT CHARACTER SET= utf8mb4;

上記の1_ddl.sqlは,zipcodeテーブルを作成するためのDDL文が入っています。

$ cat 2_load_data_infile.sql 
LOAD DATA INFILE '/docker-entrypoint-initdb.d/KEN_ALL_UTF8.CSV' INTO TABLE zipcode.zipcode FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' (code, old_zipcode, zip_code, prefecture_kana, city_kana, town_kana, prefecture, city, town, @dummy, @dummy, @dummy, @dummy, @dummy, @dummy);

上記の2_load_data_infile.sqlでは,LOAD DATA INFILE 構文を利用してファイルのロードをしていることがわかると思います。

最後にKEN_ALL_UTF8.CSVですが,こちらには第2回 MySQLにはじめてのデータを入れてみるで行った変換を事前にかけたものを配置しています。こちらのファイルの拡張子は,.CSVのため実行されることはありません。

この状態で以下のように実行をしてMySQLを起動してみましょう。今回ロードしたいデータは/docker-entrypoint-initdb.dに存在しているので,--secure-file-privの指定を変更します。

$ docker run --name michibushin -v "$PWD/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d" -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 3306:3306  -d mysql:8.0.20 --secure-file-priv=/docker-entrypoint-initdb.d

この状態で起動すると,起動した段階で以下のようにデータベースやテーブルが作成されており,データが入っていることがわかります。

mysql> show databases; 
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| zipcode            |
+--------------------+
5 rows in set (0.00 sec)

mysql> show tables;
+-------------------+
| Tables_in_zipcode |
+-------------------+
| zipcode           |
+-------------------+
1 row in set (0.00 sec)

mysql> select COUNT(*) from zipcode;
+----------+
| COUNT(*) |
+----------+
|   124340 |
+----------+
1 row in set (0.10 sec)

DockerのMySQLクライアントで日本語を扱う方法

新規環境やMySQLを初めて評価する場合などに,mysqlクライアントがインストールされてない状態で軽くMySQLを試してみたいこともあると思います。

特に今の時期だと,新卒研修などでSQLに軽く触れてもらう目的でMySQLを紹介する場合などに,ローカルにMySQLをインストールしてもらうのも非常に大変だと思います。そういった場合に,MySQLのDockerイメージに同梱されているmysqlクライアントを利用しようと考えるかもしれません。

ただ,DockerのMySQLコンテナ内に入っているmysqlコマンドラインクライアントで日本語を扱おうとすると,以下の点で困ると思います。

  • ターミナルに日本語の入力ができない
  • 日本語の出力が文字化けする

実際にdockerで起動したコンテナ内部に入り,MySQLにログインしてzipcodeテーブルで確認してみると,以下のように日本語部分が?になってしまい,文字化けしていることがわかります。

mysql> select * from zipcode limit 1;
+-------+-------------+----------+-----------------+--------------+-----------------+------------+--------+------------+
| code  | old_zipcode | zip_code | prefecture_kana | city_kana    | town_kana       | prefecture | city   | town       |
+-------+-------------+----------+-----------------+--------------+-----------------+------------+--------+------------+
| 01101 | 060         | 0600000  | ???????         | ???????????? | ??????????????? | ???        | ?????? | ?????????? |
+-------+-------------+----------+-----------------+--------------+-----------------+------------+--------+------------+
1 row in set (0.00 sec)

これらの問題の原因は,localeが正しく設定されてないため発生しています。解決するためには,localeの導入を行い設定をする必要があります。MySQLの公式イメージではベースイメージにDebianを用いているので,以下のようなDockerfileを用意し,ビルドして実行してみます。

Dockerfile

FROM mysql:8.0.20

RUN apt-get update
RUN apt-get -y install locales-all

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8

ビルドからMySQLの起動までを以下にまとめます。

$ docker build -t mysql_with_utf .
$ docker run --name michibushin -v "$PWD/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d" -e MYSQL_ROOT_PASSWORD=my-secret-pw -d -p 3306:3306  -d mysql_with_utf --secure-file-priv=/docker-entrypoint-initdb.d
$ docker exec -it michibushin /bin/sh
# mysql -uroot -pmy-secret-pw zipcode
mysql >

上記の操作で,docker内にあるmysqlクライアントを使ってログインできたところまで確認ができました。

続いて,文字化けが解消できているかを確認してみましょう。

mysql> select * from zipcode limit 1;
+-------+-------------+----------+-----------------------+--------------------------------------+-----------------------------------------------+------------+--------------------+--------------------------------+
| code  | old_zipcode | zip_code | prefecture_kana       | city_kana                            | town_kana                                     | prefecture | city               | town                           |
+-------+-------------+----------+-----------------------+--------------------------------------+-----------------------------------------------+------------+--------------------+--------------------------------+
| 01101 | 060         | 0600000  | ホッカイドウ               | サッポロシチュウオウク                         | イカニケイサイガナイバアイ                               | 北海道     | 札幌市中央区       | 以下に掲載がない場合           |
+-------+-------------+----------+-----------------------+--------------------------------------+-----------------------------------------------+------------+--------------------+--------------------------------+
1 row in set (0.00 sec)

ということで,文字化けが解消できていることが確認できました。Docker内のmysqlクライアントを使ってMySQLにアクセスすることはあまりないと思いますが,ハマった場合に思い出してもらえると幸いです。

まとめ

今回は,DockerでMySQLを扱う際に役立つ方法を2点ほど紹介させていただきました。これらの方法は初学者にMySQLを試してもらおうと思った時に非常に役に立つことが多いので,頭の片隅に入れておくと良いかもしれません。

著者プロフィール

木村浩一郎(きむらこういちろう)

株式会社オプティム 技術統括本部のエンジニア。最近はミドルウェア・インフラ周りのことも少しずつ学習しています。趣味は将棋。好きな戦法は四間飛車。

Twitter:@kk2170