• Home
  • Case Study: Automating Web Server Provisioning and Application Deployment with Ansible and YAML

Case Study: Automating Web Server Provisioning and Application Deployment with Ansible and YAML

by:admin July 22, 2025 0 Comments

Organization: Tech Innovators Inc. (A mid-sized software development company)

Challenge: Tech Innovators Inc. faced significant challenges in managing their development, staging, and production environments. Their existing process for deploying new web applications and configuring web servers (Apache and Nginx) was highly manual, leading to:

  1. Inconsistency: Different team members would configure servers slightly differently, leading to “works on my machine” issues.
  2. Time-Consuming: Setting up a new server or deploying a new version of an application could take hours, involving many manual SSH commands, file edits, and service restarts.
  3. Error-Prone: Manual steps were prone to human error, leading to downtime or security vulnerabilities.
  4. Lack of Auditability: There was no easy way to track who made what changes to a server.
  5. Scalability Issues: As the number of applications and servers grew, the manual approach became unsustainable.

The “Before YAML” Scenario:

  • Server Setup: System administrators would SSH into new servers, manually install web servers, configure virtual hosts, set up firewall rules, and install required libraries.
  • Application Deployment: Developers would manually copy application files via SCP/SFTP, adjust configuration files on the server, and then manually restart services.
  • Troubleshooting: Debugging issues was difficult due to the lack of a consistent baseline for server configurations.

The “With YAML” Solution (Ansible Playbooks):

Tech Innovators Inc. decided to adopt Ansible for automation, leveraging its YAML-based playbooks to define their desired infrastructure state and application deployment workflows.

How YAML was Used:

They created several YAML playbooks, each designed for a specific purpose:

  1. webserver_setup.yml (Infrastructure Configuration):
---
- name: Configure Web Servers
  hosts: webservers # Defines the group of servers this playbook applies to
  become: yes       # Execute tasks with sudo/root privileges

  vars:
    apache_port: 80
    nginx_port: 8080

  tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: yes

    - name: Install Apache
      ansible.builtin.apt:
        name: apache2
        state: present
      when: inventory_hostname.startswith('dev-') # Conditional for dev environment

    - name: Install Nginx
      ansible.builtin.apt:
        name: nginx
        state: present
      when: inventory_hostname.startswith('prod-') # Conditional for prod environment

    - name: Configure Apache Virtual Host
      ansible.builtin.template:
        src: templates/apache_vhost.conf.j2
        dest: /etc/apache2/sites-available/{{ app_name }}.conf
      notify: Restart Apache
      when: inventory_hostname.startswith('dev-')

    - name: Configure Nginx Site
      ansible.builtin.template:
        src: templates/nginx_site.conf.j2
        dest: /etc/nginx/sites-available/{{ app_name }}
      notify: Restart Nginx
      when: inventory_hostname.startswith('prod-')

    - name: Ensure Apache is enabled and running
      ansible.builtin.service:
        name: apache2
        state: started
        enabled: yes
      when: inventory_hostname.startswith('dev-')

    - name: Ensure Nginx is enabled and running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: yes
      when: inventory_hostname.startswith('prod-')

  handlers:
    - name: Restart Apache
      ansible.builtin.service:
        name: apache2
        state: restarted
    - name: Restart Nginx
      ansible.builtin.service:
        name: nginx
        state: restarted
  • YAML Constructs:
  • ---: Document start indicator.
  • name:: Human-readable description of the playbook/task.
  • hosts:: Specifies the target servers (from the Ansible inventory, also a YAML file).
  • become: yes: Elevates privileges for command execution.
  • vars:: Defines variables used within the playbook.
  • tasks:: A list of operations to be performed. Each task is a dictionary.
  • ansible.builtin.apt:: Ansible modules (e.g., apt, template, service) are invoked.
  • state: present: Idempotent declaration – ensures the package is installed.
  • when:: Conditional logic based on host properties or variables.
  • notify:: Triggers handlers if a task makes changes.
  • handlers:: Tasks that only run when notified (e.g., for service restarts).

2. app_deploy.yml (Application Deployment):

---
- name: Deploy Web Application
  hosts: webservers
  become: yes

  vars:
    app_name: mywebapp
    repo_url: https://github.com/techinnovators/mywebapp.git
    deploy_path: /var/www/{{ app_name }}

  tasks:
    - name: Ensure deployment directory exists
      ansible.builtin.file:
        path: "{{ deploy_path }}"
        state: directory
        mode: '0755'

    - name: Clone application repository
      ansible.builtin.git:
        repo: "{{ repo_url }}"
        dest: "{{ deploy_path }}"
        version: main # Or a specific tag/branch
        force: yes    # Force update if already exists

    - name: Install application dependencies (Node.js example)
      ansible.builtin.community.general.npm: # Example for Node.js
        path: "{{ deploy_path }}"
        state: present
      when: app_name == 'mywebapp' # Only run for this specific app

    - name: Copy application configuration
      ansible.builtin.template:
        src: templates/app_config.j2
        dest: "{{ deploy_path }}/config.js"

    - name: Ensure web server service is restarted after deployment
      ansible.builtin.service:
        name: apache2 # Or nginx based on host
        state: restarted
      listen: "Restart Webserver" # Triggered by specific tasks if needed

This playbook leverages similar YAML constructs to define the steps for pulling code, installing dependencies, configuring the application, and restarting the web server.

Benefits Achieved by Using YAML (and Ansible):

  1. Automation and Speed: Tasks that took hours manually now execute in minutes by simply running ansible-playbook webserver_setup.yml or ansible-playbook app_deploy.yml.
  2. Consistency and Reliability: Every server is configured identically, eliminating “configuration drift” and ensuring applications behave predictably across environments.
  3. Idempotence: Running the same playbook multiple times has the same outcome. If a package is already installed, Ansible won’t try to install it again, saving time and preventing errors.
  4. Human Readability: The YAML syntax made playbooks easy for the Ops and Dev teams to understand, even for those new to automation. The logical flow (tasks, variables, handlers) was intuitive.
  5. Version Control: All playbooks were stored in Git, allowing for:
    • Change Tracking: Every modification was logged, showing who changed what and when.
    • Rollbacks: Easily revert to a previous working state if a new deployment caused issues.
    • Collaboration: Developers and Ops could collaborate on automation scripts.
  6. Reduced Errors: Manual human errors were drastically reduced, leading to fewer outages and more stable environments.
  7. Scalability: As the company grew and added more servers, automating with YAML playbooks meant they could scale their operations without proportionally increasing manual effort.
  8. Knowledge Transfer: The playbooks served as living documentation of their infrastructure and application deployment processes, making onboarding new team members much smoother.

Conclusion:

This case study demonstrates how YAML, when used with a powerful automation tool like Ansible, transforms chaotic manual processes into efficient, consistent, and reliable automated workflows. Its clear, human-readable structure makes it an ideal choice for defining the desired state of systems and orchestrating complex IT operations.

Categories:

Leave Comment