2016/10/18 20:33:35

Dockerの公式MySQLは日本語が文字化けするのでutf8かutf8mb4にしたい

目次(クリックするとジャンプします)
  • 1:マルチバイト語族をなめてんのか
  • 2:文字化けについてちょっとした説明
  • 3:その1 コンテナを作る時に設定ファイルを変更する方法
  • 3.1:my.cnfを得る
  • 3.2:caracter-set-server書き足し
  • 3.3:さようならコンテナ
  • 3.4:データボリュームの準備
  • 3.5:「使う用のコンテナ」を作る
  • 4:その2 Dockerfileを修正する硬派な方法
  • 5:補足・ハマりポイント
  • 6:まとめ

マルチバイト語族をなめてんのか

ごっつり文字化けするんですよね…。

Dockerの公式MySQLはcaracter-set-serverがデフォルトのまんまのlatin1です。

mojibake-0

そりゃASCII文化圏ならutf8だろうがlatin1だろうが文字化けしないだろうさ…。

でもマルチバイト語族は常に、つ、ね、に、文字化けの恐怖に晒されるのですよ。

そこで今回はDockerの公式MySQLでちゃんと日本語を扱えるようにするための方法をまとめてみました。

記事ではMySQL5.7(mysql/5.7/Dockerfile)を使っています。

文字化けについてちょっとした説明

設定変更の方法を紹介する前にちょっとした説明です。

MySQLで文字化けの原因となるのは

caracter-set-serverがデフォルトのlatin1のままになっている為というのがあります。

日本語を扱うにはマルチバイトの文字セットが必要ですが、現状だとutf8utf8mb4を使うことになるかと思います

たとえば日本語の漢字で「泡」はutf8

E6B3A1

というコードになります。3バイトで表現されていることがわかるかと思います。

utf8という解釈をするとこのコードは「泡」ということになるのですが、1バイト毎での解釈もできてしまう余地があります。

つまりこういう風に

E6 B3 A1

1バイトの文字が3つとも解釈できるのです。latin1ではこれらが

æ ³ ¡

と解釈されます。

つまり

「泡」は「泡」になってしまうのです。

こんな風にutf8で書いた文字列がlatin1として解釈される「コードの取り違え」があると以下の様な壮絶な文字化けが発生してしまうのです。

脂肪(しぼう、英:fat)とは脂(あぶら)ともいい、動植物に含まれã‚

なにやらこんな文字化け見たことありますよね。

ちなみにアルファベット・アラビア数字・一般的な記号部分が文字化けしないのは、それらを定義しているASCIIという文字セットがほとんどの文字セットに含まれているからです。(つまり同じコードを使っているので文字化けしようがない)

latin1ASCIIを含んでいるのでアルファベット・アラビア数字・一般的な記号部分は文字化けしません。

次は具体的な方法に行きましょう。

その1 コンテナを作る時に設定ファイルを変更する方法

caracter-set-serverを恒久的に設定するにはmy.cnfを編集する必要があります。

この方法は直接手動でmy.cnfを修正する方法です。設定を確認できるので、MySQLコンテナの設定を把握したい場合などにいいかもしれません。

my.cnfを得る

まずは型になるmy.cnfを手に入れましょう。これはコンテナから引っこ抜くのが早いです。

my.cnfをコピーする用」のコンテナを作ります。

$ docker run -d --name test -e MYSQL_ROOT_PASSWORD=goma -e MYSQL_DATABASE=testdb mysql

docker cpコマンドでコンテナからmy.cnfをコピーします。

$ docker cp test:/etc/mysql/my.cnf my.cnf

エディタで開いてみましょう。

$ vim my.cnf

MySQLコンテナに作られたmy.cnfは以下のような記述になっています。

# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

#
# The MySQL Community Server configuration file.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

[client]
port            = 3306
socket          = /var/run/mysqld/mysqld.sock

[mysqld_safe]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
nice            = 0

[mysqld]
skip-host-cache
skip-name-resolve
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
#bind-address   = 127.0.0.1

#log-error      = /var/log/mysql/error.log

# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#
!includedir /etc/mysql/conf.d/

Dockerfile上での処理で

skip-host-cache
skip-name-resolve

awkでの処理によって付け足され

#bind-address   = 127.0.0.1
#log-error      = /var/log/mysql/error.log

sedでの処理でコメントアウトされています。

caracter-set-server書き足し

さてutf8をデフォルトにするためにcaracter-set-serverの設定を書き加えます。

[mysqld]の直下に

caracter-set-server = utf8

もしくは

caracter-set-server = utf8mb4

を書き加えます。以下のようになっていれば大丈夫です。

~~~~
~~~~

[mysqld]
caracter-set-server = utf8
skip-host-cache
skip-name-resolve
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp

~~~~
~~~~

または

~~~~
~~~~

[mysqld]
caracter-set-server = utf8mb4
skip-host-cache
skip-name-resolve
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
explicit_defaults_for_timestamp

~~~~
~~~~

さようならコンテナ

いま修正したmy.cnfを適用させた新しいコンテナをつくるので、「my.cnfをコピーする用」に作ったコンテナは削除して構いません。さようなら。

$ docker stop test
$ docker rm test

データボリュームの準備

さきほど修正したmy.cnfを任意のディレクトリに入れておきます( 例では/hoge/mycnf/)。そのディレクトリをデータボリュームとしてコンテナと共有することでmy.cnfをコンテナの中に入れます。

公式MySQLでインストールされているMySQLは/etc/mysql/conf.d/*.cnf形式のファイルが入っているとその設定を優先するようになっています。

# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.

/etc/mysql/my.cnfを上書きするのでも問題はないのですが、今回は/etc/mysql/conf.d/につっこんでいくスタイルでいきます。

ですのでmy.cnfという名前じゃなくても大丈夫です。拡張子さえcnfにしていれば管理しやすい様に名前を変えても問題ありません。

「使う用のコンテナ」を作る

以上のことを踏まえて「使う用のコンテナ」は以下のようにして作ります。

docker run -d --name usedb -v /hoge/mycnf/:/etc/mysql/conf.d/ -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=db mysql

これで修正したmycnfの設定が生きたコンテナが出来ました。

がっちり文字化けしていたのが…

mojibake-1

なんということでしょう…!!

mojibake-2

わ〜い。

その2 Dockerfileを修正する硬派な方法

コンテナからmy.cnfをコピーするなど軟弱なことはやっておれん、という硬派な方にはDockerfileの修正がオススメです。

まずはGitHubからDockerfileをcloneしましょう。

$ git clone git@github.com:docker-library/mysql.git

Dockerfileを開きます。

$ vim mysql/5.7/Dockerfile

いろいろ処理が書かれていますが、34行目あたりに以下のような記述があります。この部分がmy.cnfを修正している箇所になります。

# comment out a few problematic configuration values
# don't reverse lookup hostnames, they are usually another container
RUN sed -Ei 's/^(bind-address|log)/#&/' /etc/mysql/my.cnf \
    && echo 'skip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \
    && mv /tmp/my.cnf /etc/mysql/my.cnf

さらに詳しくみると以下の部分が、「[mysqld]の直下にskip-name-resolveおよびskip-host-cacheを挿入する処理」を行っている箇所です。

&& echo 'skip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \

ここを修正すればcaracter-set-serverを挿入することができるかと思います。

以下の様にしてみてください。

&& echo 'caracter-set-server = utf8\nskip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \

または

&& echo 'caracter-set-server = utf8mb4\nskip-host-cache\nskip-name-resolve' | awk '{ print } $1 == "[mysqld]" && c == 0 { c = 1; system("cat") }' /etc/mysql/my.cnf > /tmp/my.cnf \

さて修正したDockerfileをbuildしましょう。

$ docker build -t hogemysql hoge/mysql/5.7/

buildしたイメージでコンテナを作ります。

docker run -d --name hogedb -e MYSQL_ROOT_PASSWORD=goma -e MYSQL_DATABASE=db hoge/mysql

my.cnfをコピーして確認してみると…

~~~
~~~
[mysqld]                                                                                                                                 
caracter-set-server = utf8  
skip-host-cache  
skip-name-resolve 
user           = mysql  
pid-file      = /var/run/mysqld/mysqld.pid
socket       = /var/run/mysqld/mysqld.
~~~
~~~

または

~~~
~~~
[mysqld]                                                                                                                                 
caracter-set-server = utf8mb4  
skip-host-cache  
skip-name-resolve 
user           = mysql  
pid-file      = /var/run/mysqld/mysqld.pid
socket       = /var/run/mysqld/mysqld.
~~~
~~~

ちゃんとcaracter-set-server = utf8caracter-set-server = utf8mb4が挿入されていますね。

これでもじばけしないさいきょうのまいえすきゅーえるいめーじがてにはいったぞ!!

補足・ハマりポイント

もしかしたら以下のようなエラーがでてdocker buildが失敗するかもしれません。

E: Version '5.7.11-1debian8' for 'mysql-server' was not found

これはdebianリポジトリに指定したバージョンが無いときに起こるエラーのようです。この記事を書いている時ちょうど5.7.12-1debian8がリリースされたようで、5.7.11-1debian8がリポジトリからなくなりました。

Dockefileのメンテを待っても良いのですが、直接書き換えても大丈夫かと思います。今回はDockerfileの以下の様に書き換えました。

ENV MYSQL_VERSION 5.7.12-1debian8

あとで確認したらGitHubの方もすでに修正されていました。

ちなみにこの件に関してはissuesがあります。どうもバージョンを取り違えないように、その都度指定する方法にしているみたいですね。

version 5.7 fails to apt-get install mysql-server=’5.7.10-1debian8′ #139

もし上記のようなエラーがでたら確認してみてください。

まとめ

  • 公式のMySQLイメージはcaracter-set-serverがデフォルトのlatin1になっている
  • 文字化けを起こす原因になるのでcaracter-set-serverutf8utf8mb4にしたい
  • コンテナのmy.cnfを書き換えることで文字化け防ぐことができる
  • データボリュームでmy.cnfを突っ込む方法とDokcefileを修正する硬派な方法がある
  • これで文字化けしないぜ!!