ReviewBoardをNginix + uWsgiで動かす

# はじめに

RevireBoardとは、webブラウザ上でコードレビューを行うツールです。
詳しくはオフィシャルサイトをご参考に。
https://www.reviewboard.org/

正規のインストール方法によると、サポートしているミドルは以下のとおり。
> Review Board supports the following database servers:
>
>    * MySQL v5.0.31 or newer
>    * PostgreSQL
>    * sqlite v3
>
> And the following web servers:
>
>    * Apache + mod_wsgi, fastcgi, or mod_python
>    * lighttpd + fastcgi

https://www.reviewboard.org/docs/manual/dev/admin/installation/linux/

この記事では、以下の構成でReviewBoardを入れる手順を記載しています。

* MariaDB
* Nginx + uWsgi

なぜこのような構成にしたかったかというと、もともとサーバ内にMariaDBとnginxが入っていたからです。
そこにMySQLとApacheを入れるとデフォルトのポートがぶつかるし負荷も増えるので、この構成に至りました。

# 環境

```bash
$ cat /etc/redhat-release 
CentOS Linux release 7.2.1511 (Core)
```

# インストール手順
全てrootで実行してます。

## Nginx
EPELには入っているのは古いので、オフィシャルのリポジトリから最新版を入れます。
### インストール
```bash
$ yum install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
$ yum install --enablerepo=nginx nginx
```
### 自動起動設定
```bash
$ sudo systemctl enable nginx
$ sudo systemctl start nginx
```

## uWsgi

この手のツールって読み方がわからないものが多い。
uWsgiは「ウィスキー」だと思っていたけど、「ユーウィスキー」と読むらしい。

pipで入れます。
#### インストール
```bash
$ yum install python-devel gcc

$ yum install epel-release
$ yum install python-pip python-devel gcc

$ pip install uwsgi
```

## MariaDB

標準リポジトリに含まれているMariaDBを入れます。

### インストール
```bash
$ yum install mariadb mariadb-server
```
### 自動起動設定
```bash
$ systemctl enable mariadb
$ systemctl start mariadb
```
### 初期設定
設定はお好みに合わせて。

```
$ mysql_secure_installation 
/usr/bin/mysql_secure_installation: 行 379: find_mysql_client: コマンドが見つかりません

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user.  If you've just installed MariaDB, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none): 
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.

Set root password? [Y/n] ★「y」
New password: ★「MariaDBのrootパスワードを入力」
Re-enter new password: ★「もう一度入力」
Password updated successfully!
Reloading privilege tables..
 ... Success!


By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] ★「y」
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] ★「y」
 ... Success!

By default, MariaDB comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] ★「y」
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] ★「y」
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!
```

## memcached
### インストール
```bash
$ yum install memcached
```
### 自動起動設定
```bash
$ systemctl enable memcached
$ systemctl start memcached
```

## ReviewBoard
### インストール

ReviewBoardはEPELリポジトリに入っています。
なので、素直にyumでインストールします。

```bash
$ yum install ReviewBoard
```
ただ、Apaceh(httpd)を勝手に入れられてしまうのが気に食わない。

### MariaDBユーザとデータベース作成
ReviewBoardで使用するユーザ「reviewboard」と、データベース「reviewboard」を作成する。

```bash
$ mysql -u root -p
Enter password: ★「MariaDBのrootパスワードを入力」
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 10
Server version: 5.5.47-MariaDB MariaDB Server

Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others.

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

MariaDB [(none)]> CREATE DATABASE reviewboard;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> GRANT ALL ON reviewboard.* TO reviewboard@localhost IDENTIFIED BY 'reviewboardユーザのパスワード';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> quit;
Bye
```

### サイトの作成
ウィザードでReviewBoardのサイトをインストールします。

```
$ rb-site install /var/www/reviewboard


* Welcome to the Review Board site installation wizard

    This will prepare a Review Board site installation in:

    /var/www/reviewboard

    We need to know a few things before we can prepare your site for
    installation. This will only take a few minutes.


* What's the host name for this site?

    This should be the fully-qualified host name without the http://,
    port or path.

Domain Name: ★ドメインを入力


* What URL path points to Review Board?

    Typically, Review Board exists at the root of a URL. For example,
    http://reviews.example.com/. In this case, you would specify "/".

    However, if you want to listen to, say,
    http://example.com/reviews/, you can specify "/reviews/".

    Note that this is the path relative to the domain and should not
    include the domain name.

    The default is /

Root Path [/]: 


* What database type will you be using?

    You can type either the name or the number from the list below.

    (1) mysql
    (2) postgresql
    (3) sqlite3 (not supported for production use)

Database Type: 1


* What database name should Review Board use?

    NOTE: You need to create this database and grant user modification
    rights before continuing. See your database documentation for more
    information.

    The default is reviewboard

Database Name [reviewboard]: 


* What is the database server's address?

    This should be specified in hostname:port form. The port is
    optional if you're using a standard port for the database type.

    The default is localhost

Database Server [localhost]: 


* What is the login and password for this database?

    This must be a user that has table creation and modification
    rights on the database you already specified.

Database Username: reviewboard

Database Password: ★パスワードを入力

Confirm Database Password: ★パスワードを入力


* What memcached host should be used?

    This is in the format of hostname:port

    The default is localhost:11211

Memcache Server [localhost:11211]: 


* Create an administrator account

    To configure Review Board, you'll need an administrator account.
    It is advised to have one administrator and then use that account
    to grant administrator permissions to your personal user account.

    If you plan to use NIS or LDAP, use an account name other than
    your NIS/LDAP account so as to prevent conflicts.

    The default is admin

Username [admin]: 

Password: ★パスワードを入力

Confirm Password: ★パスワードを入力

E-Mail Address: ★管理者のメールアドレスを入力

Company/Organization Name (optional): 


* Enable collection of data for better support

    We would like to periodically collect data and statistics about
    your installation to provide a better support experience for you
    and your users.

    The data collected includes basic information such as your company
    name, the version of Review Board, and the size of your install.
    It does NOT include confidential data such as source code. Data
    collected never leaves our server and is never given to any third
    parties for any purposes.

    We use this to provide a user support page that's more specific to
    your server. We also use it to determine which versions to
    continue to support, and to help track how upgrades affect our
    number of bug reports and support incidents.

    You can choose to turn this off at any time in Support Settings in
    Review Board.

Allow us to collect support data? [Y/n]: n


* Installing the site...
Building site directories ... OK
Building site configuration files ... OK
Creating database ... Creating tables ...
Creating table django_admin_log
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_site
Creating table django_session
Creating table extensions_registeredextension
Creating table siteconfig_siteconfiguration
Creating table accounts_reviewrequestvisit
Creating table accounts_profile_starred_review_requests
Creating table accounts_profile_starred_groups
Creating table accounts_profile
Creating table accounts_localsiteprofile
Creating table accounts_trophy
Creating table attachments_fileattachmenthistory
Creating table attachments_fileattachment
Creating table changedescs_changedescription
Creating table diffviewer_filediffdata
Creating table diffviewer_rawfilediffdata
Creating table diffviewer_filediff
Creating table diffviewer_diffset
Creating table diffviewer_diffsethistory
Creating table hostingsvcs_hostingserviceaccount
Creating table notifications_webhooktarget_repositories
Creating table notifications_webhooktarget
Creating table reviews_group_users
Creating table reviews_group
Creating table reviews_defaultreviewer_repository
Creating table reviews_defaultreviewer_people
Creating table reviews_defaultreviewer_groups
Creating table reviews_defaultreviewer
Creating table reviews_comment
Creating table reviews_fileattachmentcomment
Creating table reviews_screenshot
Creating table reviews_reviewrequest_changedescs
Creating table reviews_reviewrequest_target_people
Creating table reviews_reviewrequest_inactive_screenshots
Creating table reviews_reviewrequest_screenshots
Creating table reviews_reviewrequest_depends_on
Creating table reviews_reviewrequest_inactive_file_attachments
Creating table reviews_reviewrequest_file_attachment_histories
Creating table reviews_reviewrequest_target_groups
Creating table reviews_reviewrequest_file_attachments
Creating table reviews_reviewrequest
Creating table reviews_screenshotcomment
Creating table reviews_review_file_attachment_comments
Creating table reviews_review_comments
Creating table reviews_review_screenshot_comments
Creating table reviews_review
Creating table reviews_reviewrequestdraft_target_people
Creating table reviews_reviewrequestdraft_depends_on
Creating table reviews_reviewrequestdraft_file_attachments
Creating table reviews_reviewrequestdraft_screenshots
Creating table reviews_reviewrequestdraft_inactive_screenshots
Creating table reviews_reviewrequestdraft_inactive_file_attachments
Creating table reviews_reviewrequestdraft_target_groups
Creating table reviews_reviewrequestdraft
Creating table scmtools_tool
Creating table scmtools_repository_review_groups
Creating table scmtools_repository_users
Creating table scmtools_repository
Creating table site_localsite_users
Creating table site_localsite_admins
Creating table site_localsite
Creating table webapi_webapitoken
Creating table django_project_version
Creating table django_evolution
Registering new SCM Tool Subversion (reviewboard.scmtools.svn.SVNTool) in database
Registering new SCM Tool Git (reviewboard.scmtools.git.GitTool) in database
Registering new SCM Tool Bazaar (reviewboard.scmtools.bzr.BZRTool) in database
Registering new SCM Tool CVS (reviewboard.scmtools.cvs.CVSTool) in database
Registering new SCM Tool Perforce (reviewboard.scmtools.perforce.PerforceTool) in database
Registering new SCM Tool Plastic SCM (reviewboard.scmtools.plastic.PlasticTool) in database
Registering new SCM Tool ClearCase (reviewboard.scmtools.clearcase.ClearCaseTool) in database
Registering new SCM Tool Mercurial (reviewboard.scmtools.hg.HgTool) in database
Installing baseline version
Evolutions in auth baseline: auth_delete_message, auth_unique_together_baseline
Evolutions in contenttypes baseline: contenttypes_unique_together_baseline
Evolutions in sessions baseline: session_expire_date_db_index
Evolutions in accounts baseline: is_private, timezone, open_an_issue, extra_data, timezone_length_30, localsiteprofile_permissions, unique_together_baseline, profile_show_closed, profile_should_send_email, profile_should_send_own_updates, profile_default_use_rich_text, reviewrequestvisit_visibility
Evolutions in attachments baseline: file_attachment_orig_filename, file_attachment_file_max_length_512, file_attachment_repo_info, file_attachment_repo_path_no_index, file_attachment_repo_revision_max_length_64, file_attachment_revision
Evolutions in changedescs baseline: fields_changed_longtext, rich_text
Evolutions in diffviewer baseline: add_parent_diffs, filediff_filenames_1024_chars, diffset_basedir, filediff_status, add_diff_hash, diffsethistory_diff_updated, filediffdata_line_counts, diffset_base_commit_id, filediffdata_extra_data, all_extra_data, raw_diff_file_data
Evolutions in hostingsvcs baseline: account_hosting_url, account_hosting_url_max_length_255, account_unique_together_baseline
Evolutions in notifications baseline: webhooktarget_extra_state, webhooktarget_extra_data_null
Evolutions in reviews baseline: change_descriptions, last_review_timestamp, shipit_count, default_reviewer_repositories, null_repository, localsite, group_incoming_request_count, group_invite_only, group_visible, default_reviewer_local_site, add_issues_to_comments, file_attachments, file_attachment_comment_extra_data, review_request_last_review_activity_timestamp, review_request_depends_on, commit_id, file_attachment_comment_diff_id, rich_text, base_comment_extra_data, unique_together_baseline, extra_data, review_request_issue_counts, group_mailing_list_charfield, review_request_draft_commit_id, group_email_list_only, review_request_file_attachment_histories, review_request_summary_index, review_request_summary_index_manual, split_rich_text, is_default_group
Evolutions in scmtools baseline: bugzilla_url_charfield, repository_raw_file_url, repository_visible, repository_path_length_255, localsite, repository_access_control, group_site, repository_hosting_accounts, repository_extra_data_null, unique_together_baseline, repository_archive, repository_hooks_uuid, repository_raw_password
Evolutions in site baseline: localsite_public
Evolutions in webapi baseline: webapitoken_jsonfield_null
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
OK
Performing migrations ... No evolution required.
OK
Creating administrator account ... OK
Saving site settings ... Saving site /var/www/reviewboard to the sitelist /etc/reviewboard/sites

OK
Setting up support ... OK


* The site has been installed

    The site has been installed in /var/www/reviewboard

    Sample configuration files for web servers and cron are available
    in the conf/ directory.

    You need to modify the ownership of the following directories and
    their contents to be owned by the web server:
        * /var/www/reviewboard/htdocs/media/uploaded
        * /var/www/reviewboard/htdocs/media/ext
        * /var/www/reviewboard/htdocs/static/ext
        * /var/www/reviewboard/data

    For more information, visit:

    https://www.reviewboard.org/docs/manual/2.5/admin/installation/creating-sites/


* Get more out of Review Board

    To enable PDF document review, enhanced scalability, GitHub
    Enterprise support, and more, download Power Pack at:

    https://www.reviewboard.org/powerpack/

    Your install key for Power Pack is:
    218a7b5d0c7a3a531f6bcd6c3ea5baeac185ea9f

    Support contracts for Review Board are also available:

    https://www.beanbaginc.com/support/contracts/
```

### 実行ユーザを追加

ここはお好きなように。
nginxユーザでもuwsgiユーザでもいいですが、ここでは「reviewboard」ユーザにしておきます。

```bash
$ useradd reviewboard -d /var/www/reviewboard -s /sbin/nologin
$ chown -R reviewboard:reviewboard /var/www/reviewboard
```

### uwsgiコンフィグファイルの作成

```ini:/var/www/reviewboard/conf/uwsgi.ini
[uwsgi]
wsgi-file=/var/www/reviewboard/htdocs/reviewboard.wsgi
static-map=/static=/var/www/reviewboard/htdocs/static
static-map=/media=/var/www/reviewboard/htdocs/media
static-map=/errordocs=/var/www/reviewboard/htdocs/errordocs
static-safe=/usr/lib/python2.7/site-packages/

http=0.0.0.0:8000
socket=/var/www/reviewboard/tmp/reviewboard.sock
chmod-socket=666
pidfile = /var/www/reviewboard/tmp/reviewboard.pid
logto = /var/www/reviewboard/logs/uwsgi.log

master=true
workers = 2
vacuum = true
thunder-lock = true

uid = reviewboard
gid = reviewboard
```

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

```:/etc/systemd/system/reviewboard.service
[Unit]
Description=ReviewBoard uWSGI server
After=syslog.target mariadb.service memcached.service

[Service]
ExecStart=/bin/uwsgi --ini /var/www/reviewboard/conf/uwsgi.ini
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target
```

### ReviewBoardを起動
```bash
$ systemctl enable reviewboard.service
$ systemctl start reviewboard.service
```

以上でReviewBoardのインストールが完了しました。
ウェブブラウザで「http://(ドメインorIP):8000」にアクセスすると、ReviewBoardのダッシュボードが表示されます。（firewall-cmdでポートを解放しておく必要があります。）

# NginxとReviewBoard(uWsgi)の連携

## Nginxコンフィグの設定

こんな感じに設定しました。

```:/etc/nginx/conf.d/default.conf
upstream reviewboard {
    server unix:///var/www/reviewboard/tmp/reviewboard.sock;
}

server {

...

    location / {
        include uwsgi_params;
        uwsgi_param X-Real-IP $remote_addr;
        uwsgi_param Host $http_host;
        uwsgi_pass reviewboard;
    }

...

}
```

本来は、ReviewBoardのstaticリソースへのアクセス処理はnginxに任せるべきですが、単純にするために全てのアクセスをuwsgiに投げています。

## Nginx再起動

編集したnginxのコンフィグを再読込させるために、nginxを再起動させます。

```bash
$ systemctl restart nginx
```

## ウェブブラウザでアクセスして確認

ウェブブラウザで「http://(ドメインorIP)」にアクセスすると、ReviewBoardのダッシュボードが表示されます。

![reviewboard_screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/125684/64059852-9ebd-a5ae-8abc-2aedffde36d0.png)

# インストール後にサブディレクトリを変更したいとき

ウェブブラウザで「http://(ドメインorIP)」にアクセスし、adminユーザでログイン。
Adminエリア > Generalメニュー > Media URL,Static URL を変更して保存。

```
Media URL: /media/
Static URL: /static/
 ↓
Media URL: /reviewboard/media/
Static URL: /reviewboard/static/
```

以下３ファイルを修正。

```None:/var/www/reviewboard/conf/settings_local.py
SITE_ROOT = '/'
 ↓
SITE_ROOT = '/reviewboard/'
```
```None:/var/www/reviewboard/conf/uwsgi.ini
static-map=/static=/var/www/reviewboard/htdocs/static
static-map=/media=/var/www/reviewboard/htdocs/media
static-map=/errordocs=/var/www/reviewboard/htdocs/errordocs
 ↓
static-map=/reviewboard/static=/var/www/reviewboard/htdocs/static
static-map=/reviewboard/media=/var/www/reviewboard/htdocs/media
static-map=/reviewboard/errordocs=/var/www/reviewboard/htdocs/errordocs
```
```None:/etc/nginx/conf.d/default.conf
    location / {
      ...
    }
 ↓
    location /reviewboard {
      ...
    }
```
reviewboardおよびnginxを再起動。
ウェブブラウザで「http://(ドメインorIP)/reviewboard」にアクセスして確認。