Operational

 

Intros

 

Assess Your Knowledge 

 

Learning Outcomes

  • Install  Ansible
  • Manage  static  and dynamic  inventory
  • Use  ansible  for  ad-hoc commands
  • Write  &  run  Playbooks  for  a  variety  of  purposes  i.e.  Configuration management, App deployment, App updates
  • Demonstrate  an  understanding  of  variables, including group  and  host  variables.
  • Explain  variable  inclusion  (include_vars and vars_files)
  • Activate  and  manage  task  control
 

Getting Started

Installation 

http://docs.ansible.com/intro_getting_started.html

http://docs.ansible.com/intro_installation.html

http://releases.ansible.com/ansible/

https://github.com/ansible/ansible/releases

 

Inventory

STATIC INI

Create an INI inventory file based off of instructor provided servers, specifying a human readable name and a connection IP address or hostname

 



web1 ansible_ssh_host=10.10.10.10
web2 ansible_ssh_host=20.20.20.20

STATIC INI


Groups

Create a web group in the inventory file



[web]
web1 ansible_ssh_host=10.10.10.10
web2 ansible_ssh_host=20.20.20.20

Groups


Use Cases for Dynamic Inventory Scripts

  • Limitations of a static inventory with a cloud like environment
  • Query nearly any data source for inventory
  • Ansible knows if the provided inventory is a script
 

Dynamic Inventory Demo

 


https://www.youtube.com/watch?v=xazOxy5cJPk

 

Lab: Configure Inventory File

  1. Configure an inventory file to include your managed nodes
  2. Use grouping to organize these nodes into groups by operating system
  3. Start playbook/play that only has a “hosts” specification, targeting all servers, without any tasks as of yet. This will still run fact gathering
  4. Run this playbook to ensure it's success in contacting your nodes

Objective: Identify the nodes that Ansible will manage

 

Targeting Hosts

  • all / *
  • Groups (webservers)
  • Exclusion (webservers:!nginxservers)
  • Intersection (webservers:&staging)
  • Combos
  • Hostname / IP
  • Regex
  • --limit

Executing a Task

ad-hoc commands are single-task executions which leverage modules

 

Modules

Modules are code that is executed on the remote host


Ansible provided core modules:

  • Written in python
  • Shipped by SSH to the target machine where they are executed
  • Return JSON data that is interpreted by ansible


Module Documentation


ansible-doc -l


ansible-doc setup
ansible-doc copy

 Examples




Rebooting all servers


ansible all -m command -a "/sbin/reboot"




Setting fork level to 10


ansible webservers -f 10 -m setup




Copy a file from the local host to inventory hosts


ansible dbservers -m copy -a "src=/path/to/file dest=/path/to/dest"




Install nginx and add a user


ansible all -m apt -a "pkg=nginx state=present"
ansible all -m user -a "name=bob state=present"




Cloning a git repo to a path


ansible appservers -m git -a "repo=https://github.com/ansible/ansible dest=/home/user/ansible"




Ensure httpd is started


ansible -m service -a "name=httpd state=started"




Backgrounding tasks, with optional polling every 60 seconds


ansible all -B 3600 -a "/usr/bin/long_running_operation --do-stuff"
ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"


Running setup module to gather and display all facts about a machine known to Ansible


ansible web1.example.com -m setup

 

  COWSAY

 

Lab: AD-HOC Commands

  1. Do this targeting only your CentOS server
  2. Create a user named www-data
  3. Install the nginx webserver
  4. Ensure nginx is set to start at boot time
  5. Ensure that nginx is running
  6. Manually verify this work is completed

Objective: Configure your CentOS node via command line Ansible interactions

Ansible Orchestrated Commands

A Review of Ansible Plays and Playbooks

 

Plays

An ordered series of tasks executed on a selection of hosts

with controls over how the tasks operate.


- name: This is a Play
  hosts: web-servers
  remote_user: fred
  sudo: yes
  connection: ssh
  gather_facts: no
  vars:
    http_port: 80
    cache_dir: /opt/cache

  tasks:
    - name: create cache dir
      file: path={{ cache_dir }} state=directory

    - name: install nginx
      yum: name=nginx state=installed
      

Playbooks

Ansible-playbook is the tool used to select

  • An ordered set of plays
  • An inventory
  • Global variables
  • Forks
  • Inventory limits

Playbook Execution


$ ansible-playbook play.yaml -i pre-prod -e "cache_dir=/srv/cache/" -f 30 -l dfw


 

Lab: Playbook

Write a playbook that performs the tasks from the prior lab.

At this time your work should be completed targeting only your CentOS node

Objective: Expand your Playbook to include previous AdHoc commands

 

Content Organiztion

Content Organization

Magic happens when you put your files in the right locations

  • ansible.cfg
  • inventory
  • variables and inclusions
  • tasks
  • handlers
  • playbooks
  • templates and files for use in tasks
  • custom modules
  • roles
 
 

Variables

Understand the basics of variables, where they come from, how they get set, and how to evaluate and manipulate them

 

Host Variables

Host variables are "facts" about a server that contains info such as:

  • Hostname
  • IP addresses
  • Date and Time information
  • CPU, Disk, Memory
  • Hardware Architecture
  • Operating System Information
 

Host Variables

Where do they come from?

  • I nventory
  • Fact gathering (setup)
  • Host_vars directory
 

Group Variables

"Facts" that apply to a grouping of servers

Group Variables

Where do they come from?

  • inventory
  • group_vars directory

The name of the files in the group_vars directory line up with the group name being targeted. "all" is a special keyword to apply facts to all hosts.

 

Variable Precedence

  • Precedence should simply feel natural
  • The most specific applied variable wins

Using Variables in YAML


Ansible uses "mustache" like variable expansion

Group_vars/all


---
apache2_version: 2.2.22-1ubuntu1

Tasks


- apt: name="apache2={{ apache2_version }}" state=present


Register

A special keyword "register" exists for all tasks, that will assign the output from a module to the variable of the specified name, for later manipulation or evaluation  in use with other tasks


- git: repo=git://github.com/ansible/ansible.git dest=/tmp/ansible
  register: ansible_git

Inspecting Variables

Different modules produce different data structures. The 'debug' module will help you inspect that data structure.


- debug: var=ansible_git

Output Sample


ok: [someserver] => {
    "ansible_git": {
        "after": "1b4ba5431b1070c63b9069d310f42e16f302db10",
        "before": "69b2d82be6ffe20f04070cddf11b7629aa8420e5",
        "changed": true,
        "invocation": {
            "module_args": "repo=git://github.com/ansible/ansible.git dest=/tmp/ansible",
            "module_name": "git"
        }
    }
}
 

Module Output Structure

All modules should return a key titled "changed" with a boolean value of "true" or "false" defining whether the module made a change

Additionally, under a failure scenario, a module would set a key of "failed" with a boolean value of "true"

 

Dynamically Adding Host Facts

During runtime you can run add additional host vars using the "set_fact" module


- local_action:
    module: wait_for
    host: "{{ ansible_default_ipv4.address }}"
    port: 22
    delay: 1
    timeout: 1
  register: port_test
  failed_when: False

- set_fact: ansible_ssh_port=2222
  when: port_test|failed

 

Magical Variables

There are several standard variables that are created and exposed by ansible

  • inventory_dir
  • inventory_hostname
  • inventory_hostname_short
  • inventory_file
  • playbook_dir
  • play_hosts
  • hostvars
  • groups
  • group_names
  • ansible_ssh_user
 

Variable Inclusion

How to properly use include_vars and vars_files to include variables in your playbooks

 

vars_files

vars_files is used in playbooks to include a file of variables outside of the usual variable locations

  • Useful for keeping secrets and site specific data outside of your Ansible repository and version control
  • Not available for usage within roles, for that you will need the include_vars module

include_vars

The include_vars module is similar to vars_files and allows for dynamic inclusion of variables within the context of a role.

  • include variables based on the target Linux Distribution
    include_vars: "../vars/{{ansible_os_family}}.yml"
  • include a directory of variable fragments
    include_vars: "{{ item }}.yml"
    with_fileglob: "../vars/*.yml"

YAML list syntax:


---
Foods:
  - Apple
  - Orange
  - Strawberry
  - Mango


 

Lab: Variables

  1. Create a group_vars directory in the same location that you housed your playbook
  2. Define list of domains in group _vars (ex. example.com, hamsandwich.com, moo.com, ansible.com) in a file named 'all'
  3. Use a print or debug interaction to verify these variables can be read by Ansible.

Objective: Populate a set of group variables

 Task control

Loops

with_items: Loops over a list, one task per item

 

Standard Loops

To save some typing, repeated tasks can be written in short-hand like so:


- name: add several users
  user: name={{ item }} state=present groups=wheel
  with_items:
     - testuser1
     - testuser2

 

Standard Loops

If you have defined a YAML list in a variables file, or the ‘vars’ section, you can also do:


- name: add several users
  user: name={{ item }} state=present groups=wheel
  with_items: users


 

Lab: Loops

Create four web directories from list of domains defined in group_vars/all

Note: Example location for these directories: /var/www/domain.com "one per site"

Objective: Create content directories for each of the domains in your list

Conditionals

Useful for

  • Acting on results of previous tasks
  • Handling cross-distro/env differences

Platform Variance in Playbooks

Task Conditionals


- name: install nginx
  yum: name=nginx state=present
  when: ansible_distribution == 'CentOS'

- name: install nginx
  apt: pkg=nginx state=present
  when: ansible_distribution == 'Debian'

Platform Variance in playbooks

group_by

Create dyanmic groups matching certain criteria


- name: grouping play
  hosts: all
  tasks:
     - name: create distro groups
       group_by: key={{ ansible_distribution }}

- name: CentOS play
  hosts: CentOS
  gather_facts: False
  tasks:
     - # tasks that only happen on CentOS go here

 

Lab: Conditionals

  1. Add tasks and conditionals to your playbook to duplicate the installation of nginx for your ubuntu node using  multiple tasks that leverage the "ansible_os_family". 
  2. After execution manually verify both environments

Objective: Alter your existing playbook to add management for the ubuntu node

 

Status Control

  • failed_when
  • changed_when
 

Local Action

  • local_action (task)
  • connection: local (play)
 

Delegation

Run a task on another server, on behalf of the server we are currently operating on.

 Templates

 

Templates

Ansible can easily transfer files to remote systems, but often it is desirable to substitute variables in other files. Variables may come from the inventory file, Host Vars, Group Vars, or Facts. Templates use the Jinja2 template engine and can also include logical constructs like loops and if statements.

 

Templates


 



# Example from Ansible Playbooks
- template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode=0644

# Copy a new "sudoers file into place, after passing validation with visudo
- action: template src=/mine/sudoers dest=/etc/sudoers validate='visudo -cf %s'

Templates

Example

Example: nginx.conf


server {
    listen       80;
    server_name  domain.com;
    access_log /var/log/nginx/domain.com-access.log;
    error_log /var/log/nginx/domain.com-error.log;
    location / {
        root   /var/www/html;
        index  index.html index.htm;
    }
}

Example of Templating


So this...


server_name domain.com;


Becomes...


server_name {{ item }};


 

Lab: Templates

  1. Insert variable substitutions into an nginx .conf and save this as nginx.j2
  2. This variable substitution should be inserted to define the custom access_log and error_log and server_locations
  3. Using the approach introduced for loops generate a "domain.com.conf file" for each domain listed in your variables and have this file exist within /etc/nginx/conf.d/

Objective: Build a reusable skeletal configuration

 Handlers

 

Handlers

Handlers are just like regular tasks in an Ansible playbook , but are only run if the Task contains a “notify” directive and also indicates that it changed something. For example, if a config file is changed then the task referencing the config file templating operation may notify a service restart handler. This means services can be bounced only if they need to be restarted. Handlers can be used for things other than service restarts, but service restarts are the most common usage.

 



---
# this might be in a file like handlers/handlers.yml
- name: restart apache
  service: name=apacheII state=restarted

Handlers

In this example, if you wanted to define how to restart apache, you would only have to do that once for all of your playbooks. You might make a handlers.yml that looks like:

 



handlers:
  - include: handlers/handlers.yml

Handlers

And in your main playbook file, just include it like so, at the bottom of a play:

Handlers

You can mix in includes along with your regular non-included tasks and handlers.

Includes can also be used to import one playbook file into another. This allows you to define a top-level playbook that is composed of other playbooks.

For Example:


- name: this is a play at the top level of a file
  hosts: all
  remote_user: root

  tasks:

  - name: say hi
    tags: foo
    shell: echo "hi..."

- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml


 



 - name: Update the Apache config
      action: copy src=httpd.conf dest=/etc/httpd/httpd.conf
      notify: restart apache

Handlers

This will call the "Restart Apache" handler whenever 'copy' alters the remote httpd.conf.

 



  - name: Update our app's configuration
      action: copy src=myapp.conf dest=/etc/myapp/production.conf
      notify:
        - restart apache
        - restart redis

Handlers

Here's how to specify more than one handler

 Example Playbook with Handle

---
- hosts: webservers
  remote_user: root
- name: This is a play that adds a conf file and restarts Apache  
  tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service: name=httpd state=started
  handlers:
    - name: restart apache
      service: name=httpd state=restarted
      




 

Lab: Handlers

  1. Add handlers to start/restart nginx as appropriate (nginx should be restarted after nginx configs are put into place )
  2. Run it

Objective: Triggers service restarts within your playbook

Best Practices

 

Version Control

Version control and Ansible go hand in hand. This topic will walk through many of the benefits of storing your ansible content in SCM and why this is itself a best practice.

Version Control

Galaxy, Git, and You!

  • role sharing
  • joys of modular playbooks
  • deploying code with the git module in a role
 

Vault

RBAC and execution model

Centralized

  • access defined by company standards
  • layer of protection against accidents and malice
  • repeatability and guaranteed working environment
  • ITIL/Change Control process friendly

RBAC and execution model

Distributed

  • rapid development and execution loop
  • change control put in hands of playbook/code devs
  • turns operational support from gatekeeping to enabling
  • aligns with other self-service IT services
  • you built it, you deploy it

Proper naming of plays, tasks, and handlers

Names have significance

  • --start-at-task
  • handler notification

$ ansible-playbook play.yaml --start-at-task="a good task"

  - name: a good task
    file: path=/etc/foobar state=absent
    notify: bounce foo service

handlers:
  - name: bounce foo service
    service: name=foo state=restarted

Playbook formatting

 whitespace, YAML, line lengths, oh my!

Playbook formatting

whitespace, line lengths, continuations

  • blank lines encouraged
  • less than 80 chars wide
  • blocks can continue on new lines with proper spacing

tasks:
  - name: one task
    command: echo

  - name: lots of arguments
    rax_dns_record: credentials=/path/to/my/creds type=A
                    data=4.2.2.2 name=www.nodomain.not

Playbook formatting

order and style of playbook directives

  • use a name
  • hosts next
  • other options
  • tags
  • vars after a blank line
  • tasks after a blank line

- name: Generate env/cell specific content
  hosts: localhost
  connection: local
  gather_facts: false
  tags:
    - localprep

  vars:
    - cachedir: cache/

  tasks:
    - name: stuff
    

Playbook formatting

to quote or not to quote

  • quote strings that have a : in them
  • quote variables if they come directly after a :
  • quote strings that use * directly after a :
  • quote entire string if first character is a quote

- name: "My name has a : in it"
  command: "{{ variable_here }}"
  when: "'some_string' in varaible_here"

hosts: "*-webs"

Playbook formatting

variable referencing

  • use {{ }} almost always
  • do not use {{ }} in conditionals and other task controllers

- name: name here
  command: echo {{ variable here }}
  register: output
  when: variable != "derp"
  failed_when: output.stderr == "FAILURE"

Playbook formatting

YAML gotchas

http://docs.ansible.com/YAMLSyntax.html

 

ansible.com

 

Assess Your Knowledge 

 

A massive thanks to the content dev team...

Chris Caillouet‎

Chris Old

David Federlein

Jesse Keating 

Justin Phelps

Matt Martz

Mike Martin

Paul Durivage

Peter Isburgh

Tim Gerla

 

ansible.com

Ansible Operational

By Rackspace University

Ansible Operational

  • 1,444