What is Ansible
Ansible is a powerful open-source automation platform that plays a key role in the world of configuration management, application deployment, and orchestration. It’s an essential tool in the platform engineering toolbox, enabling you to automate complex tasks with ease.
Ansible owes its popularity to its agentless architecture, human-readable YAML configuration files, and its ability to scale across thousands of nodes. These features make it a tool of choice to countless IT/DevOps organizations, looking for ways to efficiently automate their work processes and ensure consistency across their infrastructure.
In this tutorial, we'll cover the fundamentals of working with Ansible, from installation to creating your first playbook. In later sections, we'll dive into practical hands-on examples for some of the more advanced use cases and show why Ansible has become a go-to solution for many modern DevOps practices.
Video Guide
TLDR: You can find the main repo here.
Why Use Ansible
- Agentless architecture: Ansible operates without the need for agents on managed nodes, simplifying setup and minimizing maintenance efforts.
- Human-readable YAML: Ansible playbooks are written in YAML, making them easy to read, write, and understand, even for beginners.
- Wide platform support: Ansible works across various platforms, including Linux, Windows, cloud services, and networking devices.
- Scalability: Ansible can manage thousands of nodes, from small setups to large-scale infrastructures.
- Extensive Modules and Roles: Ansible offers a wide range of modules and roles for automating diverse tasks and easily reusing code.
- Idempotency: Ansible ensures that applying the same configuration multiple times doesn't change the system's state after the first successful run. This characteristic is a must for maintaining consistency across environments. I will show you this in action in the demo.
Ansible Fundamentals
Now let's explore how Ansible works by reviewing its core concepts, before diving deeper into how they function:
Architecture
Control node: A system on which Ansible is installed. You run Ansible commands such as ansible-inventory
on a control node.
Inventory: The inventory is created on the control node to define the hosts for Ansible to manage and deploy.
Managed node: A remote system, or host, that Ansible controls.
The Ansible Inventory File
- An inventory is a list of hosts/nodes with IP addresses or hostnames.
- The default location for inventory is /etc/ansible/hosts, but you can define a custom one in any directory.
- You can configure inventory parameters per host, such as host, user, and SSH connection parameters.
Modules
Ansible copies and runs code or binaries on each managed node as needed, to perform tasks specified in your playbooks. Each module is designed for a specific function, such as managing users on a database or configuring VLAN interfaces on network devices. You can call a single module in a task or use multiple modules within a playbook. Ansible organizes these modules into collections, making it easier to manage and use them for various automation tasks.
Plugins
Ansible plugins are pieces of code that expand the core functionality of Ansible. There are plenty of handy plugins, and you can write your own plugins as well. You can find more details about Ansible plugins here.
Playbooks
- Playbooks are the core execution units in Ansible, consisting of "Plays" that define which managed nodes (hosts) execute specific tasks.
- Ansible Playbooks are a way of sending commands to remote systems through scripts.
- Ansible playbooks configure complex system environments, enhancing flexibility by allowing scripts to be executed across multiple systems.
- Playbooks are written in YAML format and outline the tasks to be performed by Ansible.
Plays
Plays map managed nodes to tasks, containing variables, roles, and a sequence of tasks. They define how to iterate over these tasks for each host.
Roles
Roles bundle reusable content like tasks, handlers, and variables for use within a Play.
Tasks
Tasks specify actions to execute on managed hosts. They can be run individually using ad hoc commands.
Handlers
Handlers are special tasks triggered only when notified by a previous task that has made a change.
Working with Ansible Roles
Ansible roles are a powerful way to organize and manage your playbooks, making your automation more modular, reusable, and maintainable.
Using roles, you can group tasks, variables, files, and handlers into separate, self-contained units. This allows you to share functionality across different teams, environments, or projects, helping to keep your Infrastructure as Code (IaC) clean, organized, and DRY (Don't Repeat Yourself).
Roles simplify playbook management and take automation to the next level of abstraction. Instead of cluttering a single playbook with all the details, you can break down your tasks into roles and then call these roles from your playbook, ensuring a streamlined and scalable approach to automation.
While working with Ansible roles can greatly enhance your automation efforts, it is beyond the scope of this particular blog post. I will explore Ansible roles in more detail in a future article.
Managing Your Ansible Server
- The machine where Ansible is installed and from which all tasks and playbooks are run is sometimes called the Ansible server or the Ansible Control Node.
Ansible executes modules on the managed nodes, which are invoked by tasks.
This server would typically be a runner in your CI/CD pipeline.
How to Install Ansible
- Ansible can be installed on various operating systems. You can install Ansible using the command line with the package manager of your operating system. You can find more details in the official installation guide.
- To keep things simple, as you follow along with this Ansible tutorial, you can easily start a GitHub Codespace with Ansible installed from this article's repo.
Ansible Tutorial Demo
In this demo tutorial, we'll explore how to automate the setup of a Jenkins CI/CD environment on an EC2 instance using Terraform for provisioning and Ansible for configuration. Additionally, we'll leverage Docker to encapsulate Jenkins in a container, ensuring a portable and isolated environment.
Below is a diagram of what you will build:
Tools Overview
Before diving into the tutorial, let's briefly overview the tools we will be using:
- Terraform: You'll use Terraform to automate the creation of your EC2 instance and related networking resources. We won't spend too much time explaining Terraform since our focus is on Ansible.
- Ansible: Ansible will install Docker, configure the Jenkins environment, and run Jenkins inside a Docker container on the EC2 instance.
- Docker: You will use Docker to run Jenkins as a container on your EC2 instance.
Step 1: Provision Infrastructure with Terraform
The first step is to create the infrastructure needed for our Jenkins environment using Terraform.
Navigate to the Terraform directory: Start by navigating to the directory where your Terraform configuration files are located.
cd Terraform
Initialize Terraform: Initialize your Terraform environment to download the necessary provider plugins and prepare your working directory.
terraform init
Apply the Terraform configuration: Apply the Terraform configuration to create your AWS infrastructure. The -auto-approve
flag will bypass manual approval for the changes. Be careful using this flag in production environments.
terraform apply -auto-approve
Terraform will create the following resources:
- A VPC (Virtual Private Cloud) with subnets
- Security groups to control access to your EC2 instance
- An EC2 instance where Jenkins will be installed
- An Elastic IP for public access to the instance
After the process is completed, Terraform will output critical information, such as the public IP address of the EC2 instance and a private SSH key for secure access.
Step 2: Prepare Ansible Inventory and SSH Key
With the EC2 instance created, we need to prepare the Ansible inventory file, which Ansible uses to know which hosts to manage.
Additionally, we’ll prepare the SSH key that Ansible will use to authenticate with the EC2 instance.
Update the Ansible Inventory file: Replace the placeholder in the Ansible inventory file with the actual public IP address of your EC2 instance. This can be done using a simple sed
command.
sed -i "s|<placeholder_app>|$(terraform output -raw public_ip)|g" ../Ansible/inventory
After updating, verify the contents of the inventory file to ensure that the IP address has been correctly inserted.
cat ../Ansible/inventory
Extract and prepare the SSH key: Extract the private key generated by Terraform and save it to a file that Ansible can use to connect to the EC2 instance.
terraform output -raw private_key > ../Ansible/myKey.pem
Set the correct permissions on the private key file to secure it.
chmod 400 ../Ansible/myKey.pem
Step 3: Configure the EC2 instance with Ansible
Now that the infrastructure is in place and the inventory and SSH key are prepared, you can move on to configuring the EC2 instance using Ansible.
Navigate to the Ansible directory: Change your directory to where the Ansible playbook is located.
cd ../Ansible
Run the Ansible playbook: Execute the Ansible playbook to configure the EC2 instance. This playbook will install Docker, set up the Jenkins container, and ensure that everything is running correctly.
ansible-playbook --private-key myKey.pem -i inventory playbook.yaml
The playbook playbook.yaml file consists of several tasks that automate the following steps, more details will be provided in a later section:
- Install pip3 and unzip: These are required for installing Python packages and handling compressed files.
- Add Docker GPG apt Key: Adds the GPG key for the official Docker repository.
- Add Docker Repository: Configures the Docker repository in your package manager.
- Install Docker: Installs the latest version of Docker CE (Community Edition).
- Install Docker Module for Python: Installs the Docker module for Python, enabling Ansible to manage Docker containers.
- Pull Jenkins Docker Image: Pulls a pre-built Jenkins Docker image from Docker Hub.
- Set Permissions: Ensures the correct ownership and permissions for Jenkins data directories.
- Create and Start Jenkins Container: Creates and starts the Jenkins container, mapping necessary ports and volumes.
Step 4: Verify Jenkins Setup
Once the Ansible playbook has completed its execution, Jenkins should be up and running on your EC2 instance. Let’s verify that everything is set up correctly.
Access Jenkins: Open a web browser and navigate to the public IP address of your EC2 instance using port 8080.
http://public_ip:8080
This will bring up the Jenkins login page:
Retrieve Jenkins Admin password: The initial Jenkins admin password is stored on the EC2 instance. SSH into the instance to retrieve it as shown below, replacing ‘public_ip’ with your public IP.
ssh -i myKey.pem ubuntu@public_ip
Get the password:
cat /home/ubuntu/jenkins_data/secrets/initialAdminPassword
Copy this password and use it to log into Jenkins.
Complete Jenkins setup: After logging in, follow the Jenkins setup wizard to install recommended plugins or skip the plugin installation to expedite the process. Once completed, your Jenkins instance will be ready to use.
Inventory File and the Ansible Playbook: A Deeper Dive
The Inventory File
- To set up your Ansible environment, you need to create an inventory file that stores client details, such as hostnames or IP addresses and SSH ports.
- You can use SSH keys to connect to clients and simplify the process.
Ansible uses the inventory file to manage remote machines and network devices.Here is the inventory file that you used in the demo with comments:
# Define a group 'all' which includes all child groups defined in the inventory.
# In this case, the child group is 'jenkins'.
[all:children]
jenkins # This means the 'jenkins' group is a child of the 'all' group.
# Specify variables that apply to all hosts under the 'all' group.
# These variables are inherited by any host under 'jenkins' (or any other child group of 'all').
[all:vars]
ansible_user=ubuntu # The user to connect as on the remote hosts.
ansible_python_interpreter=/usr/bin/python3 # Python interpreter on the remote hosts.
# Define the 'jenkins' group which contains the Jenkins VM.
[jenkins]
jenkinsvm ansible_host=54.167.63.1 # The 'jenkinsvm' host with its corresponding IP address.
Playbooks vs. Ad Hoc Commands
You can use Ansible ad hoc commands to issue some commands on one or several servers. They are useful for tasks you rarely repeat. Therefore, you will mostly resort to using Playbooks.
To write your playbook, you need to define the desired state of a system using Ansible playbook commands. Below is the playbook.yaml with comments.
---
# This playbook will be executed on all hosts defined in the inventory.
- hosts: all
become_user: root # Run all tasks as the root user.
become: true # Enable privilege escalation (sudo) for all tasks.
tasks:
- name: Install pip3 and unzip
apt:
update_cache: yes # Update the package index before installing.
pkg:
- python3-pip # Install the Python3 package installer.
- unzip # Install the unzip utility.
register: result # Register the result of the task to a variable named 'result'.
until: result is not failed # Retry until the task succeeds.
retries: 5 # Retry up to 5 times.
delay: 5 # Wait 5 seconds between retries.
- name: Add Docker GPG apt Key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg # URL for Docker's official GPG key.
state: present # Ensure the GPG key is added.
- name: Add Docker Repository
apt_repository:
repo: deb https://download.docker.com/linux/ubuntu focal stable # Add the Docker repository for Ubuntu Focal (20.04).
state: present # Ensure the repository is present.
- name: Update apt and install docker-ce
apt:
name: docker-ce # Install the latest version of Docker Community Edition (CE).
state: latest # Ensure the latest version of Docker CE is installed.
update_cache: true # Update the package index before installing.
- name: Install Docker module for Python
pip:
name: docker # Install the Docker module for Python using pip.
- name: Pull the Jenkins Docker image
docker_image:
name: "samgabrail/jenkins-tf-vault-ansible:latest" # Specify the Jenkins Docker image to pull.
source: pull # Ensure the image is pulled from the repository.
state: present # Ensure the image is present.
force_source: yes # Force a fresh pull of the image, even if it already exists.
- name: Change file ownership, group and permissions
file:
path: /home/ubuntu/jenkins_data # Path to the Jenkins data directory on the host.
state: directory # Ensure this path is a directory.
recurse: yes # Apply the permissions recursively to all files and subdirectories.
owner: ubuntu # Set the owner of the directory to the 'ubuntu' user.
group: ubuntu # Set the group of the directory to 'ubuntu'.
- name: Create Jenkins container
docker_container:
name: "jenkins" # Name of the Docker container to be created.
image: "samgabrail/jenkins-tf-vault-ansible:latest" # Use the Jenkins Docker image pulled earlier.
state: started # Ensure the container is started and running.
ports:
- "8080:8080" # Map port 8080 on the host to port 8080 in the container (Jenkins UI).
- "50000:50000" # Map port 50000 on the host to port 50000 in the container (Jenkins agent).
volumes:
- /home/ubuntu/jenkins_data:/var/jenkins_home # Mount the Jenkins data directory on the host to the appropriate path in the container.
Integrating Ansible with env0
Integrating Ansible with env0 offers an efficient way to automate infrastructure management, leveraging the power of templates to streamline the process.
Instead of running commands manually through the CLI, you can create and manage environments directly within env0, simplifying the entire deployment process.
To demonstrate this, let's walk through how you can use env0 to replicate the same setup we previously configured using the CLI. The first step is to create a project within env0. Let's call it “Ansible Tutorial”.
You can define a new environment within this project based on a custom Ansible template. This template includes the necessary configurations, such as the Ansible version (which can be set to a specific version or left as the latest) and the SSH key.
The SSH key used here corresponds to the private key generated earlier with Terraform, ensuring secure access to your EC2 instance.
The next step involves linking this environment to your GitHub repository, specifying the folder where your Ansible playbook resides. This ensures that env0 knows where to find the necessary scripts and files to execute the automation.
You’ll also need to define environment variables, such as ‘ANSIBLE_CLI_inventory’, which tells Ansible the name of the inventory file to use. With these settings in place, you're ready to create and run your environment.
Once you initiate the run, env0 takes over, cloning the repository, setting up the working directory, loading variables, and executing the playbook. Since the environment setup was already handled via the CLI, env0 will verify that all configurations are correct.
After approving the run, env0 executes the playbook, mirroring the steps we performed earlier. The process concludes with a successful deployment, and you can review the logs to confirm that everything has been configured correctly.
The above example showcases how env0 enhances the Ansible experience, automating repetitive tasks and providing additional benefits such as version control, collaboration features, a host of integration options, and governance features like RBAC and OPA policies.
These features make env0 an excellent tool for managing complex infrastructure deployments using any popular IaC framework or a combination of frameworks such as Terraform, OpenTofu, Pulumi, CloudFormation, and more.
To learn more about how env0’s platform can help with governance and automation check out this guide: The Four Stages of Infrastructure as Code (IaC) Automation.
Conclusion
This guide highlighted the power of the Ansible Automation Platform in configuration management and application deployment through its agentless architecture and straightforward YAML playbooks.
In this blog, we focused on setting up a Jenkins CI/CD environment on AWS, where Ansible automated tasks like installing Docker and managing containers, ensuring consistent and efficient infrastructure setup.
While Terraform was briefly used to provision the AWS resources, Ansible played the central role in configuring the environment, showcasing its ability to streamline complex tasks with minimal manual input.
Finally, we also touched on integrating Ansible with env0, which further enhances automation by providing version control and environment management tools. Together, Ansible and Terraform offer a comprehensive solution for efficient infrastructure automation.
Frequently Asked Questions
Q: What is the difference between Ansible and Jenkins?
Ansible is an automation tool primarily for configuration management, whereas Jenkins is a CI/CD tool focused on automating software builds, tests, and deployments. They serve different purposes but can complement each other. Here’s a quick comparison:
Q: How do I create a directory in Ansible?
To create a directory in Ansible, use the ‘file’ module with state: directory
. Example:
- name: Create a directory
file:
path: /path/to/directory
state: directory
mode: '0755'
Q: Can Ansible be used with Windows?
Yes, Ansible can manage Windows servers. You can use modules like ‘win_shell’, and others specifically designed for Windows. You need to set up WinRM on your Windows machines for Ansible to communicate with them. Check out the docs: Using Ansible and Windows
Q: How does Ansible compare to Terraform?
Ansible and Terraform are better together. Terraform is ideal for provisioning infrastructure, while Ansible excels at configuring that infrastructure once it's up. Both tools allow you to automate your entire environment from start to finish. You can learn more here: Ansible vs Terraform: Choose One or Use Both?
Q: How do I start learning Ansible?
Start by learning the basics of YAML and command-line operations. Explore Ansible’s documentation and try hands-on labs to create simple playbooks and roles. This blog post is a great starting point!
Q: Is Ansible easy to learn?
Yes, Ansible is considered easy to learn, especially for those familiar with basic system administration. Its use of YAML for playbooks makes it accessible for beginners.
Q: How long does it take to learn Ansible?
It depends on your background, but you can learn the basics in a few days. Mastering Ansible for complex environments may take a few weeks to a few months, depending on the depth of your learning and practice.
Q: Should I learn Python or Ansible?
It depends on your goals. If you want to automate infrastructure tasks, Ansible is the way to go. If you're interested in more general-purpose programming and scripting, Python is essential. Knowing both can be extremely powerful as Ansible is written in Python, and you can extend Ansible with Python scripts.
Top comments (0)