Ansible vs. Chef: A Comparison of Automation Tools

Ansible and Chef are popular automation tools used for configuration management, provisioning, and deployment of applications and infrastructure. They help system administrators and DevOps engineers automate repetitive tasks, manage complex environments, and ensure consistency across multiple servers.


Overview:

  • Agentless Architecture: Ansible operates without needing any software installed on the target machines (nodes). It uses SSH for communication.
  • Playbooks: Automation tasks are defined in YAML files called playbooks, which are easy to read and write.
  • Modules: Ansible has a vast collection of modules that perform specific tasks (e.g., installing packages, managing services).

Use Cases:

  • Automating application deployment.
  • Configuration management.
  • Orchestrating complex multi-tier workflows.
  • Provisioning cloud infrastructure.

Example Scenarios:

Objective: Install and configure Nginx on multiple servers.

Inventory File (hosts):

[webservers]
server1.example.com
server2.example.com

Playbook (deploy_webserver.yml):

---
- name: Deploy Nginx Web Server
  hosts: webservers
  become: yes
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
      when: ansible_os_family == "Debian"

    - name: Ensure Nginx is running
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Copy Website Content
      copy:
        src: /local/path/to/website/
        dest: /var/www/html/

Command to Execute:

ansible-playbook -i hosts deploy_webserver.yml

Objective: Create a user named deploy on all application servers.

Inventory File (hosts):

[appservers]
app1.example.com
app2.example.com

Playbook (manage_users.yml):

---
- name: Manage Users
  hosts: appservers
  become: yes
  tasks:
    - name: Ensure 'deploy' user exists
      user:
        name: deploy
        state: present
        groups: sudo

Objective: Update all packages on all servers.

Playbook (update_packages.yml):

---
- name: Update Packages
  hosts: all
  become: yes
  tasks:
    - name: Update apt cache and upgrade all packages
      apt:
        update_cache: yes
        upgrade: dist

Overview:

  • Client-Server Architecture: Chef uses a central server where configurations are stored, and clients pull configurations from it.
  • Recipes and Cookbooks: Configuration details are written in Ruby-based DSL in units called recipes, grouped into cookbooks.
  • Resources: Chef provides resources that describe a particular piece of the system (e.g., packages, services).

Use Cases:

  • Managing complex configurations.
  • Enforcing system policies.
  • Automating cloud provisioning.
  • Continuous deployment and delivery.

Example Scenarios:

Objective: Install and configure MySQL server.

Create a Cookbook (database):

chef generate cookbook database

Recipe (recipes/default.rb):

package 'mysql-server' do
  action :install
end

service 'mysql' do
  action [:enable, :start]
end

execute 'Set root password' do
  command "mysqladmin -u root password 'secure_password'"
  only_if "mysql -u root -e 'show databases;'"
end

Upload Cookbook to Chef Server:

knife cookbook upload database

Assign Cookbook to Node’s Run List:

knife node run_list add node_name 'recipe[database]'

Objective: Install and configure HAProxy as a load balancer.

Recipe (recipes/default.rb):

package 'haproxy' do
  action :install
end

template '/etc/haproxy/haproxy.cfg' do
  source 'haproxy.cfg.erb'
  notifies :restart, 'service[haproxy]'
end

service 'haproxy' do
  action [:enable, :start]
end

Template (templates/default/haproxy.cfg.erb):

global
    daemon
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend http-in
    bind *:80
    default_backend servers

backend servers
    balance roundrobin
    <% @nodes.each do |node| %>
    server <%= node['hostname'] %> <%= node['ipaddress'] %>:80 maxconn 32
    <% end %>

Objective: Ensure all servers have the correct timezone set.

Recipe (recipes/default.rb):

file '/etc/timezone' do
  content 'Etc/UTC'
  notifies :run, 'execute[dpkg-reconfigure tzdata]', :immediately
end

execute 'dpkg-reconfigure tzdata' do
  action :nothing
end

  • Language: Ansible uses YAML for playbooks, making it straightforward and readable. Chef uses Ruby DSL, which may have a steeper learning curve if you’re not familiar with Ruby.
  • Architecture: Ansible is agentless and uses push-based architecture. Chef requires an agent on each node and uses a pull-based model.
  • Learning Curve: Ansible is generally considered easier for beginners due to its simplicity.

For Ansible:

  1. Install Ansible:

    sudo apt update
    sudo apt install ansible
    
  2. Set Up SSH Access: Ensure you have SSH access to your nodes from the control machine.

  3. Create an Inventory File: Define your hosts and groups.

  4. Write Simple Playbooks: Start with basic tasks like installing packages or creating files.

  5. Run Playbooks: Use ansible-playbook command to execute your playbooks.

For Chef:

  1. Install Chef Development Kit (ChefDK):

    Download from Chef Downloads.

  2. Set Up Chef Server or Use Hosted Chef: You need a Chef server for storing cookbooks and managing nodes.

  3. Configure Workstation:

    • Install the knife command-line tool.
    • Set up your knife.rb configuration.
  4. Bootstrap Nodes: Install the Chef client on nodes using the knife bootstrap command.

  5. Create Cookbooks and Recipes: Use chef generate cookbook to start.

  6. Upload Cookbooks: Use knife cookbook upload to send your cookbooks to the Chef server.



Both Ansible and Chef are powerful tools for automation and configuration management. Your choice between them may depend on factors like the size of your infrastructure, team expertise, and specific project requirements.

  • Use Ansible if:

    • You prefer an agentless system.
    • You want simplicity and quick setup.
    • You’re working in environments where installing agents is not feasible.
  • Use Chef if:

    • You have a large, complex infrastructure.
    • You need a robust pull-based configuration management system.
    • You’re comfortable with Ruby or willing to learn.

By starting with simple tasks and gradually exploring more complex scenarios, you’ll build a solid understanding of how these tools can streamline your workflow and enhance your infrastructure management capabilities.

Understanding how Ansible playbooks determine what actions to perform is key to using Ansible effectively.

In Ansible, a playbook is a YAML file that contains one or more plays. Each play applies to a group of hosts and includes a series of tasks. Each task uses a specific module to perform an action.

Modules are the building blocks in Ansible that actually execute tasks on the target systems. They are essentially small programs that perform a specific action, like installing a package, copying a file, or managing services.

So, in Ansible, it’s not the name of the playbook that determines what actions are performed, but rather the tasks within the playbook and the modules they use.

Here’s how Ansible knows what to do when you run a playbook:

  1. Playbook Execution:

    • When you run a playbook using ansible-playbook, Ansible reads the YAML file and parses the plays and tasks.
  2. Hosts and Plays:

    • Each play targets specific hosts or groups defined in your inventory.
  3. Tasks and Modules:

    • Each task within a play specifies a module and the parameters for that module.
    • The task tells Ansible what module to run and how to run it.
  4. Module Execution:

    • Ansible executes the module on the target hosts using SSH (or WinRM for Windows).
    • The module performs the action defined by the task.

Let’s look at an example task:

- name: Install Nginx
  apt:
    name: nginx
    state: present

Explanation:

  • name: A description of the task (for readability).
  • apt: The module to use (in this case, the apt module for Debian-based package management).
  • parameters:
    • name: The package to manage (nginx).
    • state: The desired state (present means it should be installed).

How Ansible Processes This Task:

  • Ansible sees that the apt module is used.
  • It knows that the apt module manages packages using the apt package manager.
  • Ansible calls the apt module with the specified parameters (name=nginx, state=present).
  • The module runs on the target host(s) and ensures that Nginx is installed.

In Chef, you write recipes using resources like package, service, etc., and Chef knows what to do based on the resource and the platform.

Similarly, in Ansible:

  • Modules are analogous to Chef resources.
  • Tasks use modules to perform actions.
  • Ansible modules are often idempotent, meaning they can be run multiple times without changing the system after the first run (if the desired state is already achieved).
  • Modules Determine Actions:

    • The module specified in each task defines what action Ansible will perform.
    • Ansible has modules for a wide range of tasks (e.g., apt, yum, service, copy, file, user).
  • Parameters Customize Behavior:

    • Parameters passed to the module control how the module behaves.
    • They specify things like package names, file paths, service states, etc.
  • Playbook Structure:

    • A playbook contains plays.
    • Each play targets hosts and includes tasks.
    • Tasks are executed in order.

Let’s write a simple playbook and break it down:

---
- name: Setup Web Servers
  hosts: webservers
  become: yes
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present

    - name: Start Nginx Service
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Deploy Website Content
      copy:
        src: /local/path/to/index.html
        dest: /var/www/html/index.html

Breakdown:

  • Play Name: Setup Web Servers - Describes the purpose of the play.

  • Hosts: webservers - Targets the group webservers in your inventory.

  • Become: yes - Runs tasks with elevated privileges (e.g., sudo).

  • Tasks:

    1. Install Nginx:
      • Module: apt
      • Action: Ensures Nginx is installed.
    2. Start Nginx Service:
      • Module: service
      • Action: Starts and enables Nginx service.
    3. Deploy Website Content:
      • Module: copy
      • Action: Copies a local file to the target server.

Ansible has extensive documentation for each module, explaining:

  • What the module does.
  • Required and optional parameters.
  • Examples of usage.

Example: apt Module Documentation

  • Description: Manages packages with the apt package manager.
  • Common Parameters:
    • name: Name of the package.
    • state: Desired state (present, absent, latest).

When writing a playbook:

  1. Identify the Task:

    • What do you want to achieve? Install a package? Copy a file? Manage a user?
  2. Find the Appropriate Module:

    • Look for a module that handles that task.
    • Use Ansible’s documentation or ansible-doc command.
  3. Use the Module in a Task:

    • Specify the module and provide necessary parameters.

You can use the ansible-doc command to get information about modules:

ansible-doc apt

This command displays detailed information about the apt module.

  • Playbook Names: The name of a playbook is for your reference and organization; it doesn’t influence what actions are performed.
  • Tasks and Modules: The modules specified in tasks tell Ansible exactly what to do.
  • Under the Hood: Ansible modules contain the logic to perform actions on target systems.
  • Modules Are Key: Understanding which module to use for a given task is essential.
  • Explore Modules:

    • Browse Ansible’s module index to familiarize yourself with available modules.
  • Practice Writing Tasks:

    • Start with simple tasks and gradually incorporate more complex modules and parameters.
  • Use ansible-doc:

    • Leverage ansible-doc to get quick access to module documentation from the command line.

Suppose you want to manage firewall rules. Here’s how you might proceed:

  1. Identify the Task:

    • Manage firewall rules using ufw (Uncomplicated Firewall) on Ubuntu.
  2. Find the Module:

    • Search for a module that manages ufw.
    • Find the ufw module.
  3. Read Module Documentation:

    • Use ansible-doc ufw to read about parameters and usage.
  4. Write the Task:

    - name: Allow HTTP through UFW
      ufw:
        rule: allow
        port: '80'
        proto: tcp
    
  • Ansible Playbooks: YAML files that define plays and tasks.
  • Tasks Use Modules: Each task specifies a module that performs an action.
  • Modules Contain Logic: Modules know how to perform actions on different systems.
  • Parameters Define Behavior: Parameters passed to modules customize their actions.

By understanding that modules are the core of how Ansible executes tasks, and that tasks specify which modules to use and how to use them, you can write effective playbooks to automate your infrastructure management.

That’s an insightful question, and I’d be happy to clarify how Chef can be used to provision new virtual machines, even though it traditionally requires a Chef client to be installed on the nodes it manages.


Chef is not just a configuration management tool; it’s a comprehensive automation platform that supports infrastructure provisioning, configuration management, and application deployment. While it’s true that the Chef client needs to be installed on nodes for configuration management, Chef provides mechanisms to provision new infrastructure and bootstrap nodes by installing the Chef client during the provisioning process.


Chef Provisioning is an extension to Chef that allows you to describe and manage infrastructure resources alongside your application code and system configurations. It provides resources that let you manage machines and machine-related entities (like load balancers, security groups) in your Chef recipes.

Key Features:

  • Write Recipes to Provision Resources: Use the same Ruby DSL to define machines, their configurations, and dependencies.
  • Multiple Drivers: Supports various drivers for different platforms (e.g., AWS, Azure, Docker, Vagrant).
  • Integration with Chef Client: Machines provisioned are automatically bootstrapped with the Chef client.

Example Recipe Using Chef Provisioning:

require 'chef/provisioning'

with_driver 'aws::us-west-2'  # Specify the AWS region

1.upto(200) do |i|
  machine "web-server-#{i}" do
    machine_options bootstrap_options: {
      image_id: 'ami-0abcdef1234567890',  # CentOS AMI ID
      instance_type: 't2.medium',
      key_name: 'my-ssh-key',
      security_group_ids: ['sg-0123456789abcdef0'],
      subnet_id: 'subnet-0123456789abcdef0'
    }
    run_list ['recipe[apache]']
  end
end
  • What This Does:
    • Loops 200 Times: Creates 200 virtual machines.
    • Defines Machine Options: Specifies details like AMI ID, instance type, SSH key, etc.
    • Applies Run List: Each machine runs the apache recipe to install and configure Apache.
    • Bootstraps Nodes: Installs the Chef client on each machine during provisioning.

Knife is the command-line tool that interfaces with the Chef server from your workstation. Knife has plugins that allow you to interact with cloud providers, enabling you to create and manage cloud resources directly.

Common Knife Plugins:

  • Knife EC2: Interact with AWS EC2 instances.
  • Knife Azure: Manage Azure resources.
  • Knife Google: Work with Google Cloud Platform.

Example Using Knife EC2:

knife ec2 server create \
  --image ami-0abcdef1234567890 \
  --instance-type t2.medium \
  --ssh-key my-ssh-key \
  --security-group-ids sg-0123456789abcdef0 \
  --subnet subnet-0123456789abcdef0 \
  --associate-public-ip \
  --node-name "web-server-1" \
  --run-list 'recipe[apache]'
  • What This Does:
    • Creates an EC2 Instance: Based on the specified AMI and instance type.
    • Bootstraps the Node: Installs the Chef client.
    • Applies the Run List: Configures the node according to the specified recipes.

Scaling to 200 Instances:

  • Scripting: Write a shell script or use a loop to repeat the command 200 times with unique node names.
  • Automation Tools: Use tools like GNU Parallel or write a custom script in Ruby or Python.

Chef can work in tandem with other infrastructure-as-code tools:

  • Terraform: Use Terraform to provision infrastructure and then use Chef to configure it.
  • CloudFormation (AWS): Deploy stacks that include bootstrapping scripts to install the Chef client.
  • Packer: Create machine images that have the Chef client pre-installed.

Bootstrapping is the process of installing the Chef client on a node and configuring it to communicate with the Chef server.

How Bootstrapping Works:

  1. Initiate Bootstrap:
    • When you provision a new machine, you initiate the bootstrap process.
  2. Install Chef Client:
    • The bootstrap script connects to the machine (usually over SSH) and installs the Chef client.
  3. Register with Chef Server:
    • The node obtains validation keys and registers itself with the Chef server.
  4. Apply Run List:
    • The Chef client runs the initial run list to configure the node.

Bootstrapping Methods:

  • Knife Bootstrap Command:

    knife bootstrap <IP_ADDRESS> \
      --ssh-user centos \
      --sudo \
      --identity-file ~/.ssh/my-ssh-key.pem \
      --node-name "web-server-1" \
      --run-list 'recipe[apache]'
    
  • Cloud Init Scripts:

    • Use cloud-init or user data scripts to install the Chef client when the instance launches.
  • Pre-baked Images:

    • Use images (AMIs) that already have the Chef client installed.

Q: If Chef needs the client to be installed on the server, how can we spin up new virtual machines?

  • Chef Can Provision and Bootstrap Simultaneously:
    • Chef provides tools (like Chef Provisioning and Knife plugins) that can both create new virtual machines and install the Chef client on them as part of the same process.
  • Automation of Client Installation:
    • The installation of the Chef client (bootstrapping) is automated during the provisioning step, so you don’t need to manually install it on each new VM.
  • Infrastructure as Code:
    • By defining your infrastructure in code (Chef recipes or scripts), you can automate the creation and configuration of resources at scale.

Let’s consider the full workflow to spin up 200 CentOS virtual machines running Apache:

  1. Write a Chef Recipe or Script:

    • Use Chef Provisioning or a script that utilizes Knife to define the desired state of your infrastructure.
  2. Provision VMs:

    • The recipe or script creates the VMs using cloud provider APIs.
    • For on-premises virtualization, integrate with tools like Vagrant or VMware drivers.
  3. Bootstrap VMs:

    • During provisioning, each VM is bootstrapped:
      • Chef client is installed.
      • The node registers with the Chef server.
      • The initial run list is applied.
  4. Configure VMs:

    • The Chef client runs the specified recipes (e.g., installing Apache).
    • Any subsequent changes to the recipes or run lists can be applied by re-running the Chef client on the nodes.

  • Provision with Terraform:

    • Use Terraform to create 200 VMs.
    • Terraform can execute provisioners that install the Chef client.
  • Bootstrap Nodes:

    • Use Terraform’s remote-exec provisioner to run the bootstrap command.
  • Configure with Chef:

    • Once bootstrapped, nodes pull configurations from the Chef server.
  • Build a Custom Image:

    • Use Packer to create a CentOS image with the Chef client pre-installed.
  • Deploy Instances:

    • Launch 200 VMs using this custom image.
  • Auto-Registration:

    • Configure the image to auto-register with the Chef server on first boot.

  • Chef Supports Provisioning: Beyond configuration management, Chef can handle infrastructure provisioning through tools like Chef Provisioning and Knife plugins.
  • Automated Bootstrapping: The Chef client can be installed automatically on new nodes during provisioning, so manual installation is not necessary.
  • Infrastructure as Code: By defining infrastructure and configurations in code, you can automate large-scale deployments efficiently.
  • Flexible Integration: Chef integrates well with other tools and cloud providers, allowing for a variety of provisioning strategies.

  1. Explore Chef Provisioning:

  2. Learn About Knife Cloud Plugins:

  3. Practice Bootstrapping Nodes:

    • Use the knife bootstrap command to get comfortable with the bootstrapping process.
  4. Consider Complementary Tools:

    • Terraform: For advanced infrastructure provisioning needs.
    • Packer: For building custom machine images.


Chef’s ability to provision and configure infrastructure makes it a powerful tool for managing large-scale deployments. By automating both the creation of virtual machines and the installation of the Chef client, you can efficiently spin up new resources and ensure they are configured consistently.


To run the Chef client in local mode and apply a recipe to your local system, use the following command:

chef-client -z -r "recipe[myapache]"
  • Options Explained:
    • -z or --local-mode: Runs the Chef client in local mode, which doesn’t require a Chef Infra Server.
    • -r or --runlist: Specifies the run list, which is a list of recipes and roles to be applied.

Note: In practice, you typically run chef-client against remote nodes to configure them. However, in this tutorial, you are configuring the same node where your VS Code and terminal are running.


To apply a recipe to a remote system, you can use the knife ssh command:

knife ssh IPADDRESS -m -x chef -P PWD 'sudo chef-client'
  • Options Explained:
    • IPADDRESS: The IP address of the remote node.
    • -m: Executes the command on multiple nodes (useful when specifying ranges or groups).
    • -x chef: Specifies the SSH username (chef in this case).
    • -P PWD: Specifies the SSH password (PWD is the placeholder for the actual password).
    • 'sudo chef-client': The command to run on the remote node, prefixed with sudo to execute with elevated privileges.

Note: In a production environment where you need to configure and maintain many nodes or servers, you typically configure chef-client to run automatically at set intervals on the nodes.

  • How It Works:
    • Scheduled Runs: chef-client is set up as a scheduled task (e.g., via cron on Linux systems) to run at regular intervals.
    • Chef Infra Server Check-In:
      • When chef-client runs, it checks in with the Chef Infra Server.
      • It retrieves the latest cookbooks and configurations.
      • It applies any updates or changes to the node.
    • Consistency and Compliance:
      • Ensures that all nodes are consistently configured.
      • Helps maintain compliance with security policies and organizational standards.

By automating chef-client runs, your fleet of nodes will always be up to date with the latest configurations, making management more efficient and reducing the risk of configuration drift.