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
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
GroupsUse 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
Lab: Configure Inventory File
- Configure an inventory file to include your managed nodes
- Use grouping to organize these nodes into groups by operating system
- Start playbook/play that only has a “hosts” specification, targeting all servers, without any tasks as of yet. This will still run fact gathering
- 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
- Do this targeting only your CentOS server
- Create a user named www-data
- Install the nginx webserver
- Ensure nginx is set to start at boot time
- Ensure that nginx is running
- 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
- Create a group_vars directory in the same location that you housed your playbook
- Define list of domains in group _vars (ex. example.com, hamsandwich.com, moo.com, ansible.com) in a file named 'all'
- 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
- 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".
- 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
- Insert variable substitutions into an nginx .conf and save this as nginx.j2
- This variable substitution should be inserted to define the custom access_log and error_log and server_locations
- 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
---
- 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
- Add handlers to start/restart nginx as appropriate (nginx should be restarted after nginx configs are put into place )
- 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
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 Operational
By Rackspace University
Ansible Operational
- 1,441