ハマログ

株式会社イーツー・インフォの社員ブログ

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

  koni   2024年6月13日


関連記事

Laravelの勉強中

入社して1か月ちょっと経ちました。最初はgitと戦ってましたが、最近はLarav…

Laravel Passportを使ったOauthサーバ

Oauth2.0の認可サーバを構築したいと思いLaravel Passportを…

30年以上の時を経て

すっかりAppleネタを投稿するbotと化したfujitaですこんにちは。 欲し…


← 前の投稿

次の投稿 →