はじめに (対象読者・この記事でわかること)

この記事は、Dockerコンテナ内で時刻を操作したいと考えている開発者・エンジニアを対象にしています。特に、コンテナ内でdateコマンドを使って時刻を変更しても、なぜか数秒で元の時刻に戻ってしまう現象に悩まされている方に向けて解説します。

この記事を読むことで、Dockerコンテナの時刻管理の仕組みが理解でき、なぜ時刻変更が無効になるのかその理由がわかります。また、libfaketimeを使った実用的な時刻偽装の方法を習得でき、テストやデバッグで時刻を自由に操作できるようになります。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - Dockerの基本的なコマンド操作(docker run, execなど) - Linuxの基本的なコマンド操作(dateコマンドなど)

Dockerコンテナで時刻が戻ってしまう原因

Dockerコンテナ内でdateコマンドを使って時刻を変更しても、なぜすぐに元に戻ってしまうのでしょうか?これはDockerの設計上の仕組みに関係しています。

Dockerコンテナは、ホストマシンのカーネルを共有して動作します。このとき、コンテナ内のプロセスはホストのカーネルによって管理されるため、システム時刻もホストと同期されています。つまり、コンテナ内で時刻を変更しても、ホスト側の時刻が変わらない限り、すぐに元に戻ってしまうのです。

また、コンテナ内で時刻を変更するには、--cap-add SYS_TIMEという特別な権限が必要ですが、それでも永続的な変更はできません。これは、コンテナの設計上、ステートレス(状態を保持しない)であることが前提だからです。

libfaketimeを使った時刻偽装の実装方法

それでは、実際にどのようにしてコンテナ内で時刻を操作するのでしょうか?ここでは、libfaketimeというツールを使った実用的な方法を紹介します。

libfaketimeとは

libfaketimeは、プロセスに対して偽の時刻を提供するLinux用のライブラリです。システム時刻自体は変更せず、特定のプロセスに対してのみ異なる時刻を返すようにすることができます。

ステップ1: libfaketimeのインストール

まず、Dockerfileを作成してlibfaketimeをインストールします。

Dockerfile
FROM ubuntu:22.04 # パッケージリストを更新し、libfaketimeをインストール RUN apt-get update && apt-get install -y \ faketime \ && rm -rf /var/lib/apt/lists/* # 作業ディレクトリを設定 WORKDIR /app # エントリーポイントスクリプトをコピー COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]

ステップ2: エントリーポイントスクリプトの作成

次に、コンテナ起動時に実行されるエントリーポイントスクリプトを作成します。

Bash
#!/bin/bash # entrypoint.sh # 環境変数から時刻を取得(デフォルトは現在時刻) FAKETIME=${FAKETIME:-""} if [ -n "$FAKETIME" ]; then echo "時刻を $FAKETIME に設定します" export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 export FAKETIME="$FAKETIME" fi # コンテナを維持するためのプロセスを起動 tail -f /dev/null

ステップ3: コンテナのビルドと実行

Dockerfileとentrypoint.shを同じディレクトリに配置し、以下のコマンドでビルドします。

Bash
docker build -t faketime-container .

そして、特定の時刻を設定してコンテナを起動します。

Bash
# 2023年1月1日に設定してコンテナを起動 docker run -d --name my-faketime-container -e FAKETIME="2023-01-01 00:00:00" faketime-container

ステップ4: 時刻の確認

コンテナ内で時刻が正しく設定されているか確認します。

Bash
docker exec my-faketime-container date # 出力: Sun Jan 1 00:00:00 UTC 2023

ハマった点やエラー解決

実装中に遭遇する可能性がある問題をいくつか紹介します。

問題1: libfaketimeが見つからない

ERROR: ld.so: object '/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' from LD_PRELOAD cannot be preloaded

このエラーは、libfaketimeのライブラリパスが異なる場合に発生します。Ubuntuのバージョンによってパスが異なるため、以下のコマンドで正確なパスを確認してください。

Bash
docker run --rm -it ubuntu:22.04 find /usr -name "libfaketime.so.1" 2>/dev/null

問題2: 一部のコマンドで時刻が偽装されない

dateコマンドは時刻が偽装されますが、hwclockコマンドや一部のアプリケーションではlibfaketimeが効かない場合があります。これは、それらのコマンドが直接システムコールを使用しているためです。

解決策

より堅牢な実装として、以下のようなDockerfileを検討してください。

Dockerfile
FROM ubuntu:22.04 RUN apt-get update && apt-get install -y \ faketime \ tzdata \ && rm -rf /var/lib/apt/lists/* # タイムゾーンを設定 ENV TZ=Asia/Tokyo # libfaketimeのパスを動的に取得するスクリプト RUN echo '#!/bin/bash\n\ LIB_FAKETIME=$(find /usr -name "libfaketime.so.1" 2>/dev/null | head -1)\n\ if [ -n "$FAKETIME" ]; then\n\ export LD_PRELOAD=$LIB_FAKETIME\n\ export FAKETIME="$FAKETIME"\n\ fi\n\ exec "$@"' > /usr/local/bin/with-faketime && chmod +x /usr/local/bin/with-faketime WORKDIR /app ENTRYPOINT ["with-faketime"] CMD ["tail", "-f", "/dev/null"]

まとめ

本記事では、Dockerコンテナ内で時刻を変更してもすぐに元に戻ってしまう原因と、libfaketimeを使った時刻偽装の方法を解説しました。

  • Dockerコンテナはホストのカーネルを共有するため、システム時刻の変更は永続化されない
  • libfaketimeを使うことで、プロセス単位で時刻を偽装できる
  • 環境変数で柔軟に時刻を設定できる仕組みを構築できる

この記事を通して、Dockerコンテナでの時刻管理の仕組みを理解し、実用的な時刻偽装の方法を習得できたでしょう。今後は、libfaketimeを使った自動テストでの時刻操作や、レガシーシステムの動作確認など、より高度な活用方法についても記事にする予定です。

参考資料