RedmineをDocker Composeで構築

はじめに

バグの管理などは通常ITS (Issue Tracking System)を利用するが、どういう形態のITSを使うかは状況によって異なる。 GitHubのIssues機能などSaaSで提供されているITSは手軽に使い始められるものの、センシティブな情報はクラウドへの配置が許されない事も多いので、そういった場合はオンプレで運用できるRedmineが重宝する。 そこで、RedmineをDocker Composeで構築して、ライトに運用してみることにしたので、その手順を記しておく。

環境

$ cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Ubuntu 18.04.4 LTS"

$ docker --version
Docker version 19.03.5, build 633a0ea838

$ docker-compose --version
docker-compose version 1.25.4, build 8d51620a

方針

  • 運用
    • 簡易な運用にする。サービスレベルは気にしない。落ちたら上げる、ぐらいで。
  • Docker Composeを利用
    • 可読性を保ったままパラメータの記述ができて便利。コンテナを起動するためのスクリプトを書かずにすむ。
  • 永続データ (DB)
    • DBサーバの運用はしない。SQLiteを利用して永続化する。
  • 永続データ (ファイル)

構築手順

以下に構築手順を示す。 永続データについては色々調べまくったが、ここでは触れずに後続の節でメモを残しておく。

(1) Composeファイルの作成

以下の内容でComposeファイルを作成する。 Composeファイルのパスは任意だが、ここでは/opt/docker-compose-service/docker-compose.ymlとして説明をすすめる。

version: '3.7'

services:
  redmine:
    image: redmine:4.1
    container_name: redmine
    volumes:
      - /var/lib/docker-compose-service/redmine/files:/usr/src/redmine/files
      - /var/lib/docker-compose-service/redmine/plugins:/usr/src/redmine/plugins
      - /var/lib/docker-compose-service/redmine/public/themes:/usr/src/redmine/public/themes
      - /var/lib/docker-compose-service/redmine/sqlite:/usr/src/redmine/sqlite
    ports:
      - 10080:3000
    environment:
      - TZ=Asia/Tokyo

RedmineのイメージはRedmine公式のものを利用する。 Webサーバはコンテナ内部ではポート番号3000で立ち上がるが、コンテナ外部にはポート番号10080で公開する。 環境変数TZタイムゾーンを指定して、表示時刻を日本標準時にしておく。 SQLiteを利用する場合は、以下のDockerHubのRedmineのサイトからの引用のとおり、単にDBの設定(環境変数の定義)をしないでおけばよい。 そうすれば、自動的にSQLiteにフォールバックされる。

If neither variable is set, the image will fall back to using SQLite.

(2) 永続データ用のディレクトリを作成

ホスト側に、docker-compise.ymlvolumesの項目で指定する、永続データ用のディレクトリを作成する。

$ sudo mkdir -p /var/lib/docker-compose-service/redmine/files
$ sudo mkdir -p /var/lib/docker-compose-service/redmine/plugins
$ sudo mkdir -p /var/lib/docker-compose-service/redmine/public/themes
$ sudo mkdir -p /var/lib/docker-compose-service/redmine/sqlite

コンテナ内で動作するRedmineのプロセスのuidとgidは、それぞれ999である。 そのため、これらディレクトリのオーナーのuidとgidも、それぞれ999にする。

$ sudo chown -R 999:999 /var/lib/docker-compose-service/redmine

(3) デフォルトデータのロード

コンテナを立ち上げて、デフォルトデータをロードする。

$ docker-compose -f /opt/docker-compose-service/docker-compose.yml up

$ docker exec -it redmine bundle exec rake redmine:load_default_data RAILS_ENV=production REDMINE_LANG=ja
W, [2020-02-16T17:59:24.731200 #32]  WARN -- : Creating scope :system. Overwriting existing method Enumeration.system.
Default configuration data loaded.

警告が気になるが、メッセージ的に環境の問題ではないように見える。 Rubyのことは詳しくないのだが、おそらくRedmineのコードの中で意図的にEnumeration.systemを再定義(?)しているのだと思われる。

動作確認

詳細は割愛するが、以下の手順を実施した後でも期待通りの状態でRedmineが利用できるなら、永続データが欠落しておらず動作確認が取れたと言える。

  1. docker-compose upを実行し、コンテナを開始
  2. チケットの作成、プラグインの追加、テーマの追加など、永続データの変更を伴う作業を実施
  3. docker-compose downを実行し、コンテナを停止
  4. docker rm redmineを実行し、コンテナを削除
  5. docker-compose upを実行し、コンテナを再び開始

ついでにsystemdでサービス化

以下の過去の記事で説明したように、Docker Composeをsystemdでサービス化しておくと、ライフサイクル(起動/停止など)の管理が容易になる。

redj.hatenablog.com

構築は以上で終了。 以降は今回の構築にあたっての調査メモ。

メモ: 永続データの配置場所のまとめ

DockerHubのRedmineのサイトには、永続データを保存しておくためには、どのディレクトリに外部のストレージをマウントすればよいかが明記されていない。 そのため、Redmineのドキュメントを元に永続データが配置されているディレクトリを以下の通りに特定した。 ドキュメントは全部は読めていないので、抜け漏れはあるかもしれない。

項目 永続データの配置場所 参考ドキュメント
データベース /usr/src/redmine/sqlite Backing up and restoring Redmine
添付ファイル /usr/src/redmine/files Backing up and restoring Redmine
プラグイン /usr/src/redmine/plugins Plugin Tutorial
テーマ /usr/src/redmine/public/themes HowTo create a custom Redmine theme

以下、永続データの配置場所の詳細を説明する。

メモ: データベースの配置場所

データベース(SQLite)について、ドキュメント「Backing up and restoring Redmine」には、以下の記述がある。

SQLite databases are all contained in a single file, so you can back them up by copying the file to another location. You can determine the file name of SQLite database by looking at config/database.yml.

日本語訳すると、以下になる。

SQLiteデータベースは単一のファイルに必要なものがすべてが含まれているため、そのファイルを他の場所にコピーすることによりバックアップを取ることができます。 SQLiteデータベースのファイル名はconfig/database.ymlの中を確認することで特定できます。

そこで確認のために、ボリュームの設定を行わずに直接DockerからRedmineを起動し、config/database.ymlの中身を表示してみる。

$ docker run -it --rm --name redmine redmine:4.1

$ docker exec -it redmine /bin/cat /usr/src/redmine/config/database.yml
production:
  adapter: "sqlite3"
  host: "localhost"
  username: "redmine"
  database: "sqlite/redmine.db"
  encoding: "utf8"

表示結果には、database: "sqlite/redmine.db"とあるので、ディレクト/usr/src/redmine/sqliteに永続用のストレージをマウントすればよい。

メモ: 添付ファイルの配置場所

添付ファイルについて、ドキュメント「Backing up and restoring Redmine」には、以下の記述がある。

All file uploads are stored in attachments_storage_path (defaults to the files/ directory). You can copy the contents of this directory to another location to easily back it up.

WARNING: attachments_storage_path may point to a different directory other than files/. Be sure to check the setting in config/configuration.yml to avoid making a useless backup.

言葉を補いつつ日本語訳すると、以下になる。

すべてのファイルは、設定項目attachments_storage_pathで指定された場所(デフォルトはディレクトリfiles/)に保存されます。 このディレクトリの内容を他の場所にコピーすることで、簡単にバックアップすることができます。

警告: 設定項目attachments_storage_pathはfiles/以外の場所を指している場合があります。 無効なバックアップを防ぐために、config/configuration.ymlの中の設定を必ず確認してください。

ということで、config/configuration.ymlの中を確認する必要がありそうだ。 しかしながら、ドキュメント「Installing Redmine」には、以下の記述がある。

If you need to override default application settings, simply copy config/configuration.yml.example to config/configuration.yml and edit the new file; the file is well commented by itself, so you should have a look at it.

言葉を補いつつ日本語訳すると、以下になる。

アプリケーション(=Redmine)のデフォルトの設定をオーバーライドする必要がある場合、config/configuration.yml.exampleをconfig/configuration.ymlにコピーして編集するだけでよいです。 そのファイルのコメントには十分な説明が書かれているので、よく読んでください。

言い換えると「すべてデフォルト値でよければconfig/configuration.ymlは不要」ということだと理解した。 Redmineの公式イメージの中を確認したがconfig/configuration.ymlは存在しなかったので、全てデフォルト値が利用されていることになる。

結局のところ、デフォルトであるディレクトfiles、つまりフルパスではディレクト/usr/src/redmine/filesに永続用のストレージをマウントすればよい。

メモ: プラグインの配置場所

プラグインについては、ドキュメント「Backing up and restoring Redmine」では何故か言及がない。 そこで、プラグイン開発のドキュメント「Plugin Tutorial」を確認すると、プラグインに必要なファイルはディレクトplugins/[プラグイン名]に集約しているようだ。 そのため、ディレクトplugins、つまりフルパスではディレクト/usr/src/redmine/pluginsに永続用のストレージをマウントすればよい。

ちなみに、公式のRedmineのイメージではプリインストールされているプラグインはないが、もしプラグインがプリインストールされているイメージを利用する場合は、アタッチするボリュームに事前にディレクトpluginsの内容をコピーするなどの追加の手順が必要になる。

メモ: テーマの配置場所

テーマについても、ドキュメント「Backing up and restoring Redmine」では何故か言及がない。 そこで、テーマのカスタマイズのドキュメント「HowTo create a custom Redmine theme」を確認すると、テーマに必要なファイルはディレクトpublic/themes/[テーマ名]に集約しているようだ。 そのため、ディレクトpublic/themes、つまりフルパスではディレクト/usr/src/redmine/public/themesに永続用のストレージをマウントすればよい。

ここで注意点がひとつある。 ディレクト/usr/src/redmine/public/themesにはプリインストールのテーマの「Alternate」と「Classic」が含まれている。 そのため、単に永続用のストレージをマウントするとこれらのテーマが見えなくなってしまう。 必要であれば、事前にこれらのテーマを永続用のストレージにコピーするなどの追加の手順が必要になる。

ちなみに、デフォルトのテーマは/usr/src/redmine/public/stylesheets/application.cssなので、/usr/src/redmine/public/themesの内容に関わらず利用できる。

メモ: ディレクトリのオーナー

コンテナにマウントするホスト側のディレクトリは、単に作成するだけではRedmineから書き込みが出来ず、以下のようにSQLiteのファイルの作成に失敗する。

$ docker-compose up
Creating redmine ... done
Attaching to redmine
(略)
redmine    | SQLite3::CantOpenException: unable to open database file
(略)
redmine exited with code 1

原因は、ホスト側のディレクトリのオーナーとRedmineのプロセスのオーナーが異なるからである。 以下のようにコンテナに入ってRedmineのプロセスの情報を確認すると、ユーザはredmine(uidは999)で、グループもredmine(gidは999)で動作していることが分かる。

$ docker run -it --rm --name redmine redmine:4.1

$ docker exec -it redmine /bin/ps -e -o user,uid,group,gid,cmd
USER       UID GROUP      GID CMD
redmine    999 redmine    999 /usr/local/bin/ruby bin/rails server -b 0.0.0.0
root         0 root         0 /bin/ps -e -o user,uid,group,gid,cmd

念のため、/etc/passwd/etc/groupを確認してみると、以下のように確かにredmineのエントリがある。

$ docker exec -it redmine /bin/cat /etc/passwd | grep redmine
redmine:x:999:999::/home/redmine:/bin/sh

$ docker exec -it redmine /bin/cat /etc/group | grep redmine 
redmine:x:999:

そのため、以下のようにホスト側で/var/lib/docker-compose-service/redmineとそのサブディレクトリの所有ユーザーと所有グループを、それぞれ999に変更する。

$ sudo chown -R 999:999 /var/lib/docker-compose-service/redmine

ただし、ホスト側ではuid 999とgid 999は別の目的で利用されている可能性がある。 セキュリティの観点では、コンテナの都合に無理やり合わせてchownするのはあまり良くないので、理想的にはユーザーネームスペースのリマッピングなど、何らかの対策が必要になる。

参考