Help us understand the problem. What is going on with this article?

最新の Redmine 4 を Docker 公式イメージで運用する

CentOS 7.6 に Redmine 4.0 を Docker 公式イメージを利用してセットアップする手順の解説。

Redmine を Docker 公式イメージで運用する」は Redmine 3.x の記事だったので、 2019/03/31 にリリースされた Redmine 4.0.3 で再検証した。

Redmine とは

1280px-Redmine_logo.svg.png
Redmine は Ruby on Rails で開発されているプロジェクト管理の OSS で、2006年にフランスの Jean-Philippe Lang によって開発された。ライセンスは GPLv21 2
日本の参照数が最も多く 3 、非アジア圏では未だに Trac の方が知名度が高い。 4 5 6

Redmine 3.x と 4.x の違い

システム要件に以下の違いがある。 7
特に Ruby on Rails が 4.2 から 5.2 に変更されている影響で一部のプラグインで動作しなくなるものがある。

Redmine 3.3 / 3.4 4.0
Ruby on Rails 4.2 5.2
Ruby 1.9.3 - 2.4 2.2.2 - 2.6
MySQL 5.0 - 5.1 5.5 - 5.7
PostgreSQL 8.3 - 9.1 9.2 -
SQL Server ? 2012 -

本稿の特徴

  • Docker 公式イメージを使用
  • ホスト OS に CentOS 7 を、データベースに MySQL 8.0 を使用
  • データベースの文字コードは utf8mb4 を使用
  • themes, plugins, files および一部の設定ファイルはホスト OS からマウント
  • Memcached の導入
  • ホスト OS 上ですべて操作
  • コマンドラインから一括設定変更
  • systemd の Timer による自動バックアップ設定
  • [記事分離予定] テーマ、プラグインの一括インストール
  • [TODO] バージョンアップ手順
  • [TODO] SSL 対応

Redmine の Docker イメージ

Docker Hub で検索すると700を超えるイメージが見つかるが、スターを5件以上獲得しているのは以下の5つ。

Docker イメージ名 スター数 ダウンロード数
redmine (公式イメージ) 795 10000K
sameersbn/redmine 307 10000K
bitnami/redmine 43 500K
74th/redmine-all-in-one 10 7.9K
inspiredgeek/redmine-alpine 7 10K

Docker のセットアップ

Docker のインストール

yum --assumeyes install yum-utils && \
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && \
yum --assumeyes install docker-ce && \
systemctl start docker && \
systemctl enable docker

CentOS Extras リポジトリから 1.13.1 がパッケージでインストールできるが、公式リポジトリを利用すると最新版がインストールできる。
2019/10/09 時点での最新バージョンは 19.03.3 になる。

Docker Compose のインストール

curl --location \
     --output /usr/local/bin/docker-compose \
     $(curl --silent --show-error \
            https://api.github.com/repos/docker/compose/releases/latest \
            | grep 'Linux-x86_64"' \
            | grep url \
            | cut --delimiter='"' --fields=4 \
     ) && \
chmod +x /usr/local/bin/docker-compose

EPEL リポジトリから docker-compose 1.18.0 がパッケージでインストールできるが、 GitHub からバイナリーをダウンロードすることで最新版が利用できる。
2019/10/09 時点での最新バージョンは 1.24.1 になる。

公式ドキュメントのインストール手順ではバージョンを指定してダウンロードする方法が記載されているが、 GitHub API を活用することで動的に最新版を取得することができる。

格納ディレクトリの作成

mkdir --parents --verbose /srv/redmine && \
matchpathcon $_ && \
semanage fcontext --add --type container_file_t $_ && \
restorecon -v $_ && \
matchpathcon $_

設定ファイル、添付ファイル、テーマ、プラグイン、ログの保存ディレクトリのマウント元として FHS 3.0GitLab を参考に /srv/redmine を作成。

4.6. SELinux コンテキスト: ファイルのラベル付け - Red Hat Customer Portal

SELinux コンテキストのラベル名は RHEL 7.5 から svirt_sandbox_file_t が廃止されたので container_file_t を使用する。 8

ボリュームラベルの :z オプション等でコンテキストのラベル名を変更することもできる。 9 10

Docker Compose

configuration.yml

config/configuration.yml
mkdir --parents --verbose /srv/redmine/config && \
echo 'default:' > /srv/redmine/config/configuration.yml

メール設定で利用する config/configuration.yml ファイルを先にマウントしておくためにファイルを作成する。

additional_environment.rb

config/additional_environment.rb
mkdir --parents --verbose /srv/redmine/config && \
cat << "_EOF_" > /srv/redmine/config/additional_environment.rb
config.cache_store = :mem_cache_store, "memcached"
_EOF_

config/additional_environment.rb ファイルで Memcached を利用する設定をする。

config.cache_store:dalli_store を指定する事例が多いが :mem_cache_store の方が汎用性が高いように見えるのでそちらを採用。 11 12

Gemfile.local

Gemfile.local
cat << "_EOF_" > /srv/redmine/Gemfile.local
gem 'dalli'
_EOF_

Memcached を利用するために Ruby 用 Memcached クライアントの dalli をインストールする Gemfile.local を用意する。

docker-compose.yml ファイル

docker-compose.yml
cat << "_EOF_" > /srv/redmine/docker-compose.yml
version: '3.7'
services:
  redmine:
    container_name: ${REDMINE:-redmine}
    image: redmine:4-passenger
    restart: always
    ports:
      - 80:3000
    depends_on:
      - ${REDMINE_DB_MYSQL:-mysql}
      - ${REDMINE_MEMCACHED:-memcached}
    environment:
      TZ: ${TZ}
      REDMINE_DB_MYSQL: ${REDMINE_DB_MYSQL:-mysql}
      REDMINE_DB_DATABASE: ${REDMINE_DB_DATABASE}
      REDMINE_DB_USERNAME: ${REDMINE_DB_USERNAME}
      REDMINE_DB_PASSWORD: ${REDMINE_DB_PASSWORD}
      REDMINE_DB_ENCODING: ${REDMINE_DB_ENCODING:-utf8mb4}
      VIRTUAL_HOST: ${VIRTUAL_HOST}
      VIRTUAL_PORT: ${VIRTUAL_PORT:-3000}
      LETSENCRYPT_HOST: ${VIRTUAL_HOST}
      LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
    volumes:
      - ${REDMINE_PATH:-.}/config/additional_environment.rb:/usr/src/redmine/config/additional_environment.rb
      - ${REDMINE_PATH:-.}/config/configuration.yml:/usr/src/redmine/config/configuration.yml
      - ${REDMINE_PATH:-.}/Gemfile.local:/usr/src/redmine/Gemfile.local
      - ${REDMINE_PATH:-.}/files:/usr/src/redmine/files:z
      - ${REDMINE_PATH:-.}/log:/usr/src/redmine/log:Z
      - ${REDMINE_PATH:-.}/plugins:/usr/src/redmine/plugins
      - ${REDMINE_PATH:-.}/public/themes:/usr/src/redmine/public/themes
  mysql:
    container_name: ${REDMINE_DB_MYSQL:-mysql}
    image: mysql:8
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    ports:
      - 3306:3306
    environment:
      TZ: ${TZ}
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${REDMINE_DB_DATABASE}
      MYSQL_USER: ${REDMINE_DB_USERNAME}
      MYSQL_PASSWORD: ${REDMINE_DB_PASSWORD}
    volumes:
      - mysql:/var/lib/mysql
  memcached:
    container_name: ${REDMINE_MEMCACHED:-memcached}
    image: memcached
    restart: always
#    ports:
#      - 11211:11211
volumes:
  mysql:
    name: ${REDMINE_DB_MYSQL:-mysql}
_EOF_

Memcached を永続的に利用する場合は config/additional_environment.rb を、メール送信を永続的に利用する場合は config/configuration.yml をマウントする必要がある。

.env ファイル

.env
DOMAIN=example.jp && \
FQDN=redmine.${DOMAIN} && \
cat << _EOF_ > /srv/redmine/.env
REDMINE_PATH=/srv/redmine
TZ=Asia/Tokyo
MYSQL_ROOT_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9!$%&()*+,-./:;<=>?@[\]^_{|}~' | head -c 16; echo)
REDMINE_DB_MYSQL=mysql
REDMINE_DB_DATABASE=redmine_$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 8; echo)
REDMINE_DB_USERNAME=redmine_$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 8; echo)
REDMINE_DB_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9!$%&()*+,-./:;<=>?@[\]^_{|}~' | head -c 16; echo)
REDMINE_DB_ENCODING=utf8mb4
REDMINE_MEMCACHED=memcached
VIRTUAL_HOST=$(echo ${FQDN})
VIRTUAL_PORT=3000
LETSENCRYPT_HOST=$(echo ${FQDN})
LETSENCRYPT_EMAIL=redmine@$(echo $DOMAIN)
_EOF_
chmod 444 /srv/redmine/.env

コンテナの起動

docker-compose --project-directory /srv/redmine up --detach && \
firewall-cmd --permanent --add-service=http{,s} && \
firewall-cmd --reload && \
firewall-cmd --list-services && \
sleep 30 && \
docker container logs --follow redmine

Completed 200 OK と表示されたら Ctrl + c で抜ける。

初期設定

デフォルトデータのロード

docker exec redmine bundle exec rake redmine:load_default_data RAILS_ENV=production REDMINE_LANG=ja

データベースの認証情報の登録

grep REDMINE_DB_PASSWORD .env
docker exec -it mysql mysql_config_editor set --host=localhost --user=redmine --password

mysql および mysqldump コマンドで --user および --password オプションを省略するために mysql_config_editor コマンドを使って認証情報を暗号化して格納する。

ロールと権限

cat << '_EOQ_' | docker exec --interactive mysql mysql redmine
UPDATE `roles` SET `permissions` = NULL WHERE `id` = '1' OR `id` = '2';
_EOQ_

設定

cat << '_EOQ_' | docker exec --interactive mysql mysql redmine && \
docker exec redmine passenger-config restart-app /usr/src/redmine
INSERT INTO `settings` (`name`, `value`) VALUES
('search_results_per_page','30'), -- ページごとの検索結果表示件数 (10)
('host_name','redmine.example.com'), -- ホスト名とパス (localhost:3000)
('protocol','https'), -- プロトコル (http)
('text_formatting','markdown'), -- テキスト書式 (textile)
('default_language','ja'), -- デフォルトの言語 (en)
('force_default_language_for_anonymous','1'), -- 匿名ユーザーにデフォルトの言語を強制 (0)
('force_default_language_for_loggedin','1'), -- ログインユーザーにデフォルトの言語を強制 (0)
('user_format','lastname_firstname'), -- ユーザー名の表示形式 (firstname_lastname)
('thumbnails_enabled','1'), -- 添付ファイルのサムネイル画像を表示 (0)
('login_required','1'), -- 認証が必要 (0)
('autologin','7'), -- 自動ログイン (0)
('max_additional_emails','0'), -- 追加メールアドレス数の上限 (5)
('session_lifetime','43200'), -- 有効期間の最大値 (0)
('session_timeout','480'), -- 無操作タイムアウト (0)
('default_users_time_zone','Tokyo'), -- タイムゾーン ()
('rest_api_enabled','1'), -- RESTによるWebサービスを有効にする (0)
('jsonp_enabled','1'), -- JSONPを有効にする (0)
('default_projects_public','0'), -- デフォルトで新しいプロジェクトは公開にする (1)
('default_projects_modules','---
- issue_tracking
- time_tracking
- wiki
- repository
- calendar
- gantt
'), -- 新規プロジェクトにおいてデフォルトで有効になるモジュールチケットトラッキング
('default_projects_tracker_ids','---
- \'1\'
- \'2\'
- \'3\'
'), -- 新規プロジェクトにおいてデフォルトで有効になるトラッカー
('cross_project_issue_relations','1'), -- 異なるプロジェクトのチケット間で関連の設定を許可 (0)
('default_issue_start_date_to_creation_date','0'), -- 現在の日付を新しいチケットの開始日とする (1)
('issue_done_ratio','issue_status'), -- 進捗率の算出方法 (issue_field)
('issue_list_default_totals','---
- estimated_hours
- spent_hours
'), -- チケットの一覧で表示する項目(合計)
('attachment_max_size','51200'), -- 添付ファイルサイズの上限 (5120)
('repositories_encodings','utf-8,cp932,euc-jp'), -- 添付ファイルとリポジトリのエンコーディング ()
('mail_from','admin@example.com'), -- 送信元メールアドレス (redmine@example.net)
('enabled_scm','---
- Git
'), -- 使用するバージョン管理システム
('commit_ref_keywords','refs,references,IssueID,*'), -- 参照用キーワード (refs,references,IssueID)
('commit_cross_project_ref','1'); -- 異なるプロジェクトのチケットの参照/修正を許可 (0)
_EOQ_

メール設定

ホスト OS の Postfix を利用する場合

Postfix の設定

cp --verbose /{usr/lib,etc}/systemd/system/postfix.service && \
sed --in-place \
    --expression="/After/ s/$/ docker.service/" \
    /etc/systemd/system/postfix.service && \
systemctl daemon-reload && \
grep After /{usr/lib,etc}/systemd/system/postfix.service && \
NETWORK=$(docker container inspect redmine --format='{{.HostConfig.NetworkMode}}') && \
SUBNET=$(docker network inspect ${NETWORK} --format='{{range .IPAM.Config}}{{.Subnet}}{{end}}') && \
GATEWAY=$(docker network inspect ${NETWORK} --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}') && \
echo ${NETWORK}, ${SUBNET}, ${GATEWAY} && \
postconf -d | egrep "^inet_interfaces|^mynetworks" && \
postconf -n | egrep "inet_interfaces|mynetworks" && \
sed --in-place=.org \
    --expression="/^inet_interfaces/ s|localhost\$|localhost, 127.0.0.1, ${GATEWAY}|" \
    /etc/postfix/main.cf && \
postconf -n | egrep "inet_interfaces|mynetworks" && \
systemctl restart postfix && \
firewall-cmd --permanent --zone=public \
             --add-rich-rule="rule family=\"ipv4\" source address=\"${SUBNET}\" service name=\"smtp\" accept" && \
firewall-cmd --reload && \
firewall-cmd --list-rich-rules

CentOS 7.6 minimal のバニラ状態を前提とした置換処理のため、環境に応じて適宜変更のこと。

configuration.yml

config/configuration.yml
NETWORK=$(docker container inspect redmine --format='{{.HostConfig.NetworkMode}}') && \
GATEWAY=$(docker network inspect ${NETWORK} --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}') && \
cat << _EOF_ > /srv/redmine/config/configuration.yml
default:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: ${GATEWAY}
      port: 25
      domain: redmine.example.jp
_EOF_

Gmail を利用する場合

config/configuration.yml
NETWORK=$(docker container inspect redmine --format='{{.HostConfig.NetworkMode}}') && \
GATEWAY=$(docker network inspect ${NETWORK} --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}')
cat << _EOF_ > config/configuration.yml
default:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      enable_starttls_auto: true
      address: "smtp.gmail.com"
      port: 587
      domain: "smtp.gmail.com"
      authentication: :login
      user_name: "example@gmail.com"
      password: "P@ssw0rd"
_EOF_

Passenger の再起動

docker exec redmine bundle exec rails runner 'Rails.cache.clear' && \
docker exec redmine passenger-config restart-app /usr/src/redmine

Memcached の設定

docker exec redmine bundle install && \
docker exec redmine passenger-config restart-app /usr/src/redmine

バックアップ設定

cat << '_EOF_' > /etc/systemd/system/backup-redmine-db.service
[Unit]
Description=Backup Redmine database

[Service]
Type=oneshot
ExecStart=/bin/bash -c "docker exec mysql mysqldump redmine | gzip > /srv/redmine/backup/redmine_db_`date +%F`.sql.gz" && \
 find /srv/redmine/backup -name redmine_db_*.sql.gz -mtime +14 | xargs rm
_EOF_
cat << '_EOF_' > /etc/systemd/system/backup-redmine-db.timer
[Unit]
Description=Backup Redmine database

[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
_EOF_
cat << '_EOF_' > /etc/systemd/system/backup-redmine-files.service
[Unit]
Description=Backup Redmine files

[Service]
Type=oneshot
ExecStart=/bin/bash -c "cd /srv/redmine && tar -cf backup/redmine_files_`date +%F`.tar.gz files" && \
 find /srv/redmine/backup -name redmine_files_*.tar.gz -mtime +14 | xargs rm
_EOF_
cat << '_EOF_' > /etc/systemd/system/backup-redmine-files.timer
[Unit]
Description=Backup Redmine files

[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
_EOF_
mkdir --parents --verbose /srv/redmine/backup && \
systemctl daemon-reload && \
systemctl enable backup-redmine-{db,files}.timer && \
systemctl list-unit-files | egrep "STATE|backup" && \
systemctl list-timers --all

プラグイン

別記事として「利便性の高い Redmine プラグイン - Qiita」にまとめた。

テーマ

RedmineUP

yum --assumeyes install unzip && \
themes=/srv/redmine/public/themes && \
token=0c7a1fefdd47f3a96fb57a4f94450dbdf98dd && \
curl -L https://www.redmineup.com/license_manager/40766?token=${token} -o ~/circle_theme-2_1_3.zip && unzip -q $_ -d ${themes}/ ; \
curl -L https://www.redmineup.com/license_manager/40830?token=${token} -o ~/a1_theme-2_0_0.zip && unzip -q $_ -d ${themes}/

token は各自で RedmineUP から取得して差し替えること。

GitHub

yum --assumeyes install git && \
themes=/srv/redmine/public/themes && \
curl -L https://github.com/akabekobeko/redmine-theme-minimalflat2/releases/download/v1.6.1/minimalflat2-1.6.1.zip -o ~/minimalflat2-1.6.1.zip && unzip -q $_ -d ${themes}/ ; \
git clone --depth 1 https://github.com/themondays/Dwarf ${themes}/dwarf && cd $_ && rm -fr * && git checkout origin/master -- production/dwarf && mv production/dwarf/* . && rmdir -p production/dwarf && cd - ; \
git clone --depth 1 https://github.com/tsi/redmine-theme-flat ${themes}/flat ; \
git clone --depth 1 https://github.com/hardpixel/minelab ${themes}/minelab ; \
git clone --depth 1 https://github.com/lqez/redmine-theme-basecamp-with-icon ${themes}/basecamp-with-icon ; \
git clone --depth 1 https://github.com/makotokw/redmine-theme-gitmike ${themes}/gitmike ; \
git clone --depth 1 https://github.com/farend/redmine_theme_farend_basic ${themes}/farend_basic ; \
git clone --depth 1 https://github.com/farend/redmine_theme_farend_fancy ${themes}/farend_fancy ; \
git clone --depth 1 https://github.com/Nitrino/flatly_light_redmine ${themes}/flatly_light ; \
git clone --depth 1 https://github.com/mrliptontea/PurpleMine2 ${themes}/purplemine2 ; \
git clone --depth 1 https://github.com/Froiden/fedmine ${themes}/fedmine ; \
git clone --depth 1 https://github.com/vsc55/redmine-theme ${themes}/pixel-cookers ; \
git clone --depth 1 https://github.com/FabriceSalvaire/redmine-improved-theme ${themes}/sass_theme

SSL 対応

docker network create nginx-proxy && \
mkdir --parents --verbose /srv/nginx-proxy && \
cat << "_EOF_" > /srv/nginx-proxy/docker-compose.yml && \
docker-compose --project-directory /srv/nginx-proxy up --detach
version: '3.7'
services:
  nginx-proxy:
    container_name: ${NGINX_PROXY:-nginx-proxy}
    image: jwilder/nginx-proxy
    labels:
      - com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy
    restart: always
    ports:
      - 80:80
      - 443:443
    volumes:
      - /srv/nginx-proxy:/etc/nginx/vhost.d
      - /srv/nginx-proxy:/usr/share/nginx/html
      - /srv/nginx-proxy/certs:/etc/nginx/certs:ro
      - /var/run/docker.sock:/tmp/docker.sock:ro
 letsencrypt:
    container_name:letsencrypt
    image: jrcs/letsencrypt-nginx-proxy-companion
    restart: always
    depends_on:
      - ${NGINX_PROXY:-nginx-proxy}
    volumes:
      - /srv/nginx-proxy:/etc/nginx/vhost.d
      - /srv/nginx-proxy:/usr/share/nginx/html
      - /srv/nginx-proxy/certs:/etc/nginx/certs:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
networks:
  default:
    name: ${NETWORK:-nginx-proxy}
_EOF_

https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion
https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion
https://hub.docker.com/r/jwilder/nginx-proxy/
https://github.com/jwilder/nginx-proxy

その他の Redmine 関連記事


  1. /trunk/doc/COPYING - Redmine 

  2. "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version." - /trunk/doc/README_FOR_APP - Redmine 

  3. Google トレンドで「redmine - すべての国、過去 12 か月間」の 地域別のインタレスト を見る 

  4. redmine, trac - 調べる - Google トレンド 

  5. redmine - Popularity Contest Statistics -- Debian Quality Assurance 

  6. trac - Popularity Contest Statistics -- Debian Quality Assurance 

  7. https://www.redmine.org/projects/redmine/wiki/RedmineInstall#Requirements 

  8. "新たに構築された 7.5 のシステムでは container_file_t しかないので fcontext を svrit_sandbox_file_t に設定できない" - 1525921 – Missing container_file_t context type within the container-selinux package shipped by rhel-7-server-extras-rpms 

  9. "z オプションは、ボリュームの内容が複数のコンテナによって共有されていると Docker に伝えます。その結果、Docker は共有コンテント・ラベルとして内容をラベル付けします。" - ボリューム・ラベル - コンテナでデータを管理する — Docker-docs-ja 1.9.0b ドキュメント 

  10. "Z オプションは、内容はプライベートで共有されるべきではないラベルと Docker に伝えます。現在のコンテナのみが、プライベートに(個別に)ボリュームを利用可能です。" - ボリューム・ラベル - コンテナでデータを管理する — Docker-docs-ja 1.9.0b ドキュメント 

  11. :mem_cache_store vs. :dalli_store? · Issue #21595 · rails/rails 

  12. "This cache store uses Danga's memcached server to provide a centralized cache for your application. Rails uses the bundled dalli gem by default." - 2.5 ActiveSupport::Cache::MemCacheStore - 2 Cache Stores - Caching with Rails: An Overview — Ruby on Rails Guides 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away