VPSで複数のNodeJSアプリをHTTPS化してホストする

VPSで複数のサービスのAPIサーバーを運用するための手順です。

**概要**

* Digital Ocean の Ubuntu イメージ(5USD/month)
* サブドメインに各アプリを紐づける
* Let’s Encrypt で SSL 化

## サーバーのセットアップ
### Digital Ocean Droplets のセットアップ
以下の設定でDropletsを作ります。
※ 他社のVPSサービスを利用する場合は、ドメインの`@` `www` `app1` `app2`をサーバーに向けてSSH接続できる状態までやってこの項を飛ばしてください。

* Image: Ubuntu
* Plan: Standard / 5USD
* Region: シンガポール
* Authentication: SSH keys

ドメインの管理画面から使うドメインの`www` `@` `app1` `app2` を上記で作ったサーバーに向けます。


```bash
# SSH 接続 できることを確認
$ ssh root@yourdomain.com
```

### メモリのスワップ領域を作る

月額5USDなどの安いVPSを利用しているとメモリ不足になることがあります。事前に十分な仮想メモリを設定しておきます。

```bash
# スワップ領域が未指定であることを確認
$ free
# スワップディレクトリの作成
$ sudo mkdir /var/swap/
# スワップファイルを作成
$ sudo dd if=/dev/zero of=/var/swap/swap0 bs=1M count=1024
$ sudo chmod 600 /var/swap/swap0
# 上記のファイルをスワップ領域として割り当てる
$ sudo mkswap /var/swap/swap0
$ sudo swapon /var/swap/swap0
# 再起動で再設定できるようにする
# fstab に `/var/swap/swap0 swap swap defaults 0 0` を追記します。
$ sudo vi /etc/fstab
# 動作確認
$ free
```

### ログインユーザーを作る
Root ユーザーでログインし続けるのはセキュリティ的によくないので新しいユーザーを追加します。

```bash
# ユーザーの追加
$ adduser taro
# 作ったユーザーにsudo権限を与える
$ usermod -aG sudo taro
# ssh 接続できるようにする
$ rsync --archive --chown=taro:taro ~/.ssh /home/taro
```

### ファイアウォールの設定
ファイアウォールを有効化してSSH接続のみを許可するようにします。

```bash
# OpenSSHを許可
$ ufw allow OpenSSH
# 有効化
$ ufw enable
# 動作確認
$ ufw status
```

### VSCODEの設定

VSCODEからリモートサーバーのファイルを編集できるようにします。

* extentions `remote ssh`をインストールします。
* Remote-SSH: connect to host…
* Add new Host
* ssh root@yourdomain.com

## NGINX で HTTPS を使う

### Nginx のインストール

新しく作ったユーザーで Nginx をインストールします。

```bash
# インストール
$ sudo apt update
$ sudo apt install nginx

# firewallにnginxを許可します
$ sudo ufw allow 'Nginx Full'

# 動作確認
$ systemctl status nginx
```

yourdomain.com にブラウザからアクセスしてnginxのデフォルト画面が表示されることを確認します。

### Nginx の設定ファイル

`/etc/nginx`にNginxの設定ファイル一式があります。その中から`/etc/nginx/conf.d/home.conf`を作って編集していきます。

```/etc/nginx/conf.d/home.conf
server {
    server_name [yourdomain].com www.[yourdomain].com;

    location / {
        root     /var/www;
        index index.html;
    }
}
server {
    server_name app1.[yourdomain].com;

    location / {
        root     /var/app1;
        index index.html;
    }
}
```

設定を読み込みんでnginxを再起動します。

```bash
# テスト
$ service nginx configtest
# 再起動
$ systemctl reload nginx
```

`var/www/index.html`, `var/app1/index.html` を適当に作ってブラウザでURLにアクセスすると各index.htmlが表示されるはずです。

### Let’s EncryptでHTTPSの設定

```bash
# certbot のインストール
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt install python-certbot-nginx

# 証明書をリクエスト
$ sudo certbot --nginx -d www.[yourdomain].com -d [yourdomain].com -d app1.[yourdomain].com
# 証明書の再発行設定
$ sudo certbot renew --dry-run
```

https でアクセスできるようになります。

## NodeJSアプリケーションをNginxでリバースproxyする
### nodeJSのインストール

```bash
# インストール
$ curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
$ sudo apt-get install -y nodejs
$ sudo apt-get install build-essential
# 動作確認
$ node -v
$ npm -v
```

### デモアプリを動かしてみる

```bash
$ mkdir ~/app1
$ cd ~/app1
$ npm init
$ npm install --save express
```

VS code で上記で作ったディレクトリにアクセスしてデモアプリを作ります。`[フォルダを作る] > [/home/taro/app1/]`

```index.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => res.send('Hello World From NodeJS!'));
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
```

アプリを起動して動作を確認します。

```bash
# サーバーに接続して実行
$ node index.js
# 別のターミナルを起動して動作を確認
$ curl http://localhost:3000
```

注意: この段階では、yourdomain.com:3000 にアクセスして動作確認できません。

### PM2をインストールしてアプリを永続化します

```bash
$ sudo npm install pm2@latest -g
$ pm2 start index.js
# 動作確認
$ curl http://localhost:3000
```

### Nginx Reverse Proxy server の設定
`Nginx Reverse Proxy server` を設定して`http://localhost:3000`を外にだします。`/etc/nginx/conf.d/home.conf`を編集します。

```/etc/nginx/conf.d/home.conf
...
server {
    server_name app1.[yourdomain].com;
    location / {
        proxy_pass http://localhost:3000;
    }
    listen 443 ssl; # managed by Certbot
...
```

`$ sudo systemctl restart nginx`で`nginx`を再起動します。
`app1.[yourdomain].com` にブラウザでアクセスするとの`Hello World From NodeJS!`が表示されるはずです。

## 別のドメインを追加してHTTPS化する
`home.conf`に新しいドメインの設定を追加します。

```/etc/nginx/conf.d/home.conf
server {
    server_name app2.[yourdomain].com;

    location / {
        root  /var/app2;
        index index.html;
    }
}
```

Let’s EncryptでHTTPS証明書を発行します。

```bash
# nginxの再起動
$ systemctl reload nginx
# 証明書のリクエスト
$ sudo certbot --nginx -d app2.[yourdomain].com
# 証明書の再発行設定
$ sudo certbot renew --dry-run
```
https で app2.yourdomain.com にアクセスできます。

## その他
* Digital Ocean で Droplets の破壊作成を繰り返すとき、DestroyではなくRebuildを選択するとIPが引き継がれるので便利です。

## 参考
* [How To Install Nginx on Ubuntu 18.04 | DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-18-04)
* [Initial Server Setup with Ubuntu 18.04](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04) 
* [How To Set Up a Node.js Application for Production on Ubuntu 18.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-18-04) 
* [物理メモリが足りない時はスワップメモリを使おう。(Ubuntu) | tyablog.net](https://tyablog.net/2017/08/11/swap-memory/)
* [2019-09-07 メモ nginx - Qiita](https://qiita.com/mutsune/items/06856b310670919080cf)
* [秩序と情報とブロッコリー: nginxのインストールからマルチドメインの設定まで（@CentOS）](http://data-hacker.blogspot.com/2014/03/nginxcentos.html)
* [【nginx】サブドメインの設定方法 - IT技術総合Wiki | CWiki](https://jpn.itlibra.com/article?id=21226)