GCP(GCE)にNode.jsアプリケーションをデプロイする

可能な限りお金は使わない方向で実現したいと思います。

## 手順一覧

1. gce(Google Compute Engine)のインスタンス作成
1. nginxを設定してhttp接続できるようにする
1. ドメインをfreenomで作る
1. Let's Encrypt でSSL証明書を作る
1. アプリをnodejsで作る
1. nginxの設定を修正しアプリをhttps接続できるようにする

## gce(Google Compute Engine)のインスタンス作成

- GCPにログインします
https://cloud.google.com/

- Compute Engineのインスタンスを作成する
ダッシュボードからCompute Engineを選び、VMインスタンス、インスタンスを作成と進みます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/7d23a96a-fe19-116d-a5c9-5c8f1424dffc.png)

- 名前を適当に決めます
デフォルトのままでも良いです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/252c9a32-2b5e-642d-1dc7-1008895064eb.png)

- リージョンをus-west1 (オレゴン)にします。
初期設定の「us-east1 (サウスカロライナ)」でも問題ありません。
多少は応答が速くなるかもという思いから日本に少しでも近い「us-west」に変更しました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/5e182713-f3b4-6c19-4cc8-ac06c948e593.png)

- マシンタイプを「micro (共有 vCPU x 1)」に変更します
毎月無料枠に収めるためには必須の作業になります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/699d1d21-b65b-0549-a564-8710704091a3.png)

- ブートディスクのサイズを30GBに変更します
任意作業です。デフォルトでは10GBだが、30GBまで無料なのでとりあえず変更するくらいでOKです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/0f61ece1-6324-8d3e-00e6-c9966a22e8d3.png)

- ファイアウォールを設定します
HTTPとHTTPSを許可します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/b11b3edb-9746-d168-3ac6-c2ca3b2c8d70.png)

- 確定
「作成」を押下しインスタンスを作成します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/01d7ba0f-8e84-b35d-0ea2-63098937305b.png)
少し待つと、インスタンスが作成されました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/a82d99c2-6074-3b7b-76de-5472cfefe6ac.png)







## nginxを設定してhttp接続できるようにする

- 「ブラウザウィンドウで開く」を押下しgceインスタンスに接続します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/f6b7cac5-51ce-b5c2-6662-6e91baa0c14e.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/1faff015-f694-6629-73da-f24df856ff2a.png)

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

```sh
$ sudo apt install nginx -y
```

- nginxのサービスを起動します

```sh
$ sudo service nginx start
```

- 動作確認します  
gceインスタンスの外部IPを確認し、httpで接続してください。  
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/fe608d95-ccf7-a169-23ca-440fe818d145.png)
以下のようにWelcome to nginx!と表示されたらOKです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/997cda63-f9f3-c499-bbe1-6cc1fedd776c.png)

httpsはまだ設定していないのでエラーになります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/0c18875f-5904-8432-b08d-81d555ca4644.png)



## ドメインをfreenomで作ります
URL
https://www.freenom.com/ja/index.html?lang=ja

- ユーザー登録してサインインします。
Googleアカウントでログインする方が簡単で良いかも。

- 「Services > Register a New Domain」からドメイン取得画面に移動する
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/20950708-6a96-5df6-cc8e-772e429d1a59.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/d7a0e78e-9011-4c16-2577-6bd51b4e59e6.png)

- 使いたいドメイン名を入力します
今回は「f01api」と入力して「Check Availability」ボタンを押しました。
短か過ぎるドメイン名だと0円にならないことがあるので、必ず「USD0.00」になっていることを確認しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/d2b08e9c-cc6f-3a91-ece8-a9c0f8587d0c.png)

- ドメイン名の最後につける文字列を「.tk」「.ml」などから選択します
今回は「.tk」と「.ml」の２つを選択してみます。
２つ取得する意味は特にありません(偽サイト対策程度の意味はありますが)。
それぞれ「Get it now!」を選択し、その後「Checkout」を選びます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/d9ba7ba5-99bb-2aa5-36e7-6dc7662c841b.png)

- DNS設定を行います
ドメイン名とIPアドレスを紐づけます。
「Use DNS > Use Freenom DNS Service > IP address」に使用するgceインスタンスの外部IPを設定します。
今回は「.tk」「.ml」に同じIPを設定しました。
後から変更も簡単に行えるのであまり気にしなくても大丈夫です。
設定できたら「Continue」を押しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/4a86a2d0-baa1-b7af-1e84-8bd16d252a41.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/b94dbf5a-95d6-6adc-eb57-fd1ffde2d753.png)

- 確定する
Total Due Todayが$0.00USDになっていることを確認し、
最下部にある「I have read and agree to the Terms & Conditions」をチェックし「Complete Order」を押しましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/18d05d67-f434-cea6-da53-6aed39ce4e07.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/c1e76ac2-4f37-f3c4-9d6d-60449208bed7.png)
「Order Confirmation」となれば成功です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/73ffc7ca-a793-7431-2862-e6676a4cb5c6.png)

- domain一覧を確認する
「Services > My Domains」を開くことでちゃんと購入できているか確認できます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/a7c75218-325e-e201-2a94-f4ac9ec5282e.png)

- 動作確認します
もしかすると購入直後はDNSが設定されておらずエラーになってしまうかもしれませんが、5分も経てば購入したドメインでnginxの画面が表示されるはずです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/2d3e1fe6-f9bb-0679-80ab-f9387674a472.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/0f7de48e-9cb4-928d-402b-dfed9dc651bf.png)

## Let's Encrypt でSSL証明書を作ります
公式の手順書があるのでこちらを参考に進めていきましょう。
https://letsencrypt.jp/docs/using.html

- cerbot-autoをインストールします

```
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
```

- cerbot-autoを実行します  
途中で何度か止まるので適宜、入力していきます。

```
$ ./certbot-auto
```

- Emailを入力します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/b81d1612-539f-a3f4-2ac5-c5c0b6a8afd3.png)

- 利用規約を確認します
URL
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
確認後「A」を入力します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/c8e5256f-4441-2728-f63e-373842831e22.png)

- Emailにお知らせを送って良いかを選択します
「N」を選択しました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/79238970-96d0-4fb4-0268-8ec75f0565c3.png)

- SSL証明書を発行したいドメインを入力します
「f01api.tk」を入力しました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/4c3dcbd6-80bc-aadc-ec25-d85e12e66344.png)

- httpをhttpsにリダイレクトするかの設定
「2」(リダイレクトする)を選択しました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/350f8d70-732a-6954-c390-3b59b0458df4.png)

- 証明書の発行に成功
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/011ec5c8-83a9-964e-bf0a-55e2204c7c21.png)
それぞれ以下にファイルが作成されたようです。
  - 証明書  
/etc/letsencrypt/live/f01api.tk/fullchain.pem
  - 秘密鍵  
/etc/letsencrypt/live/f01api.tk/privkey.pem

- https接続の確認をする
cerbot-autoのおかげでnginxの設定が書き換えられ、https://【ドメイン名】で接続が行えるようになっています。
確認してみましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/428ff41f-7d06-6b92-b9be-27f478ca680c.png)

## アプリケーションをNode.jsで作る

```sh:Node.jsをインストールします
$ curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
$ sudo apt-get install -y nodejs
```

```sh:インストールされたことの確認します(バージョン確認)
$ node --version
v10.11.0
```

```sh:ディレクトリ作成
$ cd ~
$ mkdir express-api
$ cd express-api
```

```sh:npm初期化
$ npm init -y
```

```sh:npmでexpressとbody-parserをインストール
$ npm i --save express body-parser 
```

- アプリを作成する

```js:index.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.get('/', (req, res) => res.send('Hello World!'));

app.post('/test', (req, res) => {
    let result = {};
    if (req.body.password === 'hoge') {
        result.result = "true";
    } else {
        result.result = "false";
    }
    res.json(result);
});

app.listen(3000);
```

- デーモン起動のためにforeverをインストールする

```sh:foreverインストール
$ npm i --save-dev forever
```

- scriptに起動・停止設定を追加する

```sh:
$ vi package.json
```

```json:変更前
:
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1"
},
:
```

```json:変更後
:
"scripts": {
  "start": "forever start index.js",
  "stop": "forever stop index.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},
:
```

```sh:アプリを起動する
$ npm run start

> express-api@1.0.0 start /home/hoge_gmail_com/express-api
> forever start index.js

warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: index.js
```

- 動作確認する
nginxを通していないので「http://【外部IP】:3000」で接続します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/9da47dac-5fd5-5785-b450-98924637a517.png)

- 停止方法
もしアプリを修正することになって止めたい場合は、以下のコマンドを実行します。

```sh:アプリ停止
$ npm run stop

> express-api@1.0.0 stop /home/hoge_gmail_com/express-api
> forever stop index.js

info:    Forever stopped process:
    uid  command       script   forever pid   id logfile                                   uptime
[0] b-8D /usr/bin/node index.js 20695   20702    /home/hoge_gmail_com/.forever/b-8D.log 0:0:0:33.898
```

## nginxの設定を修正しアプリをhttps接続できるようにする

```sh:
$ sudo vi /etc/nginx/sites-available/default
```

```nginx:変更前
server {
    :
    server_name f01api.tk; # managed by Certbot
    :
    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }
    :
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    :
}
```

```nginx:変更後
server {
    :
    server_name f01api.tk; # managed by Certbot
    :
    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        # try_files $uri $uri/ =404;
        proxy_pass http://127.0.0.1:3000;
    }
    :
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    :
}
```

```sh:nginx再起動
$ sudo service nginx restart
```

- 動作確認
https://【ドメイン名】で接続するとnginxの設定画面ではなくhello worldが表示(アプリの画面)が表示されること
nginxのページが表示される場合はブラウザのキャッシュを消してから確認してみてください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/214653/42999584-d997-d3ce-b6e8-b0c9aad78d08.png)

- 動作確認2
curlを使ってアプリが動作するか確認しましょう。
今回作成したアプリはpasswordをjson形式で渡すことでresultをjson形式で返すものです。
テスト用なので{"password":"hoge"}という文字列を渡すことで、trueを返し、それ以外の場合はfalseを返すようになっています。

```sh:trueパターン
$ curl -X POST https://f01api.tk/test -H "Content-type: application/json" -d '{"password":"hoge"}'
{"result":"true"}
```

```sh:falseパターン
$ curl -X POST https://f01api.tk/test -H "Content-type: application/json" -d '{"password":"fuga"}'
{"result":"false"}
```

想定通り返ってきました。

これで完成です。
お疲れ様でした。