Apache2 + Passenger + Sinatra でREST APIを構築する。

# REST API構築方針
すでにApache2でメインサービスを運用しているので、REST APIのためにネットワーク設定を変更したくない。ということでApache2でSinatraを動かす。万が一性能面で問題があったらFFI越しにRustの関数を呼び出すつもり。
Sinatraは、Apache2が公開しているメインアプリと並列にREST APIを公開する。以下の形で呼び出せるようにする。

```
http://localhost:8080/main_app
http://localhost:8080/sinatra
```

## 参考資料
1. [Installing Passenger + Apache on Ubuntu 18.04 LTS (with APT)]
(https://www.phusionpassenger.com/library/install/apache/install/oss/bionic/)
2. [Deploying an app to a sub-URI or subdirectory]
(https://www.phusionpassenger.com/library/deploy/apache/deploy/ruby/#deploying-an-app-to-a-sub-uri-or-subdirectory)
3. その他、同様の挑戦を行われた方々の記事(感謝!)

# 基本的なインストール
Vagrantで実験環境を構築する。インストール物は以下の通り。
1. Apache2
2. Ruby
3. bundler
4. Sinatra
5. sinatra-contrib
6. Passenger

メモリ1024MBでも動作するなら、本番サーバでの作業負荷も軽いはず。
`vagrant ssh`するとエラーになることがあるので、インストール最初にsshの設定を行っている。

```ruby:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"

  config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

  config.vm.synced_folder "./data", "/vagrant_data"

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
  end

  config.vm.provision "shell", inline: <<-SHELL
    # To solve the issue of "vagrant@127.0.0.1: permission denied (publickey)."
    sudo sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config; \
    sudo systemctl restart sshd;

    apt-get update
    # Installing Apache2
    apt-get install -y apache2

    # Installing Ruby & sinatra
    apt-get install -y ruby
    gem install bundler
    gem install sinatra
    gem install sinatra-contrib
    
    # [Installing Passenger + Apache on Ubuntu 18.04 LTS (with APT)]
    # (https://www.phusionpassenger.com/library/install/apache/install/oss/bionic/)
    sudo apt-get install -y dirmngr gnupg
    sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
    sudo apt-get install -y apt-transport-https ca-certificates

    sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bionic main > /etc/apt/sources.list.d/passenger.list'
    sudo apt-get update

    sudo apt-get install -y libapache2-mod-passenger
    
    sudo a2enmod passenger
    sudo a2enmod headers
    sudo apache2ctl restart
  SHELL
end
```

# インストール物バージョン確認
`vagrant ssh`でログインして確認する。パスワードは`vagrant`。

## Apache2

Apache2のバージョンによって、Apache2の設定(`Require all granted`)を書くかどうかが決まる。

```shell
$ apache2 -v
Server version: Apache/2.4.29 (Ubuntu)
Server built:   2020-08-12T21:33:25
```

## Ruby

Rubyのバージョンは、ここでは普通に使えることの確認も込めて行っている。

```shell
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
```

Rubyの実行ファイルの場所をApache2の設定(`PassengerRuby /usr/bin/ruby2.5`)に反映することになる。

## Ruby Gems (bundler,sinatra,sinatra-contrib) 

ここまでの作業でインストールされていたのは以下の通り。

```shell
$gem list
backports (3.18.2)
bigdecimal (default: 1.3.4)
bundler (2.1.4)
cmath (default: 1.0.0)
csv (default: 1.0.0)
date (default: 1.0.0)
dbm (default: 1.0.0)
did_you_mean (1.2.0)
etc (default: 1.0.0)
fcntl (default: 1.0.0)
fiddle (default: 1.0.0)
fileutils (default: 1.0.2)
gdbm (default: 2.0.0)
io-console (default: 0.4.6)
ipaddr (default: 1.2.0)
json (default: 2.1.0)
minitest (5.10.3)
multi_json (1.15.0)
mustermann (1.1.1)
net-telnet (0.1.1)
openssl (default: 2.1.1)
power_assert (0.2.7)
psych (default: 3.0.2)
rack (2.2.3, 1.6.4)
rack-protection (2.0.8.1)
rake (12.3.1)
rdoc (default: 6.0.1)
ruby2_keywords (0.0.2)
scanf (default: 1.0.0)
sdbm (default: 1.0.0)
sinatra (2.0.8.1)
sinatra-contrib (2.0.8.1)
stringio (default: 0.0.1)
strscan (default: 1.0.0)
test-unit (3.2.5)
tilt (2.0.10)
webrick (default: 1.4.2)
zlib (default: 1.0.0)
```

## Passenger 

[Step 3: check installation](https://www.phusionpassenger.com/library/install/apache/install/oss/bionic/#step-3:-check-installation)を参考に、動作確認を行う。

```shell
$ sudo /usr/bin/passenger-config validate-install
What would you like to validate?
Use <space> to select.
If the menu doesn't display correctly, press '!'

 . ●  Passenger itself
   ○  Apache

-------------------------------------------------------------------------

 * Checking whether this Passenger install is in PATH... ✓
 * Checking whether there are no other Passenger installations... ✓

Everything looks good. :-)
```

`Apache`をチェックしても認識されない。PassengerからApache2を認識する必要はないので問題ない。

メモリ使用状況を確認する。

```shell
$ sudo /usr/sbin/passenger-memory-stats
Version: 6.0.6
Date   : 2020-09-03 00:07:00 +0000

--------- Apache processes ----------
PID   PPID  VMSize     Private  Name
-------------------------------------
4238  1     153.9 MB   0.5 MB   /usr/sbin/apache2 -k start
4483  4238  1268.4 MB  0.7 MB   /usr/sbin/apache2 -k start
4484  4238  1268.4 MB  0.6 MB   /usr/sbin/apache2 -k start
### Processes: 3
### Total private dirty RSS: 1.78 MB


-------- Nginx processes --------

### Processes: 0
### Total private dirty RSS: 0.00 MB


---- Passenger processes -----
PID   VMSize    Private  Name
------------------------------
4468  389.7 MB  2.3 MB   Passenger watchdog
4471  946.3 MB  3.4 MB   Passenger core
### Processes: 2
### Total private dirty RSS: 5.64 MB
```

PassengerがRubyを認識しているかを確認する。[Determine the Ruby command that Passenger should use](https://www.phusionpassenger.com/library/deploy/apache/deploy/ruby/#determine_ruby_command)を参考にする。

```shell
$ passenger-config about ruby-command
passenger-config was invoked through the following Ruby interpreter:
  Command: /usr/bin/ruby2.5
  Version: ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
  To use in Apache: PassengerRuby /usr/bin/ruby2.5
  To use in Nginx : passenger_ruby /usr/bin/ruby2.5
  To use with Standalone: /usr/bin/ruby2.5 /usr/bin/passenger start

The following Ruby interpreter was found first in $PATH:
  Command: /usr/bin/ruby
  Version: ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
  To use in Apache: PassengerRuby /usr/bin/ruby
  To use in Nginx : passenger_ruby /usr/bin/ruby
  To use with Standalone: /usr/bin/ruby /usr/bin/passenger start

## Notes for RVM users
Do you want to know which command to use for a different Ruby interpreter? 'rvm use' that Ruby interpreter, then re-run 'passenger-config about ruby-command'.
```

`/usr/bin/ruby2.5`はApache2に設定するので覚えておく。

# Sinatraアプリケーション構成

SinatraアプリをApache2+Passenger構成で動作させるには、アプリケーション構成上の注意がある。アプリケーションのディレクトリにpublic、tmpのディレクトリが必要になる。中身は空でよい。

```shell
/home/vagrant/sinatra$ $ ls -al
total 32
drwxr-xr-x 4 vagrant vagrant 4096 Sep  3 00:59 .
drwxr-xr-x 7 vagrant vagrant 4096 Sep  3 00:46 ..
-rw-r--r-- 1 vagrant vagrant   79 Sep  3 00:50 Gemfile
-rw-r--r-- 1 vagrant vagrant   83 Sep  3 00:59 app.rb
-rw-r--r-- 1 vagrant vagrant   63 Sep  3 00:31 config.ru
drwxr-xr-x 2 vagrant vagrant 4096 Sep  3 00:31 public
drwxr-xr-x 2 vagrant vagrant 4096 Sep  3 00:31 tmp
```

```ruby:/home/vagrant/sinatra/Gemfile
source 'https://rubygems.org/'

gem 'sinatra'
gem 'sinatra-contrib'
gem 'rack'
```

```ruby:/home/vagrant/sinatra/app.rb
Bundler.require

  get '/' do
    "I did it my way"
  end
```

```ruby:/home/vagrant/sinatra/config.ru
require File.absolute_path("app.rb")

run Sinatra::Application
```

bundlerを実行し、アプリケーションの依存関係を設定する。

```shell
~/sinatra$ bundler install
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Using backports 3.18.2
Following files may not be writable, so sudo is needed:
  /usr/local/bin
  /var/lib/gems/2.5.0
  /var/lib/gems/2.5.0/build_info
  /var/lib/gems/2.5.0/cache
  /var/lib/gems/2.5.0/doc
  /var/lib/gems/2.5.0/extensions
  /var/lib/gems/2.5.0/gems
  /var/lib/gems/2.5.0/specifications
Using bundler 2.1.4
Using multi_json 1.15.0
Using ruby2_keywords 0.0.2
Using mustermann 1.1.1
Using rack 2.2.3
Using rack-protection 2.0.8.1
Using tilt 2.0.10
Using sinatra 2.0.8.1
Using sinatra-contrib 2.0.8.1
Bundle complete! 3 Gemfile dependencies, 10 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
```

Gemfile.lockができていればOK。

```/home/vagrant/sinatra/Gemfile.lock
GEM
  remote: https://rubygems.org/
  specs:
    backports (3.18.2)
    multi_json (1.15.0)
    mustermann (1.1.1)
      ruby2_keywords (~> 0.0.1)
    rack (2.2.3)
    rack-protection (2.0.8.1)
      rack
    ruby2_keywords (0.0.2)
    sinatra (2.0.8.1)
      mustermann (~> 1.0)
      rack (~> 2.0)
      rack-protection (= 2.0.8.1)
      tilt (~> 2.0)
    sinatra-contrib (2.0.8.1)
      backports (>= 2.8.2)
      multi_json
      mustermann (~> 1.0)
      rack-protection (= 2.0.8.1)
      sinatra (= 2.0.8.1)
      tilt (~> 2.0)
    tilt (2.0.10)

PLATFORMS
  ruby

DEPENDENCIES
  rack
  sinatra
  sinatra-contrib

BUNDLED WITH
   2.1.4
```

Apache2が認識するディレクトリをリンクする。

```shell
$ sudo ln -s /home/vagrant/sinatra sinatra
$ ls -l
total 4
drwxr-xr-x 2 root root 4096 Sep  2 23:30 html
lrwxrwxrwx 1 root root   21 Sep  3 00:35 sinatra -> /home/vagrant/sinatra
```

## /etc/apache2/mods-available/passenger.confの設定

[Deploying an app to a sub-URI or subdirectory]
(https://www.phusionpassenger.com/library/deploy/apache/deploy/ruby/#deploying-an-app-to-a-sub-uri-or-subdirectory)を参考に、設定ファイルを編集する。
`### Begin`から`### End`までは、Passengerのインストールの際に記述されているので、そのあとの設定を追記する。設定項目はPassengerのバージョンによって違ってくるので、バージョンの違うPassengerを使う際は注意。
`PassengerRuby`に、rubyの実行ファイルの場所を指定する。
Apache2のバージョンが2.4以上の場合、`Require all granted`を記述する。
ひとつだけ、`RackBaseURI`と書く場所があるのだが、これが正しい。

```/etc/apache2/mods-available/passenger.conf
### Begin automatically installed Phusion Passenger config snippet ###
<IfModule mod_passenger.c>
  PassengerRoot /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
  PassengerDefaultRuby /usr/bin/passenger_free_ruby
</IfModule>
### End automatically installed Phusion Passenger config snippet ###

PassengerRuby /usr/bin/ruby2.5
RackBaseURI /sinatra

Alias /sinatra /var/www/sinatra/public
<Location /sinatra>
  PassengerBaseURI /sinatra
  PassengerAppRoot /var/www/sinatra
</Location>
<Directory /var/www/sinatra/public>
  Allow from all
  Options -MultiViews
  Require all granted
</Directory>
```

# Apache2再起動

```shell
$ sudo service apache2 restart
```

念のため、状態を確認する。

```shell
$ sudo service apache2 status
● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: active (running) since Thu 2020-09-03 00:25:40 UTC; 47s ago
  Process: 4811 ExecStop=/usr/sbin/apachectl stop (code=exited, status=0/SUCCESS)
  Process: 4817 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
 Main PID: 2595 (code=exited, status=0/SUCCESS)
    Tasks: 73 (limit: 1152)
   CGroup: /system.slice/apache2.service
           ├─4871 /usr/sbin/apache2 -k start
           ├─4874 Passenger watchdog
           ├─4878 Passenger core
           ├─4892 /usr/sbin/apache2 -k start
           └─4893 /usr/sbin/apache2 -k start

Sep 03 00:25:40 ubuntu-bionic systemd[1]: Starting The Apache HTTP Server...
Sep 03 00:25:40 ubuntu-bionic apachectl[4817]: AH00558: apache2: Could not reliably determine the server's fully qualified d
Sep 03 00:25:40 ubuntu-bionic systemd[1]: Started The Apache HTTP Server.
```

Passengerも起動している。

# 動作確認

まずはメインアプリ。
![20200903_main_app.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2059/3b6e389a-3e09-8061-af80-555b47a37e00.png)

Sinatraの動作確認。
![20200903_sinatra_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2059/d5bd6759-a635-2834-871a-5020554f0ec3.png)

うまくいかないときは、エラー画面がでる。
![20200903_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2059/c93d94ca-9109-a86d-c995-1ad4961b2d9c.png)

Passengerのエラー画面すら出ないときは、そもそもPassengerまでたどり着いていない。Apache2自体の設定ミスの可能性がある。エラーが出た時は、`/var/log/apache2/error.log`を確認する。

# 課題

勉強不足により、解決していない項目は以下の通り。

1. Bundlerを分かってない。`gem install sinatra`しなくていいのでは。
2. Sinatraがauto reloadしない。Gemfileに'sinatra-contrib'を書いただけではダメか。
3. Sinatraアプリの配置。公開サービスのセキュリティは慎重に。
4. Sinatraアプリの認証処理。
5. Sinatra(Ruby)からFFI越しにRustの関数を呼ぶ方法。