Rails Deployment using Ansible — DevOps Project 04

Bhavyansh @ DiversePixel
8 min readJun 17, 2024

--

Github repo here. The ansible playbooks here.

We will be deploying Tattle in this one

Know the project: Tattle is a real time chat application built with rails as a monolith

We will be deploying tattle on Amazon EC2 instance using Ansible controller in another EC2 instance.

For deploying rails using capistrano, we will be following commands from this guide by GoRails:

Let’s begin!

First we will create security groups and key pair for our two instances (control and target) then we will spin up our control instance from which we will command the target instance to deploy tattle.

SG for ansible instance:

SG for tattle instance:

  • SSH from MY IP and from tattle-control-sg
  • Port 80 from MY IP

Tags are a must.

Create Key pair for tattle

Launch the instances

Now we will log in to the control instance and write ansible configuration for deploying tattle.

Based on AMI, install Ansible:
https://docs.ansible.com/ansible/latest/installation_guide/installation_distros.html

First inventory, which provide information about the targets

then playbook, with first task being: we want to be able to SSH as a new user: deploy

Get the key on tattle-control and check connection using ping module

ansible tattle_prod -m ping -i inventory (instead of tattle_prod, you can use '*' or regex)

It shouldn’t be asking if we’re sure and want to continue connecting, so we will change the file

cat /etc/ansible/ansible.cfg

Take a backup, then run the command as root, in /etc/ansible/

Now we can either make changes here directly, or create an ansible.cfg file in pwd, let’s not change the default and create a file named ansible.cfg in pwd.

Inventory setup complete, configuration setup complete, now we will create playbook, again, name of playbook can be anything.

Run the playbook

Now let’s login to our target instance and test if we succeeded

Great going, now we can start to write our ansible playbook.

Step 1: We will create a ‘deploy’ user and add it to sudo group

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html

To generate the hash:

It will give you the hash that can be used in place of the hashed value.

Run the playbook to test user creation

User deploy gets created, with our password supplied

Step 2: Copy .ssh folder

The next step is to copy .ssh folder from /home/ubuntu/ to /home/deploy/ so that we can ssh in our app using the key as deploy user.

We can login from our machine as user deploy

Next we write plays for installing ruby on our target

Prereqs:

After writing the playbooks and deploying code from dev machine

We receive the mail, we login

Testing redis (real-time chat feature in our app):

Database persistence? Let’s see:

All working great!

Questions, suggestions always welcome.

Connect with me on X @ bhavyansh001, I am learning and building in public!

Some raw of mine notes below which you can totally ignore, official documentation is KING.

Ansible — Decoding DevOps

Ansible, a powerful automation engine, has become a staple in the DevOps toolbox. It simplifies and streamlines repetitive tasks, making infrastructure management, application deployment, and configuration changes a breeze. In this comprehensive guide, we’ll explore the world of Ansible, diving deep into its functionalities, key components, and practical examples.

Understanding Ansible’s Core Principles

At its heart, Ansible follows a simple yet effective philosophy:

  • Agentless: Unlike other automation tools, Ansible doesn’t require any agents to be installed on the target machines. It connects using SSH or other secure protocols, making it lightweight and easy to implement.
  • YAML-based: Ansible uses YAML, a human-readable format, to define playbooks, which are the blueprint of your automation tasks. This ensures clear and concise configuration.
  • Idempotency: Ansible guarantees idempotency, meaning a playbook can be run multiple times without causing unintended changes. This ensures consistency and predictability.

Key Components of the Ansible Ecosystem

Let’s explore the essential building blocks of Ansible:

  • Inventory: This is a central file containing information about your managed nodes. It’s the foundation of your automation, specifying which machines Ansible should connect to.

Example inventory file (hosts):

[webservers]
server1 ansible_host=192.168.1.100
server2 ansible_host=192.168.1.101
[dbservers]
dbserver ansible_host=192.168.1.200
  • Playbooks: These are the heart of Ansible. They consist of tasks, roles, and variables that define the desired state of your infrastructure. Each playbook aims to achieve a specific goal, such as deploying a web application or configuring a database.

Example playbook (deploy_web_app.yml):

---
- name: Deploy web application
hosts: webservers
become: yes
  tasks:
- name: Install Apache
apt:
name: apache2
state: present
- name: Copy web application files
copy:
content: |
<html>
<body>
<h1>Welcome to my web app!</h1>
</body>
</html>
dest: /var/www/html/index.html
  • Modules: Ansible modules are pre-built scripts that perform specific actions, like installing software packages, managing users, or restarting services. You can find a vast library of modules for various tasks.

Example module usage (install_postgres.yml):

---
- name: Install PostgreSQL
hosts: dbservers
become: yes
  tasks:
- name: Install PostgreSQL
apt:
name: postgresql
state: present
  • Roles: Roles are collections of modules and variables organized for specific functionalities. They allow you to modularize your playbooks, making them more reusable and maintainable.

Example role (roles/postgres/tasks/main.yml):

---
- name: Install PostgreSQL
apt:
name: postgresql
state: present
- name: Configure PostgreSQL
template:
src: templates/pg_hba.conf.j2
dest: /etc/postgresql/12/main/pg_hba.conf
mode: '0644'
  • Variables: Variables store dynamic information that can be used within playbooks. They allow for customization and flexibility, enabling you to adapt your automation to different environments.

Example variable usage (group_vars/all.yml):

---
db_username: mydbuser
db_password: mydbpass

Provisioning Servers: The Ansible Way

Ansible excels at provisioning servers, configuring them to meet your specific needs.

Configuration Files: Ansible can manipulate and manage configuration files, ensuring consistency across your infrastructure.

Example configuration file management (config_files.yml):

---
- name: Manage configuration files
hosts: all
become: yes
  tasks:
- name: Copy configuration file
copy:
content: |
# My configuration file
foo = bar
dest: /etc/myapp/config

Ansible: More than Just Server Management

Ansible’s capabilities extend beyond server provisioning. It can be used for:

  • Orchestration: Coordinate the execution of complex tasks across multiple servers, ensuring smooth and efficient deployments.
  • Cloud Automation: Automate provisioning and configuration of cloud infrastructure services like AWS, Azure, and Google Cloud Platform.
  • Security Hardening: Implement security best practices, such as password management, user access control, and firewall configuration.
  • Application Deployment: Automate the deployment of applications, configuring dependencies and ensuring proper installation.

The Power of Ad-hoc Commands

Beyond playbooks, Ansible offers a powerful command-line interface for quick and ad-hoc tasks. These commands execute quickly and are ideal for simple one-time actions.

Example ad-hoc command:

ansible -m ping -i hosts all

The Importance of Documentation

Effective documentation is paramount for any automation solution. Ansible encourages the use of comments within playbooks and detailed documentation for modules, roles, and variables.

Mastering Ansible: A Step-by-Step Guide

Let’s delve into a practical example of how to use Ansible to provision a web server:

  1. Setup: Install Ansible on your control machine.
sudo apt-get install ansible
  1. Inventory: Create an inventory file with the IP addresses or hostnames of your web servers.
[webservers]
server1 ansible_host=192.168.1.100
server2 ansible_host=192.168.1.101
  1. Playbook: Write a playbook to install necessary packages, configure web server settings, and deploy your application.
---
- name: Deploy web application
hosts: webservers
become: yes
  tasks:
- name: Install Apache
apt:
name: apache2
state: present
- name: Copy web application files
copy:
content: |
<html>
<body>
<h1>Welcome to my web app!</h1>
</body>
</html>
dest: /var/www/html/index.html

Execution: Run the playbook using the ansible-playbook command.

ansible-playbook -i hosts deploy_web_app.yml

Variable definition in Ansible:

  1. In playbook
vars:
http_port: 80

2. In inventory file

  • group_vars/all (least priority among these)
  • group_vars/group_name
  • host_vars/hostname (highest priority among these)

3. Roles: Include vars from files in playbook (vars_files)

Ansible fact vars (gets generated automatically from setup module):
gather_facts

  • ansible_os_family
  • ansible_processor_cores
  • ansible_devices
  • ansible_architecture
  • ansible_default_ipv4
  • ansible_kernel

debug module is used to print a message

register: dbout, will store json output in dbout variable, which can be printed usnig msg, var

dbout.name to access key (name) from dictionary (dbout)

-C for dry run

Conditionals & loops:

  • when
  • loop, used with variable “{{ item }}”

File and template:

  • copy
  • template- reads template, jinja2 templating, it is intelligent, we can define vars in group_vars/all file and then in template use jinja2 “{{…}}”
    folder templates: pg-conf_ubntu (name doesn’t matter)
    backup: yes
  • create folder using file module

Handlers:

Will be in dormant state, when there is requirement they will be notified, then they execute the task, can do more than just restart

restart only when a condition is met, we can use when also but it is better

in the task, in same column as task, handlers: and notify:

Roles:

In our playbook we have

  • Global declaration
  • Variables
  • Files
  • Templates (.j2)
  • Tasks
  • Handlers

We will use roles for segregation, used for reusability

Easier when you already have a working playbook

mkdir roles

cd roles

ansible-galaxy init post-install (name of role)

then in playbook we can give roles:

templates and copy module do not require templates/ and files/ to be specified it will be looked in the correct dirs in roles

defaults/main.yml has low priority than vars/main.yml, both are used for storing vars

write playbook first then convert to roles.

--

--

Bhavyansh @ DiversePixel
Bhavyansh @ DiversePixel

Written by Bhavyansh @ DiversePixel

Hey I write about Tech. Join me as I share my tech learnings and insights. 🚀

No responses yet