プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Docker 版 Pleasanter で正しくリマインダーを送るためのタイムゾーン設定について

こちらの記事で触れたリマインダーの時間がずれる件ですが、 GitHub に Issue #608 を立てたところ、コンテナのタイムゾーンに関するコメントがつきました。

また、似たようなタイムゾーン絡みと思われる Issue #566 でも、コンテナのタイムゾーンを確認するようにあコメントがありました。

これらからすると、どうも、OS(コンテナ)のタイムゾーンが影響しているっぽいようです。というわけで、なんとかならないかいろいろと試してみたので、そのときの顛末をメモっておきます。

なお、動作環境は以下の通りです。

  • QNAP TS-262
  • QTS 5.2.3.3006
  • Container Station 3.0.9.1038 (2024/11/08)
  • Pleasanter 1.4.13.0 (docker 版)

コンテナのタイムゾーンを変更してみる

冒頭に書いたように、どうも、時間周りの処理はコンテナ側のタイムゾーン設定も絡んでいるようです。コンテナのタイムゾーンはデフォルトだと UTC になります。ということで、まずは、下記を参考にして、コンテナのタイムゾーンを JST (Japan Standard Time, 日本標準時)に設定します。

Dockerコンテナのタイムゾーン変更方法 #Docker - Qiita

具体的には、 docker-compose.yml で

services:
  db:
    container_name: postgres
    image: postgres:16
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
      - POSTGRES_DB
      - POSTGRES_HOST_AUTH_METHOD
      - POSTGRES_INITDB_ARGS
      - TZ=Asia/Tokyo
    volumes:
      - type: volume
        source: pg_data
        target: /var/lib/postgresql/data
  pleasanter:
    container_name: pleasanter
    image: implem/pleasanter:1.4.13.0
    depends_on:
      - db
    ports:
      - '50001:8080'
    environment:
      Implem.Pleasanter_Rds_PostgreSQL_SaConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_SaConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_OwnerConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_OwnerConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_UserConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_UserConnectionString}
      TZ: Asia/Tokyo
    volumes:
      - type: bind
        source: ./Mail.json
        target: /app/App_Data/Parameters/Mail.json
      - type: bind
        source: ./BackgroundService.json
        target: /app/App_Data/Parameters/BackgroundService.json
      - type: bind
        source: ./Service.json
        target: /app/App_Data/Parameters/Service.json
volumes:
  pg_data:
    name: ${COMPOSE_PROJECT_NAME:-default}_pg_data_volume

のようにしてコンテナに対して、環境変数 TZ でタイムゾーンを指定するようにしました。

この docker-compose.yml を使って、こちらの記事の手順でコンテナを起動します。

実際にコンテナに入って、タイムゾーンを確認してみると、

pleasanter のコンテナでは

[user1@nas01 pleasanter-tmp]$ docker exec -it pleasanter2 /bin/bash
root@582f1c28348d:/app# date
Mon Mar 31 18:31:06 JST 2025
root@582f1c28348d:/app# 

タイムゾーンが JST になってますね。もう一つの postgres のほうも

[user1@nas01 pleasanter-tmp]$ docker exec -it postgres2 /bin/bash
root@74f21960a562:/# date
Mon Mar 31 06:31:32 PM JST 2025
root@74f21960a562:/#

タイムゾーンが変わってますね。 Postgres 自身の設定を確認すると、

root@74f21960a562:/# psql -U Implem.Pleasanter_Owner Implem.Pleasanter
psql (16.8 (Debian 16.8-1.pgdg120+1))
Type "help" for help.

Implem.Pleasanter=> show timezone;
  TimeZone  
------------
 Asia/Tokyo
(1 row)

Implem.Pleasanter=> 

こちらも問題なくタイムゾーン設定が反映されてますね。

テスト

さて、コンテナのタイムゾーンを変更したので、これで一度テストしてみます。『入門プリザンター』の『7.4 リマインダーを設定する』に従って、リマインダーを設定します。

これで、リマインダーが正しく送られるはずです。指定した時刻になると、正しくリマインダーが動いたようで、ちゃんとメールがやってきました。

と喜んだのもの束の間、なぜか下記のように大量のメールがやってきていました。

これ、リマインダーに該当するレコードって、1件しかないんですよ(というより、レコードそのものもテスト用の1件だけ)。

あきらかに挙動がおかしいですね。取り急ぎ、リマインダーの設定を『無効』にして、

あふれそうなメールを削除しておきます。あーあ。

試行錯誤とその結果

ここから、何が問題なのかいろいろと探りましたが、なかなか正しい挙動になりません。結局、1日ぐらい費やしてやっと見つけたのは、 Service.json の TimeZoneDefault の設定を変更してやる。というものでした。

つまり、最終的に Service.json を

{
    "Name": "Implem.Pleasanter",
    "TimeZoneDefault": "Asia/Tokyo",
    "DefaultPassword": "pleasanter",
    "DeploymentEnvironment": null,
    "WithoutChangeDefaultPassword": false,
    "DefaultLanguage": "ja",
    "AbsoluteUri": "http://nas01:50001",
    "MaxRequestBodySize": 30000000,
    "RequireHttps": false,
    "AnnouncementSiteId": 0,
    "ShowProfiles": true,
    "ShowChangePassword": false,
    "ShowStartGuide": true,
    "Demo": false,
    "DemoUsagePeriod": 60
}

のようにして、 TImeZoneDefault に Asia/Tokyo を設定しておけば、リマインダーが動作した際に、設定した JST の時刻に、1件のレコードにつき1つメールが送られるようになりました。

結論

ということで、すくなとも手元の docker 環境では、

  • CodeDefiner の /z 指定: Asia/Tokyo
  • コンテナのタイムゾーン設定:JST (TZ環境編素で指定)
  • Service.json の TimeZoneDefault 設定:Asia/Tokyo

の3つをそろえてやらないと、リマインダーとして期待した動作をしてくれませんでした。ご参考までに。

(おまけ)タイムゾーン設定がうまくいかなかった原因について

元々 Docker で動作させる際の手順の際に CodeDefiner を呼び出していますが、この時、 タイムゾーンを引数で指定しています。

docker-compose.codedefiner.yml

services:
  codedefiner:
    container_name: codedefiner
    image: implem/pleasanter:codedefiner
    depends_on:
      - db
    command: "_rds /y /l \"ja\" /z \"Asia/Tokyo\""
    environment:
      Implem.Pleasanter_Rds_PostgreSQL_SaConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_SaConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_OwnerConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_OwnerConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_UserConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_UserConnectionString}

また、下記の公式ドキュメントの書きっぷりからして、 Service.json の TimeZoneDefault の設定は無視される( CodeDefiner の引数の設定が優先される)のだろうと思っていました。

が、どうも、少なくとも手元の環境では、 Service.json も変更しないと正しく動作しなかったようです。

このへん、どういう事情なんだろうか?と思って、もう少し調べると、いい記事がありました。

qiita.com

これによると、 CodeDefiner でタイムゾーンと言語を指定した場合、 Service.json 内の記述を『書き換える』ようです。確認してみます。

docker-compose.codedefiner.yml をこちらの記事で書いたものから、下記のように変更します。

services:
  codedefiner:
    container_name: codedefiner2
    image: implem/pleasanter:codedefiner
    depends_on:
      - db
    command: "_rds /y /l \"ja\" /z \"Asia/Tokyo\""
    environment:
      Implem.Pleasanter_Rds_PostgreSQL_SaConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_SaConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_OwnerConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_OwnerConnectionString}
      Implem.Pleasanter_Rds_PostgreSQL_UserConnectionString: ${Implem_Pleasanter_Rds_PostgreSQL_UserConnectionString}
    volumes:
      - type: bind
        source: ./Mail.json
        target: /app/Implem.Pleasanter/App_Data/Parameters/Mail.json
      - type: bind
        source: ./BackgroundService.json
        target: /app/Implem.Pleasanter/App_Data/Parameters/BackgroundService.json
      - type: bind
        source: ./Service.json
        target: /app/Implem.Pleasanter/App_Data/Parameters/Service.json

変更点は、バインドマウントを使って、ホスト側の Service.json を使うようにしました。

これを用いて、 codedefienr を実行して、

[user1@nas01 pleasanter-tmp2]$ docker compose -f docker-compose.yml -f docker-compose.codedefiner.yml run --rm codedefiner
[+] Creating 1/0
 ✔ Container postgres3  Running                                                                                                                                                                                                     0.0s 
<INFO> Starter.Main: Implem.CodeDefiner 1.4.13.0
<INFO> RdsConfigurator.UpdateDatabase: Implem.Pleasanter
<INFO> UsersConfigurator.Execute: Implem.Pleasanter_Owner
<INFO> UsersConfigurator.Execute: Implem.Pleasanter_User
<INFO> SchemaConfigurator.Configure: Implem.Pleasanter
<INFO> Configurator.OutputLicenseInfo: 
ServerName: db
Database: pleasanterdb
Deadline: 01/01/0001
Licensee: 
Users: 0
<INFO> Configurator.OutputLicenseInfo: This edition is "Community Edition".
(略)
<INFO> PrivilegeConfigurator.Execute: Implem.Pleasanter_User
<SUCCESS> Starter.ConfigureDatabase: Database configuration has been completed.
<SUCCESS> Starter.Main: All of the processes have been completed.
[user1@nas01 pleasanter-tmp2]$ 

ホスト側の Service.json を表示させてみると、

[user1@nas01 pleasanter-tmp2]$ cat Service.json
{
  "Name": "Implem.Pleasanter",
  "TimeZoneDefault": "Asia/Tokyo",
  "DefaultPassword": "pleasanter",
  "DeploymentEnvironment": null,
  "WithoutChangeDefaultPassword": false,
  "DefaultLanguage": "ja",
  "AbsoluteUri": "http://nas01:50001",
  "MaxRequestBodySize": 30000000,
  "RequireHttps": false,
  "AnnouncementSiteId": 0,
  "ShowProfiles": true,
  "ShowChangePassword": false,
  "ShowStartGuide": true,
  "Demo": false,
  "DemoUsagePeriod": 60
}[user1@nas01 pleasanter-tmp2]$ 

TimeZoneDefault と DefaultLanguage が書き換わってますね。その意味では、公式ドキュメントの表記は、 CodeDefiner の引数を優先するという意味ではなく、文字通り『書き換える』ということを指しているのですね。

でも、今回使っているのは、 docker 版です(おまけにバインドマウントも使っていません)。なので、仮に CodeDefiner の実行により Service.json を書き換えても、 上にあげた記事中で指摘されているように Postgres の Users テーブルのデフォルト値には反映されても、 pleasanter コンテナ内の Service.json ファイルの内容には影響を及ぼしません。

なるほどね。だから、 pleasanter コンテナ側の Service.json を書き換えておかないと、デフォルトのタイムゾーンが UTC となり、正しく動作してくれなかったということになるようです。納得、納得。

もっとも、メールが山ほど飛んできた部分は相変わらずなぞですが、これもタイムゾーン設定が一貫していないことが何かの要因なのでしょう、きっと。

(おまけ)その2

上記の推測が正しいならば、 Docker でパラメータファイルを変更する場合の手順

pleasanter.org

の場合も、各パラメータファイルを DockerFile 内で COPY しているので、 CodeDefiner の引数で指定したタイムゾーンが pleasanter コンテナ側に反映されないのは同じように思えます。ということで、これも似たような現象が起きるように思えますね。こちらは試してないのですが、どなたか知ってますかね?

ま、なんにせよ、 Pleasanter の Docker 版を使う際は、タイムゾーンの取り扱いにはちょっと注意したほうがいいかも、ということでした。