Ruby on Rails: building a highly available application

In this tutorial, we’re going to dig deeper into the deployment of a highly available and scalable RoR application. But first, let’s make a quick overview about the RESTful architecture with whom we’re gonna implement our app.

RESTful architecture:

RESTful architectures carry some architectural constraints that were inherited from various networked distributed architectural styles. Client and server in RESTful architectures are orthogonal, meaning there is a clear separation of concerns and functions between them. User logic is moved completely to the client side and server components are kept simple to improve scalability. This separation also allows the two components to be developed and evolve independently, so long as they respect the communication protocols and defined interfaces. In a RESTful architecture, each request is treated independently. This means the architecture of the server is stateless and requests cannot take advantage of any stored context and must carry all the necessary information to be understood and processed. Each response also needs to be labeled as cacheable or noncacheable. If a response is cacheable, the client can store and reuse the data for network efficiency (i.e., cache the data to reduce round-trips).

REST architectures interact with resources identified

MVC pattern:

Ruby on Rails is a Model-View-Controller (MVC) framework. MVC is a software architectural pattern that divides a given application into three interconnected logic blocks. In the three blocks, the internal representation of some information or object (the model) is separated from the way the user interacts with this information (the controller) and the way this information is presented to the user (the view).

MVC abstraction

Docker:

Start by setting up the files needed to build the app. The app will run inside a Docker container containing its dependencies. Defining dependencies is done using a file called Dockerfile. To begin with, the Dockerfile consists of:

Dockerfile

That’ll put your application code inside an image that builds a container with Ruby, Bundler and all your dependencies inside it.

Next, open the editor and create a Gemfile and Gemfile.lock. Keep the lock file empty and specify the rails version in the Gemfile. (ex: 5 or 6)

Gemfile

Next, provide an entrypoint script to fix a Rails-specific issue that prevents the server from restarting when a certain server.pid file pre-exists. This script will be executed every time the container gets started. entrypoint.sh consists of:

entrypoint.sh

Now you should be able to implement a docker-compose file. This file describes the services that comprise your app (a database and a web app), how to get each one’s Docker image (the database just runs on a pre-made PostgreSQL image, and the web app is built from the current directory), and the configuration needed to link them together and expose the web app’s port.

docker-compose.yml

Next, you’ll need to build the project and generate the rails app skeleton as specified within the Gemfile:

compose command

Now that you’ve got a new Gemfile, you need to build the image again. (This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild using the build command.)

build command

The app is now running but you’ll need to configure the database. By default, Rails expects a database to be running on localhost - so you need to point it at the db container instead. You also need to change the database and username to align with the defaults set by the postgres image.

database.yml

Finally, create the database:

db:create command

Now, we’re done with docker and docker compose. The next step will be orchestrating and making our application auto-scalable.

Kubernetes: Orchestration

The portability and reproducibility of a containerized process mean we have an opportunity to move and scale our containerized applications across clouds and datacenters. Containers effectively guarantee that those applications run the same way anywhere, allowing us to quickly and easily take advantage of all these environments.

All containers in Kubernetes are scheduled as pods, which are groups of co-located containers that share some resources. Furthermore, in a realistic application we almost never create individual pods; instead, most of our workloads are scheduled as deployments, which are scalable groups of pods maintained automatically by Kubernetes.

In this Kubernetes YAML file, we have two objects, separated by the ---:

  • A Deployment, describing a scalable group of identical pods. In this case, you’ll get just one replica, or copy of your pod, and that pod (which is described under the template: key) has just one container in it, based off of your apppweb image from the previous step in this tutorial.
  • A NodePort service, which will route traffic from port 30001 on your host to port 8080 inside the pods it routes to, allowing you to reach your bulletin board from the network.

Also, notice that while Kubernetes YAML can appear long and complicated at first, it almost always follows the same pattern:

  • The apiVersion, which indicates the Kubernetes API that parses this object
  • The kind indicating what sort of object this is
  • Some metadata applying things like names to your objects
  • The spec specifying all the parameters and configurations of your object.

Now, let’s deploy the application:

deployment command

Kubernetes: Scaling and Auto-scaling

Now let’s scale the application by replicating the database and the server each three times:

scale command

Check the pods, if running:

scaled pods

Check deployments:

deployments

To make the application more lucid, we can activate auto-scaling. The parameter that we’ll choose will be the CPU usage.

auto-scaling

Congratulation! you’ve created a scalable RoR application that is highly available.

Distributed storage: (amazon S3)

The first thing we need to do is to create a Bucket in S3. Then add user policy to grant access to files. Make sure to give the user the necessary permissions to have read/write access to the S3 Bucket. Next, update config/storage.yml Finally, add aws-sdk-s3 gem to your Gemfile and install the dependency.

Let’s build our app!

Our app will be, mainly, a blog where the user can authenticate and perform CRUD operation on article model. (for styling I do recommend bulma: it’s a nice css library and opensource)

Step 1: Create a Rails app within the container

Step 2: Generate the post model

Step 3: Generate controllers

Step 4: Configure routes

Step 5: Migrate the data

Step 6: Integrate the devise gem to handle authentication

For more information about the implementation, please consult my GitHub repository. I’ve pushed the whole application.

Thank you for your attention!

I’m a writer, a developer and an open source contributor