Table of Contents
Francisco Quintero © 2018
Introduction
My very first intensive Ubuntu servers experience was back then in year 2014. The company had hosted 20+ Drupal websites in a dedicated server in hosting provider. As you should know, dedicated machines are expensive. One day, my boss in that company told me:
Frank, we need to start saving money. I think we can save a lot if we move all sites to basic Linode VPS. If you’re able to move all those websites to Linode before [GIVEN DATE], the amount of dollars in the hosting service bill will be yours.
Then, to buy me in, he said:
I rather give you that money than to the hosting provider.
I was in.
After the first two migrated websites, everything went very good and real quick, though it was a real pain in the ass. The whole migration, in general terms, consisted of:
In the hosting provider:
- Backing up the MySQL database for the Drupal website
- Zipping the website files in a tar format
- Downloading the zip
In my local environment:
- Create a new db and import the exported data
- Unzip the zipped file in the adequate folder
- Check everything was in order
Doing the process local helped a lot because I used Vagrant to replicate an Ubuntu server. Every step done locally was replicated, almost exactly, in the server.
When everything local was completed, I’d move to the next steps.
In Linode:
- Launch a new basic Ubuntu server VPS
- Create all necessary server records(A, TXT, CNAME)
In the Linode server:
- Setup a new system user
- Setup the new user SSH login
- Harden the server
- Setup firewall rules
- Deactivate root login
- Set an SSH port greater than 10000
- Install required software
- Configure software
- Create folder structure
- Upload zip file
- Upload database backup
- Unzip file and place files in given folder
- Import database data
- Create Apache virtual host
After everything was loading correctly in a subdomain, the original domain would point to the Linode nameservers and I’d checked it was loading as it should. Then moved on to the next website.
Yes, they were lots of steps for 20+ websites. None of those steps were automatized and back then I didn’t know how to automat them.
Fortunately, I finished on time and got USD 500 in my bank account. However, I told myself I wouldn’t do that kind of manual work again. I set to learn Bash scripting and ways to automate everything I could as much as it would be possible.
From that day on I got into the Ops world. I created scripts for doing a kind of deploy of Drupal websites and kept trying and doing more scripts for several things I saw were very repetitive and could be automated. One thing took me to another and learnt even more about Bash, also learnt Ansible, Terraform, Docker, Jenkins.
I took every chance I had to try and learn new tools to make my life as a developer a bit easier and now all that knowledge is in this handbook.
Objective
This handbook main objective is to help you get started in the process of setting up and configure Ubuntu servers(from 14.04+) to host Ruby on Rails apps. Born from doing several configurations for previous Ruby on Rails applications, many sections described in this handbook can be used for many other type of web apps.
The Backend Handbook contains information to configure development tools such as Vagrant or Jenkins. Also covers how to setup servers like the ones you get in services like in Digital Ocean or Linode, from scratch, creating system users, and more.
List of software installed in the process include:
- Development libraries(curl, htop, gcc, python)
- PostgreSQL
- RVM
- Ruby
- NodeJS
- Redis
- WKHTMLTOPDF
- etc
About the Author
Francisco Quintero is a software engineer from Colombia. Although his first experiences were with Visual Basic .NET, he fell in love with web development when discovered free books(HTML, CSS, and JavaScript) published by recognized Spanish instructor Javier Eguiluz. Since then, web development is what he does and became passionated about.
Setup
This section will help you to get started setting up your brand new server. This process, though easy to complete, can get a wrong turn when commands are not issued carefully.
One of the things I’ve learnt about setting up servers before writing this handbook is that computers are better than humans when automating things, so, it’s way better to have scripts and then use them as many times as we need rather than manually issuing commands in the console one by one.
Linode
Configurig an Ubuntu machine in Linode requires you to set it up from scratch(they give you the OS and root user). To have a working Ubuntu server you’d need to:
- Create a non-root user
- Set up user SSH access
- Harden SSH service configuration(change default port, etc)
- Activate firewall(iptables or UFW)
- More…
Create New non-root user
A non-root user is a system user that can log in to the server but in order to run superuser commands needs to precede them with sudo
and inputing her password.
Add a user
NOTE: the
#
symbol denotes the commands are run as root user, not as a system user. NOTE: the$
symbol denotes the commands are run as a system user.
Example:
Give that user permissions to run sudo
commands
Example:
References
You can always see the man pages for previous commands to learn more about them or reading their docs online.
Online documentation:
Set up User SSH Access
In order to access a Linode machine from your development computer(or any other machine) you need an SSH key that identifies that user on the server. Using SSH authentication is recomended, and certainly, more secure than password authentication.
Generate a New SSH Key
Using a computer with Linux or macOS:
When generating ssh keys, the prompt will ask you to enter a passphrase. That is a secret word to further protect your key. I, normally, don’t assign any to my ssh keys for server access.
Follow the steps in the console prompt and take note of where the generated keys(they come in pair) are located in your computer.
Now, login into the server (via password access) and create a .ssh
folder in the user’s home folder and then give all permissions only to the owner:
Back in your computer, proceed to upload the generated ssh key. In the folder you generated the keys should be placed two files: id_rsa
and id_rsa.pub
. We are going to upload the public key (id_rsa.pub
).
In the ssh key generation prompt you can give any other name to the keys. I usually name them according to the project I’m working on.
Upload the file using the scp
command. It has the syntax scp ORIGIN DESTINATION
Example:
Finally, login once again into the server and move the id_rsa.pub
key into the authorized_keys
file that is read by the SSH service.
References
See man pages for more details in the previously used commands.
Online docs:
Harden SSH Service from Configuration File
Leaving the default configuration for the ssh service is not recommendable because it leaves your server open to attacks from intruders or anyone with the intention of breaking your machine.
By modifying some settings in the sshd_config
file your server will be more secured though not impenetrable, but something is better than nothing.
The sshd_config
is located (in Ubuntu 14.04) at /etc/ssh/sshd_config
. The next commands suppose you are editing that file.
Open the Configuration File
Following settings is how the file settings should look. If you find them this way, it is all ok.
If you are using nano editor you can find lines in a file using the CTRL + W key combination and typing the desire word.
Change the Default Port
22 is the default. Change to a 5 digit number, like 12781, to make it more secure.
Deactive Root Access
Deactivate Password Access
Restart SSH Service Configuration
Exit the nano editor with CTRL + O (save changes) & CTRL + X.
References
In the man pages, as always.
Online docs:
Dependencies
This section includes all software a basic Ruby on Rails application would require in order to run: databases, datastores, web servers, app servers, libraries, and more.
The list of packages or software projects described in this section was gathered from my experience working in different RoR projects. It might be very useful for several scenarios or might lack other packages for other, but regardless, it works as a good starting point.
Remember # and $ symbols
The #
symbol denotes the commands are run as root user, not as a system user. In the other hand, the $
symbol denotes the commands are run as a system user.
Examples
Command run as root
Command run as system user
Library Essentials
This installation makes sure the machine has the basic software the rest of the programs you’re going to install need. They provide libraries, commands or modules to those software.
What are they for?
Some of them are standard libraries the software that you install in your machine is going to need to work correctly.
Others are just useful software that should be installed because some guides tells you to use them or because sooner or later you’d be needing them.
How to install them?
Create a file $ nano install_basic_dependencies.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
.
and run it
NOTE The list of packages to install is splited with continuation lines for readability.
-y
flag is for accepting the prompt without needing the user to type y/N
Docs
See man pages for apt-get
command $ man apt-get
or online
Database Systems and Datastores
A database is:
A database management system (DBMS) is a computer software application that interacts with the user, other applications, and the database itself to capture and analyze data.
A datastore is:
A data store is a repository for persistently storing and managing collections of data which include not just repositories like databases, but also simpler store types such as simple files, emails etc.
PostgreSQL
PostgreSQL is a ORDBMS(Object-Relational Database Management System).
What is this for?
Storing and accessing data through the Structured Query Language(SQL).
How to install it?
Create a file $ nano install_postgresql.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
.
Then run it
Verify
You can check PostgreSQL is up and running by issuing $ sudo service postgresql status
command in your terminal. It should return the version and port the database server is working.
Docs
A full list of PostgreSQL manuals can be found their official site.
MySQL
MySQL is a RDBMS(Relation Database Management System).
What is this for?
Storing and accessing data through the Structured Query Language(SQL).
How to install it?
Create a file $ nano install_mysql.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
.
Run the script
Docs
All MySQL documentation and manuals can be found in their official page.
Redis
Redis is an in-memory data structure store that can be used as a database, cache and/or message broker.
What is this for?
Regarding Ruby on Rails applications, Redis can be used as a database for storing background jobs data. Sidekiq is a popular gem that leverages Redis to work.
You can also use a Redis as a cache or message broker, however, solutions like RabbitMQ are better suited for this job as there have been issues about Redis losing data.
How to Install Redis
There packages available for many operating systems that make it easy to install Redis with just a couple of commands.
Ubuntu 14.04+
Run the following commands:
MacOS
If you Homebrew installed just run:
From Source
Create a file $ nano install_redis.hs
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
, then run the instructions in the file
and wait for redis to be installed on your machine.
NOTE this way of install is called from source and the main difference between this and installing from
apt-get
is getting up to date packages. Sometimes the apt packages are really outdated.
Verify Installation
Check redis is running with sudo service redis_6379 status
. It should output something like Redis is running (PORT NUMBER).
Docs
You can find all redis documentation in its official page.
Read more about Redis feautures.
About installing redis:
- How to Install and configure Redis on Ubuntu 14.04
- Install and config Redis on Mac OS X via Homebrew
Web Servers and App Servers
A web server is:
A web server is a computer system that processes requests via HTTP, the basic network protocol used to distribute information on the World Wide Web.
An application server is:
An application server is a software framework that provides both facilities to create web applications and a server environment to run them.
Nginx Web Server
Nginx is a web server that happens to be as well a reverse proxy, load balancer, and HTTP cache.
What is this for?
Using nginx you can have many things that otherwise would require you to install individual pieces of software to have a great architecture for your app:
- You can use nginx as a web server in a client-server architecture
- Also works as a reverse proxy, meaning that nginx can work as first layer and send requests, according to predefined rules, to one or more backend services(Jenkins, Apache, etc)
- Similar to what a reverse proxy would do, you can use nginx to work as a load balancer and distribute request to other web servers throught redirects
- Nginx can also be used as a cache and you would not need to install things like Varnish, Redis or Memcache
How to install it?
Create a file $ nano install_nginx.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
.
Finally, run the script to install it:
Verify Installation
If you need to verify Nginx is up, you can do it with $ sudo service nginx status
.
Docs
Nginx documentation can be found here.
Phusion Passenger
Passenger is an application server that allows you to securely operate web apps, microservices & APIs with outstanding reliability, performance and control. By acting as a process manager, reverse proxy and by providing operations tools, Passenger enables you to quickly launch and easily maintain Ruby, Node.js, Python and Meteor apps.
What is this for?
Let’s try to define first what is an application server.
In one side we have web servers which are normally used for serving static content via the HTTP protocol.
In the other side, we got applications servers which can also do the job of a web server and do more things. The main difference(when both use no extra plugins) is that app servers can server dynamic content via the HTTP protocol or others.
In the case of Passenger, its job is to handle business logic in applications coded in the supported languages.
How to install it?
Create a file $ nano install_passenger.sh
and add the following content:
save the file by pressing CTRL + O
and then press CTRL + X
to exit it.
Now, run the script:
Depending on the version of passenger you installed you either need to:
A) uncomment lines passenger_root
and passenger_ruby
in /etc/nginx/nginx.conf
file and then restart nginx with sudo service nginx restart
.
B) Edit /etc/nginx/nginx.conf
and uncomment include /etc/nginx/passenger.conf;
by removing the #
character in the beginning of the line and then restart nginx with sudo service nginx restart
.
Verify Installation
One of the ways to verify Passenger is working is by taking a look to the processes list and check one called watchdog is available.
Docs
See Phussion Passenger extensive documentation for more help.
Libraries
This section covers installation of some specific dependencies, libraries, software or stuff that some application might need.
- WKHTMLTOPDF for generating PDF files from HTML pages
- Image Magick for manipulating image files
- Jenkins for continuous integration
- RVM(Ruby Version Manager) for managing Ruby versions
- NVM(Node Version Manager) for managing Node versions
NodeJS
NodeJS is an open source and cross platform JavaScript runtime environment for developing server tools and/or web applications.
Although Node.js is not a JavaScript framework, many of its basic modules are written in JavaScript, and developers can write new modules in JavaScript.
What is this for?
Because of it non-blocking and event-driven architecture real time communication applications, browser games and web applications that demand high throughput, scalability and concurrence can be designed and develop using nodejs.
How to install it?
Create a file $ nano install_nodejs.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
.
Run its instructions:
Verify Installation
You can verify Node is installed with either $ node -v
or $ node --version
. The output should return a number which is the current Node version in your system.
Additionally, you can check the installation path with $ which node
or $ command -v node
.
Docs
NodeJS documentation can be found in the official site
NVM - Node Version Manager
By using NVM you can manage(install, uninstall, use) different NodeJS versions just a few commands away.
What is this for?
Imagine you work in three projects where NodeJS is being used. Now, in your system you have installed Node version 8.11.1. Good, you can work all of your projects with no problem with that version… Until a dependency/colleague updates something and your current version is no longer supported.
Now, of course you could uninstall it and install a newer one but, What if one of those projects can’t work with an updated version(greater than 8.11, in this example)?
You could either use virtual environments with VirtualBox or Vagrant or you can use NVM to easily install and handle different NodeJS versions in your computer with not much trouble.
How to install it?
Create a file $ nano install_nvm.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
, then run the file:
Verify Install
You can verify NVM is installed with command -v nvm
or nvm
or nvm --version
.
Docs
See NVM GitHub project’s page for more details.
RVM
Easy, manage(install, uninstall, use) different ruby versions just a few commands away.
What is this for?
With RVM you can manage several Ruby versions easily. Without a solution like RVM(rbenv, for example) you might need to uninstall a previous Ruby version to install a differente one. Forget about it with RVM.
Besides, you can handle gemsets to have customized and context specific ruby gems. This context can be different projects or environments.
Most important thing about RVM is that rubies and gemsets are isolated from each other.
How to install it?
Create a file $ nano install_rvm.sh
and add the following content:
NOTE: do not run as sudo
To save the file press CTRL + O
and then to exit press CTRL + X
nano editor.
Now you can run the instructions:
and wait for RVM to be installed.
Bonus: install ruby
After installing RVM, you can proceed to install ruby:
NOTE: you should change the version of ruby for the desired one
You can run these commands one by one or place them in a file. Before create a file $ nano install_ruby.sh
and add the following
run the instructions in the file
Verify Installation
Check RVM is installed with $ rvm -v
or $ rvm --version
and the install path with $ which rvm
.
Docs
Find everything about RVM in its official site
WKHTMLTOPDF
WKHTMLTOPDF is a command line tool for rendering HTML docs into PDF. This utility runs headless, meaning it needs no GUI to be operated or used.
What is this for?
Many web applications display useful information to their users. That info can be in the form of tables or well-crafted UIs, and though most of the data is not so important, there’s important info user might required in a portable format.
A good example of important info is invoices for payments or subscriptions. The customer might want to download them to enter the info in a system they own/use or for backup purposes. Whatever the case, they’d like to export that HTML view into something they can grasp. This is when tools such as WKHTMLTOPDF can prove themselves useful.
Be aware WKHTMLTOPDF makes sense to be used when the info is, mainly, generated from a backend because of its multiple implementations in programming languages such as Ruby.
How to install it?
Create a file $ nano install_wkhtmltopdf.sh
and add the following content:
NOTE: this script installs an specific version for this library.
You can find more versions in the downloads page
Save the file press CTRL + O
and then to exit press CTRL + X
. Run the file:
Verify Installation
Test it is working
You should see a generated PDF file with the Google front page as content.
Docs
See complete WKHTMLTOPDF docs in the project page
Image Magick
Image magick is both a command line tool and a library to work and manipulate images.
What is this for?
When working with images in rails applications, you’re mostly going to work using carrierwave or paperclip gems. Whenever you need to crop or process images, you’re going to need to install image magick so those gems can do the processing.
How to install it?
Create a file $ nano install_image_magick.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
.
Run the installation script
NOTE: this is takes a lot of time to be completed. Be patient.
Docs
More documentation about image magick can be found the official site
Jenkins
Jenkins helps to automate the non-human part of the whole software development process, with now common things like continuous integration, but by further empowering teams to implement the technical part of a Continuous Delivery.
What is this for?
As an automation tool, Jenkins is widely(but not only) for continuous integration. However, with a Jenkins basic install you can also run test suites, generate documents, and even deploy software to servers.
Also, Jenkins is not limited to any specific software or programming language.
Regarding RoR apps(which are the reason of this handbook), using Jenkins you could:
- run test suite when commits are sent to repository(i.e, GitHub, Bitbucket)
- email specified developers in case tests break
- package source code to make it deployable
- with plugins or its bash integration, define deployment rules or commands
- run other tasks, email developers about deployment, and more.
How to install it?
Create a file nano install_jenkins.sh
and add the following content:
To save the file press CTRL + O
and then to exit press CTRL + X
nano. Now you can run it:
See more installation options in Jenkins docs.
Docs
See Jenkins documentation in the official web site.
Configure Tools
In this chapter we’ll see how to configure tools for doing backend tasks.
Let’s see how to do it with Vagrant and Jenkins. They’re both independent tools but once we learn how they work will find out they can work together.
Jenkins
Jenkins is an automation server that supports plugins to help in the process of building, deploying and/or automating software projects.
A Jenkins installation can be as complex as we want it to be. Has the main advantage of being free and open source and the disadvantage is that once we install it on a server, we have to take care of everything.
Initial Configuration
Once Jenkins is installed in your system(local, Vagrant, or web server) we need to follow some steps to complete the installation and then proceed to configure it.
In your browser go to http://localhost:8080
and wait for a Jenkins page to appear.
The page will indicate where to find the Jenkins initial admin password.
The file is located in /var/jenkins/secrets
, so the command is:
Paste the output in the input and click the Continue button at the end of the form.
Now your Jenkins is ready to be used!
Setup for the Cloud
To access Jenkins instance installed in a EC2 machine, for example, probably you’re going to need to configure a virtual host for the web server your EC2 server uses.
As I’m a fan of Nginx, here’s a config for nginx:
Replace CI.YOURCOMPANY.COM
for your server IP address or a valid subdomain.
Checkout other configs in this gist and this other.
Plugins
One of the most popular plugins in Jenkins is the GitHub one. Jenkins alone can’t do much so installing plugins is a most to take the best out of Jenkins.
For projects hosted on GitHub, installed the respective plugin. The docs provide instructions on how to install and configure.
One of the most important benefits of using this plugin is being able to trigger actions in Jenkins whenever a new commit is made to a given branch.
So one could setup a job to do a deployment whenever master
branch gets new commits. For this kind of setup you’d be better using a GitHub Personal Token.
Be mindful the token should have the scope
admin:org_hook
For Ruby on Rails
When using Jenkins or any other CI tool to run tests for Rails apps, there’s one thing we need to be aware:
The Jenkins server need everything your Rails app needs to run.
Let’s say your app in development needs the following list of dependencies:
- RVM
- Ruby
- Bundler
- A database: PostgreSQL or MySQL
- NodeJS
Then you’d have to install them all in the Jenkins server. Once they’re available you can run commands for the Rails app in the Jenkins instance:
These commands would go in a build step. If any of those commands fail, Jenkins will notify you and you should take actions to fix them.
This article shows how to do a setup that run tests and deploys using Capistrano. It’s a bit complex but can serve as an example.
Tips and Tricks
- Restart your Jenkins server from the a web browser with
JENKINSURL/safeRestart
- Force restart making running jobs to be lost with
JENKINSURL/restart
- In the server you can restart it with
sudo service jenkins restart
- Jenkins execution logs normally should live in
/var/log/jenkins
Docs
Vagrant
Vagrant is a tool for creating development environments with ease. Leveraging VirtualBox, you can use Vagrant to create unique and isolated development environments for almost everyting(I’ve tried PHP and RoR development environments).
What’s best about Vagrant is that you can make these environments repeatable and portable, meaning you configure it once, use it many more times.
In a simplified way, what Vagrant does is create a virtual machine(VM) with OS you indicate, sync the VM filesystem with its host filesystem, so that you can work as usual in your OS, i.e, Linux Mint, and run your application inside the virtual machine. This way, you can also keep your machine from being polluted with software related to an specific application.
Provisioning
Vagrant virtual machines can be provisioned with several tools:
- Shell Scripts
- Ansible
- Chef
- Salt
- Puppet
- even Docker(acting as a Docker host)
Docs
Brew World
In the Linux world, we normally have tools to install software. In Debian we have dpkg
, in Ubuntu distros it is apt-get
, in Redhat based distros it’s yum
.
All nice but when we are given a MacBook Pro to work on in web development we don’t have any of them. Also happens that in the Mac world many software can be installed via GUI but some don’t.
MacOS counts with MacPorts but in many articles you’ll found out that Homebrew is favoured over MacPorts. That’s why Homebrew have a section this handbook.
Homebrew
Homebrew is a package manager for the macOS operating system. It’s easy to install and use.
To install run this script in a terminal window:
To verify it installed run:
Install X with Brew
Let’s see how to install some popular software using Homebrew in MacOS.
RVM dependencies
The Ruby Version Manager need many dependencies to be installed. Usually, the command rvm requirements
would take care but when not you can install many of its dependencies as follows:
htop
htop is top
on stereoids. Definetely, a most for the lovers of termianl process viewers.
To install with Brew just:
PostgreSQL
The popular database can also be installed with Homebrew with just one command:
Redis
The popular datastore for caches and more can be installed pretty easily:
WKHTMLTOPDF
A powerful open-source software to generate PDF documents from HTML ones.
In MacOS can be installed using Homebrew:
jq
Not to be confused with the JavaScript library jQuery
. jq
is a software that simplifies handling JSON data in the command line.
It’s very powerful and in this gist you can see how I used to process some JSON from AWS Code Deploy service.
Pandoc
Not so DevOps related but one of the tools I used to build this ebook is Pandoc. A very powerful, cross-platform software to generate any kind of document from any kind of document.
In MacOS can be installed as follows:
Relevant Links
- Thoughts on macOS Package Managers
- Error installing any ruby version with RVM on OSX
- Getting Started with PostgreSQL on Mac OSX
- Install and config Redis on Mac OS X via Homebrew
- WKHTMLTOPDF
- jq
Additional Resources
In this section you can find hand-picked links about the DevOps culture, Linux, and related stuff. Any topic that is not fully explained will have a link here to a blog post, documentation or somewhere else you could have more detailed information.
Books
Some useful books to further advance your Linux or backend tools/languages knowledge. Most of them are for free.
Guides
More comprenhensive and/or explained guides might be placed here.
- Comprehensive Guide to AWS
- How to Write the Perfect Commit
- How to Set Up a Firewall with UFW on Ubuntu 14.04
- Automate EC2 Instance Setup with user-data Scripts
Links & Resources
Repos, tutorials, gists, etc.
Linux
Server
Others
CI = Continuous Integration CD = Continuous Delivery CD = Continuous Deployment
Concepts
Problems and Issues?
If you get any kind of error, have doubts or would like to suggest modifications, go to this handbook repo in GitHub and open an issue describing what you mean.