Run multiple Sinatra apps on a single server (CentOS 7)
There’s a considerable lack of information about rack apps deployment other than Ruby on Rails, so I made this small tutorial for deploying Sinatra apps to CentOS 7 servers.
I’ll use a Digital Ocean droplet as an example but should work anywhere else. These are some of the technologies we’ll use:
- CentOS 7 (Operative System)
- Nginx (Front Facing Web Server)
- Unicorn (App Server)
- Ruby 2.2.2 (Programming Language)
- Sinatra (DSL for Rack apps)
Server Configuration
This is a very basic server configuration, there are multiple steps to secure our server but we won’t talk about this on this post.
Log In
Conect to your server, log as the root
user using the following command (substitute the highlighted word with your server’s public IP address):
Complete the login process by accepting the warning about host authenticity if it appears, then providing your root authentication (password or private key).
Create user
This example creates a new user called “pggalaviz”, but you should replace it with a user name that you like:
Next, assign a password to the new user (again, substitute “pggalaviz” with the user that you just created):
Enter a strong password, and repeat it again to verify it. We have a new user account with regular account privileges. However, we need to do administrative tasks. As root, run this command to add your new user to the wheel group (substitute the highlighted word with your new user):
Now your user can run commands with super user privileges!
Public Key Authentication
If you don’t have a public key already, generate one by running this command at the terminal of your local machine (your computer not the server)!
Press ‘return key’ to accept (don’t modify the path) and add a password to your keys (this last step is optional). If you already had or you just created your keys run this command on your local machine to print your public key (id_rsa.pub):
it should print something like this:
Select it all and copy it to the clipboard. On the server as the root user enter this command to switch to the user we created at the begining of this tutorial:
Now we’re going to create a folder named .ssh and change its permissions for security reasons:
Create a new file by running:
Enter ‘insert’ mode by pressing i
and paste your previoulsy copied ssh key. press ESC and :x
to save changes.
Change the file permissions:
and return to the root user by running:
Now, you’ll be able to login to your server by running something like
Restrict Root Login
Log in to your server using your user (not root) and open the SSH configuration file by running:
Probably you’ll need to add sudo before the command.
Look for a line that looks like: #PermitRootLogin yes
.
Enter insert mode by pressing i
and edit the line so it looks like PermitRootLogin no
Disabling remote root login is highly recommended on every server!
Press :x
and enter to save changes and then run the following command to reload SSH.
Install Dependencies
Install EPEL and update
First install the EPEL package:
Then you should confirm that all packages are updated by running:
Install Packages
Install all the packages needed such as gcc, make, git, binutils, etc.
Setting Up a Ruby Environment
We’ll use ‘rbenv’ to install and manage Ruby versions, just run these commands:
you can check installation by running rbenv
.
If nothing appears then logout from the server, log in again using ssh and run rbenv
, everything should work now.
Now we’ll install Ruby:
This will install Ruby version 2.2.2, this can take some minutes. After install let’s set the default version our shell will use by running:
Finally let’s verify ruby was installed properly with this command:
After ruby is installed run this command to install gems without documentation and save yourself some time and disk usage:
Now lets install some basic gems:
Whenever you install a new version of Ruby or a gem that provides commands, you should run the rehash sub-command. This will install shims for all Ruby executables known to rbenv, which will allow you to use the executables:
Create a simple Sinatra App
Just for this tutorial lets create a simple sinatra app.
We’ll use Sinatra’s modular style, start by creating the following archives and folders inside our sample_app folder:
Inside our Gemfile lets add:
Inside the app.rb file lets add:
Inside the config.ru lets add:
Inside the views/home.erb lets add:
Finally on app’s directory run:
Configure Unicorn
Now we need to configure Unicorn to serve our app.
And copy this inside the file:
Create Unicorn Init Script
Now we’ll create an init script so we can manage our unicorn server.
And inside this file we’ll add:
#!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn app server
# Description: starts unicorn using start-stop-daemon
### END INIT INFO
set -e
USAGE="Usage: $0 "
# app settings
USER="pggalaviz"
APP_NAME="sample_app"
APP_ROOT="/home/$USER/$APP_NAME"
ENV="production"
# environment settings
PATH="/home/$USER/.rbenv/shims:/home/$USER/.rbenv/bin:$PATH"
CMD="cd $APP_ROOT && bundle exec unicorn -c unicorn.rb -E $ENV -D"
PID="$APP_ROOT/tmp/pids/$APP_NAME.pid"
OLD_PID="$PID.oldbin"
# make sure the app exists
cd $APP_ROOT || exit 1
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_PID && kill -$1 `cat $OLD_PID`
}
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "Starting $APP_NAME"
su - $USER -c "$CMD"
;;
stop)
echo "Stopping $APP_NAME"
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
echo "Force stopping $APP_NAME"
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload|upgrade)
sig USR2 && echo "reloaded $APP_NAME" && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && exit 1
;;
*)
echo >&2 $USAGE
exit 1
;;
esac
Check that you change # app settings
to your app and user data.
Save and exit the file. Now to enable unicorn to start on boot lets run:
Lets start our Unicorn server now:
Configure Nginx
Install Nginx
First we need to install Nginx to our server. Just run:
next, we’ll enable it to start on server boot:
Configuration
First we’ll open the nginx configuration file by running:
Then change its content to look like the one below.
user pggalaviz;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
gzip on;
index index.html index.htm;
include /etc/nginx/conf.d/*.conf;
}
Check you changed the user to yours. Now let’s create our server file:
and lets add this content:
Change to match your user and app’s data. Save and exit. Now let’s start our nginx server by running:
If everything worked fine you should be able to visit your server IP address or your domain (if you already pointed it to the server) and see our home page.
By following this steps you can deploy any number of apps in the same server, just limited by your server’s resources.
Updating & Adding apps
Let’s assume you’re using GIT as your version control system and your Sinatra app code is living there (Any app).
You can log in to your server and clone the git repo to the folder where your apps will live.
For each app you’ll need to create a “unicorn.rb” file inside the app’s folder then a “Unicorn init script” and an “Nginx configuration file” on the server as we previously did.
you can start any unicorn process by typing:
If you are updating your app first pull the repo code from git to your app’s folder and then type:
And your app should be updated accordingly.