Setting Up a Master-Node Architecture with Ansible and SSH: A Step-by-Step Guide

Setting Up a Master-Node Architecture with Ansible and SSH: A Step-by-Step Guide
Photo by Laura Jaeger / Unsplash

Hello, tech enthusiasts! If you're looking to build a scalable, automated infrastructure—perhaps for a home lab, a small cluster, or even the foundation of a Kubernetes setup—Ansible is your best friend. In this blog post, I'll walk you through setting up a master node that can orchestrate multiple worker nodes using Ansible for configuration management. We'll focus on secure, passwordless SSH connections to make everything seamless.

This guide assumes you're working with Ubuntu Server machines (or similar Debian-based systems). The master node will handle Ansible playbooks, while the worker nodes will be configured to accept connections from the master. We'll use a handy bash script to automate the SSH key setup and inventory management.

Why This Architecture?

In a master-node setup, the master acts as the control plane, pushing configurations, updates, and commands to the nodes via Ansible. This is perfect for:

  • Automating deployments in a cluster.
  • Managing multiple servers without manual logins.
  • Scaling operations for DevOps workflows.

Prerequisites:

  • All machines running Ubuntu Server (or compatible).
  • Static IP addresses for nodes on your local network.
  • Basic terminal access to each machine.
  • Sudo privileges on all systems.

Let's dive in!

Step 1: Setting Up the Master Node

The master node needs Python 3, an SSH client, and Ansible installed. Run these commands in your master node's terminal.

Install Ansible
Update your package list and install:

sudo apt update && sudo apt install ansible -y && ansible --version

This will confirm Ansible is ready. You should see output like ansible [core 2.x.x] or similar, depending on your Ubuntu version.

Install SSH Client
If it's not already there:

ssh -V >/dev/null 2>&1 || sudo apt install openssh-client -y

Ensure Python 3 is Installed
Ansible relies on Python. Check and install if needed:

command -v python3 >/dev/null 2>&1 || (sudo apt update && sudo apt install python3 -y)

With that, your master is primed to manage the fleet!

Step 2: Preparing the Worker Nodes

Each worker node must allow SSH access from the master. Log into each node's terminal (physically or via initial SSH with password) and run these steps.

Start and Enable SSH Service

sudo systemctl start ssh
sudo systemctl enable ssh
sudo systemctl status ssh

The status command should show the service as active (running). If not, troubleshoot with journalctl -u ssh.

Install OpenSSH Server

sudo apt install openssh-server -y

Update and Upgrade the System
Keep things fresh:

sudo apt update && sudo apt upgrade -y

Repeat this for every node. Now, they're listening for connections.

Step 3: Connecting Nodes to the Master with Passwordless SSH

Back on the master node, we'll use a script to:

  • Generate an SSH key pair (if none exists).
  • Copy the public key to the node for passwordless access.
  • Add the node to an Ansible inventory file (playbooks/inventory.ini).

First, create the script in your repository's root directory. Name it setup_remote_ssh.sh and paste the following content:

#!/bin/bash

# Usage: ./setup_remote_ssh.sh user@hostname node_name
#
# Replace user@hostname with the actual remote system (e.g., ubuntu@example.com)
# node_name is the alias for this node in the Ansible inventory file.
#
# This script will create an SSH key pair if none exists in ~/.ssh/id_ed25519,
# then copy the public key to the remote system's authorized_keys file.
# You will be prompted for the remote user's password during the copy step.
# Finally, it will append the node to an Ansible inventory file (inventory.ini) in the current 
# directory, creating the file if it doesn't exist.

REMOTE=$1
NODE_NAME=$2

if [ -z "$REMOTE" ] || [ -z "$NODE_NAME" ]; then
    echo "Error: Please provide the remote system (e.g., user@hostname) and node name as arguments."
    exit 1
fi

# Parse user and host from REMOTE
if [[ $REMOTE == *@* ]]; then
    USER=${REMOTE%%@*}
    HOST=${REMOTE#*@}
else
    echo "Error: Remote must be in the format user@hostname."
    exit 1
fi

# Check if the private key exists; if not, generate a new key pair
if [ ! -f ~/.ssh/id_ed25519 ]; then
    echo "No SSH key found. Generating a new Ed25519 key pair..."
    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
    echo "Key pair generated successfully."
else
    echo "Existing SSH key found. Using ~/.ssh/id_ed25519.pub."
fi

# Copy the public key to the remote system
echo "Copying public key to $REMOTE..."
ssh-copy-id -i ~/.ssh/id_ed25519.pub $REMOTE

# Append to Ansible inventory
INVENTORY="./playbooks/inventory.ini"
if [ ! -f "$INVENTORY" ]; then
    echo "[all]" > "$INVENTORY"
fi
echo "$NODE_NAME ansible_host=$HOST ansible_user=$USER" >> "$INVENTORY"
echo "Added $NODE_NAME to $INVENTORY."

echo "Setup complete. You should now be able to SSH to $REMOTE without a password."
echo "To test if the connection is possible, run: ssh $REMOTE"

Make it executable:

chmod +x setup_remote_ssh.sh

Run it for each node:

./setup_remote_ssh.sh user@node_ip node_name
  • Replace user@node_ip with the node's credentials (e.g., ubuntu@192.168.1.100).
  • node_name is a friendly alias like worker1.

You'll be prompted for the node's password once during key copy. After that, SSH is passwordless!

Verifying the Setup

  • Test SSH: ssh user@node_ip (no password needed).
  • Check Ansible inventory: cat playbooks/inventory.ini. It should list your nodes under [all].
  • Ping all nodes with Ansible: ansible all -m ping -i playbooks/inventory.ini. Success means green lights!

Potential Pitfalls and Tips

  • Firewall Issues: Ensure UFW or firewalls allow SSH (port 22). Run sudo ufw allow ssh if needed.
  • Key Generation: We're using Ed25519 for modern security—stronger than RSA.
  • Scaling Up: Add more nodes by re-running the script. Ansible will handle the rest.
  • Security Note: Use strong passwords initially, and consider disabling password auth in /etc/ssh/sshd_config after setup for added security.
  • If you're on a non-local network, ensure nodes are accessible (VPN, port forwarding, etc.).

This setup is the bedrock for more advanced architectures. Next time, we could extend this to deploying Docker or Kubernetes. Have questions or run into issues? Drop a comment below!

Happy automating! 🚀