Ansible Basics

Ayush Goyal

Imagine

  • There is a critical openssl update that needs to be quickly deployed on all your 100 servers.
  • easy if you try???
  • You have to setup mysql master-slave on 10 servers(1 master, 9 slaves).
  • isn't hard to do???
  • Your servers have reached there resource limit and you quickly need to add 10 more servers to meet the demand.
  • You may say I am a dreamer. But I am not the only one.

What is ansible and why use it?

A simple configuration management and orchestration tool.

  • To have a adaptable and reproducible configuration managing thousands of servers.
  • It is designed for cloud. Works out of the box with Amazon web services.
  • Easily extendable, many times without requiring to patch core software.
  • Its build on top of python, yaml and ssh. Doesn't require much setup to start using.
  • Its declarative and idempotent.

Installing ansible on mac

brew install python
easy_install pip
pip install -Iv ansible==1.7.2

Quickly setup a test vagrant

  • Create a folder for your development environment

    mkdir testansible
    cd testansible
    
  • Initialize Vagrant with the box you just imported

    vagrant init ubuntu-12.04
    
  • Uncomment the private network line in Vagrantfile and change the ip

    config.vm.network "private_network", ip: "192.168.33.13"
    
  • Start Vagrant Instance

    vagrant up
    

Key Ansible Concepts

Inventory file

  • Ansible works through an inventory file to know which hosts it can connect to
  • Inventory can be either static or dynamic
  • Today we will deal with just static inventory file.
  • In production, we use dynamic inventory which uses cloud apis

An example Static Inventory

Ansible static inventory uses INI-like format.

  • It looks something like this

    mail.example.coma          ;<~host
    
    [webservers]
    foo.example.com
    bar.example.com
    
    [dbservers]                ;<~group
    one.example.com   id=10    ;<~host_vars
    two.example.com   id=11    ;<~host_vars
    foo.example.com
    
    [ranged_hosts]
    www[01:50].example.com
    db-[a:f].example.com
    
    [webservers:vars]          ; group_vars
    http_port=80
    
  • A host in ansible jargon is a single target, which you can connect to and deploy on. You can define hosts using ranges (ranges are inclusive)
  • You can organise hosts in a group to target multiple hosts simultaneously.
  • host_vars have variables associated with a host, which you can use to manage your config. These variables/facts can either be fetched from a central inventory db or can be defined.
  • group_vars are variables for an entire group. These can be fetched from inventory

Our own inventory file

Add this in hosts file inside the testansible folder

[servers]
server ansible_ssh_host=192.168.33.13 ansible_ssh_user=vagrant ansible_ssh_private_key_file=.vagrant/machines/default/virtualbox/private_key

[localhost]
127.0.0.1 ansible_connection=local ec2_tag_Name=localhost

Run ad-hoc command using ansible

Now you have inventory, you can execute your own ad-hoc commands

  • Run this on terminal:

    ansible -i hosts server -m shell -a "uptime"
    
  • Lets target a group this time

    ansible -i hosts servers -m ping
    
  • Let's fetch and use a hostvar this time

    ansible -i hosts localhost -m shell -a "echo {{ec2_tag_Name}}"
    

Ansible Modules

Ansible modules is a logical unit in ansible, which when specified with required parameters, can perform certain action.

 

  • shell: run adhoc shell commands
  • ping: module to test connectivity to host
  • copy: module to copy file from localhost to remote server
  • ec2: launch an ec2 instance

Ansible Playbooks

Ansible playbooks is the language of ansible. It allows you to design plans for orchestration, config management, deployment.

  • It's just a yaml file and has a very minimal syntax.
  • A simple playbook file looks like this. This one has a single play. Let's create a playbook.yml file in our testansible dir:

    ---
    - name: playbook to setup apache
      hosts: server
      sudo: yes
      tasks:
      - name: ensure apache is at the latest version
        apt: name=apache2 state=latest update_cache=yes
      - name: ensure apache is running
        service: name=apache2 state=started
    
  • We can execute a playbook on a host like this:

    ansible-playbook -i hosts playbook.yml
    

Ansible Playbook: An extended example

---
- name: playbook to setup apache
  hosts: server
  sudo: yes
  vars:
    http_port: 80
  vars_files:
    - vars_files/apache.yml
  tasks:
    - name: ensure apache is at the latest version
      apt: name=apache2 state=latest update_cache=yes
      tags: install_apache
    - name: copy apache conf
      template: src=site.conf.j2 dest=/etc/apache2/sites-enabled/site.conf mode=0640 owner=apache group=apache
      notify: restart apache
    - name: ensure apache is running
      service: name=apache2 state=started
   handlers:
    - name: restart apache
      service: name=apache2 state=restarted
  • tasks are collection of steps to be executed one after another
  • handlers are tasks which run only when triggered.
  • vars are various variables in a playbook, used for templates etc. They can be passed at runtime to modify behaviour
  • vars_files are collection of vars, stored in seperate yaml files
  • tags are identifier for tasks. You can skip or run a task by using ansible-playbook cli options.

Ansible Playbook: Roles

Roles are abstracted logical grouping of tasks, handlers and vars.

---
- name: playbook to setup apache
  hosts: server
  sudo: yes
  #pre_tasks:
  roles:
    - apache
  #post_tasks:
  • Roles can be created in a roles directory relative to your playbook path
  • Roles directory structure looks something like this

    $ tree roles/apache
    roles/apache
    ├── README.md
    ├── defaults
    │   └── main.yml
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── tasks
    │   └── main.yml
    ├── files
    │   └── apache2.conf
    ├── templates
    │   └── site.conf.j2
    └── vars
        └── main.yml
    

A simple apache role

  • create roles directory inside your testansible

    mkdir -p roles/apache/{defaults,tasks,handlers,templates}
    
  • add this to roles/apache/defaults/main.yml

    http_name: john lennon
    
  • add this to roles/apache/tasks/main.yml

    - name: ensure apache is at the latest version
      apt: name=apache2 state=latest update_cache=yes
    - name: copy test html page
      template: src=test.html.j2 dest=/var/www/test.html mode=0644 owner=root group=root
      notify: restart apache
    - name: ensure apache is running
      service: name=apache2 state=started
    
  • add this to roles/apache/handlers/main.yml

    - name: restart apache
      service: name=apache2 state=restarted
    
  • add this to roles/apache/templates/test.html.j2

    
    <html>    
      <head>
         <title>{{http_name}}'s Page</title>
      </head>
      <body>
        <p>This is {{http_name}}'s webpage</p>
      </body>
    </html>
                            
  • Once you have setup all your create a apache.yml playbook in your testansible directory

    - name: playbook to setup apache
      hosts: server
      sudo: yes
      roles:
        - apache
    
  • Execute your playbook using:

    ansible-playbook -i hosts apache.yml -e "http_name=ayush"
    

Extras

Extras: Ansible Doc

You can lookup documentation for modules using ansible-doc utility

  • list all modules

    ansible-doc -l
    
  • check documentation of a module using:

    ansible-doc apt
    

Extras: Loops and conditionals in ansible

Although not hard to do and explain, loops and conditionals can be setup in ansible playbooks using with_items and when keywords Checkout reference for ansible documentation link for it.

Extras: YAML and jinja

  • Ansible uses yaml and jinja for its language syntax.
  • Variables in playbook use jinja syntax. Templates also use jinja
  • A good understanding of yaml and jinja is required to understand ansible really well.
  • Checkout documentation resources on yaml and jinja at the end.

Extras: Ansible Vault

You can have encrypted vars files. There is a utility ansible-vault to encrypt a vars files

  • If you encrypt vars files using ansible-vault, you need to provide the password when running playbook using ansible-playbook
  • This is useful when you have publicly accesible library and you want to keep your parameters secret.
  • Refer to links in references for more info

Extras: Vagrant Ansible Provisioner

Vagrant has an ansible provisioner facility, which can generate the ansible inventory file and run playbook with just vagrant up

  • It requires some configuration to be done in Vagrantfile
  • Looks something like this:

    config.vm.provision "ansible" do |ansible|
      ansible.playbook = "playbook.yml"
      ansible.tags = "logrotate"
      ansible.groups = {
        "group1" => ["machine1"],
        "group2" => ["machine2", "machine3"],
        "all_groups:children" => ["group1", "group2", "group3"],
        "group1:vars" => { "variable1" => 9, "variable2" => "example" }
      }
    end
    

What's next

World Peace. Maybe?

Exercise

  • Create a new vagrant machine
  • Write an ansible playbook which when executed:
    • setup nginx, along with config to render php pages
    • copy an html page in nginx document root which renders the current time
    • This setup should work without manually doing anything just by running the playbook.
  • Bonus task: set it up so that all of this gets done with just vagrant up after destroying the old machine

Ansible Basics

By Ayush Goyal

Ansible Basics

A presentation on ansible-docs

  • 1,973