動機
Pythonの例を出していますが、Python に限らないDocker一般論です。
普通によく利用されるDockerオフィシャルのコンテナでの話です。
Dockerコンテナ内で、PythonのUnicodeEncodeError、UnicodeDecodeErrorで悩まされたことがありました。
ベースになるイメージでja_JP.UTF-8
の locale が用意されていないのに、ついうっかりDockerfile内で
ENV LANG ja_JP.UTF-8
とかやって、localeにja_JP.UTF-8
を指定してしまっていたのが原因でした。
私が気付いたときは Python を使っていたので UnicodeEncodeError という形で問題が現れたのですが、案件によっては原因不明の文字化けといった現象になるかもしれません。
知っていればどうということはないのですが、知らないとなかなか原因にたどり着けないのでここでメモしておきます。
再現テスト
例えば Python 使いの人なら、次のようにすれば問題を再現できます。
オフィシャルの python のコンテナイメージ(latest)を pull します
$ docker pull python
Dockerコンテナを起動し、シェルを立ち上げます
$ docker exec -it python /bin/bash
コンテナ内のシェルで、環境変数LANGを設定します。
# export LANG=ja_JP.UTF-8
エラー再現用のPythonスクリプトを作成・実行します。
# echo "print(\"ほげ\")" > test.py
# python test.py
実行結果は以下のようになります。
Traceback (most recent call last):
File "test.py", line 1, in <module>
print("\u307b\u3052")
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
ここで
# export PYTHONENCODING=utf-8
とかやっても状況は改善しません。
オフィシャルのコンテナにja_JP.UTF-8
locale がインストールされていないのが原因なのですから。
確認のためにシェル上で
# locale -a
を実行すると
C
C.UTF-8
POSIX
の3つしか表示されません。
この問題、原因が分かっても解決するのは簡単ではありません。
対策
STEP1 現在のlocaleの確認
まずはコンテナの locale を確認します。
pullしてきたコンテナ内で
# echo $LANG
を実行してみます。
ここで、
C.UTF-8
が表示されて、
# locales -a
の表示に
C.UTF-8
が含まれていて、現状取り組んでいる案件でja_JP.UTF-8
を使う必要がないのであれば、環境変数を変更せず、LANG=C.UTF-8
の状態にしておくのが無難です。
参考: LANGやPYTHONIOENCODINGを設定してもUnicodeDecodeErrorが出る時の更なる確認ポイント
STEP2 ベースイメージの確認
どうしてもja_JP.UTF-8
を使う必要がある場合は、localeの生成から始める必要があります。
これは、ディストリビューション、バージョンによってやり方が違うので、コンテナのベースになっているディストリビューションが何なのかを調べるところから始める必要があります。
例えばオフィシャルの python の場合、https://hub.docker.com/_/python/ を参照すると、2018年2月13日現在、デフォルトの python:latest は、 Debian jessie がベースになっていることがわかります。
STEP3 ja_JP.UTF-8 localeのインストール
オフィシャルの python:latest イメージには、locales パッケージが入っていませんので、そのインストールから始める必要があります。
# apt-get update
# apt-get install locales
# echo "ja_JP UTF-8" > /etc/locale.gen
# locale-gen
debianのまともな解説を読んでいると、/etc/locale.genのja_JP.UTF-8のコメントを解除する
などと書いてああります。
参考: Debian/Ubuntuのデフォルトロケールを変更する
ですが、そのためにテキストエディタを起動していては処理を自動化できません。どうせコンテナの中では必要最低限の1プロセスしか動かさないので、少々強引ですがここでは
# echo "ja_JP UTF-8" > /etc/locale.gen
という処理をしています。これなら後で自動化したいときに、Dockerfile の RUN コマンドの中に含めてしまうことができますね。
これで、先のスクリプトが動くようになりました。
# python test.py
ほげ
ベースイメージが Alpine、 Ubuntu、CentOS などの場合は、それぞれのディトリビューションの流儀に従って locale を生成する必要があります。
まとめ
レンタルサーバーやVPS、VirtualBoxの仮想環境などでしたら、OSインストールの際に locale の生成までやってしまうのでついつい見落としてしまうのですが、Dockerコンテナの場合、オフィシャルのイメージではそこまで面倒をみてくれていないことが普通にあります。
使う前にlocaleがどのような状態になっているのか確認するようにしましょう。