CentOS7 + Apache でサブディレクトリに HackMD を公開する

[HackMD](https://hackmd.io) 超便利ですよね。PCとスマホで同時に開き、編集がリアルタイムに同期するさまを見たときは感動＆興奮しました。

そんな HackMD を職場のオンプレミス環境に導入したときの備忘録です。そのサーバーではすでに Apache Web Server が動いているので、リバースプロキシを使ってサブディレクトリに公開します。


## 条件

* CentOS 7.4
* Apache 2.4
* 公開URL: `https://example.com/hackmd/`
* HackMD データ保存先: MySQL
* HackMD 認証方法: GitLab OAuth のみ


## HackMD インストール

※ 公式の [README](https://github.com/hackmdio/hackmd) 通りに進めます

1. Node.js, npm をインストール: `sudo yum install -y nodejs npm`
2. MySQL にて HackMD 用のユーザーおよびデータベースを作成
3. GitLab にてアプリケーション登録
    * Redirect URI: `https://example.com/hackmd/auth/gitlab/callback`
    * Scopes: `api`
4. ソースをダウンロード: `git clone https://github.com/hackmdio/hackmd.git`
5. セットアップスクリプトを実行: `bin/setup`
6. 設定ファイル(`config.json`)を編集
7. DB設定ファイル(`.sequelizerc`)を編集
    * url: `mysql://USERNAME:PASSWORD@localhost:3306/DBNAME` 
8. DBマイグレーション実行: `node_modules/.bin/sequelize db:migrate`
9. アセットファイルをビルド: `npm run build`
    * ちなみにRAM:4GB環境ではスラッシングが発生しOSごと死にました。アイドル時に最低3GBは空いてないとダメかも
    * `emitError: EMFILE: too many open files` エラーが出てうまくいかない場合は、`ulimit -n 4096` にしてファイルオープン上限数を一時的に上げてから実行
    * どうしてもうまくいかない時は、`rm -rf public/build/ public/views/build/` でビルド済みファイルを削除すると直るかも
10. 動作確認: `NODE_ENV='production' node app.js`
    * "HTTP Server listening at port 8001" と表示されればOK

```json:config.json
{
    "production": {
        "domain": "example.com", //公開ドメイン名
        "urlpath": "hackmd", //公開サブディレクトリ名
        "port": 8001, //ポートを変更
        "usessl": false, //リバプロ〜HackMDはHTTPで通信
        "protocolusessl": true, //URLはhttps://で表記
        "usecdn": false, //ネット接続できない環境なのでCDN使わず
        "imageUploadType": "filesystem", // もちろんクラウドなど使えるはずもないのでローカル
        "allowanonymous": false, //ゲストを不許可
        "email": false, //メールでのサインアップ不許可
        "db": {
            "username": "DB_USERNAME",
            "password": "DB_PASSWORD",
            "database": "DB_DBNAME",
            "host": "localhost",
            "port": "3306",
            "dialect": "mysql"
        },
        "gitlab": {
            "baseURL": "https://gitlab.ipnoc.net",
            "clientID": "GITLAB_APPLICATION_ID",
            "clientSecret": "GITLAB_APPLICATION_SECRET"
        }
    }
}
```

usessl を false、protocolusessl を true とすることで HackMD サーバーはHTTPで待ち受けるが、URLは `https://` で出力されるというリバプロ向けセッティングになります。


## Apache リバースプロキシ設定

HackMD はリアルタイム更新を実現するため WebSocket を使います。なので特定URLおよび、Upgrade header もしくは Connection header があったら、WebSocket へアップグレードするようにします。

```conf:/etc/httpd/conf/httpd.conf
#
# HackMD configuration
#

# WebSocket upgrade
ProxyPreserveHost On

RewriteEngine On
RewriteCond %{REQUEST_URI} ^/hackmd/socket.io     [NC,OR]
RewriteCond %{HTTP:UPGRADE} ^WebSocket$           [NC,OR]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$          [NC]
RewriteRule .* ws://localhost:8001%{REQUEST_URI}  [P,QSA,L]

# Reverse proxy
ProxyPass /hackmd http://localhost:8001
ProxyPassReverse /hackmd http://localhost:8001
```

設定が終わったら、`sudo apachectl configtest` で設定をチェックしましょう。


## Systemd で HackMD 自動起動

1. サービス定義ファイルを `/etc/systemd/system` に作成
2. サービス定義を反映: `sudo systemctl daemon-reload`
3. サービス起動: `sudo systemctl start hackmd`
4. サービス自動起動設定: `sudo systemctl enable hackmd`

```conf:/etc/systemd/system/hackmd.service
[Unit]
Description=HackMD
After=syslog.target network.target

[Service]
Type=simple
Environment=NODE_ENV='production'
ExecStart=/usr/bin/node /path/to/hackmd/app.js
WorkingDirectory=/path/to/hackmd
KillMode=process
Restart=always
User=root
Group=root

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

最後に Apache を再起動して正常に見られるかチェックします。また可能であればOSごと再起動して自動起動するかもチェックしておくとベターですね。

それでは良いリアルタイム Markdown ライフを！！

---

## バグ情報

2017/11/1 現在、サブディレクトリに公開すると一部のWebフォントが404になってしまう不具合があります。[バグレポ](https://github.com/hackmdio/hackmd/issues/616)したのでそのうち修正してもらえると思いますが、それまでは asset ファイルを検索してURLを手動で修正するなどしてください。