GCP (GCE) + Rocket (Rust) + Nginx で作る公開Webサーバの構築手法

# TL; DR

`http://localhost:8000`へのリバースプロキシ設定を行いましょう。



# はじめに

[Rocket](https://rocket.rs/)と呼ばれる、RustのWebアプリケーションフレームワークが存在します。
また、Rocketを用いたWebサーバ構築手法についての記述ページも多く存在します。

> RustのRocketでWebアプリケーションを作ってみる
> https://qiita.com/hhatto/items/b118442051a67babcc0d
>
> Rust RocketでのWebAPIサーバーの書き方を解説してみる
> https://qiita.com/yukinarit/items/c5128e67d168b4f39983

しかし、どのサイトを見ても実際にポート番号80で一般公開する手法が存在しませんでした。
RocketのWebサーバ構築手法では、ポート番号8000でローカル上に構築し、構築したページを確認する（いわゆる`http://localhost:8000`）までに留まっています。

また、[GCP](https://cloud.google.com/?hl=ja)のようなクラウドシステム上で構築する場合、そもそもローカル上での確認手法自体がわからないという問題点もあります。

ここでは、表題の通り、GCPでNginxを利用してRocketによるWebサーバを一般公開する手法について記します。

__Nginxを使いたくない場合__
[おまけ：Nginxを使わない場合の問題点（未解決）](https://qiita.com/Morichan/items/69709ff07bda50ef045b#おまけnginxを使わない場合の問題点未解決)の節を参照してください。
以外と難しいことがわかります。



# GCPにおけるGCEの構築

これは数多くの他サイトで紹介されているため、あまり野暮な詳細説明は省きます。
下記サイトの「Google Compute Engineインスタンスを作成する」節を参考にしました。

> 【クラウド初心者向け】Google Cloud Platform（GCP）でWebサイトを公開してみよう！
> http://yebisupress.dac.co.jp/2018/08/23/publish_website_with_google-cloud-platform/

また、僕はUbuntu18.04を使ったため、以降のコマンドなどが異なる場合があります。
その際は適宜読み替えてください。

注意点としては、インスタンス作成時にファイアウォールとして「HTTPトラフィックを許可する」と「HTTPSトラフィックを許可する」の両方にチェックを入れることです。
せっかくなのでHTTPSでも通信したいですよね。
恐らくここでチェックを入れなくても、後で設定すればできるようになると思います。

また、構築したGCEとSSH通信する手法については、下記サイトを参考にしました。

> 【GCP入門編・第3回】難しくない！ Google Compute Engine (GCE) でのインスタンス起動方法！
> https://www.topgate.co.jp/gcp03-google-compute-engine-launch-instance

......後半に、Nginxのインストール方法をちゃっかり書いてますね。
僕が最初に見た際は、Nginxを使うという発想がそもそもなかったため見てませんでした。



# RustおよびRocketのインストール

これについても多くは語りません。
下記サイトを参考にしました。

> UbuntuにRustをインストールする
> https://qiita.com/yoshiyasu1111/items/0c3d08658560d4b91431
>
> Getting Started - Rocket Programming Guide
> https://rocket.rs/v0.4/guide/getting-started/

注意点としては、ちゃんとupdateとupgradeをしましょう！ってことくらいです。

> Rust: error: could not exec the linker `cc`: No such file or directory
> https://qiita.com/Morix1500/items/f4a71a037bd267ef62d7

......はい、僕はしっかり引っかかりましたよ。



# Rocketの動作確認

ここが本題1です。

GCEインスタンスに別のコンソールからSSH接続してください。
全く同じユーザで構いません。

そこで、下記curlコマンドを叩けば、動作確認ができます。

```sh:Rocket動作確認コマンド
curl http://localhost:8000
```

......言われてみたらその通りですよね。
僕は上記の発想に至るまで1日かかってしまいました。



# NginxのインストールおよびHTTPS接続設定

これについても省略します。
下記サイトを参考にしました。

> GCP(GCE)にNode.jsアプリケーションをデプロイする
> https://qiita.com/yfujii01/items/41c15d885830deea8ed1

ついでに、ドメイン取得とHTTPS接続の確認もしました。

注意点としては、外部IPアドレスを固定していない点です。
そこまで難しいことは無いと思いますが、以下の理由により行っていません。

- 外部IPアドレスを固定化することによる金銭的デメリットなどの把握不足
- 将来的なDNSやロードバランサなどの構築による外部IPアドレス再設定の手間

既に理解できている方は、適宜設定してください。
GCPでのIPアドレス固定化については調べたら簡単に出てくると思います。



# NginxとRocketの連携

本題2であり、この記事を書いた動機でもあります。

色々調べましたが、根本原因までは理解が及んでいません。
下記サイトを参考にしました。

> ASP.NET Core アプリを公開する(Nginx, CentOS 7.2) # リバースプロキシの設定
> https://webbibouroku.com/Blog/Article/aspcore-centos-nginx#outline__5_3

Nginxの設定ファイルを修正します。
デフォルトでは`server`を中括弧で囲んだ設定が3つ存在しますが、2番目の中身を書換えます。

```nginx:/etc/nginx/sites-available/default
# 省略
server {
    # 省略
}
# 省略

server {
    # 省略

    # 以下をコメントアウト
    # root /var/www/html;
    #
    # # Add index.php to the list if you are using PHP
    # index index.html index.htm index.nginx-debian.html;
    # server_name your-domain.tld; # 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;
    # }

    # 以下を追記
    location / {
        proxy_pass http://localhost:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # 省略
}

# 省略
server {
    # 省略
}
```

`root`、`index`、および`server_name`の項目をコメントアウトし、`location /`の項目を書換えます。

`location /`の項目では、`proxy_pass`を`http://localhost:8000`に設定します。
これにより、Rocketの確認時に利用したURLおよびポート番号をWebサーバ内部で利用するため、Webサーバ外部からのリクエストを`localhost:8000`にリダイレクトできます。



# おまけ：Nginxを使わない場合の問題点（未解決）

Rocketのみで公開Webサーバを構築する際の問題点として、ポート番号80を受信するアプリの起動はルート権限が必要である点が挙げられます。

つまり、`cargo run`でのRocket実行時は、ルート権限での打上げが必要というわけです。

しかし、解決策には次のような壁が潜んでいました。

## ポート番号の設定方法がわからない

`cargo run`で実行できるWebアプリのポート番号は、デフォルトで8000番です。
ポート番号80に変更する必要がありますが、その設定をする方法を探すのに苦労しました。

答えとしては、`cargo run`を実行するディレクトリに`Rocket.toml`ファイルを置き、次のような設定を記述すればよいです。

```toml:Rocket.toml
[development]
address = "localhost"
port = 8000
log = "normal"

[staging]
address = "0.0.0.0"
port = 80
log = "normal"

[production]
address = "0.0.0.0"
port = 80
log = "critical"
```

上記ファイルでは、受け付けるIPアドレス、受け付けるポート番号、および出力ログファイル名を記述しています。
上記以外の設定も可能です。
各項目についてはDevelopment版、Staging版、およびProduction版として、ビルド時に次のコマンドにおけるオプションで設定できます。

```sh:Production版でのビルドおよび実行コマンド
ROCKET_ENV=production cargo run
```

> RustのWebアプリ(Rocket)をHerokuにデプロイする
> https://qiita.com/ovrmrw/items/b316f01f4a936fec9b85
>
> RustでWebアプリケーション作成 メモ
> https://qiita.com/miyagaw61/items/1dcb2526bbc6668337b4

## `sudo cargo run`が実行できない

sudo時に環境変数PATHを引き継いでくれない問題がありました。
この問題については解決できず、次の策に移りました。

> Vagrant + CentOS7 で、RustのWebフレームワークRocketを発射する - My Favorite Things - Coding or die.
> http://tobi462.hatenablog.com/entry/2016/12/30/211440

## rootユーザでの`cargo run`が実行できない

これについては全くわかりませんでした。
Rustインストール時にnightlyの設定を忘れていたのか、あるいは別の問題だったのかは定かではありません。
調べていく途中に、次の壁にぶつかりました。

## ルート権限でポート番号80を開くのは危ない

よく考えたらその通りですね。
ということで、`sudo cargo run`は止めて別の手法を探らなくてはなりません。

> How do I run `sudo cargo`? : rust
> https://www.reddit.com/r/rust/comments/82slu0/how_do_i_run_sudo_cargo/



# おわりに

Rustの勉強をしたくて、ちょうどいい教材としてRocketがあることは数年前から知っていたのですが、公開Webサーバとして運用する手法がわからず、長期間ずっと温めていたネタが消化できました。
また、昨年公開したGoogle Assistantアプリの報酬として頂いたGCP利用料を使えるいい機会だったため、GCEを使って遊んでみました。

localhostへのリバースプロキシ設定は、他のWebアプリケーションフレームワークでも利用できそうですから、もし公開方法がわからないWebアプリケーションフレームワークを利用している方は、使ってみる1つの手法として参考にしてみてください。
~~負荷などの影響はわかりません。~~



# 他：参考サイト

> Client Area - Freenom
> https://my.freenom.com/
>
> yukinarit/rocket-webapi: Making JSON webapi server with Rust and Rocket 🚀
> https://github.com/yukinarit/rocket-webapi
>
> Deploy Rocket using Letsencrypt and nginx
> https://gist.github.com/belst/ff36c5f3883f7bf9b06c379d0a7bed9e
>
> Rust Webserver
> https://vishus.net/content/rust-webserver
>
> How to deploy Rust Web Application – Steadylearner – Medium
> https://medium.com/@steadylearner/how-to-deploy-rust-web-application-8c0e81394bd5