プロジェクト

全般

プロフィール

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

はじめに

RedmineをCentOS 7で動かすセットアップメモです。
RedmineをCentOS 6上で動かすーUnicornとNginx編の環境でのCentOS 7版です。
もちろんSELinuxは"Enforcing"です。

動作環境

ハードウェア構成

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

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

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

ソフトウェア構成

項目 使用ソフトウェア 備考
OS CentOS 7.0 64bit版
Redmine Redmine 3.0.0
Ruby Ruby 2.0.0 CentOS 7標準
RDBMS MariaDB 5.5 CentOS 7標準
Rackサーバー Unicorn
Webサーバー Nginx 1.7.3 EPEL Nginx公式yumリポジトリからインストール

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

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

開発ツール等

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

MariaDB

MariaDBのCentOS 7標準パッケージインストール

~$ sudo yum install mariadb-server mariadb-devel
  :

MariaDBの設定

MariaDBは、/etc/my.cnf から、/etc/my.cnd.d/* をインクルードするようになりました。
サーバー用なので、各種設定は、/etc/my.cnf.d/の中に記載します。

  • /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
    
チューニング設定例(4GBメモリサーバー機)

メモリ4GB搭載環境で、Redmine側に3GBを割り付けて、残り1GBをMariaDBに割り振る想定でのパラメータを追記します。

  • /etc/my.cnf.d/server.cnf
    [mysqld]
    character-set-server = utf8
    
    innodb_buffer_pool_size = 512M
    innodb_log_file_size = 128M
    read_buffer_size = 2M
    

MariaDBの起動

CentOS 7からサービス(デーモン)の起動終了はsystemctlコマンドで行います。

~$ sudo systemctl start mariadb.service

~$ systemctl status mariadb
mariadb.service - MariaDB database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled)
   Active: active (running) since 土 2014-07-12 21:42:13 JST; 45min ago
 Main PID: 15320 (mysqld_safe)
   CGroup: /system.slice/mariadb.service
           tq15320 /bin/sh /usr/bin/mysqld_safe --basedir=/usr
           mq15490 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysq...
  • .serviceは省略可能なようです

CentOS 7からサービス(デーモン)の自動起動設定はsystemctlコマンドで行います。

MariaDBを自動起動する設定

~$ sudo systemctl enable mariadb.service
ln -s '/usr/lib/systemd/system/mariadb.service' '/etc/systemd/system/multi-user.target.wants/mariadb.service'

コンソールクライアントでMariaDBに接続します。コマンドはなぜかmysqlです。

~$ mysql -uroot
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 5.5.37-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)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)

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

Connection id:          3
Current database:       mysql
Current user:           root@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server:                 MariaDB
Server version:         5.5.37-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:                 59 min 51 sec

Threads: 1  Questions: 45  Slow queries: 0  Opens: 15  Flush tables: 2  Open tables: 41  Queries per second avg: 0.012
--------------

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

MariaDB [none]>

インストール直後はMariaDBのrootユーザーにパスワードが設定されていないので、パスワードを設定しておきます。インストール直後のユーザー一覧は次のとおりです。

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

rootユーザーはhostごとに計4つ存在します。それぞれにパスワードを設定します。

MariaDB [none]> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('magdala');
Query OK, 0 rows affected (0.00 sec)

MariaDB [none]> SET PASSWORD FOR 'root'@'avelo' = PASSWORD('magdala');
Query OK, 0 rows affected (0.00 sec)

MariaDB [none]> SET PASSWORD FOR 'root'@'127.0.0.1' = PASSWORD('magdala');
Query OK, 0 rows affected (0.00 sec)

MariaDB [none]> SET PASSWORD FOR 'root'@'::1' = PASSWORD('magdala');
Query OK, 0 rows affected (0.00 sec)

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

Ruby

~$ sudo yum install ruby ruby-devel

CentOS 7では、ruby 2.0.0がベースラインとなっています。

Redmineのインストール

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

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

Redmine本体の入手と展開

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

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

~$ cd /var/lib
lib$ sudo tar xzf ~/redmine-3.0.0.tar.gz
lib$ sudo ln -s redmine-3.0.0/ redmine
lib$ ls -l
  :
lrwxrwxrwx.  1 root    root      14  7月 13 05:14 redmine -> redmine-3.0.0/
drwxrwxr-x. 16    1000    1000 4096  7月  6 21:44 redmine-3.0.0

Redmineのデータベース設定

データベース設定ファイル(database.yml)の作成

/var/lib/redmine/config/database.ymlを編集します。Redmineにはサンプルとしてdatabase.yml.sampleが含まれているので、このサンプルからMySQL用の設定を抜き出し修正するとよいでしょう。MariaDBはMySQL用の設定を使用できます。
production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: xxxxxxxx
  encoding: utf8
  • サンプルにはproduction以外にdevelopment、testの設定がありますが、通常productionのみ使用するので設定はproductionだけ記述します。
  • adapter項はMariaDB(MySQL)5.5の場合はmysql2を指定します。
  • database項はMariaDB上でRedmineのテーブルを収容するデータベース名を指定します。
  • username項はMariaDBのデータベースに接続するMariaDBのユーザー名を指定します。
  • password項はusernameで指定したMariaDBのユーザーのパスワードを指定します。
  • encoding項はMariaDBで使用する文字エンコーディングを指定します。通常utf8です。

MariaDBのデータベースユーザーのパスワードを記述しているのでパーミッションを厳しく設定しておきます。

~$ sudo chmod 600 /var/lib/redmine/config/database.yml

MariaDB上にRedmine用のデータベースと接続用ユーザーを作成

database.ymlのdatabase項に指定したMariaDB上でRedmineのテーブルを収容するデータベースをMariaDB上に作成します。

~$ mysql -uroot -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 5.5.37-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)]> 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)

database.ymlのusername項とpassword項に指定したユーザーをMariaDB上に作成します。

MariaDB [(none)]> GRANT ALL ON redmine.* TO 'redmine'@'localhost' IDENTIFIED BY 'xxxxxxxx' WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)
  • RedmineからMariaDBへは同じマシン上でUNIXソケットを使って接続するのでホスト名はlocalhostとしています。
  • WITH GRANT OPTIONを指定するとそのユーザーが持つ権限範囲以内の権限を他のユーザーに付与できます。

Redmineのメール接続設定

設定ファイル(configuration.yml)の作成

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

/var/lib/redmine/config/configuration.ymlを作成します。

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

上記例では記載していませんが、メールサーバによってはパスワードを記述するのでパーミッションを厳しく設定しておきます。

~$ sudo chmod 600 /var/lib/redmine/config/configuration.yml

Redmineユーザーの作成

Redmineを動かすときのユーザー権限として、Linuxにredmineユーザーを作成します。展開したRedmineディレクトリ以下の所有権をredmineユーザーに変更します。

~$ sudo useradd redmine
~$ sudo passwd redmine
ユーザー redmine のパスワードを変更。
新しいパスワード:
  :
~$ cd /var/lib/redmine
redmine$ sudo chown -R redmine.redmine .

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

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

bundlerのインストール

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

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

$ gem list

*** LOCAL GEMS ***

bigdecimal (1.2.0)
io-console (0.4.2)
json (1.7.7)
psych (2.0.0)
rdoc (4.0.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を指定し、そこへインストールします。

ユーザーredmineで以下を実行します。

~$ cd /var/lib/redmine
redmine$ bundle install --path vendor/bundler --without development test
Fetching gem metadata from https://rubygems.org/.........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Installing rake 10.1.1
  :

Redmineの初期化

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

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

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

redmineユーザーで以下を実行します。

redmine$ bundle exec rake generate_secret_token

  • 注記)bundlerでRedmine固有ディレクトリ(--path vendor/bundler)にRubyモジュールをインストールしたので、rakeコマンドを実行する際はbundle exec rake xxx のようにbundle execをつけて実行する必要があります。

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

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

redmineユーザーで以下を実行します。

redmine$ bundle exec rake db:migrate RAILS_ENV=production
  :

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

redmine$ mysql -uredmine -p
Enter password: XXXXXXXX
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 10
Server version: 5.5.37-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)]> USE redmine
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

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)

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

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

WEBrick動作確認のためファイアウォールでポート3000を一時的に許可します。

~$ sudo firewall-cmd --add-port=3000/tcp
success

WEBrickを実行します。redmineユーザーで以下を実行します。

redmine]$ ruby script/rails server webrick -e production
=> Booting WEBrick
=> Rails 3.2.19 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2014-07-13 07:31:54] INFO  WEBrick 1.3.1
[2014-07-13 07:31:54] INFO  ruby 2.0.0 (2013-11-22) [x86_64-linux]
[2014-07-13 07:31:54] INFO  WEBrick::HTTPServer#start: pid=9370 port=3000

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

Started GET "/" for 192.168.1.33 at 2014-07-13 07:50:44 +0900
Processing by WelcomeController#index as HTML
  Current user: anonymous
  Rendered welcome/index.html.erb within layouts/base (31.2ms)
Completed 200 OK in 237.2ms (Views: 92.1ms | ActiveRecord: 25.5ms)

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

^C[2014-07-13 07:56:22] INFO  going to shutdown ...
[2014-07-13 07:56:22] INFO  WEBrick::HTTPServer#start done.
Exiting
redmine$

Unicornのセットアップ

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

Unicornのインストール

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

gem "unicorn" 

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

redmine$ bundle update
  :
Installing kgio 2.9.2
  :
Installing raindrops 0.13.0
  :
Installing unicorn 4.8.3
Your bundle is updated!
Gems in the groups development and test were not installed.

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

コマンドラインからUnicornを起動してRedmineの動作を確認します。
先にWEBrick確認時に一時的にファイアウォールにあけたポート3000を使用します。

redmine$ bundle exec unicorn_rails -l 3000 -E production
I, [2014-07-13T08:25:53.245083 #9802]  INFO -- : listening on addr=0.0.0.0:3000 fd=9
I, [2014-07-13T08:25:53.245344 #9802]  INFO -- : worker=0 spawning...
I, [2014-07-13T08:25:53.246279 #9802]  INFO -- : master process ready
I, [2014-07-13T08:25:53.247224 #9805]  INFO -- : worker=0 spawned pid=9805
I, [2014-07-13T08:25:53.247358 #9805]  INFO -- : Refreshing Gem list
I, [2014-07-13T08:25:55.514811 #9805]  INFO -- : worker=0 ready

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

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

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

Unicornプロセスに対する制御

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

終了方法

  • フォアグラウンドプロセスとして起動した場合、Ctrl-Cで停止します。
  • デーモンプロセスとして起動した場合、シグナルINT(強制終了)またはQUIT(グレースフル停止)をマスタープロセスに送ります。
    • シグナルINT: ワーカープロセスを即終了させて自らも終了

再起動方法

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

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

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

  • シグナルQUIT: ワーカープロセスがリクエストの処理を終えるまで待って終了
  • そのほか
    • シグナルWINCH: ワーカープロセスをグレースフルに停止しますが自らは終了しません
    • シグナルTTIN: ワーカープロセスを1つ増やします
    • シグナルTTOU: ワーカープロセスを1つ減らします

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

次のように調べます。

$ pgrep -f 'unicorn_rails master'
3783
$

Unicornの設定

RedmineをCentOS 6上で動かすーUnicornとNginx編ページのUnicornの設定項を参照

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

CentOS 7から、サービスの仕組みが変わったので、サービス起動ファイルもCentOS 7用に作成し直します。

Systemd用のUnicornサービス定義ファイルの作成

  • /usr/lib/systemd/system/redmine-unicorn.service
    [Unit]
    Description=Redmine Unicorn Server
    After=mariadb.service
    
    [Service]
    WorkingDirectory=/var/lib/redmine
    Environment=RAILS_ENV=production
    SyslogIdentifier=redmine-unicorn
    PIDFile=/var/lib/redmine/tmp/pids/unicorn.pid
    
    ExecStart=/usr/local/bin/bundle exec "unicorn_rails -c config/unicorn.rb -E production" 
    ExecStop=/usr/bin/kill -QUIT $MAINPID
    ExecReload=/bin/kill -USR2 $MAINPID
    
    [Install]
    WantedBy=multi-user.target
    
サービス起動順序に注意

CentOS 7から、OS起動時のサービス立ち上げは、並行して実行されるようになりました。
Redmine(Unicorn)は、起動時にデータベースに接続できないとエラー終了してしまいます。
そこで、[Unit]セクションにAfterで、mariadb.serviceが実行されてからredmine-unicorn.serviceが起動されるよう順序を指定します。

Unicornのサービス起動確認

~$ sudo systemctl start redmine-unicorn.service
~$ sudo systemctl status redmine-unicorn.service
redmine-unicorn.service - Redmine Unicorn Server
   Loaded: loaded (/etc/systemd/system/redmine-unicorn.service; disabled)
   Active: active (running) since 日 2014-07-13 13:23:02 JST; 4min 21s ago
 Main PID: 10117 (ruby)
   CGroup: /system.slice/redmine-unicorn.service
           tq10117 unicorn_rails master -c /var/lib/redmine/config/unicorn.rb...
           tq10122 unicorn_rails worker[0] -c /var/lib/redmine/config/unicorn...
           tq10125 unicorn_rails worker[1] -c /var/lib/redmine/config/unicorn...
           mq10127 unicorn_rails worker[2] -c /var/lib/redmine/config/unicorn...

 7月 13 13:23:02 avelo systemd[1]: Starting Redmine Unicorn Server...
 7月 13 13:23:02 avelo systemd[1]: Started Redmine Unicorn Server.
 7月 13 13:26:55 avelo systemd[1]: Started Redmine Unicorn Server.

ファイアウォールのポート8282を一時的に開放

~$ sudo firewall-cmd --add-port=8282/tcp
success

リモートからブラウザでポート8282にアクセスしてRedmineが利用できることを確認します。

Systemdに自動起動登録

~$ sudo systemctl enable redmine-unicorn.service
ln -s '/etc/systemd/system/redmine-unicorn.service' '/etc/systemd/system/multi-user.target.wants/redmine-unicorn.service'

参考

Unicornのメモリ使用状況の調査

CentOS 6.5にruby 2.1.2を入れてRedmineをUnicorn上で動かしていたとき、ファイルのアップロードをするとファイルサイズ分のメモリを消費し、その後そのメモリをunicornプロセスが喰ったままとなってOS全体のメモリ枯渇を引き起こしていました。

今回、CentOS 7とその標準搭載ruby 2.0.0でRedmineをUnicorn上で動かしたので、再度ファイルのアップロード時のメモリ喰いを評価してみました。

Unicornメモリ使用状況調査#1

250MBのファイルをアップロードしたら、unicornのtimeout設定(デフォルトの60秒を30秒に変更)にひっかかってしまいワーカープロセスがkillされてしまいました。
  • unicorn.stderr.log
    E, [2014-07-13T14:35:18.066964 #16275] ERROR -- : worker=2 PID:16287 timeout (31
    s > 30s), killing
    E, [2014-07-13T14:35:18.853430 #16275] ERROR -- : reaped #<Process::Status: pid 16287 SIGKILL (signal 9)> worker=2
    I, [2014-07-13T14:35:19.394439 #19944]  INFO -- : worker=2 ready
    

Unicornメモリ使用状況調査#2

unicornのtimeoutを300秒に増やし、500MBのファイルのアップロードを行いました。

unicorn起動直後
root     25084 15.1  9.9 451376 101504 ?       Ssl  14:56   0:03 unicorn_rails m
redmine  25114  0.0  9.4 451524 96680 ?        Sl   14:57   0:00 unicorn_rails w
redmine  25117  0.0  9.4 451528 96668 ?        Sl   14:57   0:00 unicorn_rails w
redmine  25119  0.0  9.4 451520 96672 ?        Sl   14:57   0:00 unicorn_rails w
  • 各ワーカープロセスの物理メモリ常駐量(RSS)は96MB

ファイルアップロード完了時

root     25084  2.9  9.9 451376 101448 ?       Ssl  14:56   0:03 unicorn_rails m
redmine  25114  4.3 55.0 1358688 560284 ?      Sl   14:57   0:05 unicorn_rails w
redmine  25117  0.4 10.0 455984 102296 ?       Sl   14:57   0:00 unicorn_rails w
redmine  25119  0.3  9.9 454616 100988 ?       Sl   14:57   0:00 unicorn_rails w

しばらくRedmineをアクセスしていると

root     25084  0.4  9.6 451376 98628 ?        Ssl  14:56   0:03 unicorn_rails m
redmine  25114  0.7 11.4 473652 116888 ?       Sl   14:57   0:05 unicorn_rails w
redmine  25117  0.2 10.5 461152 107392 ?       Sl   14:57   0:01 unicorn_rails w
redmine  25119  0.2 10.5 463244 107480 ?       Sl   14:57   0:01 unicorn_rails w

と、それまでメモリ使用量がVSZ(仮想メモリ使用量)が1.3GB、RSS(物理メモリ使用量)が560MBだったワーカープロセス25114が、しばらくすると他のワーカープロセスとほぼ同じ量に減りました。

Rubyのメモリ管理について

CentOS 6.5 - ruby 2.1.2と、CentOS 7.0 - ruby 2.0.0との間でメモリの解放について振舞いが異なる結果となりました。この振る舞いにおいてOSが関与することはなさそうなので、ruby 2.0.0と2.1.2の違いを調べてみました。

すると、ruby 2.1においてガベージコレクタ(GC)の大幅な変更がありました。
2.0までのGCは、マーク&スイープ方式ですが、2.1から世代別方式(Restricted Generational Garbage Collection: RGenGC)となりました。

Nginxのセットアップ

Nginxのサイトでは、CentOS 7向けのyumリポジトリはmainline用(開発最新版)に用意があります。

yumリポジトリの設定とNginxのインストール

  • /etc/yum.repos.d/nginx.repoを作成します。
    [nginx]
    name=nginx repo
    baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
    gpgcheck=0
    enabled=1
    
  • インストールします。
    ~$ 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/sysconfig/nginx
    /usr/lib/systemd/system/nginx.service
    /usr/libexec/initscripts/legacy-actions/nginx
    /usr/libexec/initscripts/legacy-actions/nginx/upgrade
    /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
    ~$
    
  • インストールした段階ではnginxサービスは自動起動設定されていません。
    ~$ systemctl status nginx
    nginx.service - nginx - high performance web server
       Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled)
       Active: inactive (dead)
         Docs: http://nginx.org/en/docs/
    
  • nginxサービスの自動起動設定
    ~$ sudo systemctl enable nginx.service
    ln -s '/usr/lib/systemd/system/nginx.service' '/etc/systemd/system/multi-user.target.wants/nginx.service'
    ~$ systemctl status nginx
    nginx.service - nginx - high performance web server
       Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled)
       Active: inactive (dead)
         Docs: http://nginx.org/en/docs/
    

nginxの設定

ポート80のデフォルト設定は使用しない(Redmine用に別途設定)のでリネームしておきます。

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

/etc/nginx/conf.d/redmine.confを新規作成します(以下は後ほど述べる問題あり)。

pstream unicorn-redmine {
    server unix:/tmp/unicorn_redmine.sock;
}

server {
    listen 80;
    server_name nostrum;

    root /var/lib/redmine/public;
    client_max_body_size 1G;

    location / {
        try_files $uri/index.html $uri.html $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;
    }

    error_page 500 502 503 504 /500.html;
}

設定ファイルの検証を行います。

~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

ファイアウォールにポート80を設定

ポート80を許可するようにファイアウォールを設定します。

まず、現在適用されているファイアウォールのゾーンを確認します。

~$ sudo firewall-cmd --get-default-zone
public

publicゾーンで許可されているサービスを確認します。
~$ sudo firewall-cmd --list-services --zone=public
dhcpv6-client ssh

httpサービス(ポート80)が許可されていないので、永続的に許可を設定します。
~$ sudo firewall-cmd --add-service http --zone=public --permanent
success

publicゾーンで許可されているサービスにhttpが追加されたことを確認します。
~$ sudo firewall-cmd --list-services --zone=public
dhcpv6-client http ssh

Nginxサービス起動

サービス起動エラー事例#1

~$ sudo systemctl start nginx.service
Job for nginx.service failed. See 'systemctl status nginx.service' and 'journalctl -xn' for details.

おや、失敗です。
statusを見よとあるので、見てみます(-lオプションを付けるとエラーがフルに見れます)。
~$ sudo systemctl -l status nginx.service
nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled)
   Active: failed (Result: exit-code) since 日 2014-07-13 20:17:02 JST; 18s ago
     Docs: http://nginx.org/en/docs/
  Process: 27906 ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf (code=exited, status=1/FAILURE)

 7月 13 20:17:02 avelo systemd[1]: Starting nginx - high performance web server...
 7月 13 20:17:02 avelo nginx[27906]: nginx: [emerg] open() "/etc/nginx/conf.d/redmine.conf" failed (13: Permission denied) in /etc/nginx/nginx.conf:31
 7月 13 20:17:02 avelo nginx[27906]: nginx: configuration file /etc/nginx/nginx.conf test failed
 7月 13 20:17:02 avelo systemd[1]: nginx.service: control process exited, code=exited status=1
 7月 13 20:17:02 avelo systemd[1]: Failed to start nginx - high performance web server.
 7月 13 20:17:02 avelo systemd[1]: Unit nginx.service entered failed state.

/etc/nginx/nginx.conf の31行目で/etc/nginx/conf.d/redmine.confの読み込みでパーミッションエラーが発生とあります。

~$ ls -l /etc/nginx/conf.d/
合計 12
-rw-r--r--. 1 root root 1097  7月 11 21:32 default.conf.orig
-rw-r--r--. 1 root root  427  7月 11 21:32 example_ssl.conf
-rw-r--r--. 1 root root  667  7月 13 20:12 redmine.conf

一見問題なさそうです。うーん、と悩んで、あ、SELinuxかも?
~$ ls -lZ /etc/nginx/conf.d/
-rw-r--r--. root root system_u:object_r:httpd_config_t:s0 default.conf.orig
-rw-r--r--. root root system_u:object_r:httpd_config_t:s0 example_ssl.conf
-rw-r--r--. root root unconfined_u:object_r:user_home_t:s0 redmine.conf

Zオプションを指定すると、設定が違っているのが分かります。
別なマシンのredmine.confをホームディレクトリ経由でコピーしたのが原因です。

restoreconで本来の設定に合わせます。

~$ cd /etc/nginx/conf.d
conf.d$ sudo restorecon redmine.conf
conf.d$ ls -Z
-rw-r--r--. root root system_u:object_r:httpd_config_t:s0 default.conf.orig
-rw-r--r--. root root system_u:object_r:httpd_config_t:s0 example_ssl.conf
-rw-r--r--. root root unconfined_u:object_r:httpd_config_t:s0 redmine.conf

他のファイルとじゃっかん異なりますが、たぶんhttpd_config_tが付いていればいけるでしょう。

conf.d$ sudo systemctl start nginx.service

今回はエラーなく実行できました。
conf.d$ sudo systemctl status nginx.service
nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled)
   Active: active (running) since 日 2014-07-13 20:43:42 JST; 6s ago
     Docs: http://nginx.org/en/docs/
  Process: 27925 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
  Process: 27924 ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
 Main PID: 27928 (nginx)
   CGroup: /system.slice/nginx.service
           tq27928 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx....
           mq27929 nginx: worker process

 7月 13 20:43:42 avelo systemd[1]: Starting nginx - high performance web.....
 7月 13 20:43:42 avelo nginx[27924]: nginx: the configuration file /etc/n...k
 7月 13 20:43:42 avelo nginx[27924]: nginx: configuration file /etc/nginx...l
 7月 13 20:43:42 avelo systemd[1]: Failed to read PID from file /run/ngi...nt
 7月 13 20:43:42 avelo systemd[1]: Started nginx - high performance web ...r.
Hint: Some lines were ellipsized, use -l to show in full.

まあ動いたようです。

サービス起動エラー事例#2

Nginxが無事動いて、ブラウザからRedmineにアクセスしたら、Internal error 発生です。

/var/log/nginx/error.log を見たら

2014/07/13 21:10:00 [crit] 28047#0: *3 connect() to unix:/tmp/unicorn_redmine.sock failed
 (2: No such file or directory) while connecting to upstream, client: 192.168.1.18,
 server: avelo, request: "GET / HTTP/1.1", upstream: "http://unix:/tmp/unicorn_redmine.sock:/",
 host: "avelo" 

/tmpにsockファイルがあるか確認しましたが、ありました。
~$ ls -l /tmp
srwxrwxrwx. 1 root root     0  7月 13 20:55 unicorn_redmine.sock
  :

いろいろ調べていたところ、次の記事を見かけました。
Making Fedora 17,Unicorn and Nginx Work Together
この記事では「Don't put the socket file in the /tmp directory.」とあります。理由も解説されており、まとめるとNginxのサービス起動設定ファイルでPrivateTmp=trueが設定されているため、他のプロセス(Unicorn)と/tmp下(/var/tmpも)のファイルが共有できないからです。

回避策はUNIXドメインソケットファイルを/tmp以外の場所(例:/var/lib/redmine/tmp/sockets 下)に設けることです。

  • /var/lib/redmine/config/unicorn.rb の修正
    -listen "/tmp/unicorn_redmine.sock", :backlog => 32
    +listen "tmp/sockets/unicorn.sock", :backlog => 32
    
  • /etc/nginx/conf.d/redmine.conf の修正
    -    server unix:/tmp/unicorn_redmine.sock;
    +    server unix:/var/lib/redmine/tmp/sockets/unicorn.sock;
    

サービス起動エラー事例#3

Nginxが無事動き、エラー事例#2の回避策を講じたあと、ブラウザからRedmineにアクセスしたら、Internal error 発生です。

/var/log/nginx/error.log を見たら

2014/07/13 21:51:17 [crit] 28164#0: *1 connect() to unix:/var/lib/redmine/tmp/sockets
/unicorn.sock failed (13: Permission denied) while connecting to upstream, client:
 192.168.1.18, server: avelo, request: "GET / HTTP/1.1", upstream:
 "http://unix:/var/lib/redmine/tmp/sockets/unicorn.sock:/", host: "avelo" 

うむ、おそらくSELinuxでしょうか
redmine$ ls -lZ tmp/sockets/
srwxrwxrwx. root root system_u:object_r:var_lib_t:s0   unicorn.sock

Nginx(http)からアクセスできないかと思われます。
~$ sudo setenforce 0

これならアクセスできました。
/var/log/audit/audit.log を調べると
type=AVC msg=audit(1405256491.134:1898): avc:  denied  { write } for  pid=28164
comm="nginx" name="unicorn.sock" dev="vda3" ino=17529898 scontext=system_u:syste
m_r:httpd_t:s0 tcontext=system_u:object_r:var_lib_t:s0 tclass=sock_file
type=AVC msg=audit(1405256491.134:1898): avc:  denied  { connectto } for  pid=28164
 comm="nginx" path="/var/lib/redmine-2.5.2/tmp/sockets/unicorn.sock" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:system_r:init_t:s0
 tclass=unix_stream_socket

と、やはりSELinuxが原因でした。

このログの抜粋をaudit2allowにかけます。

~$ sudo cat /var/log/audit/audit.log | grep nginx | audit2allow
#============= httpd_t ==============
allow httpd_t init_t:unix_stream_socket connectto;
allow httpd_t var_lib_t:sock_file write;

ポリシーファイル形式(テキスト)にします。

~$ sudo cat /var/log/audit/audit.log | grep nginx | audit2allow -m nginx 
module nginx 1.0;

require {
        type httpd_t;
        type init_t;
        type var_lib_t;
        class sock_file write;
        class unix_stream_socket connectto;
}

#============= httpd_t ==============
allow httpd_t init_t:unix_stream_socket connectto;
allow httpd_t var_lib_t:sock_file write;

内容が妥当であれば、この内容でポリシーファイル形式(バイナリ)を作成します。

~$ sudo grep nginx /var/log/audit/audit.log | audit2allow -M nginx
~$ ls
nginx.pp

ポリシーファイル形式(バイナリ)を適用します。

~$ sudo semodule -i nginx.pp

unicornとnginxを起動します。

~$ sudo systemctl start redmine-unicorn.service
~$ sudo systemctl start nginx.service

unicornを自動起動設定します。

~$ sudo systemctl enable redmine-unicorn.service

参考資料

SELinux policy for nginx and GitLab unix socket in Fedora 19

クリップボードから画像を追加 (サイズの上限: 1 GB)