Build REST API with Apache2 + Passenger + Sinatra.

REST API construction policy

I'm already running the main service on Apache2, so I don't want to change the network settings for the REST API. So I run Sinatra on Apache2. In the unlikely event that there is a performance problem, I will call the Rust function over FFI. Sinatra publishes a REST API in parallel with the main app published by Apache2. Make it callable in the following form.

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

Reference material

  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. Other articles by people who have taken on similar challenges (thanks!)

Basic installation

Build an experimental environment with Vagrant. The installation items are as follows.

  1. Apache2
  2. Ruby
  3. bundler
  4. Sinatra
  5. sinatra-contrib
  6. Passenger

If it works with 1024MB of memory, the workload on the production server should be light. Since vagrant ssh may cause an error, ssh is set at the beginning of installation.

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 "[email protected]: 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

Installation version confirmation

Log in with vagrant ssh to check. The password is vagrant.

Apache2

The version of Apache2 determines whether to write Apache2 settings (Require all granted).

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

Ruby

The Ruby version is also confirmed here to be usable normally.

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

The location of the Ruby executable file will be reflected in the Apache2 settings (PassengerRuby /usr/bin/ruby2.5).

Ruby Gems (bundler,sinatra,sinatra-contrib)

The following is what was installed by the work so far.

$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

Check the operation by referring to Step 3: check installation.

$ 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. :-)

Even if I check ʻApache`, it is not recognized. There is no need to recognize Apache2 from Passenger, so there is no problem.

Check the memory usage status.

$ 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

Make sure Passenger recognizes Ruby. Refer to Determine the Ruby command that Passenger should use.

$ 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'.

Remember that / usr / bin / ruby2.5 is set in Apache2.

Sinatra application configuration

To run the Sinatra app in Apache2 + Passenger configuration, there are some precautions in application configuration. Public and tmp directories are required in the application directory. The contents may be empty.

/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

/home/vagrant/sinatra/Gemfile


source 'https://rubygems.org/'

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

/home/vagrant/sinatra/app.rb


Bundler.require

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

/home/vagrant/sinatra/config.ru


require File.absolute_path("app.rb")

run Sinatra::Application

Run bundler and set application dependencies.

~/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.

OK if Gemfile.lock is created.

/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

Link a directory that Apache2 recognizes.

$ 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

Settings in /etc/apache2/mods-available/passenger.conf

[Deploying an app to a sub-URI or subdirectory] Edit the configuration file by referring to (https://www.phusionpassenger.com/library/deploy/apache/deploy/ruby/#deploying-an-app-to-a-sub-uri-or-subdirectory). Since ### Begin to ### End are described when installing Passenger, add the settings after that. The setting items differ depending on the version of Passenger, so be careful when using different versions of Passenger. Specify the location of the ruby executable file in PassengerRuby. If the version of Apache2 is 2.4 or higher, describe Require all granted. There is only one place to write RackBaseURI, which is correct.

/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 restart

$ sudo service apache2 restart

Check the status just in case.

$ 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 is also running.

Operation check

First of all, the main app. 20200903_main_app.png

Check the operation of Sinatra. 20200903_sinatra_1.png

If it doesn't work, an error screen will appear. 20200903_error.png

If you don't even see the Passenger error screen, you haven't reached Passenger in the first place. There is a possibility of a misconfiguration of Apache2 itself. If you get an error, check /var/log/apache2/error.log.

Task

The items that have not been resolved due to lack of study are as follows.

  1. I don't know Bundler. You don't have to do gem install sinatra.
  2. Sinatra does not auto reload. Is it not enough to just write'sinatra-contrib' in the Gemfile?
  3. Placement of the Sinatra app. Be careful about the security of public services.
  4. Authentication process for Sinatra app.
  5. How to call a Rust function from Sinatra (Ruby) over FFI.

Recommended Posts

Build REST API with Apache2 + Passenger + Sinatra.
Hello World (REST API) with Apache Camel + Spring Boot 2
REST API testing with REST Assured
REST API test with REST Assured Part 2
FileUpload with Rest on Apache Wicket
Sinatra app with ActiveRecord died in Passenger 6.0.5
How to build API with GraphQL and Rails
[Spring Boot] Get user information with Rest API (beginner)
Customize REST API error response with Spring Boot (Part 2)
Build Rails (API) x MySQL x Nuxt.js environment with Docker
Customize REST API error response with Spring Boot (Part 1)
Compatible with Android 10 (API 29)
Build Doma1 with Ant
Build Growai with Centos7
About Apache Inference API
Build Java with Wercker
Build bazel with alpine
Implement REST API with Spring Boot and JPA (Application Layer)
Implement REST API with Spring Boot and JPA (Infrastructure layer)
Elasticsearch Operation via REST API using Apache HttpClient in Java
What I was addicted to with the Redmine REST API
Implement REST API with Spring Boot and JPA (domain layer)
Implement a simple Rest API with Spring Security & JWT with Spring Boot 2.0
Talk about uploading files using slack API with Apache HttpPost