Libertyコンテナにロケールとタイムゾーンを設定する

Libertyコンテナにロケールとタイムゾーンを設定する方法のメモ。

## ロケールの設定

[Libertyの公式DockerHub](https://hub.docker.com/_/websphere-liberty/)でDockerfileをたどっていくと、

websphere-liberty:javaee8 > websphere-liberty:kernel > ibmjava:8-jre > ubuntu:16.04

となっており元のイメージはUbuntuである。

ロケールを確認するとPOSIXとなっている。

```shell-session
$ docker run --rm -it websphere-liberty:18.0.0.3-kernel locale
LANG=
LANGUAGE=
LC_CTYPE="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
LC_COLLATE="POSIX"
LC_MONETARY="POSIX"
LC_MESSAGES="POSIX"
LC_PAPER="POSIX"
LC_NAME="POSIX"
LC_ADDRESS="POSIX"
LC_TELEPHONE="POSIX"
LC_MEASUREMENT="POSIX"
LC_IDENTIFICATION="POSIX"
LC_ALL=
$
```

日本語ロケールを組み込むには以下のようにする。18.0.0.3のある変更から`USER`が`root`ではなく`1001`となっているので、`apt-get`を実行するためには`USER`を変更する必要がある。

```Dockerfile:Dockerfile
FROM websphere-liberty:18.0.0.3-kernel
USER 0
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y language-pack-ja && \
    rm -rf /var/lib/apt/lists/*
USER 1001
ENV LANG ja_JP.UTF-8
```

language-pack-jaをインストールすると依存性でlocales、language-pack-ja-baseもインストールされる。

LANGではなくLC_ALLを設定することもできるが、LANGのほうがよさそう。

ビルドする。

```
docker build -t websphere-liberty:18.0.0.3-kernel-ja .
```

コンテナを実行して確認する。

```shell-session
$ docker run --rm -it websphere-liberty:18.0.0.3-kernel-ja locale
LANG=ja_JP.UTF-8
LANGUAGE=
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_PAPER="ja_JP.UTF-8"
LC_NAME="ja_JP.UTF-8"
LC_ADDRESS="ja_JP.UTF-8"
LC_TELEPHONE="ja_JP.UTF-8"
LC_MEASUREMENT="ja_JP.UTF-8"
LC_IDENTIFICATION="ja_JP.UTF-8"
LC_ALL=
$
```

localesだけインストールして以下とすることも可能。

```Dockerfile:Dockerfile
FROM websphere-liberty:18.0.0.3-kernel
USER 0
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y locales && \
    rm -rf /var/lib/apt/lists/* && \
    locale-gen ja_JP.UTF-8
USER 1001
ENV LANG ja_JP.UTF-8
```

Shift_JISの場合は`locale-gen`ではなく`localedef`を使用する必要がある。

```Dockerfile:Dockerfile
FROM websphere-liberty:18.0.0.3-kernel
USER 0
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y locales && \
    rm -rf /var/lib/apt/lists/* && \
    localedef -f SHIFT_JIS -i ja_JP ja_JP.SJIS
USER 1001
ENV LANG ja_JP.SJIS
```

```
docker build -t websphere-liberty:18.0.0.3-kernel-ja-sjis .
```

```
$ docker run --rm -it websphere-liberty:18.0.0.3-kernel-ja-sjis locale
LANG=ja_JP.SJIS
LANGUAGE=
LC_CTYPE="ja_JP.SJIS"
LC_NUMERIC="ja_JP.SJIS"
LC_TIME="ja_JP.SJIS"
LC_COLLATE="ja_JP.SJIS"
LC_MONETARY="ja_JP.SJIS"
LC_MESSAGES="ja_JP.SJIS"
LC_PAPER="ja_JP.SJIS"
LC_NAME="ja_JP.SJIS"
LC_ADDRESS="ja_JP.SJIS"
LC_TELEPHONE="ja_JP.SJIS"
LC_MEASUREMENT="ja_JP.SJIS"
LC_IDENTIFICATION="ja_JP.SJIS"
LC_ALL=
```

## タイムゾーンの設定

タイムゾーンはCentOSは環境変数を設定するだけでよいが、Ubuntuはそれだけでは足りないようだ。+9時間になっていない。

```shell-session
$ docker run --rm -it centos:7 date
Tue Oct 30 12:55:12 UTC 2018
$ docker run --rm -it -e "TZ=Asia/Tokyo" centos:7 date
Tue Oct 30 21:55:21 JST 2018
$ docker run --rm -it ubuntu:16.04 date
Tue Oct 30 12:55:34 UTC 2018
$ docker run --rm -it -e "TZ=Asia/Tokyo" ubuntu:16.04 date
Tue Oct 30 12:55:43 Asia 2018
```

tz-dataをインストールしてあれこれする。

```Dockerfile:Dockerfile
FROM websphere-liberty:18.0.0.3-kernel
USER 0
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata && \
    rm -rf /var/lib/apt/lists/* && \
    ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
USER 1001
ENV TZ Asia/Tokyo
```

最後の`ENV TZ Asia/Tokyo`はなくてもよい。

ビルドする。

```
docker build -t websphere-liberty:18.0.0.3-kernel-jst .
```

確認。

```shell-session
$ docker run --rm -it liberty-jst:kernel date
Mon Oct 15 17:07:55 JST 2018
$
```

## オフライン環境でビルドする場合

事前にインターネットにつながる環境でパッケージを取得しておき、オフライン環境でビルドする。

### オンライン環境でのパッケージの取得

カレントディレクトリーをマウントし、rootユーザーを指定してコンテナを起動して中に入る。

```
docker run --rm -it -u 0 -v $PWD:/work websphere-liberty:18.0.0.3-kernel bash
```

必要なパッケージをダウンロードオンリーモードで取得する。

```
apt-get update
apt-get install -y -d language-pack-ja
apt-get install -y -d tzdata
```

`/var/cache/apt/archives`にダウンロードしたファイルがあるので、マウントしたディレクトリーにコピーする。

```
root@de5cc55463e1:/# ls -l /var/cache/apt/archives
total 6404
-rw-r--r-- 1 root root 2016058 Jun 28  2016 language-pack-ja-base_1%3a16.04+20160627_all.deb
-rw-r--r-- 1 root root 1147434 Jan  9  2018 language-pack-ja_1%3a16.04+20171210_all.deb
-rw-r--r-- 1 root root 3212542 Jan 17  2018 locales_2.23-0ubuntu10_all.deb
-rw-r----- 1 root root       0 Oct  5 18:08 lock
drwx------ 2 _apt root    4096 Oct 30 13:09 partial
-rw-r--r-- 1 root root  165668 Oct 29 10:08 tzdata_2018g-0ubuntu0.16.04_all.deb
root@de5cc55463e1:/# cp /var/cache/apt/archives/*.deb /work
root@de5cc55463e1:/# exit
exit
$ ls -l
total 12808
-rw-r--r--  1 sotoiwa  staff      278 10 30 21:52 Dockerfile
-rw-r--r--  1 sotoiwa  staff  2016058 10 30 22:10 language-pack-ja-base_1%3a16.04+20160627_all.deb
-rw-r--r--  1 sotoiwa  staff  1147434 10 30 22:10 language-pack-ja_1%3a16.04+20171210_all.deb
-rw-r--r--  1 sotoiwa  staff  3212542 10 30 22:10 locales_2.23-0ubuntu10_all.deb
-rw-r--r--  1 sotoiwa  staff   165668 10 30 22:10 tzdata_2018g-0ubuntu0.16.04_all.deb
$
```

### オンライン環境でのビルド

Dockerfileは以下のようにする。以下は日本語ロケールの設定とタイムゾーンの設定を両方やる例。

```
FROM websphere-liberty:18.0.0.3-kernel
USER 0
COPY *.deb /tmp/
RUN DEBIAN_FRONTEND=noninteractive dpkg -i \
    /tmp/language-pack-ja-base_1%3a16.04+20160627_all.deb \
    /tmp/language-pack-ja_1%3a16.04+20171210_all.deb \
    /tmp/locales_2.23-0ubuntu10_all.deb \
    /tmp/tzdata_2018g-0ubuntu0.16.04_all.deb && \
    rm -rf /tmp/*.deb && \
    ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
USER 1001
ENV LANG ja_JP.UTF-8
ENV TZ Asia/Tokyo
```

## おまけ

Javaからどう見えるかも確認する。

確認用の簡単なJavaプログラムを作成する。

```java:Main.java
import java.util.Date;
import java.util.Locale;

public class Main
{
    public static void main (String[] args) throws java.lang.Exception
    {
        System.out.println("file.encoding = " + System.getProperty("file.encoding"));
        System.out.println("console.encoding = " + System.getProperty("console.encoding"));
        System.out.println("Date() = " + new Date());
        System.out.println("Locale Code = " + Locale.getDefault());
        System.out.println("こんにちは！");
    }
}
```

Main.javaをコンパイルしてLibertyのコンテナの中にクラスファイルを配置するDockerfileを作成する。LibertyのコンテナにはJDKが入っていないので、マルチステージビルドを使う。

```Dockerfile:Dockerfile
FROM ibmjava:8-sdk as builder
COPY Main.java /
RUN javac -encoding UTF-8 /Main.java
FROM websphere-liberty:18.0.0.3-kernel
COPY --from=builder /Main.class /
```

ビルドする。

```
docker build -t liberty-test:18.0.0.3-kernel .
```

```
$ docker run --rm -it liberty-test:18.0.0.3-kernel java Main
file.encoding = ANSI_X3.4-1968
console.encoding = null
Date() = Tue Oct 30 13:54:28 UTC 2018
Locale Code = en_US
??????
```

ロケールとタイムゾーンを設定してビルドする。

```Dockerfile:Dockerfile
FROM ibmjava:8-sdk as builder
COPY Main.java /
RUN javac -encoding UTF-8 /Main.java
FROM websphere-liberty:18.0.0.3-kernel
USER 0
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y language-pack-ja && \
    rm -rf /var/lib/apt/lists/*
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y tzdata && \
    rm -rf /var/lib/apt/lists/* && \
    ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
USER 1001
ENV LANG ja_JP.UTF-8
ENV TZ Asia/Tokyo
COPY --from=builder /Main.class /
```

ビルドする。

```
docker build -t liberty-test2:18.0.0.3-kernel .
```

確認する。

```
$ docker run --rm -it liberty-test2:18.0.0.3-kernel java Main
file.encoding = UTF-8
console.encoding = null
Date() = Tue Oct 30 22:57:43 JST 2018
Locale Code = ja_JP
こんにちは！
$
```

## 補足

コンテナにロケールやタイムゾーンを指定する必要があるかというと、そもそもあまり必要でないかもしれない。また、OSのレベルで設定しなくても、Javaのシステムプロパティーで`file.encoding`や`console.encoding`や`user.timezone`を設定すればすむかもしれない。

```shell-session
$ docker run --rm -it liberty-test:18.0.0.3-kernel java -Dfile.encoding=UTF-8 -Duser.timezone=Asia/Tokyo Main
file.encoding = UTF-8
console.encoding = null
Date() = Tue Oct 30 22:58:35 JST 2018
Locale Code = en_US
こんにちは！
$
```

## 参考リンク

[Ubuntu 16.04(xenial)のDockerコンテナのタイムゾーンを日本(JST)にする(Dockerfileで)](https://qiita.com/windyakin/items/d73157966d414978b7cf)

[reconfigure timezone inside docker not working](https://stackoverflow.com/questions/40923545/reconfigure-timezone-inside-docker-not-working)

[Ubuntuでローカルにパッケージをダウンロード・インストールする](https://s.webry.info/sp/vogel.at.webry.info/201702/article_3.html)

[Docker で apt-get install するときに TERM 系のエラーが出る](http://maku77.github.io/docker/term-error.html)