プロジェクト

全般

プロフィール

RedmineをCentOS 6上で動かすーUnicornとNginx編

はじめに

RedmineをCentOS 6上で動かすためのセットアップメモです。

Ruby on Railsアプリケーションを実行するアプリケーションサーバーとしてUnicorn、WebサーバーとしてNginxを組み合わせた環境を構築します。

まず、動作環境を述べます。次にRedmineを動かすのに必要となるRubyおよびMySQL(MariaDB)のインストールを行います。そして、Redmine(本体)のインストールとRedmine組み込みのWEBrickでの確認を行います。それからUnicornのセットアップと確認を行います。最後にNginxのセットアップと確認を行います。

動作環境

ハードウェア構成

AMD A10-6800K CPUと16GBメモリのPC上の仮想化ゲスト上で動かします。
ホストのハードウェア仕様は自作PC-F2A85Vを参照。

ゲストの(仮想)ハードウェアの仕様は次のとおりです。
CPUとメモリは、動かしながら様子を見て増加させていくため、初期は少ない値としています。

項目 内容 備考
仮想化ホストOS CentOS 6.5 64bit版
仮想化ソフトウェア OS標準搭載KVM
仮想CPU数 1 必要に応じて順次増加
メモリ 1024MB 2048MB 必要に応じて順次増加
ディスクイメージ qcow2 場合によってRAW(スパースなし)に変更
ファイルシステム ext4

ソフトウェア構成

項目 使用ソフトウェア 備考
OS CentOS 6.5 64bit版
Redmine Redmine 2.5.1
Ruby Ruby 2.1.2 CentOS 6標準の1.8.7は性能的に厳しいので最新版に差し替え
RDBMS MariaDB 5.5 CentOS 6標準のMySQL 5.1は古いので、CentOS 7で標準搭載のMariaDBに差し替え
Rackサーバー Unicorn
Webサーバー Nginx 1.6.0 EPEL Nginx公式yumリポジトリからインストール

rubyのバージョンについて

Redmine公式ページのインストールガイド には、サポートしているrubyのバージョンが次のように記載されています。

ruby 1.8.7, 1.9.2, 1.9.3, 2.0.0, jruby-1.7.6

最新版を使うときはオウンリスクでとなります。
ruby 2.1.1は互換性を損なう変更(Hash#reject の不具合)があり、Railsアプリケーションが影響を受けるので使用しません。

CentOS 6の必要なパッケージをインストール

Redmineをインストールし動かすにあたり必要となるCentOS 6パッケージをインストールします。

Rubyのインストール

CentOS 6に標準搭載されるRubyはバージョンが1.8.7とRedmineの動作条件には辛うじて含まれるものの古いためパフォーマンスに難があります。そこで、Rubyの新しいバージョン2.1.2に差し替えます。

Ruby 2.1.2のCentOS 6用RPMパッケージを作成しインストールします。CentOS 6用にRubyのRPMを作成する方法については次を参照ください。作成したRPMパッケージは本プロジェクトの[ファイル]に添付しています。

EPELリポジトリを参照する設定をyumに追加

~$ sudo rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm

ruby 2.1.2のインストール

上述で作成したRPMパッケージをマシン上に持ってきてyum localinstallコマンドでインストールしました。

~$ sudo yum localinstall ruby-2.1.2-1.el6.x86_64.rpm
  • 既にrubyパッケージがインストールされていると、うまくアップデートできないかもしれません。そのときは一旦古いrubyをアンインストールしてから新しいrubyをインストールします。

その他パッケージのインストール

CentOS 6のyumリポジトリおよびEPELリポジトリから必要なパッケージをインストールします。

$ sudo yum groupinstall "Development Tools" 
  :
$ sudo yum install openssl-devel readline-devel zlib-devel curl-devel libyaml-devel
  :
$ sudo yum install ImageMagick ImageMagick-devel
  :

Redmineのセットアップ

インストール方針

Redmineは、基本的にはデータベースアプリケーションです。データベースはデータが蓄積されるにつれパフォーマンスが低下していきます。逆に初期の空っぽに近い状態では遅いデータベース(SQLite)を使っていても性能差を体感することはありません。なので、安直に試していいと思って運用を開始してしまうと、長く運用され蓄積が増えていった将来にデータベースの性能で泣かされかねません。ここはしっかり構築しておきたい点です。

また、RedmineのプログラムはRubyですが、これはメモリをけっこう喰いますので、メモリを十分用意しておくのが大切です。運用を始めてまだそれほどデータが蓄積されていないのに異様に重い、応答に時間がかかる、といった場合、物理メモリが欠乏してスワップに入っていることがよくあります。経験的には、物理メモリ2GBでは頻繁にスワップに陥り、3GBで時々発生、4GBではたまに発生、といった感じでしょうか。大規模に使うともっと多く必要かと思います。

MariaDBのインストール

Redmineがサポートしているデータベースは、MySQL 5.0以降、PostgreSQL 8.2以降、Microsoft SQLServer 2008以降、SLQite 3です。
この中でMySQLはRedmineとの連携の実績も多く、チューニングをはじめとした情報が豊富です。CentOS 6は標準でMySQL 5.1を搭載していますが、今ではちょっと(大分)古くなっています。新しいバージョンのMySQL 5.6を使おうかと思っていましたが、先日リリースされたRed Hat Enterprise Linux 7ではMySQL 5.5からフォークしたMaria DB 5.5が標準搭載されているので、今回はMariaDB 5.5を使うことにします。いずれCentOS 7にしたときはMariaDB 5.5を使うことになりますので。

https://mariadb.org/

yumリポジトリにMariaDBを追加しMariaDBパッケージをインストール

MariaDBはCentOS 6向けにyumリポジトリを提供しているので、これを追加します。
https://downloads.mariadb.org/mariadb/repositories/

このページから、OS(Linuxディストリビューション)にCentOS、リリースにCentOS 6(64bit)、MariaDBのバージョンに5.5を選択すると、yumリポジトリ設定内容が表示されます。これをファイルに写して作成します。

  • /etc/yum.repos.d/MariaDB.repo
    # MariaDB 5.5 CentOS repository list - created 2014-06-26 11:57 UTC
    # http://mariadb.org/mariadb/repositories/
    [mariadb]
    name = MariaDB
    baseurl = http://yum.mariadb.org/5.5/centos6-amd64
    gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
    gpgcheck=1
    
~$ sudo yum install MariaDB-server MariaDB-devel

MariaDB設定

設定ファイルは、/etc/my.cnfではなく、/etc/my.cnf.d/mysql-clients.cnfと/etc/my.cnf.d/server.cnfとに分けて記述します。/etc/my.cnfもありますが、ここには/etc/my.cnf.d(の下にあるファイル)をインクルードする指示が書かれています。

まずはテーブルの言語がlatin1にならないようにutf8設定を追加
  • /etc/my.cnf.d/server.cnf
    [mysqld]
    character-set-server = utf8
    
  • /etc/my.cnf.d/mysql-clients.cnf
    [mysql]
    default-character-set = utf8
    show-warnings
    

MariaDBの起動

MariaDBパッケージをインストールすると、service名はmysqlで登録されます。chkconfigで自動起動設定済みです。

~$ sudo service mysql start
Starting MySQL.. SUCCESS!
~$

軽く動作確認メモ

status内容

statusの実行結果を次に示します。

MariaDB [(none)]> status
--------------
mysql  Ver 15.1 Distrib 5.5.38-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:          3
Current database:
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server:                 MariaDB
Server version:         5.5.38-MariaDB MariaDB Server
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock
Uptime:                 8 min 30 sec

Threads: 2  Questions: 53  Slow queries: 0  Opens: 17  Flush tables: 2  Open tables: 42  Queries per second avg: 0.103
--------------

MariaDB [(none)]>
デフォルトのデータベースエンジンを調べる

show enginesの実行結果を次に示します。

MariaDB [none]> show engines;
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                                    | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
| CSV                | YES     | CSV storage engine                                                         | NO           | NO   | NO         |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                                      | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables                  | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears)             | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                                      | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Percona-XtraDB, Supports transactions, row-level locking, and foreign keys | YES          | YES  | YES        |
| ARCHIVE            | YES     | Archive storage engine                                                     | NO           | NO   | NO         |
| FEDERATED          | YES     | FederatedX pluggable storage engine                                        | YES          | NO   | YES        |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                                         | NO           | NO   | NO         |
| Aria               | YES     | Crash-safe tables with MyISAM heritage                                     | NO           | NO   | NO         |
+--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
10 rows in set (0.00 sec)
パラメータの一覧とデフォルト値
~$ mysqld --verbose --help

結果は長いのでほんの一部だけ抜粋しますが、コマンドラインでのパラメータ指定方法、現在の設定値が一覧表示されます。
character-set-client-handshake                    TRUE
collation-server                                  utf8_general_ci
connect-timeout                                   10
datadir                                           /var/lib/mysql/
default-storage-engine                            InnoDB
innodb-additional-mem-pool-size                   8388608
innodb-buffer-pool-size                           134217728
innodb-flush-method                               (No default value)
innodb-log-buffer-size                            8388608
innodb-log-file-size                              5242880
innodb-log-files-in-group                         2

MariaDB接続ユーザー"root"のパスワード設定

インストール直後はMariaDBのrootユーザーにパスワードが設定されていません。userテーブルでデフォルトで作成されているユーザーとホスト、パスワードを見てみます。

インストール直後のuserテーブルの内容を次に示します。

$ mysql -uroot
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 5.5.38-MariaDB MariaDB Server

Copyright (c) 2000, 2014, Oracle, Monty Program Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> SET PASSWORD FOR root@localhost=PASSWORD('mypassword');
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> SELECT user,host,password FROM mysql.user;
+------+-----------+----------+
| user | host      | password |
+------+-----------+----------+
| root | localhost |          |
| root | nostrum   |          |
| root | 127.0.0.1 |          |
| root | ::1       |          |
|      | localhost |          |
|      | nostrum   |          |
+------+-----------+----------+
6 rows in set (0.00 sec)

MariaDB [(none)]>

MariaDBは、ユーザーとホストの組み合わせで権限制御が行われます。
rootユーザーが4レコードありますが、これはMySQLにどう接続してくるかによって使い分けられます。
localhostと127.0.0.1は同じように思いますが異なります。localhostはUNIXソケット、127.0.0.1はTCP/IPの接続になるらしいです。::1はTCP/IPv6です。
userが空なのは匿名ユーザーです。

まず、rootユーザー4レコードにパスワードを設定します。

MariaDB [(none)]> UPDATE mysql.user SET password = PASSWORD('mypassword') WHERE user = 'root';
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4  Changed: 4  Warnings: 0

MariaDB [(none)]> SELECT user,host,password FROM mysql.user;
+------+-----------+-------------------------------------------+
| user | host      | password                                  |
+------+-----------+-------------------------------------------+
| root | localhost | *FABE5482D5AADF36D028AC443D117BE1180B9725 |
| root | nostrum   | *FABE5482D5AADF36D028AC443D117BE1180B9725 |
| root | 127.0.0.1 | *FABE5482D5AADF36D028AC443D117BE1180B9725 |
| root | ::1       | *FABE5482D5AADF36D028AC443D117BE1180B9725 |
|      | localhost |                                           |
|      | nostrum   |                                           |
+------+-----------+-------------------------------------------+
6 rows in set (0.00 sec)

MariaDB [(none)]> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]>

Redmine本体のインストール

RedmineはRedmineを実行するためには、Redmine本体のほかにRedminが使用している各種Rubyモジュール(ライブラリ集、rubyではgemパッケージと呼ぶ)が必要です。Ruby on RailsもRubyモジュールです。Rubyモジュールは、bundlerという仕組みを使ってインストールします。

Rubyモジュールは、システム共通の場所に入れるかRedmine固有の場所に入れるかの選択肢がありますが、トラブルを避けるためRedmine固有の場所にインストールします。

Redmine本体の入手と展開

Redmine公式サイトのダウンロードページ より最新安定版(Latest stable releases)をダウンロードします。2014-06-28時点でVer.2.5.1が最新です。

ダウンロードしたRedmine本体を展開します。インストールディレクトリは/var/lib/の下にredmine-<バージョン番号>となるよう展開し、シンボリックリンクファイル/var/lib/redmineを使用するredmineのディレクトリを指すように作成します。

~$ cd /var/lib
lib$ sudo tar xzf ~/install/redmine-2.5.1.tar.gz
lib$ sudo ln -s redmine-2.5.1 redmine
lib$ ls -l
  :
lrwxrwxrwx.  1 root    root    13  6月 27 21:04 2014 redmine -> redmine-2.5.1
drwxrwxr-x. 16    1000  1000 4096  3月 30 01:56 2014 redmine-2.5.1
  :

Redmineのデータベース設定

/var/lib/redmine/config/database.ymlを作成します。Redmineにはサンプルとしてdatabase.yml.sampleが含まれているので、このサンプルからMySQL用の設定を抜き出し修正するとよいでしょう。

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: mypassword
  encoding: utf8
  • サンプルにはproduction以外にdevelopment、testの設定がありますが、通常productionのみ使用するので設定はproductionだけ記述します。
  • adapterはMariaDB(MySQL)5.5の場合はmysql2を指定します。
  • databaseは、MariaDB上でRedmineのテーブルを収容するデータベース名を指定します。ここで指定した名称のデータベースをMariaDB上に作成します。
  • encodingはMariaDBの設定に合わせてutf8とします。
~$ mysql -uroot -p
Enter password: 
MariaDB [(none)]> CREATE DATABASE redmine;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| redmine            |
| test               |
+--------------------+
5 rows in set (0.00 sec)

MariaDB [(none)]> 
  • hostは、Redmineから使用するMariaDBが動いているホストを指定します。通常同じマシン上でRedmineとMariaDBを動かすのでUNIXソケットを使うlocalhostとします。
  • usernameとpasswordは、RedmineからMariaDBに接続するときに使用するMariaDBのユーザー・パスワードです。ここで指定したユーザー・パスワードをMariaDBに作成し、先に作成したredmineデータベースの管理権限を付与します。
MariaDB [(none)]> GRANT ALL ON redmine.* TO 'redmine'@'localhost' IDENTIFIED BY
'mypassword' WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]>
  • ALLはALL PRIVILEGESと同義です。UNIXソケットを使って接続するのでホスト名はlocalhostとしています。WITH GRANT OPTIONを指定するとそのユーザーが持つ権限範囲以内の権限を他のユーザーに付与できます。

メール設定

/var/lib/redmine/config/configuration.ymlを作成します。Redmineにはサンプルとしてconfiguration.yml.sampleが含まれているので、このサンプルから設定を抜き出し修正するとよいでしょう。
今回はメールサーバーをローカルとして使用します。

# Simple SMTP server at localhost

production:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: "localhost" 
      port: 25

Rubyモジュール群のインストール

Redmine本体が依存するRubyモジュール群をインストールします。Rubyモジュールのインストールには、rubyのbundlerを使用します。

bundlerのインストール

bundlerもRubyモジュール(gemパッケージ)です。これはシステム共通場所にインストールします。

システム共通にインストールされているgemパッケージを一覧します。

~$ gem list

*** LOCAL GEMS ***

bigdecimal (1.2.4)
io-console (0.4.2)
json (1.8.1)
minitest (4.7.5)
psych (2.0.5)
rake (10.1.0)
rdoc (4.1.0)
test-unit (2.1.2.0)
~$

bundlerはないので、インストールします。

~$ sudo gem install bundler --no-rdoc --no-ri
Fetching: bundler-1.6.3.gem (100%)
Successfully installed bundler-1.6.3
1 gem installed
~$

bundlerモジュールのドキュメントをローカルでは参照しないので--no-rdocと--no-riを指定しました。

これでbundlerを使用できるようになったので、Redmineが必要とするRubyパッケージ群をインストールします。

Redmineが依存するRubyパッケージ群のインストール

Redmineが依存するRubyパッケージは、Redmineのインストールディレクトリ下のvender/bundlerを指定し、そこへインストールします。

~$ cd /var/lib/redmine-2.5.1
redmine-2.5.1$ sudo bundle install --path vendor/bundler --without development test
  :

Redmineの初期化

Redmine本体および依存するRubyモジュールのインストールが終わったら、Redmineの初期化を行います。

セッションデータ暗号化の鍵生成

改ざん防止のため、セッションデータを格納するクッキーを暗号化する鍵をランダムに生成します。

redmine-2.5.1$ sudo bundle exec rake generate_secret_token
  • 注記)インストール手順書等でrake xxx となっているコマンドは、Rubyモジュール群をRedmine固有のディレクトリにインストールした場合は、bundle exec rake xxx のようにbundle execをつけて実行する必要があります。

データベースのスキーマを構築

Redmineが使用するデータベースにスキーマを構築します。

redmine-2.5.1$ sudo bundle exec rake db:migrate RAILS_ENV=production
  :

MariaDBに入ってテーブル一覧を確認します。

~$ mysql -uredmine -p
Enter password:
MariaDB [(none)]> use redmine
Database changed
MariaDB [redmine]> show tables;
+-------------------------------------+
| Tables_in_redmine                   |
+-------------------------------------+
| attachments                         |
| auth_sources                        |
| boards                              |
| changes                             |
| changeset_parents                   |
| changesets                          |
| changesets_issues                   |
| comments                            |
| custom_fields                       |
| custom_fields_projects              |
| custom_fields_roles                 |
| custom_fields_trackers              |
| custom_values                       |
| documents                           |
| enabled_modules                     |
| enumerations                        |
| groups_users                        |
| issue_categories                    |
| issue_relations                     |
| issue_statuses                      |
| issues                              |
| journal_details                     |
| journals                            |
| member_roles                        |
| members                             |
| messages                            |
| news                                |
| open_id_authentication_associations |
| open_id_authentication_nonces       |
| projects                            |
| projects_trackers                   |
| queries                             |
| queries_roles                       |
| repositories                        |
| roles                               |
| schema_migrations                   |
| settings                            |
| time_entries                        |
| tokens                              |
| trackers                            |
| user_preferences                    |
| users                               |
| versions                            |
| watchers                            |
| wiki_content_versions               |
| wiki_contents                       |
| wiki_pages                          |
| wiki_redirects                      |
| wikis                               |
| workflows                           |
+-------------------------------------+
50 rows in set (0.00 sec)

MariaDB [(none)]>

Redmine内蔵WebサーバーWEBrickによる動作確認

Redmineには内蔵のWebサーバーWEBrickがあるので、Redmine単独での起動確認をWEBrickで行います。
なお、WEBrickは開発・テスト用であり、運用には耐えられません。

まず、iptablesを停止しファイアウォールの影響を除外します。

redmine-2.5.1$ sudo service iptables stop
iptables: チェインをポリシー ACCEPT へ設定中filter         [  OK  ]
iptables: ファイアウォールルールを消去中:                  [  OK  ]
iptables: モジュールを取り外し中:                          [  OK  ]

WEBrickを実行します。

redmine-2.5.1$ sudo ruby script/rails server webrick -e production
=> Booting WEBrick
=> Rails 3.2.17 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2014-06-28 03:22:21] INFO  WEBrick 1.3.1
[2014-06-28 03:22:21] INFO  ruby 2.1.2 (2014-05-08) [x86_64-linux-gnu]
[2014-06-28 03:22:21] INFO  WEBrick::HTTPServer#start: pid=4957 port=3000

Webブラウザを立ち上げ、Redmineを動かしているマシンにポート3000でアクセスします。
うまくいっていれば、WebブラウザにRedmineの画面が表示され、WEBrickを起動したコンソールにアクセスメッセージが表示されます。

Started GET "/" for 192.168.1.38 at 2014-06-28 03:23:20 +0900
Processing by WelcomeController#index as HTML
  Current user: anonymous
  Rendered welcome/index.html.erb within layouts/base (41.7ms)
Completed 200 OK in 186.0ms (Views: 67.7ms | ActiveRecord: 21.8ms)

WEBrickを停止します。WEBrickを起動したコンソールでCtrl-Cを入力します。

^C[2014-06-28 03:24:09] INFO  going to shutdown ...
[2014-06-28 03:24:09] INFO  WEBrick::HTTPServer#start done.
Exiting
redmine-2.5.1$

Unicornのセットアップ

Rubyアプリケーションを動かすアプリケーションサーバー(Rackサーバー)のUnicornをセットアップします。

Unicornのインストール

UnicornはRubyモジュール(gem)として提供されるので、bundlerでインストールします。
Redmineをインストールしたディレクトリ下(例:/var/lib/redmine-2.5.1)に、Gemfile.localというファイルを作成し、そこに以下を記述します。

gem "unicorn"

Redmineをインストールしたディレクトリで bundle update を実行します。

redmine-2.5.1$ sudo bundle update
Fetching gem metadata from https://rubygems.org/.........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.1.1
Using i18n 0.6.9
  :
Installing kgio 2.9.2
Using mysql2 0.3.16
Using net-ldap 0.3.1
Using ruby-openid 2.3.0
Using rack-openid 1.4.2
Using rails 3.2.17
Installing raindrops 0.13.0
Using redcarpet 2.3.0
Using rmagick 2.13.2
Installing unicorn 4.8.3
Your bundle is updated!
Gems in the groups development and test were not installed.
redmine-2.5.1$

インストール後の単独起動確認

コマンドラインからUnicornを起動してRedmineの動作を確認します。
ipdtablesサービスが稼動していたら一旦停止させておきます。

redmine-2.5.1$ sudo bundle exec unicorn_rails -l 8081 -E production
I, [2014-06-28T13:57:45.681407 #1692]  INFO -- : listening on addr=0.0.0.0:8081 fd=9
I, [2014-06-28T13:57:45.681687 #1692]  INFO -- : worker=0 spawning...
I, [2014-06-28T13:57:45.682839 #1692]  INFO -- : master process ready
I, [2014-06-28T13:57:45.684321 #1695]  INFO -- : worker=0 spawned pid=1695
I, [2014-06-28T13:57:45.684552 #1695]  INFO -- : Refreshing Gem list
I, [2014-06-28T13:57:50.476651 #1695]  INFO -- : worker=0 ready

これでWebブラウザからポート8081で接続し動作確認をします。

コマンドラインオプションは次です。

    --listen -l [アドレス:]ポート
        ソケットのエンドポイントを指定 
    --config-file -c ファイル
        設定ファイルを指定 
    -D
        デーモンプロセス起動指定 
    -E <RAILS_ENV>
        production等を指定 

Unicornプロセスに対する制御

ちょっと難しいのがUnicornのプロセス制御です。Unicornは、マスタープロセス1つにワーカープロセスが複数という構成です。

終了方法

  • フォアグラウンドプロセスとして起動した場合、Ctrl-Cで停止します。
  • デーモンプロセスとして起動した場合、シグナルINT(強制終了)またはQUIT(グレースフル停止)をマスタープロセスに送ります。
    • シグナルINT: ワーカープロセスを即終了させて自らも終了
    • シグナルQUIT: ワーカープロセスがリクエストの処理を終えるまで待って終了
  • そのほか
    • シグナルWINCH: ワーカープロセスをグレースフルに停止しますが自らは終了しません
    • シグナルTTIN: ワーカープロセスを1つ増やします
    • シグナルTTOU: ワーカープロセスを1つ減らします

再起動方法

デーモンプロセスに対する設定ファイル再読み込みトリガーはシグナルHUPをマスタープロセスに送ります。このとき、preload_appがfalseだとアプリケーションプログラムも再読み込みされますが、preload_appがtrueだと再読み込みされません。

シグナルUSR2を使うと、古いマスタープロセスは新しいプロセスを作ってソケットを引継ぎます。古いプロセスはそのまま残っています。pidファイルは新プロセスの値に書き換えられます。古いpidファイルは.oldbinの拡張子が付いたファイルに変更されます。preload_appがtrueのときにアプリケーションプログラムを再読み込みするにはこのシグナルUSR2を使います。

シグナルUSR2を送っただけでは古いマスタープロセスとその配下のワーカープロセスは終了しないので、シグナルUSR2を送ったあとに古いマスタープロセスに対してシグナルQUITを送るとよいのかと思います。

実験:usr2で3つ以上のマスタープロセスは起動しない

最初にデーモン起動し、シグナルUSR2を送ると新しいマスタープロセスとそのワーカープロセス群が生成されました。
続いて新しいマスタープロセスにシグナルUSR2を送ってもさらに新しいマスタープロセスは生成されませんでした。
古いマスタープロセスをシグナルINTを送って終了させたあと、残ったマスタープロセスにシグナルUSR2を送ったところ、さらに新しいマスタープロセスとワーカープロセスが生成されました。

マスタープロセスのプロセスIDを調べる

次のように調べます。

$ pgrep -f 'unicorn_rails master'
3783
$

Unicornの設定

Redmineインストールディレクトリ下のconfigディレクトリに、unicorn.rbというファイル名で設定を記述します。
Unicorn起動時にオプションで設定ファイルを指定します。

公式サイトの解説ページ(次のURL)
http://unicorn.bogomips.org/Unicorn/Configurator.html

Redmine用Unicorn設定例

自宅サイトで検証中のunicorn.rbを添付します。Unicornのワーカープロセスをroot権限ではなくredmineユーザー権限で動かすように設定を記述しています。
unicorn.rb

この設定ファイルを使う場合は、CentOS上でユーザーredmineを作成しておきます。

~$ sudo useradd redmine

Redmine以下のディレクトリ・ファイルの所有者をredmineユーザーに変更します。

~$ cd /var/lib/redmine-2.5.1
redmine-2.5.1$ sudo chown -R redmine.redmine .

Unicornの設定項目メモ

unicorn.rbの設定内容についてのメモです。

worker_processes

応答性を良好に保つには、コア数以上のワーカーを指定します。ただし、仮想化環境ではCPU使用率とメモリ使用量を見ながらワーカー数を許容範囲まで増やします。

listen

リクエストを受け付けるプロトコルとポート、オプション設定を指定します。

UNIXドメインソケットの場合の例

listen "/tmp/unicorn_redmine.sock", :backlog => 64

TCPソケットの場合の例
listen 8080, :tcp_nopush => true

  • 複数のプロトコルを列挙することで複数のプロトコル・ポートを扱うことができます。
  • backlogは、workerが作業中でもコネクションのリクエストを受理して待機しておくことができる個数で、デフォルトは1024です。例えば1件平均30msで捌く処理に対して1000個バックログによる待ちがあると、新たにリクエストした処理は結果が返るまで30秒間待たされることになります。それだったらコネクションを受け付けずにエラーにした方がよいということがあります。参考までに自宅で運用しているRedmineのログからCompleted行にある処理時間の平均は約100msでした。
  • tcp_nopushは、TCP_CORK(Linux)を制御します。デフォルトはfalseです。trueにすると、TCPフレームの断片が小出しに送られることを抑止するので、リモートにあるNginxのタスクを早めに起こさずにすませます。
timeout

workerがこの秒数以上処理に費やすとプロセスを落とします。デフォルトは60秒です。大抵の処理はずっと短いので60秒は大きすぎる値ですが、中に時間のかかる処理があれば処理時間に応じて値を増やします。

Redmineでは、大きなサイズの添付ファイルのアップロード・ダウンロードが該当します。Redmineの添付ファイルサイズ上限のファイルを実際にアップロード・ダウンロードさせてかかる時間を計測し、それを許容できる処理時間を設定します。

ここで設定するunicornのタイムアウト値とNginxのタイムアウト値が不整合だとかなり怪しい挙動となりますので、両者のタイムアウト値を整合させるようにしてください(unicornのタイムアウト値+αをnginxのタイムアウト値にする、αは1ないし2秒程度、等)。

pid

unicornを起動したときにそのプロセスIDを記録しておくファイルを指定します。
例を次に示します。

pid "tmp/pids/unicorn.pid"
stderr_path、stdout_path

Redmineインストールディレクトリ下のlogディレクトリの中にログファイルを生成します。

stderr_path 'log/unicorn.stderr.log'
stdout_path 'log/unicorn.stdout.log'
preload_app

trueに設定すると、マスタープロセス起動時にアプリケーションをロードし、ワーカープロセスをフォークするとアプリケーションが実行可能となります。複数ワーカープロセスでコードを共有するため、メモリ使用効率もよくなります。デメリットは、ワーカープロセスを再起動してもアプリケーションはロードされない点です。

before_fork

ワーカープロセスをforkする前にマスタープロセスによって呼ばれます。

after_fork

ワーカープロセスがforkされた後に呼び出されます。

ENV["HOME"]

rootユーザーで稼動するマスタープロセスがredmineユーザーで稼動するワーカープロセスを生成したときに、環境変数HOMEはrootユーザーのままとなっています。この状況でRedmineでsubversionを使用していると、次のエラーメッセージが多数出力されます。

svn: 警告: ファイル '/root/.subversion/servers' を開けません: 許可がありません

そこで、after_forkの処理ブロックの中に環境変数HOMEをredmineユーザーのホームディレクトリを指定する記述を入れます。
参考にしたブログ

Unicornのサービス化(起動終了制御)

Unicornの起動・終了をCentOSのサービスとして管理できるようにします。
/etc/init.dの下にサービス制御ファイルunicornを作成します。

unicornサービスファイル例

インターネット上で見つけたunicornサービスファイルです。本ページに添付しています。
コマンド 内容
start unicorn起動
stop シグナルQUIT送信
force-stop シグナルTERM送信
restart シグナルUSR2送信し、5秒待って古いマスタープロセスにシグナルQUIT送信
reload
upgrade シグナルUSR2送信
rotate シグナルUSR1送信
ファイルの配置とサービスへの登録
~$ sudo cp unicorn /etc/init.d
~$ sudo chkconfig -add unicorn 
~$ sudo chkconfig unicorn on
Redmineアプリケーションパスの設定

/etc/unicorn/redmine.conf

RAILS_ENV=production
RAILS_ROOT=/var/lib/redmine

シンボリックリンク /var/lib/redmine を作成します。

~$ link -s /var/lib/redmine-2.5.1 /var/lib/redmine

サービスの起動
~$ sudo service unicorn start
/var/lib/redmine: Starting
~$ ps aux|grep [u]nicorn |awk '{print $1, $2, $11, $12}'
root 2506 unicorn_rails master
redmine 2511 unicorn_rails worker[0]
redmine 2513 unicorn_rails worker[1]
redmine 2516 unicorn_rails worker[2]
~$

Webブラウザから、ポート8282に接続して確認します。
ファイアウォール(iptables)が稼動していると通らないので、一時停止しておきます。

サービスの終了
~$ sudo service unicorn stop
/var/lib/redmine: Stopping
~$ ps aux|grep [u]nicorn |awk '{print $1, $2, $11, $12}'
~$
サービスの再起動
~$ sudo service unicorn start
/var/lib/redmine: Starting
~$ ps aux|grep [u]nicorn |awk '{print $1, $2, $11, $12}'
root 2548 unicorn_rails master
redmine 2553 unicorn_rails worker[0]
redmine 2555 unicorn_rails worker[1]
redmine 2558 unicorn_rails worker[2]
~$ sudo service unicorn restart
/var/lib/redmine: Killing old master 2548
~$ ps aux|grep [u]nicorn |awk '{print $1, $2, $11, $12}'
root 2574 unicorn_rails master
redmine 2580 unicorn_rails worker[0]
redmine 2582 unicorn_rails worker[1]
redmine 2585 unicorn_rails worker[2]

Nginxのセットアップ

WebサーバーのNginxをセットアップします。
Nginx公式ページ(日本語)

CentOS 6向けには、EPELリポジトリにNginx 1.0のRPMが置かれています。2014-06現在の最新版は1.6なのでかなりの年代物といえます。最新版はNginx公式サイトのリポジトリでRPMが提供されています。今回はNginxの公式サイトで提供される安定版の最新版である1.6をインストールすることにします。

標準のhttpd(Apache)を停止させるかデフォルトポート番号を80以外に変更

今回はRedmineのアクセスで使うNginxを80ポートで使用します。そこで、CentOS 6のhttpd(Apache)が80ポートで動かないように、httpdをサービス起動から除外する、アンインストールする、あるいはhttpdのポート番号を80以外に変更するかの処置をしておきます。

  • httpdをアンインストールする
    ~$ sudo yum erase httpd
  • httpdをサービス起動から除外する
    ~$ sudo chkconfig -del httpd
  • httpdのポート番号を80以外に変更する
    /etc/httpd/conf/httpd.conf
    ServerName www.torutk.com:8086 
    Listen 8086
    

yumリポジトリにNginxを追加しNginxパッケージをインストール

NginxはCentOS 6向けにyumリポジトリを提供しているので、これを追加します。
Pre-Build Packages for Stable version

このページの"CentOS 6"のリンクをコピーし、以下のように実行します。

~$ sudo rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm

すると、/etc/yum.repos.d/nginx.repoが追加されました。内容は次のとおりです。

# nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
enabled=1

nginxをインストールします。

~$ sudo yum install nginx

インストールされたファイルは次のとおりです。

~$ rpm -ql nginx
/etc/logrotate.d/nginx
/etc/nginx
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
/etc/nginx/conf.d/example_ssl.conf
/etc/nginx/fastcgi_params
/etc/nginx/koi-utf
/etc/nginx/koi-win
/etc/nginx/mime.types
/etc/nginx/nginx.conf
/etc/nginx/scgi_params
/etc/nginx/uwsgi_params
/etc/nginx/win-utf
/etc/rc.d/init.d/nginx
/etc/sysconfig/nginx
/usr/sbin/nginx
/usr/share/nginx
/usr/share/nginx/html
/usr/share/nginx/html/50x.html
/usr/share/nginx/html/index.html
/var/cache/nginx
/var/log/nginx
~$

インストール完了時点で、サービスがchkconfigに登録されています。

~$ chkconfig --list nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off
~$

Nginxの設定

Nginxの設定は、/etc/nginx/nginx.conf と、そこからインクルードしている/etc/nginx/conf.d/*.conf とから成ります。

デフォルトのnginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}
  • worker_processesは1ですが、1つのワーカープロセスでworker_connectionsに定義されている数(ここでは1024)までのリクエストを扱えるので、十分かもしれません。

デフォルトのdefault.conf

インストール時に /etc/nginx/conf.d/default.conf が置かれています。

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

ポート80でデフォルトのサーバーが定義されています。Redmineでポート80を使用するので、このファイルを削除またはリネームしておきます。

~$ sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.orig

Redmine用の設定ファイル

/etc/nginx/conf.d/redmine.conf を新規作成します。
本ページに添付しています。
redmine.conf

upstream unicorn-redmine {
    server unix:/tmp/unicorn_redmine.sock;
}
  • リバースプロキシの上流サイトとしてUnicornを指定します。同一マシン上でUnicornとNginxを動かす場合は、UNIXドメインソケットで接続可能です。Unicornで指定したUNIXドメインソケットのパスを指定します。
server {
  listen 80;
  server_name www.torutk.com;
  • ポート番号 80 で受け入れるようにします。サーバー名はDNS名を指定しました。
    root /var/lib/redmine/public;
  • Redmineの静的コンテンツのルートディレクトリを指定します。
    client_max_body_size 640M;
  • クライアントからのリクエストの本体サイズの最大値で、これを超えると413エラーを返します。デフォルトは1MBと小さいため、Redmineの添付ファイルのアップロードの最大サイズに合わせて大きくします。
    location / {
        try_files $uri/index.html $uri.html $uri @app;
    }
  • URIが/(ルート)で始まるもの(実質すべて)は、そのURIに対応するファイルがあるかを試し、あればそれを、なければ@app(後述)に処理を委譲します。
    location @app {
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_connect_timeout 60;
        proxy_read_timeout 60;
        proxy_send_timeout 600;
        proxy_pass http://unicorn-redmine;
    }
  • proxy_redirectは、上位サーバー(Unicorn)から受け取ったLocationおよびRefreshヘッダーの書き換えを抑止します。
  • proxy_set_headerは、リバースプロキシ経由でクライアントのIPアドレスを上位サーバー(Unicorn)に渡すヘッダーに格納します。
  • タイムアウトは長めにとっています(ファイルのアップロード・ダウンロード等に対応)
  • proxy_passは、upstreamで定義した"unicorn-redmine"へプロキシします。
    error_page 500 502 503 504 /500.html;
  • 上位サーバー(Unicorn)がリクエストに対して応答できないときに、エラードキュメントを指定します。既にドキュメントルートに/var/lib/redmine/publicを指定しているので、/500.htmlはRedmineの500.htmlを指しています。

ファイアウォール(iptables)でポート80を許可する

動作確認時はファイアウォール(iptables)を停止させていましたが、本番稼動ではファイアウォールを動作させてポート80を許可する設定をしておきます。

現状のiptables内容を確認します。

~$ sudo iptables -vL --line
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1     1309  111K ACCEPT     all  --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED
2        0     0 ACCEPT     icmp --  any    any     anywhere             anywhere
3        0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
4        5   268 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpt:ssh
5      217 18374 REJECT     all  --  any    any     anywhere             anywhere            reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 REJECT     all  --  any    any     anywhere             anywhere            reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT 1395 packets, 1114K bytes)
num   pkts bytes target     prot opt in     out     source               destination
~$

  • 外部からの着信は、Chain INPUTのルールが適用されます。
    • (1) セッション確立済みのパケット(ESTABLISHED)や確立済みプロトコルに関連したパケット(RELATED)の受信を許可
    • (2) ICMPパケットの受信は許可
    • (3) ループバックインタフェース(ローカルホスト)の受信は許可
    • (4) TCPでポート22(ssh)宛の新規接続を許可
    • (5) 以上に合致しないパケットの受信は拒絶

(4)のルールに倣って、TCPでポート80(http)宛の新規接続を許可を(5)の前に追加します。

~$ sudo iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport http -j ACCEPT
~$ sudo iptables -vnL --line
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1     1684  136K ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
2        0     0 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0
3        0     0 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
4        5   268 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22
5        0     0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:80
6      220 18947 REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT 5 packets, 728 bytes)
num   pkts bytes target     prot opt in     out     source               destination

コマンドで追加しただけでは再起動時に反映されないので保存します。

~$ sudo service iptables save
iptables: ファイアウォールのルールを /etc/sysconfig/iptable[  OK  ]中:
~$

その他Nginxの設定

SSL設定

SELinuxの設定

あれ、SELinuxが有効(Enforcing)だがRedmineが動いている

そういえば、セットアップ作業を通じてSELinuxを制御していないのを思い出し、SELinuxの設定を確認してみました。

~$ getenforce
Enforcing

お、Enforcingのままでも稼動しています。

SubversionおよびGitとの連携

SubversionおよびGitへのアクセスで使用するユーザー認証は、SubversionおよびGit側ではなくRedmineのユーザー認証でまかないます。その場合、SubversionおよびGitへのアクセスはapache httpサーバーが前提となります。

SubversionとRedmineの認証連携

Subversion 1.8のインストール

  • CentOS 6でsubversionのバージョンを上げる
    CentOS 6の標準搭載Subversionは、バージョンが1.6.11と今では古いので、バージョン1.8を別にインストールします。ここの記述に沿って、subversion、httpd、mod_dav_svnをインストールします。
  • Subversion
    Subversionのバージョンと機能については、ここにメモを記載しました。

Nginxがポート80を使用しているので、apacheは80以外のポートを使用します。

  • /etc/httpd/conf/httpd.conf の修正
    @@ -133,7 +133,7 @@
     # prevent Apache from glomming onto all bound IP addresses (0.0.0.0)
     #
     #Listen 12.34.56.78:80
    -Listen 80
    +Listen 8086
    
     #
     # Dynamic Shared Object (DSO) Support
    

SELinuxが有効(Enforced)のときは、ポート番号を変更した後httpdの起動で失敗します。

$ sudo service httpd start
httpd を起動中: httpd: Could not reliably determine the server's fully qualified domain name, using 192.168.1.11 for ServerName
(13)Permission denied: make_sock: could not bind to address [::]:8086
(13)Permission denied: make_sock: could not bind to address 0.0.0.0:8086
no listening sockets available, shutting down
Unable to open logs

そのときは、SELinuxのhttp_port_t に使用したいポート番号を追加します。

~$ sudo semanage port -l | grep http
http_cache_port_t              tcp      3128, 8080, 8118, 8123, 10001-10010
http_cache_port_t              udp      3130
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t            tcp      5988
pegasus_https_port_t           tcp      5989
~$

semanageコマンドがなければ、CentOS 6標準パッケージのpolicycoreutils-pythonをインストールします。

~$ sudo semanage port -a -t http_port_t -p tcp 8086
~$ sudo semanage port -l|grep http_port
http_port_t                    tcp      8086, 80, 81, 443, 488, 8008, 8009, 8443, 9000
pegasus_http_port_t            tcp      5988

iptablesで8086ポートを開通します。

認証連携に必要なパッケージのインストール

  • mod_perl
    ~$ sudo yum install mod_perl
  • perlモジュール群(1)
    ~$ sudo yum install perl-LDAP perl-Params-Validate perl-Module-Runtime \
    perl-Module-Implementation perl-Class-Accessor perl-Class-Data-Inheritable \
    perl-Crypt-PasswordMD5 perl-Test-Simple
  • DB連携モジュール(MySQL/MariaDB)
    ~$ sudo yum install perl-DBD-MySQL
  • perl-Authen-Simpleモジュール
    $ sudo yum install --enablerepo=epel-testing perl-Authen-Simple
  • perl-Authen-Simple-LDAPモジュール
    CentOS 6でPerl-Authen-Simple-LDAPのRPMパッケージを作るに添付したRPMパッケージ"perl-Authen-Simple-LDAP-0.3-1.el6.noarch.rpm"をダウンロードしインストールします。

Redmine.pmのインストール

Redmineに附属するRedmine.pmをperlの検索パスにシンボリックリンクします。

~$ sudo ln -s /var/lib/redmine/extra/svn/Redmine.pm /usr/lib64/perl5/vendor_perl/Apache2/Redmine.pm

httpd用設定ファイルの作成

/etc/httpd/conf.d/subversion.conf

LoadModule dav_svn_module     modules/mod_dav_svn.so
LoadModule authz_svn_module   modules/mod_authz_svn.so

PerlLoadModule Apache2::Redmine
PerlLoadModule Authen::Simple::LDAP
PerlLoadModule IO::Socket::SSL

<Location /svn>
  DAV svn
  LimitXMLRequestBody 0
  SVNPathAuthz off
  SVNParentPath /var/lib/svn/
  AuthType Basic
  AuthName "Redmine" 
  Require valid-user

  PerlAccessHandler Apache::Authn::Redmine::access_handler
  PerlAuthenHandler Apache::Authn::Redmine::authen_handler
  RedmineDSN "DBI:mysql:database=redmine;host=localhost" 
  RedmineDbUser "redmine" 
  RedmineDbPass "mypassword" 
</Location>

subversionリポジトリの作成

Redmineとの認証連携をする場合、subversionのリポジトリには命名規約があります。

  • 書式
    <redmineプロジェクト識別子>[.<拡張子>]
    
    • Subversionリポジトリ名は、Redmineプロジェクト識別子に一致させる
    • あるいは、Redmineプロジェクト識別子にピリオドを付加しその後ろに任意の文字列を追加する

subversionリポジトリは、apacheユーザー権限でアクセスできるようにします。

subversionリポジトリ作成
$ sudo svnadmin create /var/lib/svn/eval-perf.nolla
$ sudo chown -R apache.apache /var/lib/svn

ポート80でsubversionを受ける

諸般の事情でポート80でsubversionへのアクセスを受けたい場合、Nginx側にリバースプロキシ設定を追加します。

  • /etc/nginx/conf.d/redmine.conf に追記
    server {
        :
        location /svn {
            proxy_pass http://localhost:8086;
        }
        :
    }
    

GitとRedmineの認証連携

Git 1.9のインストール

  • CentOS 6でgitのバージョンを上げる
    CentOS 6の標準搭載Gitは、バージョンが1.7.1と古い上基本的なバグもあって使いづらいので、バージョン1.9を別にインストールします。このページの記載に沿ってgitをインストールします。今回、先に上述のとおりWANdiscoのsubversionをインストールしているので、このページの依存関係解決の暫定処置を実施する必要があります。

その他必要なパッケージのインストール

SubversionとRedmineの認証連携でインストールしたものと同じものが必要となります。

httpd用設定ファイルの作成

/etc/httpd/conf.d/git.conf

PerlLoadModule Apache2::Redmine
PerlLoadModule Authen::Simple::LDAP

SetEnv GIT_PROJECT_ROOT /var/lib/git
SetEnv GIT_HTTP_EXPORT_ALL
SetEnv REMOTE_USER=$REDIRECT_REMOTE_USER

ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

<LocationMatch /git/>
  PerlAccessHandler Apache::Authn::Redmine::access_handler
  PerlAuthenHandler Apache::Authn::Redmine::authen_handler
  AuthType Basic
  AuthName Git

  RedmineDSN "DBI:mysql:database=redmine;host=localhost" 
  RedmineDbUser "redmine" 
  RedmineDbPass "mypassword" 
  RedmineGitSmartHttp yes

  Require valid-user
</LocationMatch>

gitリポジトリの作成

Redmineとの認証連携をする場合、gitリポジトリには命名規約があります。

  • 書式
    <redmineプロジェクト識別子>[.<拡張子>]
    • gitリポジトリ名はRedmineプロジェクト識別子に一致させる
    • あるいは、Redmineプロジェクト識別子にピリオドを付加しその後ろに任意の文字列を追加する

gitリポジトリは、apacheユーザー権限でアクセスできるようにします。

gitリポジトリの基点作成例
$ sudo mkdir /var/lib/git

SELinux有効下ではhttpdからの読み書きができるように設定します。

$ sudo semanage fcontext -a -t httpd_sys_content_t '/var/lib/git'
$ sudo restorecon -R /var/lib/git
gitリポジトリの作成例
$ cd /var/lib/git
$ sudo git init --bare --shared eval-perf.yksi
$ cd eval-perf.yksi/
$ sudo git update-server-info
$ sudo mv hooks/post-update.sample hooks/post-update
$ sudo git config http.receivepack true
$ cd ..
$ sudo chown -R apache.apache .

ポート80でgitを受ける

諸般の事情でポート80でgitへのアクセスを受けたい場合、Nginx側にリバースプロキシ設定を追加します。

  • /etc/nginx/conf.d/redmine.conf に追記
    server {
        :
        location /git {
            proxy_pass http://localhost:8086;
        }
        :
    }
    

トラブルシューティング

巨大なファイルをアップロードするとエラーになりその後Redmineが著しく重い

巨大な(GB超)ファイルをアップロードすると、Unicornのワーカープロセスの1つのメモリ使用量(RSS)が一気に増大し、NoMemoryErrorを出して失敗します。さらに、そのUnicornのワーカープロセスが残ったままになって以降Redmineが重くて使い物にならなくなります。

なお、1GB超のファイルをダウンロードしてみたところ、Unicornのワーカプロセスのメモリ使用量に変化は見受けられませんでした。

エラー状況

1.1GBのファイルを添付したところ、Internal Server Errorが発生しました。

  • /var/lib/redmine/log/production
    NoMemoryError (failed to allocate memory):
      app/controllers/attachments_controller.rb:85:in `upload'
    
  • top
    Mem:   1020384k total,   956516k used,    63868k free,     4196k buffers
    Swap:  1048568k total,   403008k used,   645560k free,    17800k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
     3316 redmine   20   0 1479m 754m 1440 S  0.0 75.7   0:02.41 ruby
     1132 mysql     20   0  705m  29m  432 S  0.0  3.0   1:57.70 mysqld
     3303 root      20   0  342m  27m  912 S  0.0  2.7   0:03.90 ruby
     3313 redmine   20   0  342m  26m  780 S  0.0  2.7   0:00.04 ruby
     3311 redmine   20   0  346m  26m  800 S  0.0  2.6   0:00.42 ruby
     3350 postfix   20   0 81788 3332 2480 S  0.0  0.3   0:00.01 pickup
     3349 nginx     20   0 45580 1788  448 S  0.0  0.2   0:00.00 nginx
     3347 root      20   0 45212 1204  292 S  0.0  0.1   0:00.00 nginx
    

topの状況から、unicornのワーカープロセスの1つがファイルアップロード処理を実行している過程でメモリをどんどん喰っていき、これ以上メモリを取れずにエラーになったようです。

物理メモリを増強して再現確認

物理メモリを1GBから2GBに増強してもう一度同じファイルを添付してみました。
結果は添付に成功しました。

  • top
    Mem:   1922524k total,  1805328k used,   117196k free,     4456k buffers
    Swap:  1048568k total,   408452k used,   640116k free,   398316k cached
    
      PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
     1270 redmine   20   0 2045m 1.2g 3348 S  0.0 65.5   0:06.48 ruby
     1138 mysql     20   0  705m  27m 5024 S  0.3  1.5   0:02.39 mysqld
     1259 root      20   0  440m  27m 3244 S  0.0  1.5   0:02.85 ruby
     1267 redmine   20   0  445m  26m 2816 S  0.0  1.4   0:00.34 ruby
     1265 redmine   20   0  445m  26m 2756 S  0.0  1.4   0:00.34 ruby
     1388 root      20   0 98.0m 2928 2896 S  0.0  0.2   0:00.08 sshd
    

    しかし、添付ファイルのサイズとほぼ同じ常駐メモリを喰っているunicornワーカープロセスが残っています。このままではワーカープロセスが次々肥大化しいずれはNoMemoryErrorが発生します。

解決策その1)unicorn-worker-killer

本格的なWebサーバーやアプリケーションサーバーは、リクエスト数やメモリ使用量が閾値を超えるとプロセスを再起動する「リサイクル」の仕組みがあるそうです。
Unicornにはリサイクルの仕組みはないのですが、Unicorn Worker Killer というRubyパッケージがあります。これはUnicornワーカープロセスに対するリクエスト数あるいはメモリ使用量を監視し閾値を超えたら終了させることができます。

参考資料のUnicornにunicorn-worker-killerへのリンクを掲載しています。

インストール

Unicorn Worker Killerのgemファイルをインストールします。

/var/lib/redmine/Gemfiles.localに次を追記します。

gem "unicorn-worker-killer" 

bundle updateを実行します。
~$ cd /var/lib/redmine
redmine$ sudo bundle update
Fetching gem metadata from https://rubygems.org/.........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.1.1
  :
Installing unicorn-worker-killer 0.4.2
Your bundle is updated!
Gems in the groups development and test were not installed.
~$

/var/lib/redmine/config.ru に追記します。
--- config.ru.1 2014-03-30 01:56:42.000000000 +0900
+++ config.ru   2014-06-30 16:42:50.642004774 +0900
@@ -1,4 +1,13 @@
 # This file is used by Rack-based servers to start the application.

+# Unicorn self-process killer
+require 'unicorn/worker_killer'
+
+# Max requests per worker
+use Unicorn::WorkerKiller::MaxRequests, 3072, 4096
+
+# Max memory size (RSS) per worker
+use Unicorn::WorkerKiller::Oom, (192*(1024**2)), (256*(1024**2))
+
 require ::File.expand_path('../config/environment',  __FILE__)
 run RedmineApp::Application

Unicornを再起動
~$ sudo service unicorn restart

再度大きなファイルをアップロードすると、unicornワーカープロセスのRSSが1.2GBに達します。
その後、いくつかRedmineのページを開きリクエストを繰り返していると、unicornの標準エラー出力に次のメッセージが表示され、メモリ肥大したワーカープロセスがkillされ、新たにワーカープロセスが立ち上がりました。

/var/lib/redmine/log/unicorn.stderr.log

W, [2014-06-30T17:06:50.549746 #1623]  WARN -- : #<Unicorn::HttpServer:0x000000024c6628>: worker (pid: 1623) exceeds memory limit (1308868608 bytes > 202455902 bytes)
W, [2014-06-30T17:06:50.549839 #1623]  WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 1623) alive: 1199 sec (trial 1)
I, [2014-06-30T17:06:50.899445 #1609]  INFO -- : reaped #<Process::Status: pid 1623 exit 0> worker=2
I, [2014-06-30T17:06:51.077862 #1699]  INFO -- : worker=2 ready

Unicorn Worker Killerは、メモリ使用量のチェックをデフォルトではリクエストが16回発生する都度行います。

この後のtopの状況は次です。

Mem:   1922524k total,   601884k used,  1320640k free,    11528k buffers
Swap:  1048568k total,   202740k used,   845828k free,   180676k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 1619 redmine   20   0  451m 103m 3328 S  0.0  5.5   0:01.60 ruby
 1617 redmine   20   0  445m  96m 3416 S  0.0  5.1   0:01.41 ruby
 1138 mysql     20   0  705m  30m 5920 S  0.0  1.6   0:07.20 mysqld
 1699 redmine   20   0  437m  29m 2100 S  0.0  1.6   0:00.04 ruby
 1609 root      20   0  437m  10m 5424 S  0.0  0.6   0:03.50 ruby
 1641 root      20   0 98.0m 3956 2996 S  0.0  0.2   0:00.11 sshd

SELinux設定のトラブルシュート

Can't open file '/var/lib/svn/repoa/db/txn-current-cock': Permission denied

SELinuxで/var/lib/svn以下のディレクトリにApache httpdサーバーがアクセスできる許可がないため発生します。

/var/lib/svn ---> unconfined_u:object_r:var_lib_t:s0

httpdサーバーが読み書きする場所には、httpd_sys_content_t を設定しておきます。

~$ sudo semanage fcontext -a -t httpd_sys_content_t '/var/lib/svn(/.*)?'
~$ sudo restorecon -R /var/lib/svn

bundle install/updateでSSLエラー

とある環境でbundle updateしたところ次のエラーがでました。

Gem::RemoteFetcher::FetchError: SSL_connect returned=1 errno=0 state=SSLv3 
read server certificate B: certificate verify failed

gemパッケージのリポジトリサーバーとSSL通信する際に、gemパッケージのリポジトリサーバーから取得したSSL証明書の正当性を証明できないためにエラーとなっています。このエラーメッセージはWeb上にあちこちで言及されています。

rubyが使用する認証局証明書は次で確認できます。

$ ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE'
/etc/pki/tls/cert.pem
$

ファイアウォールがある環境

社内イントラネット上のサーバーからファイアウォールを介してインターネットにgemパッケージを取りに行くときに、httpsプロトコルのプロキシーがらみで起きたようです。

サーバー証明書のチェックを無効にしての回避方法があります。
gem(bundle)実行ユーザーのホームディレクトリに.gemrcファイルを作成し、中に次を記述します。

:ssl_verify_mode: 0

  • sudoコマンドでgem (bundle)を実行するときは、/root/.gemrcに配置

参考資料

Redmine

インストール関係

Unicorn

Nginx


ほぼ8年前に更新