概要
日時の概念が重要となる機能をテストする際、システム日付を変更してテストを行うことがあると思います。Dockerコンテナを使用して自動テストを行う場合もテストコード内でシステム日付を変更し、テストが終わったらコンテナを破棄すれば実現できると思っていました。しかし、実際に試してみたところホスト側の日付も変更されてしまい想定する挙動とは違っていました。
ここではDockerコンテナ内のシステム日付だけを変更してテスト環境を用意する方法についてご紹介したいと思います。
結論
Dockerコンテナで libfaketime を使えばできます。
コンテナ内の特定のプロセスだけシステム日付を変更したり、システム全体の日付の変更もできるため、デーモンプロセスにも対応できます。
Dockerfile
FROM centos:6
# タイムゾーン設定
ENV TZ Asia/Tokyo
# 依存パッケージのインストール
RUN yum install -y gcc git
# libfaketimeのインストール
WORKDIR /
RUN git clone https://github.com/wolfcw/libfaketime.git
WORKDIR /libfaketime/src
RUN make install \
&& echo -e '/usr/local/lib/faketime/libfaketime.so.1' > /etc/ld.so.preload
# 環境変数の設定
# Java プログラムを実行する場合は DONT_FAKE_MONOTONIC が必要です。
# システム日付の変更は1秒間隔でチェックします。
ENV DONT_FAKE_MONOTONIC=1
ENV FAKETIME_CACHE_DURATION=1
# 作業ディレクトリの変更
WORKDIR /
# システム日付の変更
# /etc/faketimerc または環境変数 FAKETIME にフォーマットを指定します。
# フォーマットは「相対日時」や「指定日時」などが指定できます。
# 詳しくは以下のURLを参照してください。
# https://github.com/wolfcw/libfaketime
# テストコマンド実行
CMD date \
&& echo -e '@2000-01-01 09:00:00' > /etc/faketimerc \
&& date \
&& echo -e '+3d' > /etc/faketimerc \
&& date \
&& echo -e '-10y' > /etc/faketimerc \
&& date \
&& rm -f /etc/faketimerc \
&& date
Dockerコンテナの実行方法
(1) イメージをビルドします。
docker build -t faketime .
初回のみ CentOS のイメージのビルドに少し時間がかかります。
(2) ビルドしたイメージ一覧を確認します。
docker images
以下のようなイメージ一覧が表示されます。
REPOSITORY TAG IMAGE ID CREATED SIZE
faketime latest 64794ea54ab0 8 seconds ago 489MB
centos 6 70b5d81549ec 3 months ago 195MB
(3) コンテナをビルドして検証用コマンドを実行します。
docker run --rm <faketimeイメージのIMAGE ID>
検証用のコマンドが実行され、コンテナ内のシステム日付が変更されていることが確認できます。
Thu Aug 2 17:21:29 JST 2018 <現在日時>
Sat Jan 1 08:59:59 JST 2000 <2000-01-01 09:00:00を起点にした日時>
Sun Aug 5 17:21:29 JST 2018 <3日後>
Mon Aug 4 17:21:29 JST 2008 <10年前>
Thu Aug 2 17:21:29 JST 2018 <現在日時>
システム日付の変更にはそれぞれ以下のフォーマットを指定しています。
フォーマット | 説明 |
---|---|
@2000-01-01 09:00:00 | 2000-01-01 09:00:00を起点にした日時 |
+3d | 3日後 |
-10y | 10年前 |
現在日時を基準とした相対日時、ある時点からの日時などが指定できます。
フォーマットの詳しい説明は libfaketime をご参照ください。
ホスト側のシステム日付に影響を与えることなくDockerコンテナ内のシステム日付を変更しながらテストが実施できます。ぜひ自動テストに組み込んで試してみてください。
注意点
libfaketime の説明書にも記載されていますが、Javaプロセスに対してシステム日付の変更を反映させるためには環境変数に DONT_FAKE_MONOTONIC=1
を設定する必要があります。設定されていない場合はプロセスがハングするようです。
説明書からの引用
- Java-/JVM-based applications work but you need to pass in an extra argument
(DONT_FAKE_MONOTONIC). See usage basics below for details. Without this
argument the java command usually hangs.