Crash course in Ansible http://people.redhat.com/mlessard/qc/presentations/Mai2016/AnsibleWorkshopWA.pdf
“Ansible” is a fictional machine capable of superluminal communication (faster than light communication)
Use cases:
- provisioning
- configuration management
- application deployments
- rolling upgrades - CD
- security and compliance
- orchestration
Ansible has a powerful and simple declarative language. (You just specify what you want, not it should be done)
Key components:
- Modules (Tools)
- bits of code copied to the target system
- executed to satisfy the task declaration
- customizable
- examples:
- cloud modules, database modules, files, monitoring, network, notification modules
- commonly used modules:
- apt/yum, copy, file, git etc
- Tasks
- Inventory
- contains the information about hosts in ini format
- Plays
- Playbook
We can run commands using one of the several modules and giving it the required arguments and specifying the hosts file
- each command needs to have an inventory specified with -i <hosts file>
- ansible all -i ./hosts -m command -a “uptime”
- this 🔝 uses the command module, gives it argument “uptime” and runs it in all hosts mentioned in the hosts file
- the hosts file has:
[lh] localhost ansible_connection=local
We can install HTTPD package: ansible all -i ./hosts -m apt -a “name=httpd state=present”
We can start/stop HTTPD service: ansible all -i ./hosts -m service -a “name=httpd enabled=yes start=started”
We can test ansible connections to all the hosts using ping ansible all -i ./hosts -m command -a “ping”
Or 🔝 we can use the ping module! ansible all -i ./hosts -m ping
- name: This is a play # this is the name of the play
hosts: web-servers # we select hosts here
remote_user: ec2-user # the arguments for the playbook
become: yes # the arguments for the playbook, do you want to be superuser?
gather_facts: no # the arguments for the playbook
vars: # here we define the variables to be used in the tasks later
state: present
tasks: # here we define the tasks using modules, giving them args (possibly from the vars)
- name: Install Apache
yum: name=httpd state={{ state }}
Here, in the tasks, we used the yum module and passed it args like we did in the commands 🔝
We can run the playbook like so:
ansible-playbook play.yml -i hosts
Perform a “dry run” ansible-playbook play.yml -i hosts –check
Loops are possible in the playbooks - the playbooks are a DSL!
tasks:
- name: Install Apache and PHP
yum: name={{item}} state={{state}}
with_items:
- httpd
- php
Many types of loops:
- with_nested
- with_dict
- with_fileglob
- with_together
- with_sequence
- until
- with_random_choice
- with_first_found
- with_indexed_items
- with_lines
We have handlers that run a task if it has “changed” status
tasks:
- yum: name={{item}} state=installed
with_items:
- httpd
- memcached
notify: Restart Apache
handlers:
- name: Restart Apache
service: name=httpd state=restarted
tasks:
- name: Install Apache and PHP
yum: name={{item}} state={{state}}
with_items:
- httpd
- php
tags:
- configuration
Tags are used to specify where to run the playbook
ansible-playbook example.yml –tags “frontend-prod” ansible-playbook example.yml –skip-tags “frontend-prod”
We have special tags like “tagged”, “untagged”, “all”
We can register task outputs as well (for debugging etc)
- shell: httpd -v | grep version | awk '{print $3}' | cut -f2 -d'/'
register: result
- debug: var=result
Run these when some condition is satisfied
tasks:
- name: Install Apache and PHP
yum: name={{item}} state={{state}}
with_items:
- httpd
- php
tags:
- configuration
when: ansible_os_family == "RedHat"
By default, ansible stops on errors We can add ignore_error parameter to skip potential errors
tasks:
- name: Install Apache and PHP
yum: name={{item}} state={{state}}
with_items:
- httpd
- php
ignore_errors: yes
# we can define the condition on which to declare the failure
- name: this command prints FAILED when it fails
command: /usr/bin/example-command -x -y -z
resiter: command_result
faield_when: "'FAILED' in command_result.stderr"
# managing errors using blocks
tasks:
- block:
- debug: msg='i execute normally'
- command: /bin/false
- debug: msg='i never execute, cause ERROR!'
rescue:
- debug: msg='I caught an error'
- command: /bin/false
- debug: msg='I also never execute :-('
always:
- debug: msg="this always executes"
- name: All server setup
hosts: all
become: yes # we'll need to be root
vars:
selinux: permissive
tasks:
- name: Change SELinux to permissive mode
selinux:
policy: targeted
state: "{{ selinux }}"
- name: Copy motd file
copy:
content: "Welcome to my server!" dest=/etc/motd
- name: Web server setup
hosts: web-server
become: yes # we'll need to be root
tasks:
- name: Install HTTPD
yum: name=httpd start=present
notify: Restart Apache
- name: Start and enable httpd
service: name=httpd restarted=restarted
when: just_installed_httpd
- name: Copy hello world
copy:
content: "Hello World!"
dest: /var/www/html
- name: Set sshd.conf to not allow root login
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PermitRootLogin "
insertafter: "^PermitRootLogin" line="no"
notify: RestartSSH
handlers:
- name: Restart Apache
service: name=httpd state=restarted enabled=yes
- name: RestartSSH
service: name=sshd state=restarted enabled=yes
Now, we can run this and pass the vars: ansible-playbook -i ../hosts lab2.yml -e “selinux=permissive”
The precedence of variables:
- Extra vars
- Task vars (only for the task)
- Block vars
- Role and include vars
- Play vars_files
- Play vars_prompt
- Play vars
- Set_facts
etc
Ansible has some special variables as well:
- hostvars
- group_names
- is a list (array) of all the groups the current host is in
- groups
- is a list of all the groups and hosts in the inventory
We use debug to view the content
- name: debug
hosts: all
tasks:
- name: Show hostvars[inventory_hostname]
debug: var=hostvars[inventory_hostname]
Templates allow us to create dynamic configuration files using variables
- template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode=0644
Jinja2 is just like Django templating system
{{ variable }}
{% for server in groups.webservers %}
{{ server }}
{% endfor %}
We have variables
{% set my_var='this-is-a-test' %}
{{ my_var | replace('-', '_') }}
In YAML, template variable must be quoted
vars:
var1: {{ foo }} <<< ERROR!
var2: “{{ bar }}”
var3: Echoing {{ foo }} here is fine
Roles are a redistributable and reusable collection of:
- tasks
- files
- scripts
- templates
- variables
Roles are used to setup and configure services
- install packages
- copying files
- starting daemons
Example: Apache, MySQL, Nagios etc
Directory structure: roles
- myapp
- defaults
- files
- handlers
- meta
- tasks
- templates
- vars
Create folder structure for the role using ansible-galaxy init <role name>
- hosts: webservers
roles:
- common
- webservers
- { role: myapp, dir: '/opt/a', port: 5000 }
- { role: foo, when: "ansible_os_family == 'RedHat'" }
- hosts: webservers
serial: 1
pre_tasks:
- command:lb_rm.sh {{ inventory_hostname }}
delegate_to: lb
- command: mon_rm.sh {{ inventory_hostname }}
delegate_to: nagios
roles:
- myapp
post_tasks:
- command: mon_add.sh {{ inventory_hostname }}
delegate_to: nagios
- command: lb_add.sh {{ inventory_hostname }}
delegate_to: lb