イントラ環境向け複数Redmine構築メモと使用プラグイン一覧

ベストな方法かどうかはわからないですが、どのように環境構築しているのかを自分のメモ代わりに書いています。
もし、もっとこうした方がいいよと言うのがあればコメントお願いします。

# Redmineの利用目的

社内でRedmineを20サイト以上運用管理しています。社内におけるRedmineの利用目的としては

- 情報共有
- 文書管理
- ナレッジ共有
- 案件管理・出荷業務管理
- ITIL的運用(サポート、問い合わせ)
- プロジェクト管理
- 在庫・出荷情報管理

となっていて、Redmine本来のプロジェクト管理として使っているところは少ないです。
ExcelからAccess使ってマクロ組んで...の代わりに、RedmineのチケットをDBとして利用している使い方が多い状態です。
また、使用する部署、部門横断的な管理等があり、それぞれ管理ポリシーが異なるため同一ホストでは運用が困難なため結果として複数ホストになっています。


# サーバー環境
OS: CentOS 7.4
HTTP Server: Apache 2.4
DB: MariaDB
DB管理: phpmyadmin (PHPは7.1)
Ruby: 2.3

Rubyはソースコードからコンパイルしています。Rubyが最新バージョンでないのは一部のプラグインでRuby 2.4に対応していないものがあったので2.3にしています。
他は、yumでインストールしています。

インストール時の流れは
http://blog.redmine.jp/articles/3_4/install/centos/
http://blog.redmine.jp/articles/3_0/installation_centos/
を参照してください。

## Apacheの設定
複数のRedmineを1台のサーバーで構築するので、VirtualHostを使用します。
passengerの設定とVirtualHostのファイル構成は以下のようにしています。

```/etc/httpd/conf.d/
/etc/httpd/conf.d/passenger.conf
/etc/httpd/conf.d/virtualhost.conf
/etc/httpd/conf.d/virtualhost/<ホスト名>.conf
```

VirtualHostの設定ファイルを1ホストに対して1ファイルとすることで、複数建てる時にコピーして使用できるので便利です。

```/etc/httpd/conf.d/passenger.conf
LoadModule passenger_module /usr/local/lib/ruby/gems/2.3.0/gems/passenger-5.1.11/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
  PassengerRoot /usr/local/lib/ruby/gems/2.3.0/gems/passenger-5.1.11
  PassengerDefaultRuby /usr/local/bin/ruby
</IfModule>

# Passengerが追加するHTTPヘッダを削除するための設定（任意）。
#
Header always unset "X-Powered-By"
Header always unset "X-Runtime"

# 必要に応じてPassengerのチューニングのための設定を追加（任意）。
# 詳しくはPhusion Passenger users guide(https://www.phusionpassenger.com/library/config/apache/reference/)参照。
PassengerMaxPoolSize 20
PassengerMaxInstancesPerApp 4
PassengerPoolIdleTime 864000
PassengerHighPerformance on
PassengerStatThrottleRate 10
PassengerSpawnMethod smart
PassengerFriendlyErrorPages off
PassengerStartTimeout 120
```
```/etc/httpd/conf.d/virtualhost.conf
# VirtualHost設定群読み込み
IncludeOptional conf.d/virtualhost/*.conf
```
```/etc/httpd/conf.d/virtualhost/ホスト名.conf
<VirtualHost *:80>
        ServerName <ホスト名>.example.local
        DocumentRoot /var/www/redmine/site/<ホスト名>/public
        ErrorLog logs/<ホスト名>-error_log
        CustomLog logs/<ホスト名>-access_log combined
</VirtualHost>

PassengerPreStart http://<ホスト名>.example.local
```
RedmineのVirtualHostは、/var/www/redmine/site以下にホスト名をフォルダ名にしてRedmineをインストールしています。

## MariaDBの設定

物忘れが激しいので、RedmineのDBを作成する際は、ユーザ名とDB名を同じにしています。
パフォーマンスチューニングは、 https://github.com/major/MySQLTuner-perl を利用して適宜調整しています。

```/etc/my.cnf.d/server.cnf
#
# These groups are read by MariaDB server.
# Use it for options that only the server (but not clients) should see
#
# See the examples of server my.cnf files in /usr/share/mysql/
#

# this is read by the standalone daemon and embedded servers
[server]

# this is only for the mysqld standalone daemon
[mysqld]
character-set-server = utf8
skip-name-resolve
thread_cache_size=4
query_cache_size=0
query_cache_type=0
query_cache_limit=8M
innodb_file_per_table=1
innodb_log_file_size=16M
tmp_table_size=32M
max_heap_table_size=32M
table_open_cache=512
join_buffer_size=256K

# スロークエリの出力設定
slow_query_log=ON
# スロークエリと判定する秒数
long_query_time=3
# スロークエリログの場所
log-slow-queries=/var/log/mariadb/mariadb_slow.log

performance_schema=on

# this is only for embedded server
[embedded]

# This group is only read by MariaDB-5.5 servers.
# If you use the same .cnf file for MariaDB of different versions,
# use this group for options that older servers don't understand
[mysqld-5.5]

# These two groups are only read by MariaDB servers, not by MySQL.
# If you use the same .cnf file for MySQL and MariaDB,
# you can put MariaDB-only options here
[mariadb]

[mariadb-5.5]
```
チューニング中の値になっていますので、数字は参考にしないでください。
適宜、mysqltunerを使用してパラメータ設定を行ってください。

# 使用しているプラグインとテーマ

テーマと多数のプラグインを各Redmineのサイトに適用するのは困難なため、/var/www/redmine/theme, /var/www/redmine/plugin のフォルダ以下にそれぞれ使用するテーマとプラグインを入れます。
各Redmineのサイト用のフォルダ(/var/www/redmine/site/<ホスト名>/)のplugin, public/theme フォルダ以下で、先のテーマ、プラグインをシンボリックリンクします。シンボリックリンクを行う際は相対パスにせず絶対パスにします。(フォルダ移動させた時も不整合が起こらないようにするため)
このようにすることで、同一のプラグインを使用していてパッチの適用忘れやバージョンアップ忘れを防ぎます。

必ず入れているプラグインの方では、Redmine3.4.2で動作確認(簡単に)していますが、一部でしか使用していないプラグインはまだ未検証なものがあります。

## 使用しているテーマ

Redmineのテーマは、
https://github.com/farend/redmine_theme_farend_fancy
を使用しています

## 使用しているプラグイン

### ほぼ必ず入れれているプラグイン

|プラグイン名称          |機能                                               |備考                              |
|:----------------------|:--------------------------------------------------|:---------------------------------|
|[Clipboard image paste](http://www.redmine.org/plugins/clipboard_image_paste)|クリップボードイメージの貼り付け|   |
|[Redmine Banner plugin](https://github.com/akiko-pusu/redmine_banner)|バナー表示をさせる。|管理者からの通知に便利|
|[Redmine Default Custom Query](https://github.com/hidakatsuya/redmine_default_custom_query)|プロジェクト毎にデフォルトのカスタムクエリーを指定できる||
|[DMSF](http://www.redmine.org/plugins/dmsf)|文書、画像の管理を可能にする|ZIP圧縮で文字化けするのでパッチ必要。easyredmine社でもメンテされている|
|[Knowledgebase](https://github.com/alexbevi/redmine_knowledgebase)|ナレッジベース|メンテ復活!!|
|[Redmine LDAP Sync](https://github.com/thorin/redmine_ldap_sync)|LDAP情報の同期、参照フィールドの追加、グループ割り当てが可能||
|[Redmine Multiple Projects per Issue plugin](https://github.com/nanego/redmine_multiprojects_issue)|一つのチケットを複数プロジェクトに割り当て||
|[Redmine Startpage plugin](https://github.com/txinto/redmine_startpage)|Redmineのデフォルトの概要ページを任意のページに変更できる|TOPページプロジェクトを作成してWikiページをStartPageにしているケースが多い|
|[Redmine Tags](https://github.com/ixti/redmine_tags/)|チケット、Wikiへのタグ追加|redmineup(旧redmine crm)社がサポート？|
|[Redmine Wiki Extensions plugin](http://www.r-labs.org/projects/r-labs/wiki/Wiki_Extensions_en)|Wikiの機能拡張|Tag機能はRedmine Tagsと被るので無効にする|
|[Redmine Wiki Lists plugin](https://www.r-labs.org/projects/wiki_lists/wiki/Wiki_Lists_en)|Wikiページにチケット一覧を表示||
|[Sidebar Hide Plugin](https://github.com/bdemirkir/sidebar_hide)|サイドバーを隠すボタン追加|view customizeでも可能|
|[Spectator plugin](https://github.com/alpcrimea/redmine-spectator-plugin)|管理者が他人に成りすますことができる||
|[View Customize plugin](https://github.com/onozaty/redmine-view-customize)|神プラグイン! javascriptができればなんでもできる。||
|[WikiNG](http://projects.andriylesyuk.com/projects/wiking)|視覚的に便利なマクロ追加と、オリジナルのmacro追加可能|本家 3.4.x対応|

### 一部で使用しているプラグイン

|プラグイン名称          |機能                                               |備考                              |
|:----------------------|:--------------------------------------------------|:---------------------------------|
|[Computed custom field](https://github.com/annikoff/redmine_plugin_computed_custom_field)|カスタムフィールドで計算ができる|チケット作成後に計算結果が反映される。view customizeでできない計算も可能(内部データ参照可能)。easyredmine社でもメンテされている。|
|[Lycheeプラグイン](https://lychee-redmine.jp/)|ガントチャート、EVM、リソース管理等||
|[Redmine Agile](https://www.redmineup.com/pages/plugins/agile)|タスクカンバン||
|[Redmine Checklist](https://www.redmineup.com/pages/plugins/checklists)|チケットにチェックリスト追加|Pro版はテンプレート登録可能|
|[Redmine Contact](https://www.redmineup.com/pages/plugins/crm)|コンタクトリスト(会社、顧客)を作成できる||
|[Redmine Introductions plugin](https://github.com/nanego/redmine_introductions)|チュートリアルを作成できる||
|[Redmine Issue Templates plugin](https://bitbucket.org/akiko_pusu/redmine_issue_templates)|チケットテンプレート作成|個人的にはView Customizeでやる方が好きだが、View Customize触れない人でもテンプレートが作成できる|
|[Redmine Products](https://www.redmineup.com/pages/plugins/products)|製品情報を登録できる||
|[Redmine Q&A plugin](https://www.redmineup.com/pages/plugins/questions)|QAサイト構築||
|[Redmine Wiki Unc plugin](https://github.com/bearmini/redmine_wiki_unc)|fileリンクをサポートできる|できるだけFileリポジトリを使用しているが、アクセス権の関係上できない場合はこれを使う。ただし、IEのみ対応になる|

## プラグインパッチ

### DMSF

DMSFの保存フォルダ指定は、最新のものは相対パスで指定するようになっています。
Redmineのフォルダ移行する際にも相対パスの方が都合がよいのでDMSFを最新のものにアップデートしたら変更をお勧めします。(デフォルトは、dmsfになっていますが、旧DMSFからアップグレードした場合は、files/dmsf になると思います)

DMSFで複数ファイル選択してダウンロードもしくはフォルダーのダウンロードを行った際に、UTF-8のままZIP圧縮を行うので、Windowsで展開した際に文字化けが起こるので、SJISに変換する必要があります。

```diff
diff -urN ../../redmine/plugins/redmine_dmsf/app/controllers/dmsf_controller.rb redmine_dmsf/app/controllers/dmsf_controller.rb
--- ../../redmine/plugins/redmine_dmsf/app/controllers/dmsf_controller.rb       2017-10-30 17:37:11.602348740 +0900
+++ redmine_dmsf/app/controllers/dmsf_controller.rb     2017-11-09 09:47:57.086032137 +0900
@@ -413,11 +413,15 @@

   def zip_entries(zip, selected_folders, selected_files)
     member = Member.where(:user_id => User.current.id, :project_id => @project.id).first
+    encoding = "utf-8"
+    encoding = "sjis" if request.env["HTTP_USER_AGENT"] =~ /Windows/ && request.env["HTTP_ACCEPT_LANGUAGE"] =~ /^ja/
+
     if selected_folders && selected_folders.is_a?(Array)
       selected_folders.each do |selected_folder_id|
         folder = DmsfFolder.visible.find_by_id selected_folder_id
         if folder
-          zip.add_folder(folder, member, (folder.dmsf_folder.dmsf_path_str if folder.dmsf_folder))
+#         zip.add_folder(folder, member, (folder.dmsf_folder.dmsf_path_str if folder.dmsf_folder))
+          zip.add_folder(folder, member, (@folder.dmsf_path_str if @folder), encoding)
         else
           raise FileNotFound
         end
@@ -432,7 +436,8 @@
         unless (file.project == @project) || User.current.allowed_to?(:view_dmsf_files, file.project)
           raise DmsfAccessError
         end
-        zip.add_file(file, member, (file.dmsf_folder.dmsf_path_str if file.dmsf_folder)) if file
+#       zip.add_file(file, member, (file.dmsf_folder.dmsf_path_str if file.dmsf_folder)) if file
+        zip.add_file(file, member, (@folder.dmsf_path_str if @folder), encoding) if file
       end
     end
     max_files = Setting.plugin_redmine_dmsf['dmsf_max_file_download'].to_i
diff -urN ../../redmine/plugins/redmine_dmsf/lib/dmsf_zip.rb redmine_dmsf/lib/dmsf_zip.rb
--- ../../redmine/plugins/redmine_dmsf/lib/dmsf_zip.rb  2017-10-30 17:37:11.640347920 +0900
+++ redmine_dmsf/lib/dmsf_zip.rb        2017-11-09 09:53:10.165323530 +0900
@@ -20,6 +20,7 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

 require 'zip'
+require 'kconv'

 class DmsfZip

@@ -43,7 +44,8 @@
     @zip.close if @zip
   end

-  def add_file(file, member, root_path = nil)
+# def add_file(file, member, root_path = nil)
+  def add_file(file, member, root_path = nil, encoding = "utf-8")
     unless @files.include?(file)
       string_path = file.dmsf_folder.nil? ? '' : "#{file.dmsf_folder.dmsf_path_str}/"
       string_path = string_path[(root_path.length + 1) .. string_path.length] if root_path
@@ -53,6 +55,11 @@
       else
         string_path += file.formatted_name(Setting.plugin_redmine_dmsf['dmsf_global_title_format'])
       end
+
+     if encoding == "sjis"
+        string_path = string_path.tosjis()
+     end
+
       @zip_file.put_next_entry(string_path)
       File.open(file.last_revision.disk_file, 'rb') do |f|
         while (buffer = f.read(8192))
@@ -63,15 +70,25 @@
     end
   end

-  def add_folder(folder, member, root_path = nil)
+# def add_folder(folder, member, root_path = nil)
+  def add_folder(folder, member, root_path = nil, encoding = "utf-8")
+
     unless @folders.include?(folder)
       string_path = "#{folder.dmsf_path_str}/"
       string_path = string_path[(root_path.length + 1) .. string_path.length] if root_path
+
+     if encoding == "sjis"
+        string_path = string_path.tosjis()
+     end
+
       @zip_file.put_next_entry(string_path)
       @folders << folder
-      folder.dmsf_folders.visible.each { |subfolder| self.add_folder(subfolder, member, root_path) }
-      folder.dmsf_files.visible.each { |file| self.add_file(file, member, root_path) }
+#     folder.dmsf_folders.visible.each { |subfolder| self.add_folder(subfolder, member, root_path) }
+#     folder.dmsf_files.visible.each { |file| self.add_file(file, member, root_path) }
+      folder.dmsf_folders.visible.each { |subfolder| self.add_folder(subfolder, member, root_path, encoding) }
+      folder.dmsf_files.visible.each { |file| self.add_file(file, member, root_path, encoding) }
+
     end
   end
```

### knowledgebase

イントラ環境下だとインターネット側にアクセスする場合だけだとおもいます。Gemfileを以下のようにします

不具合修正を含めたものは
https://github.com/crosspoints/redmine_knowledgebase
で公開しています
2018/1/12時点で本家側に取り込まれましたので本家のものを使用ください。
以下不要です

```Gemfile
gem 'redmine_acts_as_taggable_on', :git => "https://github.com/alexbevi/redmine_acts_as_taggable_on"
gem 'ya2yaml'
gem 'awesome_nested_set'
```

不具合修正パッチ

```diff
--- redmine_knowledgebase-master/app/views/articles/show.html.erb       2017-11-19 22:57:32.000000000 +0900
+++ redmine_knowledgebase/app/views/articles/show.html.erb      2018-01-11 16:13:19.261609100 +0900
@@ -18,8 +18,9 @@
   <% end %>

                <li><%= link_to_if_authorized l(:label_new_comment), { :controller => "articles", :action => "comment",
:article_id => @article.id, :project_id => @project}, :class => "icon icon-comment", :remote => true, :method => :get %>
</li>
+               <li> <%= watcher_link(@article, User.current) %></li>
                <li>
-      <%= render :partial => 'watchers/watchers', :locals => {:watched => @article} %>
+      <%= render :partial => 'articles/watchers', :locals => {:watched => @article} %>
     </li>
        </ul>
        <br />
diff -uprN redmine_knowledgebase-master/app/views/categories/show.html.erb redmine_knowledgebase/app/views/categories/sh
ow.html.erb
--- redmine_knowledgebase-master/app/views/categories/show.html.erb     2017-11-19 22:57:32.000000000 +0900
+++ redmine_knowledgebase/app/views/categories/show.html.erb    2018-01-11 16:13:19.385997800 +0900
@@ -20,7 +20,8 @@
     <li><%= link_to_if_authorized l(:label_new_category), { :controller => 'categories', :action => 'new', :parent_id =
> @category.id, :project_id => @project}, :class => 'icon icon-add' %></li>
     <li><%= link_to_if_authorized l(:label_edit_category), { :controller => 'categories', :action => 'edit', :id => @ca
tegory.id, :project_id => @project}, :class => 'icon icon-edit' %></li>
     <li><%= link_to_if_authorized l(:label_delete_category), { :controller => 'categories', :action => 'destroy', :id =
> @category.id, :project_id => @project}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-de
l' %></li>              <li>
-      <%= render :partial => 'watchers/watchers', :locals => {:watched => @category} %>
+    <li><%= watcher_link(@category, User.current) %></li>
+      <%= render :partial => 'categories/watchers', :locals => {:watched => @category} %>
     </li>
        </ul>
        <h3><%= l(:title_browse_by_category) %></h3>
```

### WikiNG

buttonマクロが動作しない場合は以下のようにする

```wiking.css
a.wiki-class-button {
    border-width: 1px;
    border-style: solid;
    -moz-border-radius: 6px;
    border-radius: 6px;
    background-color: #507aaa;
    border-color: #809fc3 #2C4056 #2C4056 #809fc3;
    -moz-text-shadow: 1px 1px 1px #2C4056;
    text-shadow: 1px 1px 1px #2C4056;
    padding: 0.5em 1.25em;
    text-decoration: none;
    color: #fff;
    display: inline-block;
}
a:active.wiki-class-button {
    border-color: #2C4056 #809fc3 #809fc3 #2C4056;
}
```

View Customizeで定義してもOK