Ansible
General Information
Ansible is a tool for performing remote tasks on systems. (either ad-hoc or via “playbooks”)
You will need to use ansible as a user that has ssh keys setup on systems so you are not prompted for passwords during parallel command operations.
If you already have Ansible installed/configured, playbook downloads can be found here.
Checklist
- Distro(s): Any
Install
Ansible comes in many distributions repos already.
CentOS 7
yum install ansible
Configure
Main Ansible config file
- /etc/ansible/ansible.cfg
- Edit this file to point Ansible to your inventory files.
Repo Controlled
It is highly recommended to put Ansible playbooks, roles, and inventory into version control. (Example: git or svn)
One suggested directory structure/workflow is:
- Shared, read-only/production use at:
- /ansible/
- Each system administrator would then clone a copy of the repo into their home directory for local changes/testing before committing working modifications to the repo.
- Have an automated job sync the shared location every so often.
- Example: Have cron perform a git pull for /ansible/ every 30 mins.
Model content tree under /ansible/ after Ansible's best practices alternative structure.
Inventory
Inventory setup is the most important configure task.
In order for Ansible to send commands to systems, they must be listed in inventory.
Suggested directory structure (after Ansible's best practices alternative structure)
- Production Inventory: /ansible/inventories/production/hosts
- Systest Inventory: /ansible/inventories/systest/hosts
- Development Inventory: /ansible/inventories/development/hosts
Auto Generated Inventory
You will not want to introduce a manual inventory process.
Suggested workflow for auto generating your inventory files:
- Cron Job to execute inventory generation script: /etc/cron.d/ansible-generate-inventory
- Example: Executes every 30 minutes. (*/30 * * * *)
- Script that generates inventory files: /ansible/scripts/inventory-file-gen.py
- Source: The script should contact some sort of system via APIs that all newly deployed systems automatically register to. (Such as Spacewalk, Foreman/Katello, etc)
- Target directory for auto generated inventory files: /root/repos/ansible/inventories/
- Files are generated here and any changes committed to the repo (svn/git).
- Example: If there were committed changes, a repo sync job will pick them up and place them in /ansible/inventories/ at the top of every hour. (0 *)
- Example User Accounts Involved
- autorepo → Repo user account with read/write access to an ansible repo for inventory file changes.
- autoadmin → API user account for contacting the inventory source.
Ansible Commands
Examples of Ansible commands.
Note: All host-patterns check the inventory files for matches ONLY. If a system is not in the inventory file(s), Ansible can't send a command to it.
General Ansible command syntax
ansible <host-pattern> [-m module-name] [-a arguments] [options]
- see 'man ansible' for more details
List Hosts
Listing hosts matched by the given pattern and do nothing else.
List hosts in the webservers_nginx group from dev inventory
ansible webservers_nginx -i /ansible/inventories/development/hosts --list-hosts
List all hosts from dev inventory
ansible all -i /ansible/inventories/development/hosts --list-hosts
List all hosts in the “webservers_nginx” group (from any inventory)
ansible webservers_nginx --list-hosts
List all hosts matching “web*” pattern (from any inventory)
ansible "web*" --list-hosts
Test Connection
The module 'ping' is not an ICMP echo command; it tests a remote login (ssh) to the system and verifies a working remote python environment.
Test connection to webservers_nginx in dev inventory only
ansible webservers_nginx -i /ansible/inventories/development/hosts -m ping
Test connection to all systems in dev inventory
ansible all -i /ansible/inventories/development/hosts -m ping
Test connection to all webservers_nginx (from any inventory)
ansible webservers_nginx -m ping
Test connection to all systems in the “webservers_nginx” group (from any inventory)
ansible webservers_nginx -m ping
Test connection to systems matching a pattern (from any inventory)
ansible "web*" -m ping
Test connection to all systems (from any inventory)
ansible all -m ping
Ad-Hoc Commands
Ad-hoc commands are for one off commands that are not saved in a playbook.
- Ad-hoc Intro: http://docs.ansible.com/ansible/intro_adhoc.html
Modules: Command and Shell
If no module is specified, the “command” module is used.
- The command module can execute basic commands, but cannot do shell specific functions such as piping and redirects
- If you need that type of shell functionality, specify the “shell” module
Check uptime of the group 'webservers_nginx' from dev only
ansible webservers_nginx -i /ansible/inventories/development/hosts -a 'uptime'
Check uptime of all systems in dev
ansible all -i /ansible/inventories/development/hosts -a 'uptime'
Check uptime of the group 'webservers_nginx' (from any inventory)
ansible webservers_nginx -a 'uptime'
Check the last 10 lines in /var/log/messages (using sudo)
ansible "web*" -a 'tail /var/log/messages' -b
- -b ⇒ or 'become', uses the default privilege escalation, which is sudo on Linux systems.
Equivalent command as the above
ansible "web*" -a 'sudo tail /var/log/messages'
Piping example; the shell module must be specified
ansible "web*" -m shell -a 'cat /var/log/messages | grep -i error' -b
Modules: Copy and File
Transferring files over scp using Ansible to multiple servers at once.
Copy a local file (src=) to all systems in the 'webservers_nginx' group (dest=)
ansible webservers_nginx -m copy -a "src=/etc/yum.conf dest=/tmp/yum.conf"
Set a file's ownership and permissions
ansible webservers_nginx -m file -a "dest=/tmp/yum.conf mode=750 owner=root group=root"
Remove a file
ansible webservers_nginx -m file -a "dest=/tmp/yum.conf state=absent"
- This same command will recursively delete if a directory is the target
Modules: Packages
Managing packages with Ansible.
Ensure a package is installed, do not update the package if it is installed
ansible my_group -m yum -a "name=dstat state=present"
Ensure package is the latest version (update if not)
ansible my_group -m yum -a "name=dstat state=latest"
Ensure package is not installed (remove if it is)
ansible my_group -m yum -a "name=dstat state=absent"
Modules: Services
Managing system services with Ansible.
Compatible with SysV and Systemd.
- Note: For systemd specific commands such as “mask”, you will need to use the systemd Ansible module instead.
Ensure httpd is started (start if not)
ansible my_group -m service -a "name=httpd state=started"
Restart a service
ansible my_group -m service -a "name=httpd state=restarted"
Ensure a service is stopped
ansible my_group -m service -a "name=httpd state=stopped"
Ansible Playbooks
“Playbooks are Ansible’s configuration, deployment, and orchestration language. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process.”
More Details: http://docs.ansible.com/ansible/playbooks.html
Playbook Directory Structure
Playbooks should be repo controlled.
Following the examples on this page:
- /ansible/playbooks
Playbooks map ansible groups to roles
- Example playbook
# File: webservers_nginx.yml # Description: Nginx Webservers # Last Updated: 2018-04-08 # Recent Changes:-Initial release # hosts: group_name or 'all' - hosts: webservers_nginx # roles: located in ../roles/ roles: # role: role to assign to hosts, tags: tag(s) to give entire role - { role: webservers-nginx, tags: webservers-nginx } # Gather host facts for this playbook gather_facts: yes
- All systems in the ansible group “webservers_nginx” are given the roles:
- webservers-nginx
- When a playbook is executed, all tasks in the assigned roles are run (unless only specific tasks/actions are selected using tags and/or limits).
Gather a subset of facts
If you do need to gather facts, consider gathering a subset of facts instead of everything in order to keep the fact collection fast.
- Example: Collect only the ansible_distribution facts
# Gather host facts for this playbook gather_facts: yes # Gather only ansible_distribution info (OS attributes) gather_subset: - '!all' - '!min' - 'distribution'
- Facts returned by the above subset
"ansible_distribution": "CentOS", "ansible_distribution_file_parsed": true, "ansible_distribution_file_path": "/etc/redhat-release", "ansible_distribution_file_variety": "RedHat", "ansible_distribution_major_version": "7", "ansible_distribution_release": "Core", "ansible_distribution_version": "7.5.1804", "ansible_os_family": "RedHat",
- You can test your subset commands like this
- ad-hoc
ansible -m setup -a 'gather_subset=!all,!min,distribution' localhost
Available Fact Subsets:
- all
- min
- hardware
- network
- virtual
- ohai
- facter
See the Roles section for what happens next.
Roles Directory Structure
Roles contain all of the tasks that will be run when a playbook is executed against it.
Roles should be repo controlled. Top level directory example is:
- /ansible/roles
An example role called “webservers-nginx” is located:
- /ansible/roles/webservers-nginx
The contents of a role directory are:
- files
- Files to be used with the 'copy' or 'script' resource. Files located in this directory can be referred to by just the filename without directory path in task yml files.
- handlers
- Actions that are only executed when certain tasks report changes. Requires 'main.yml' to execute.
- meta
- Role dependencies. Requires 'main.yml' to execute.
- tasks → The main section to work with
- Tasks/actions to take when the role is executed via a playbook. Requires 'main.yml' to execute.
- templates
- Files to be used with the 'template' resource. Templates end in '.j2' (jinja2)
- vars
- Variables associated with the role. Requires 'main.yml' to execute.
Playbook/Role Creation
How to create a new playbook and role.
- Playbook: Maps Ansible group(s) to role(s)
- Role: Defines tasks to complete against the hosts
External References
- Intro to Playbooks: http://docs.ansible.com/ansible/playbooks_intro.html
- Intro to modules: http://docs.ansible.com/ansible/modules_intro.html
- All modules index: http://docs.ansible.com/ansible/list_of_all_modules.html
- Navigate to your local copy of the version controlled ansible repo. Example:
cd ${HOME}/repos/ansible/
- Update your local copy of the version controlled ansible repo to the latest version
#git git pull #svn svn update
- New Role
- Navigate to the roles directory, then copy your role template directory to a new name
cd roles/ cp -R template-role/ my-new-role
- Modify the role's files as needed to create tasks, files, handlers, etc.
- Download zip archive of an example role template.
- New Playbook
- Navigate to the playbooks directory
cd ${HOME}/repos/ansible/playbooks/
- Copy your playbook template to a new playbook yaml file.
cp TEMPLATE_PLAYBOOK.yml my_new_playbook.yml
- Playbook Template
- TEMPLATE_PLAYBOOK.yml
# File: TEMPLATE_PLAYBOOK.yml # Description: PLAYBOOK DESCRIPTION HERE # Last Updated: 2018-03-15 # Recent Changes:-Initial release # hosts: group_name or 'all' - hosts: - group_name_here # roles: located in ../roles/ roles: # role: role to assign to hosts, tags: tag(s) to give entire role - { role: role-name, tags: tag-name } # Do not gather host facts for this playbook (comment out/remove if you need facts) gather_facts: no
- Edit the new playbook (vim ansible/playbooks/my_new_playbook.yml)
- hosts: my_ansible_group roles: - { role: my-new-role, tags: my-new-role }
- In the above example, the playbook “my_new_playbook” states that the group “my_ansible_group” has the role of “my-new-role”, and all tasks in the role “my-new-role” has the tag “my-new-role”
- The tag can be anything, but keeping it the same as the role name is useful if you want to limit the execution of a playbook to a specific role using the “tags” command line argument to ansible-playbook
- Tags are inherited at all tasks below it.
- When the playbook is executed, the tasks defined in the role will be executed against the group
- Test Playbook
- See the next section for details on testing the playbook before committing changes to a repo.
Playbook Testing
Test your playbook before committing it to a repo.
- Add these functions to your shell's config
#bash = ~/.bashrc #zsh = ~/.zshrc # Enable Ansible test environment ansible_local_enable(){ export ANSIBLE_INVENTORY="${HOME}/repos/ansible/inventories" export ANSIBLE_ROLES_PATH="${HOME}/repos/ansible/roles" echo -e "Ansible environmental variables set to:" env | grep ANSIBLE } # Disable Ansible test environment ansible_local_disable(){ unset ANSIBLE_INVENTORY unset ANSIBLE_ROLES_PATH echo -e "Ansible environmental variables have been unset. There should be no variable results after this line." env | grep ANSIBLE }
- Source the file
#bash source ~/.bashrc #zsh source ~/.zshrc
- Enable local variables
ansible_local_enable
- Test playbook locally
- Syntax check
ansible-playbook --syntax-check ${HOME}/repos/ansible/playbooks/my_new_playbook.yml
- List tasks
ansible-playbook -b -i ${HOME}/repos/ansible/inventories/development/dev ${HOME}/repos/ansible/playbooks/my_new_playbook.yml --list-tasks
- Run against a test system
ansible-playbook -b -i ${HOME}/repos/ansible/inventories/development/dev ${HOME}/repos/ansible/playbooks/my_new_playbook.yml --limit mytestsystem
- Tests successful, disable local variables
ansible_local_disable
- Commit playbook/role to the repo
Playbook Commands
Different methods to run playbooks.
WARNING: If you do NOT specify an inventory file with “-i PATH”, the playbook will run against ALL HOSTS in the group, which is probably not what you want.
NOTE: If you need to use group_vars per inventory type (dev/test/prod), the full path to the inventory file needs to be used. (Example: /ansible/inventories/development/hosts )
Playbook Commands: Syntax Check
After creating a playbook, it is useful to check the syntax before running it to catch obvious errors.
Run a syntax check (will NOT execute the playbook)
ansible-playbook --syntax-check /ansible/playbooks/my_playbook.yml
Playbook Commands: Aliases
Playbook commands can get rather long, some useful aliases to shorten them.
Put in your ~/.bashrc or ~/.zshrc file
# Ansible aliases alias apd='ansible-playbook -b -i /ansible/inventories/development/hosts' alias apt='ansible-playbook -b -i /ansible/inventories/systest/hosts' alias app='ansible-playbook -b -i /ansible/inventories/production/hosts'
Additionally, create a symlink to playbooks
ln -s /ansible/playbooks /playbooks
Now, your playbook commands can look like this
# Dev inventory apd /playbooks/myplaybook.yml # Test inventory apt /playbooks/myplaybook.yml # Prod inventory app /playbooks/myplaybook.yml
- limits, tags, etc can also be appended as normal.
Playbook Commands: List
Preparation/sanity check commands to do before running a playbook.
List what hosts the playbook will run against (from dev inventory)
ansible-playbook -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --list-hosts
- -i or –inventory → Path to the inventory (alternative is a comma separated list of hosts or single hostname with a trailing comma)
List what tasks the playbook will execute (from dev inventory)
ansible-playbook -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --list-tasks
Playbook Commands: Run
Commands to actually run a playbook. You SHOULD do the list commands first to make sure what you are about to run is expected.
- The list commands (–list-hosts and –list-tasks) can be appended to any of the following commands to check them first before running.
Run playbook (dev env; all in group) against all system groups defined in playbook from dev inventory using sudo for privilege escalation
ansible-playbook -b -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml
- -b or –become → Use privilege escalation (default of sudo)
Run playbook (dev env; all in group; only configure) against all groups defined in playbook from dev inventory using sudo for privilege escalation, only execute actions with the matched tags “configure”
ansible-playbook -b -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --tags configure
- –tags configure → Only execute tasks in the playbook that have been tagged “configure”
- roles, import_tasks, or individual tasks can be tagged
Run playbook (dev env; range of systems) against all groups defined in playbook from dev inventory using sudo for privilege escalation, further limit to hosts web01-05“
ansible-playbook -b -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --limit "$(echo web{01..05})"
- –limit “$(echo web{01..05})” → Filter results of host match to only include these systems
Run playbook (dev env; one system) against all groups defined in playbook from dev inventory, limit to a single hostname (MYHOSTNAME)
ansible-playbook -i /ansible/inventories/development/hosts /ansible/playbooks/my_playbook.yml --limit MYHOSTNAME