libfaketimeを使ってdocker-composeの複数コンテナでダミー時刻を設定する
私たちがDockerなどで使っているコンテナ技術の実態は、仮想マシン(VM)のそれとは異なり、あくまでLinuxのnamespace機能によるプロセスの分離に他なりません。
…とそれっぽいことを言ってみましたが、要するに仮想マシンじゃないのでハードウェアやカーネルは分離・仮想化されないということです。
つまりコンテナの内側と外側の時計は共通なので、コンテナ内で時刻を変えるとホストPC(コンテナ外)にも影響してしまいます。そこでlibfaketimeを使って、自由にコンテナ内の時刻を変えられるようにしてみました。
https://github.com/wolfcw/libfaketime
TL;DR
- libfaketimeを使うと、時刻をダミーの固定値やn年後などに変えられる
- libfaketimeでは環境変数だけでなく、ファイルで時刻を指定することができる
- ファイルを共有することで、ダミー時刻をコンテナ間で連動できる
libfaketimeの基本的な使い方
この記事の目標は複数のコンテナ間でダミー時刻を同期することですが、まずはlibfaketimeを普通に使ってみます。READMEにそって、まずmakeを使ってインストールします。
git clone https://github.com/wolfcw/libfaketime.git cd libfaketime make install
これでlibfaketimeを使って、日時を表示するdateコマンドを実行するには、
LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 FAKETIME="@1990-05-24 00:00:00" date
とします。1つ目の LD_PRELOAD
で libfaketime.so.1 ライブラリを読み込み、 2つ目の FAKETIME
で偽の日時を指定した上で、dateを実行しています。もっと詳しい使い方はlibfaketimeのREADMEにかいてあるのでそちらを参照ください。
複数コンテナ間でlibfaketimeを共有する
上の方法ではmakeでインストールする必要があって既存の開発環境に追加しづらいですし、ダミー日時の指定が環境変数なのも取り扱いづらいです。
しかし、/etc/faketimerc
というファイルでダミー日時を指定する方法があります。それを使うと、例えばこんなDockerfileやcompose.yamlを作ることができます
# Dockerfile FROM debian:bookworm-slim RUN apt-get update && apt-get install -y \ build-essential \ git \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN git clone https://github.com/wolfcw/libfaketime.git \ && cd libfaketime \ && make install
# compose.yaml services: faketime: build: . volumes: - libfaketime:/usr/local/lib/faketime - ./faketimerc:/etc/faketimerc # 例1)適当なUbuntuでdateを3秒おきに実行する ubuntu: image: ubuntu:latest command: /bin/bash -c "while true; do date --iso-8601='seconds'; sleep 3; done" volumes: - libfaketime:/usr/local/lib/faketime - ./faketimerc:/etc/faketimerc environment: - LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 depends_on: - faketime # 例2)AmazonLinux 2023のPythonで3秒おきに日時表示 ※ AL2だとGLIBCが古くてlibfaketimeが動かない al2023: image: amazonlinux:2023 command: python3 -u -c "import datetime; import time; import itertools; [print(datetime.datetime.now()) or time.sleep(3) for _ in itertools.count()]" volumes: - libfaketime:/usr/local/lib/faketime - ./faketimerc:/etc/faketimerc environment: - LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 depends_on: - faketime # 例3)WordPressの環境(LAMP環境)でも動く wordpress: image: wordpress:latest volumes: - libfaketime:/usr/local/lib/faketime - ./faketimerc:/etc/faketimerc environment: - LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1 - WORDPRESS_DB_HOST=wordpress-db:3306 - WORDPRESS_DB_USER=wordpress - WORDPRESS_DB_PASSWORD=password - WORDPRESS_DB_NAME=wordpress ports: - 8080:80 depends_on: - wordpress-db - faketime wordpress-db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: password volumes: - wp-db-data:/var/lib/mysql volumes: libfaketime: driver: local wp-db-data: driver: local
ちょっと長いですが、compose.yamlを見ればやりたいこと・使い方が分かるかと思います。実際に動かすには、上のDockerfile、compose.yamlと、faketimercの合計3ファイルを保存してdocker compose up
するだけです。
基本となるfaketimeサービス(Dockerfile)は、libfaketimeをビルドしてコンテナ内にインストールします。そのままでは他のコンテナと共有できませんが、ボリュームを使うことで生成されたlibfaketime.so.1を他のコンテナで利用します。
例1と例2がシンプルなサンプルで、どちらも3秒ごとに現在日時を出力するだけのコンテナです。例2はPythonのワンライナーがちょっと分かりづらいですが、それ以外はとても簡単です。使うポイントとしては、
volumes: - libfaketime:/usr/local/lib/faketime - ./faketimerc:/etc/faketimerc environment: - LD_PRELOAD=/usr/local/lib/faketime/libfaketime.so.1
のボリュームと環境変数を設定することで、faketimercに記載した偽の時刻を参照するようになります。
例えばfaketimercの中身を -30d
にして、実際にコンテナを起動してみると、
現在日時が2024-06-13 10:30に大して、30日前の日時を表示しているのが分かります。
また例3は、実務に近い例としてWordPressのコンテナを追加してみました。WordPress特有の環境変数やDBが煩雑ですが、上のボリュームと環境変数の設定をするだけでlibfaketimeを使うようになってます。
試しにfaketimercに+100y
と記入してからWordPressをインストールしてみると、
最初のテスト投稿が、今日から36500日後の2124年5月20日になりました。
注意
たとえば例のcompose.yamlでAmazon Linux 2023でなくAmazon Linux 2を使うと、glibcのバージョンの問題でlibfaketimeがエラーで使えません。その場合は、別途その環境にあわせてfaketimelibをビルドする必要があります。おそらく、同じ理由でalpineでも使えないと思います。
また、一部のミドルウェアはlibfaketimeをバイパスしてしまって効かないことがあるそうです。libfaketimeのREADMEに書いてありますが、この場合はどうしようもありません。
最後に
libfaketimeの使用例は何個か記事があったのですが、複数コンテナで同じダミー時刻を使う例がなかったので記事を書いてみました。まだちゃんとした開発環境には使っていないのと、ちょっと意図しない挙動になるケースもあったりなかったりしたので、ちゃんと実用できそうであればまた何かブログに書きます