私たちが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の使用例は何個か記事があったのですが、複数コンテナで同じダミー時刻を使う例がなかったので記事を書いてみました。まだちゃんとした開発環境には使っていないのと、ちょっと意図しない挙動になるケースもあったりなかったりしたので、ちゃんと実用できそうであればまた何かブログに書きます







