Operations 29 min read

How Ansible Can Deploy 100 Servers in 10 Minutes: A Hands‑On Guide

This article explains why Ansible is the preferred automation tool, outlines its core advantages and architecture, and provides a step‑by‑step, code‑rich tutorial—from installing the control node and configuring SSH keys to writing inventories, ad‑hoc commands, Playbooks, Roles, and a real‑world 100‑server deployment case—showing how to achieve massive scaling with minimal effort.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
How Ansible Can Deploy 100 Servers in 10 Minutes: A Hands‑On Guide

Introduction

Can you deploy Nginx on 100 servers within an hour? The author answered this interview question by using Ansible to finish the job in ten minutes, demonstrating Ansible’s agent‑less, SSH‑based automation that can boost operational efficiency tenfold.

Why Ansible Is the Top Choice for Automation

Core Pain Points of Manual Operations

Low efficiency : manual per‑host configuration is slow and error‑prone.

Inconsistent configurations : leads to hard‑to‑debug differences.

Slow scaling : manual deployment cannot keep up with traffic spikes.

Difficult auditing : lack of change records makes troubleshooting hard.

High knowledge transfer cost : experience is hard to standardize.

Key Advantages of Ansible

Ansible, developed by Red Hat and open‑source, stands out among tools like Puppet, Chef, and SaltStack:

Agent‑less architecture : only SSH and Python are required on target machines, reducing intrusion and maintenance cost.

Easy to learn : YAML Playbooks read like natural language; most ops staff can start within a day.

Idempotent execution : repeated runs produce the same result, preventing drift.

Strong community : 8000+ built‑in modules, Ansible Galaxy roles, active open‑source support.

Ansible Architecture and Workflow

Control node (Ansible host)
  ↓
SSH to target host
  ↓
Push temporary Python script
  ↓
Execute task and return result
  ↓
Delete temporary file

Core components:

Inventory : defines managed hosts.

Playbook : describes task flow.

Module : individual operation units (yum, copy, service, etc.).

Role : reusable Playbook fragments.

Plugin : extends functionality.

Applicable Scenarios

Bulk server initialization

Application deployment and updates

Configuration file distribution

Scheduled jobs and health checks

Database backup and recovery

Security hardening and compliance

Disaster‑recovery drills

A large e‑commerce company used Ansible on 5,000+ servers, cutting release time from two hours to five minutes and reducing human error from 15% to under 1%.

Hands‑On: From Zero to Mastery

Step 1 – Environment Preparation and Installation

1. Install Ansible on the control node

Recommended Linux control node.

# CentOS/RHEL
sudo yum install -y epel-release
sudo yum install -y ansible

# Or via pip
sudo yum install -y python3-pip
sudo pip3 install ansible

# Verify
ansible --version

Similar commands are provided for Ubuntu/Debian (using the official PPA) and macOS (Homebrew or pip).

2. Configure SSH key authentication

#!/bin/bash
# setup_ssh_keys.sh – batch configure SSH keys
if [ ! -f ~/.ssh/id_rsa ]; then
  ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N ""
fi

SERVERS=("192.168.1.10" "192.168.1.11" "192.168.1.12")
SSH_USER="root"
SSH_PASS="your_password"

for server in "${SERVERS[@]}"; do
  echo "Configuring $server ..."
  sshpass -p "$SSH_PASS" ssh-copy-id -o StrictHostKeyChecking=no ${SSH_USER}@${server}
  if [ $? -eq 0 ]; then
    echo "✓ $server configured successfully"
  else
    echo "✗ $server configuration failed"
  fi
done
echo "SSH key configuration completed"

3. Create project directory structure

mkdir -p ~/ansible-project/{inventories,playbooks,roles,files,templates,vars}
cd ~/ansible-project
# inventories/ – host lists
# playbooks/ – .yml files
# roles/ – reusable components
# files/ – static files to copy
# templates/ – Jinja2 templates
# vars/ – variable files

Step 2 – Inventory Configuration

# inventories/hosts – static inventory example
web1 ansible_host=192.168.1.10 ansible_user=root

[webservers]
web1 ansible_host=192.168.1.10
web2 ansible_host=192.168.1.11
web3 ansible_host=192.168.1.12

[databases]
db1 ansible_host=192.168.1.20
db2 ansible_host=192.168.1.21

[production:children]
webservers
databases

[webservers:vars]
ansible_user=root
http_port=80
max_clients=200

[databases:vars]
ansible_user=root
mysql_port=3306

A dynamic inventory script (Python) is also shown for cloud environments.

Step 3 – Ad‑Hoc Commands

Examples include ping, shell commands, package installation, service management, file copying, user creation, and system information gathering, with common flags explained.

Step 4 – Playbook Development

Real‑World Example 1: Deploy Nginx to a Web Farm

# playbooks/deploy_nginx.yml
---
- name: Deploy Nginx to Web Servers
  hosts: webservers
  become: yes
  vars:
    nginx_port: 80
    nginx_user: www-data
    document_root: /var/www/html
  tasks:
    - name: Install EPEL repo (CentOS only)
      yum:
        name: epel-release
        state: present
      when: ansible_distribution == "CentOS"

    - name: Install Nginx
      yum:
        name: nginx
        state: present
      notify: restart nginx

    - name: Create web root directory
      file:
        path: "{{ document_root }}"
        state: directory
        owner: "{{ nginx_user }}"
        group: "{{ nginx_user }}"
        mode: '0755'

    - name: Deploy nginx.conf template
      template:
        src: ../templates/nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        mode: '0644'
      notify: reload nginx

    - name: Deploy index.html
      copy:
        content: |
          <!DOCTYPE html>
          <html>
          <head><title>Welcome to {{ ansible_hostname }}</title></head>
          <body>
            <h1>Server: {{ ansible_hostname }}</h1>
            <p>IP: {{ ansible_default_ipv4.address }}</p>
            <p>Deployed by Ansible at {{ ansible_date_time.iso8601 }}</p>
          </body>
          </html>
        dest: "{{ document_root }}/index.html"
        owner: "{{ nginx_user }}"
        mode: '0644'

    - name: Open firewall for HTTP
      firewalld:
        service: http
        permanent: yes
        state: enabled
        immediate: yes
      when: ansible_distribution == "CentOS"
      ignore_errors: yes

    - name: Start and enable Nginx
      systemd:
        name: nginx
        state: started
        enabled: yes

    - name: Wait for Nginx to be up
      wait_for:
        port: "{{ nginx_port }}"
        delay: 5
        timeout: 30

    - name: Verify Nginx response
      uri:
        url: "http://{{ ansible_default_ipv4.address }}"
        status_code: 200
      register: nginx_health

    - name: Show deployment result
      debug:
        msg: "✓ Nginx deployed – http://{{ ansible_default_ipv4.address }}"

  handlers:
    - name: restart nginx
      systemd:
        name: nginx
        state: restarted
    - name: reload nginx
      systemd:
        name: nginx
        state: reloaded

The corresponding nginx.conf.j2 template is provided.

Real‑World Example 2: Bulk Server Initialization

# playbooks/init_servers.yml
---
- name: Bulk Initialize Linux Servers
  hosts: all
  become: yes
  vars:
    timezone: "Asia/Shanghai"
    ntp_servers:
      - ntp.aliyun.com
      - ntp.tencent.com
    admin_users:
      - name: deploy
        ssh_key: "ssh-rsa AAAAB3... [email protected]"
    security_packages:
      - firewalld
      - fail2ban
  tasks:
    - name: Set hostname
      hostname:
        name: "{{ inventory_hostname }}"

    - name: Configure /etc/hosts
      lineinfile:
        path: /etc/hosts
        line: "{{ ansible_default_ipv4.address }} {{ inventory_hostname }}"
        state: present

    - name: Set timezone
      timezone:
        name: "{{ timezone }}"

    - name: Install basic utilities
      yum:
        name:
          - vim
          - git
          - wget
          - curl
          - net-tools
          - htop
          - iotop
          - sysstat
          - lsof
          - telnet
        state: present

    - name: Install and configure chrony for NTP
      block:
        - name: Install chrony
          yum:
            name: chrony
            state: present
        - name: Deploy chrony.conf template
          template:
            src: ../templates/chrony.conf.j2
            dest: /etc/chrony.conf
            backup: yes
          notify: restart chrony
        - name: Ensure chrony is running
          systemd:
            name: chronyd
            state: started
            enabled: yes

    - name: Tune kernel parameters
      sysctl:
        name: "{{ item.key }}"
        value: "{{ item.value }}"
        state: present
        reload: yes
      loop:
        - { key: 'net.ipv4.tcp_tw_reuse', value: '1' }
        - { key: 'net.ipv4.tcp_fin_timeout', value: '30' }
        - { key: 'net.ipv4.tcp_keepalive_time', value: '1200' }
        - { key: 'net.core.somaxconn', value: '65535' }
        - { key: 'vm.swappiness', value: '10' }
        - { key: 'fs.file-max', value: '655350' }

    - name: Create admin accounts
      user:
        name: "{{ item.name }}"
        shell: /bin/bash
        groups: wheel
        state: present
      loop: "{{ admin_users }}"

    - name: Deploy SSH public keys
      authorized_key:
        user: "{{ item.name }}"
        key: "{{ item.ssh_key }}"
        state: present
      loop: "{{ admin_users }}"

    - name: Harden SSH configuration
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "{{ item.regexp }}"
        line: "{{ item.line }}"
        state: present
      loop:
        - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
        - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
        - { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
        - { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
      notify: restart sshd

    - name: Open basic firewall ports (CentOS only)
      firewalld:
        service: "{{ item }}"
        permanent: yes
        state: enabled
        immediate: yes
      loop:
        - ssh
        - http
        - https
      when: ansible_distribution == "CentOS"

    - name: Disable SELinux (optional)
      selinux:
        state: disabled
      when: ansible_distribution == "CentOS"

    - name: Clean system caches
      shell: |
        yum clean all
        rm -rf /tmp/* /var/tmp/*
      args:
        warn: false

    - name: System update (optional)
      yum:
        name: '*'
        state: latest
        exclude: kernel*
      when: update_system | default(false)

  handlers:
    - name: restart chrony
      systemd:
        name: chronyd
        state: restarted
    - name: restart sshd
      systemd:
        name: sshd
        state: restarted

Real‑World Example 3: Deploy Docker Environment

# playbooks/install_docker.yml
---
- name: Bulk Install Docker
  hosts: all
  become: yes
  vars:
    docker_version: "20.10.24"
    docker_compose_version: "2.20.0"
    docker_registry_mirrors:
      - "https://mirror.ccs.tencentyun.com"
      - "https://registry.docker-cn.com"
  tasks:
    - name: Remove old Docker packages
      yum:
        name:
          - docker
          - docker-client
          - docker-client-latest
          - docker-common
          - docker-latest
          - docker-engine
        state: absent

    - name: Install prerequisite packages
      yum:
        name:
          - yum-utils
          - device-mapper-persistent-data
          - lvm2
        state: present

    - name: Add Docker repository
      yum_repository:
        name: docker-ce
        description: Docker CE Repository
        baseurl: https://mirrors.aliyun.com/docker-ce/linux/centos/$releasever/$basearch/stable
        gpgcheck: yes
        gpgkey: https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
        enabled: yes

    - name: Install Docker packages
      yum:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
        state: present

    - name: Create /etc/docker directory
      file:
        path: /etc/docker
        state: directory
        mode: '0755'

    - name: Configure daemon.json
      copy:
        content: |
          {
            "registry-mirrors": {{ docker_registry_mirrors | to_json }},
            "log-driver": "json-file",
            "log-opts": {"max-size": "100m", "max-file": "3"},
            "storage-driver": "overlay2",
            "exec-opts": ["native.cgroupdriver=systemd"]
          }
        dest: /etc/docker/daemon.json
        mode: '0644'
      notify: restart docker

    - name: Start and enable Docker service
      systemd:
        name: docker
        state: started
        enabled: yes

    - name: Install docker‑compose binary
      get_url:
        url: "https://github.com/docker/compose/releases/download/v{{ docker_compose_version }}/docker-compose-linux-x86_64"
        dest: /usr/local/bin/docker-compose
        mode: '0755'

    - name: Verify Docker installation
      command: docker --version
      register: docker_version_output
      changed_when: false

    - name: Verify docker‑compose installation
      command: docker-compose --version
      register: compose_version_output
      changed_when: false

    - name: Show installation results
      debug:
        msg:
          - "Docker version: {{ docker_version_output.stdout }}"
          - "Docker Compose version: {{ compose_version_output.stdout }}"

    - name: Add admin user to docker group
      user:
        name: deploy
        groups: docker
        append: yes
      when: "'deploy' in ansible_facts.getent_passwd"

  handlers:
    - name: restart docker
      systemd:
        name: docker
        state: restarted

Step 5 – Role‑Based Management

Roles encapsulate reusable logic. Example: a MySQL role with defaults, tasks, handlers, templates, and variables is shown, followed by a Playbook that applies the role to a database host group.

Case Study: Deploy 100 Servers in Ten Minutes

Background

An e‑commerce company needed to scale 100 cloud servers before a major sale. Manual deployment would take days; using Ansible the whole process completed in under ten minutes.

Implementation Steps

Prepare inventory (1 min) : Use a cloud‑API script to generate an Ansible inventory with 100 IPs.

Batch configure SSH keys (2 min) : Distribute the control‑node public key to all hosts with an authorized_key ad‑hoc command.

Execute bulk deployment (7 min) : Run a Playbook that initializes the OS, installs Docker, and starts the application container, using a serial batch size of 20 to avoid network congestion.

Validate results (1 min) : Perform health‑check HTTP requests and ping all hosts to confirm 100 % success.

Key Optimizations

Concurrency control : Set serial: 20 in the Playbook and -f 50 on the command line.

Failure handling : Use max_fail_percentage: 10 and ignore_errors: yes on non‑critical tasks.

Performance tuning : Adjust forks = 50, enable SSH pipelining, and cache facts in ansible.cfg.

Results

Deployment time: 9 min 38 s (including init, software install, app rollout).

Success rate: 100 % (100/100).

Average per‑host time: 5.8 s.

Human effort: 1 operator.

Cost saving: >2.5 days of manual work avoided.

Best Practices and Advanced Tips

Organize Playbooks with include_tasks and import_playbook for modularity.

Understand variable precedence (CLI > Playbook vars > Role vars > Inventory host/group vars > Role defaults).

Encrypt secrets with ansible‑vault and store them in version control.

Use block‑rescue‑always for robust error handling and cleanup.

Develop custom modules (example check_port.py) when built‑in modules are insufficient.

Future Outlook

Adoption of Ansible AWX/Tower for web‑based orchestration.

Event‑driven Ansible for automated incident response.

Deep integration with Kubernetes, Terraform, and cloud‑native ecosystems.

AI‑assisted Playbook generation and self‑healing automation.

Mastering Ansible can increase operational efficiency by an order of magnitude; start replacing repetitive manual tasks with Ansible today for a more elegant and reliable workflow.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Configuration ManagementInfrastructureAnsiblePlaybook
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.