linux_wiki:ansible

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.

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 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

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

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

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 are for one off commands that are not saved in a playbook.


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

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

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"

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


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 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.

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

  1. Navigate to your local copy of the version controlled ansible repo. Example:
    cd ${HOME}/repos/ansible/
  2. Update your local copy of the version controlled ansible repo to the latest version
    #git
    git pull
     
    #svn
    svn update
  3. New Role
    1. Navigate to the roles directory, then copy your role template directory to a new name
      cd roles/
      cp -R template-role/ my-new-role
    2. Modify the role's files as needed to create tasks, files, handlers, etc.
      1. Download zip archive of an example role template.
  4. New Playbook
    1. Navigate to the playbooks directory
      cd ${HOME}/repos/ansible/playbooks/
    2. Copy your playbook template to a new playbook yaml file.
      cp TEMPLATE_PLAYBOOK.yml my_new_playbook.yml
      1. 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
    3. Edit the new playbook (vim ansible/playbooks/my_new_playbook.yml)
      - hosts: my_ansible_group
        roles:
          - { role: my-new-role, tags: my-new-role }
      1. 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”
        1. 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
        2. Tags are inherited at all tasks below it.
      2. When the playbook is executed, the tasks defined in the role will be executed against the group
  5. Test Playbook
    1. See the next section for details on testing the playbook before committing changes to a repo.

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

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 )


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 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.

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

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

  • linux_wiki/ansible.txt
  • Last modified: 2019/05/25 23:50
  • (external edit)