Ansible: Difference between revisions

From Leo's Notes
This page was last edited on 14 September 2020, at 23:15.
No edit summary
No edit summary
Line 28: Line 28:
}}<br />
}}<br />


== Usage ==
==Usage==
All tasks are written as a list item. Each list item must be either a task or a call to include another file containing tasks.
All tasks are written as a list item. Each list item must be either a task or a call to include another file containing tasks.
{| class="wikitable"
{| class="wikitable"
Line 100: Line 100:
|Debug messages
|Debug messages
|-
|-
|shell
|command
|
|{{Highlight
|
| code = - name: Extract archive
<nowiki>  command: chdir=/srv/ /bin/tar xvf wordpress-{ { wp_version } }.tar.gz creates=/srv/wordpress</nowiki>
| lang = yaml
}}
|Runs a command
|-
|-
|file
|file
Line 150: Line 154:
|-
|-
|import_role
|import_role
|name:  
|name:
|imports an entire role by its name
|imports an entire role by its name
|-
|-
Line 238: Line 242:




== Concepts ==
==Concepts==


===Inventory===
===Inventory===
Line 333: Line 337:
}}
}}


=== Roles ===
===Roles===
Roles define a set of tasks that should be done. For example, you may want to create a webserver role that contains instructions on setting up Apache and another database role with instructions on setting up MySQL. Creating a new role involves creating the following directory structure:
Roles define a set of tasks that should be done. For example, you may want to create a webserver role that contains instructions on setting up Apache and another database role with instructions on setting up MySQL. Creating a new role involves creating the following directory structure:
{{Highlight
{{Highlight
Line 375: Line 379:
}}<br />
}}<br />


==== Other Notes on roles ====
====Other Notes on roles====
Roles can be included as dependencies. Under meta, define something like:
Roles can be included as dependencies. Under meta, define something like:
{{Highlight
{{Highlight

Revision as of 23:15, 14 September 2020


Quick Overview

Ansible is an open source application that automates configuration management and software/application deployment. It manages remote hosts via SSH or PowerShell. Tasks can be executed on one or more machines which can be on different networks. Hosts can be targeted specifically or by groups defined in an inventory file.

Commands are executed through modules. Modules can take in any number of variables. Modules can also return data back to Ansible which can be used later on by other modules. Common modules that are in use are shell, for running shell commands. A single module can be executed via the ansible command. Multiple modules can be executed through the use of a playbook which describes the list of tasks to perform.

Inventory contains a list of hosts managed by Ansible. By default, it uses the hosts file at /etc/ansible/hosts. However, this can be changed to something else such as a static .ini file, a .yaml file using the -i parameter or to some dynamic method of obtaining hosts.

Tasks are used by playbooks to execute a module with a specific set of arguments. Ansible goes through a playbook in sequential order as it executes tasks that match the host pattern that is given.

Ansible uses Jinja as its template engine and can be used to render configuration files.

Quick Demo

The quickest way to get your toes wet with Ansible is to try it out with a small set of Docker containers. Using the two docker images (TODO: link to docker files) and docker-compose, we can bring up a small lab to test Ansible with (TODO: link to docker-compose.yml).

Each container has an ansible account with ansible as the password. Using ansible, you can reach all of them using password authentication with the --ask-pass flag. To make the containers use SSH keys going forward, generate a new SSH key on the head node, then run this (TODO: link to playbook) playbook.

You may want to turn off SSH host key checking by creating an ansible.cfg file:

[defaults]
host_key_checking = false

You can then ping all the nodes by running:

# ansible -i inventory.ini all -m shell -a 'hostname'


Usage

All tasks are written as a list item. Each list item must be either a task or a call to include another file containing tasks.

Module parameter Description
yum/dnf
- name: Install NTP
   yum:
     name: ntp
     state: present

 - name: Install common packages
   dnf:
     name:
     - git
     - vim
     - zsh
Installs a package.

You may also use loops to call the module multiple times for each item. Eg:

- name: Install packages
  dnf:
    name: "{{ item }}"
  with_items:
  - git
  - vim

This is something which isn't required for the yum/dnf modules since version 2.0.

template
- name: Configure NTP
  notify: restart ntp
  template:
    src: ntp.conf.j2
    dest: /etc/ntp.conf
Copy a templated file.

Notice that we also call 'restart ntp' when the file is changed. 'restart ntp' is a task defined under the handlers/main.yml file.

Template may include variables passed to this playbook.

service
- name: Start the ntp service
  service: name=ntpd state=started enabled=yes

- name: restart ntp
  service: name=ntpd state=restarted
Ensures something is started and enabled on startup
debug
- name: "testing something"
  debug:
    msg: "tem: {{ item }}"
  with_items:
    - 1
    - 2
Debug messages
command
- name: Extract archive
  command: chdir=/srv/ /bin/tar xvf wordpress-&#123; { wp_version } }.tar.gz creates=/srv/wordpress
Runs a command
file
- name: Create MariaDB log file
  file: path=/var/log/mysqld.log state=touch owner=mysql group=mysql mode=0775

- name: Create MariaDB PID directory
  file: path=/var/run/mysqld state=directory owner=mysql group=mysql mode=0775
Creates a file or directory
firewalld
- name: insert firewalld rule
  firewalld:
    port: { { mysql_port }}/tcp
    permanent: true
    state: enabled
    immediate: yes
Manages the firewall
git
- name: Copy the code from repository
  git:
    repo: { { repository }}
    dest: /var/www/html/
You can clone an entire git repository to some location.

Note that an entire task can be written in one line.

Module parameter Description
import_tasks filename Imports a set of tasks from the given yaml file.
import_role name: imports an entire role by its name

conditionally load something with when: "some value". You can load facts from ansible_facts[]. Eg. when: "ansible_facts['os_family'] == 'RedHat'"

More about loops https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#playbooks-loops


ansible -m shell -a 'whoami' all


ansible -b -K -m user -a 'name=testuser' all

-b for sudo

-K to ask for sudo password

-a to specify a variable, such as a user.


roles is a list of actions to run on a target machine in a given order

playbooks determine which roles to run


mkdir roles/basic/tasks

vi roles/basic/tasks/main.yaml

-name: 'install vim'

apt: pkg=vim state=installed


playbook.yaml

- hosts: all

become: true

roles:

- basic


ansible-playbook playbook.yaml

pass in -K to run as sudo as well


roles can be passed lists

-name: 'install item'

apt: pkg={{item }} state = installed

with_items:

- asdf


copy files over

-name: 'add file'

copy: src=./pwd/files/bash.bashrc dest=/etc/bashrc owner=root group=root mode=0644

files are copied from the role's sub directory.

mkdirroles/basic/files/bash.bashrc


run shell command with the shell module

-name: 'run something'

shell: creates=/asdfasdf mv /etc/bashrc /home/bash.rc

Runs command if the creates file does not exist.


Concepts

Inventory

Either static (.ini or .yaml) or dynamic. Located at /etc/ansible/hosts and each host may define specific parameters.

[webservers]
web1.example.com ansible_user=bob
web2.example.com  ansible_host=192.168.81.142 ansible_port=3333

[apservers]
ap1.example.com
ap2.example.com

[dbservers]
db[01:10].example.com

Can be grouped (webserers, apservers), or left ungrouped at the very top (for .ini files only). The 'all' group captures everything too. Groups can be nested. Use the ':' in group names for .ini files to represent hierarchy. Ranges can also be specified with brackets.

ansible -i /path/to/inventory all -m shell -a 'echo hello world' -f 5

Additional variables can be set as well. Variables can either be set to each host, or to a group. Since groups can be nested, the most immediate group variable takes presidence.

Playbooks

A series of tasks to configure something can be bundled into a playbook. Playbooks can be applied with the ansible-playbook command.

When applying a playbook, the recap will output the following:

  • ok - task ran successfully with no changes being made
  • changed - task ran successfully with changes being made
  • failed - task failed to run
  • unreachable - host was unreachable
  • skipped - task was skipped
  • ignored - task was ignored
  • rescued - ?

playbooks look like this:

---
- name: Handler demo 1
  hosts: frt01.example.com
  gather_facts: no
  become: yes

  tasks:
    - name: Update Apache configuration
      template:
        src: template.j2
        dest: /etc/httpd/httpd.conf
      notify: Restart Apache

    - name: restart all services
      command: echo "this task will restart all services"
      notify: "restart all services"

  handlers:
    - name: Restart Apache
      service:
        name: httpd
        state: restarted
      listen: "restart all services"

Handlers can be called with notify. Handlers can also listen for a specific event with the listen directive.

Loops can be done by specifying a list of items:

---
- name: Install Apache
  hosts: frt01.example.com
  gather_facts: no
  become: yes

  tasks:
    - name: Install Apache package
      yum:
        name: httpd
        state: latest
    - name: Open firewall for Apache
      firewalld:
        service: "[[:Template:Item]]"
        permanent: yes
        state: enabled
        immediate: yes
      loop:
        - "http"
        - "https"
    - name: Restart and enable the service
      service:
        name: httpd
        state: restarted
        enabled: yes

Roles

Roles define a set of tasks that should be done. For example, you may want to create a webserver role that contains instructions on setting up Apache and another database role with instructions on setting up MySQL. Creating a new role involves creating the following directory structure:

roles/
   installapache/
     tasks/
     handlers/
     templates/
     vars/
     defaults/
   installtomcat/
     tasks/
     meta/

Each role should be its own subdirectory in the roles directory. Each role should also have folders for each of: tasks for all ansible tasks the role should perform, handlers containing all handlers in this role, files for files needed by the role, such as configuration files, templates containing templating data, vars containing values that override defaults set in the defaults directory, meta for any metadata needed for this role, such as dependency data. In each of these directories, the main.yml file will be read and used if it exists.

In the example above, we have a installapache role and installtomcat role.

Inside installapache/tasks/main.yml, we can load the appropriate set of tasks depending on the distribution:

- name: import a tasks based on OS platform 
  import_tasks: centos.yml 
  when: ansible_distribution == 'CentOS' 
- import_tasks: ubuntu.yml 
  when: ansible_distribution == 'Ubuntu'

We would then create a centos.yml and ubuntu.yml within this same tasks directory with all the tasks that should be done in this role. Eg.

---
- name: Install Apache using yum
  yum:
    name: "httpd"
    state: latest
- name: Start the Apache server
  service:
    name: httpd
    state: started


Other Notes on roles

Roles can be included as dependencies. Under meta, define something like:

dependencies:
  - role: common
    vars:
      some_parameter: 3
  - role: apache
    vars:
      apache_port: 80
  - role: postgres
    vars:
      dbname: blarg
      other_parameter: 12

Ansible Galaxy contains a bunch of other roles that can be used.

See Also

There are lots of resources online to help you get started. Some guides I've stumbled across which I found helpful were:

To set up the initial user, use something like the following. Use --ask-pass when running the playbook so that you log in using a password first, then this playbook will set up the SSH keys.