Playbooks

The DebOps Collection includes a set of Ansible playbooks pre-written for convenience. Below you will find more details about them and their usage within and outside DebOps.

How Ansible roles and playbooks work together

Just to recap: Ansible uses modules to perform tasks on hosts, these tasks can be combined into "playbooks" for greater functionality (variables, ability to get facts about hosts, easier repeated use, assigning playbooks to specific hosts or host groups). The contents of playbooks (list of tasks, variables, files and templates) can be further extracted into "roles" to allow even more flexibility and ability to use parts of the codebase in multiple places (the Don't Repeat Yourself or "DRY" * principle).

The roles are not usable on their own - Ansible can execute a role using the import_role module, but it won't be very useful, for example Ansible will not have any facts about that host. The playbook still needs to be present, to define a host-role relationship in larger environments, to join multiple roles together into usable services (for example a firewall role and webserver role working together to configure a HTTP service). Therefore to make things easier, playbooks for DebOps roles are provided with the project itself.

You can find more about Ansible playbooks themselves in the official documentation.

Default set of playbooks in DebOps

The default playbooks provided in DebOps try to create an fairly typical Debian or Ubuntu deployment. There's a set of common services expected to be configured everywhere; some of them have exceptions for specific cases like virtual machines or containers. The playbooks included in the project are not supposed to be modified; users can create their own playbooks if needed.

Location of playbooks in the system

Easy way of browsing playbooks is using the GitHub web interface. The link points to the master git branch, but you can select your preferred tag or branch if needed.

Playbooks can be found in different locations depending on the installation method. In the DebOps git repository, playbooks are located in the ansible/playbooks/ subdirectory. In the Python package (if installed on the UNIX user account), they can be found at ~/.local/lib/python3.9/site-packages/debops/_data/ansible/collections/ansible_collections/debops/debops/playbooks/ path (the Python version might differ). If DebOps was installed using Ansible Galaxy collection, playbooks will be located in the debops.debops collection, under the playbooks/ directory.

The directory structure will look similar to this:

user@host:~/src/debops$ tree -L 2 ansible/playbooks
ansible/playbooks/
├── bootstrap-ldap.yml
├── bootstrap-sss.yml
├── bootstrap.yml
├── common.yml
├── COPYRIGHT
├── layer/
│   ├── agent.yml
│   ├── app.yml
│   ├── common.yml
│   ├── env.yml
│   ├── hw.yml
│   ├── net.yml
│   ├── srv.yml
│   ├── sys.yml
│   └── virt.yml
├── ldap/
│   ├── get-uuid.yml
│   ├── init-directory.yml
│   └── save-credential.yml
├── reboot.yml
├── service/
│   ├── ansible.yml
│   ├── apache.yml
│   ├── ...
│   ├── yadm.yml
│   └── zabbix_agent.yml
├── site.yml
├── templates/
│   └── debops__tpl_macros.j2
├── tools/
│   ├── 6to4.yml
│   ├── debug.yml
│   └── dist-upgrade.yml
└── upgrade.yml

All of the *.yml files are the playbooks. The site.yml is the "main entry point" where most of the playbooks are included. One of the playbooks included is the layer/common.yml playbook, which includes a set of roles used on most of the hosts managed by DebOps (you can use it via a shortcut, common.yml, provided for backwards compatibility with older DebOps releases).

Playbooks and Ansible inventory groups

Each playbook defines an Ansible inventory group on which it will be applied. The [debops_all_hosts] inventory group is used in the layer/common.yml playbook, as well as all service playbooks of the roles that are included in the common set. Each playbook in the service/ subdirectory defines its own inventory group, [debops_service_<name>] which can be used to activate that specific service. This way, users can execute the site.yml playbook after selecting which services should be configured on which hosts using Ansible inventory groups. On the other hand, since each service has a corresponding playbook, it can be executed directly to narrow the scope of the current Ansible run.

DebOps services are organized into layers

Due to a large number of services managed by DebOps, they are organized in "layers", for example services which configure hardware directly, system services, networking, virtualization and so on. The playbooks are executed sequentially in order of appearance, so the layer order is important. Main order of layers is defined in the site.yml playbook, but if needed, each layer can be executed in the order specified by the user.

Check the playbooks in the ansible/playbooks/layer/ directory to see the order of execution of specific services.

Note

This setup applies since DebOps v3.1.0, older releases use a set of symlinks to achieve a similar result.

How to use provided playbooks with Ansible directly

An example Ansible inventory which can be used with this set of playbooks to configure a nginx webserver:

# ansible/inventory/hosts

[debops_all_hosts]
server.example.org

[debops_service_nginx]
server.example.org

You can apply a specific playbook using the ansible-playbook command by specifying it directly. For example, with the cloned git repository in ~/src/debops/, you can run the command:

$ ansible-playbook ~/src/debops/ansible/playbooks/site.yml

This will apply the layer/common.yml playbook as well as the service/nginx.yml playbook. Since all project playbooks are involved, execution might be a little slow. To speed it up, you can execute specific playbooks:

$ ansible-playbook ~/src/debops/ansible/playbooks/layer/common.yml \
                   ~/src/debops/ansible/playbooks/service/nginx.yml

If DebOps collection has been installed from Ansible Galaxy, Ansible provides a way to execute playbooks from the collection directly:

$ ansible-playbook debops.debops.site

$ ansible-playbook debops.debops.layer.common \
                   debops.debops.service.nginx

The playbook syntax is <namespace>.<collection>.<playbook> with playbooks located in subdirectories separated by a dot (.).

How to use playbooks via the debops command

One of the reasons for the debops scripts was to make it easier for DebOps users to utilize the same set of Ansible playbooks in multiple environments (project directories). The debops command knows how to parse the list of short playbook names specified on the command line and find the corresponding playbooks in different places - the debops.debops Ansible Collection as a default, other Ansible Collections, the ansible/playbooks/ subdirectory of the project directory.

The above examples when executed using the debops script inside of a DebOps project directory, would look something like this:

$ debops run site

$ debops run layer/common service/nginx

As you can see, the syntax is a bit different - the debops script uses the slash (/) separator in the playbook paths. When slashes are found, the script tries to resolve the path to a given playbook by itself. If dots are used, the script passes the name of the playbook to Ansible so that the ansible-playbook can resolve the name using its internal mechanisms. This means that specifying just the playbook names using dots won't work:

$ debops run layer.common
ERROR! the playbook: layer.common could not be found

But if you specify the Ansible Collection in the playbook name, it will work fine:

$ debops run debops.debops.layer.common

You can also specify the collection in playbook paths with slashes, so that the debops script will resolve the path to playbook, not ansible-playbook:

$ debops run debops.debops/layer/common

You can find more details about the debops run and debops check commands syntax in the debops run documentation.

How to include DebOps playbooks in your own playbooks

A common question among DebOps users is a way to include their own playbooks or roles before or after DebOps playbooks. It's a bit tricky, since the official playbooks are supposed to be read-only. On the other hand, Ansible doesn't have a concept of "optional playbooks" - all playbooks specified in a given run, either via the command line or imported into other playbooks, must exist before Ansible execution can proceed. So in DebOps there's no way to provide some kind of hooks where external playbooks can be plugged in later.

Let's create an example playbook in the DebOps project directory, ansible/playbooks/custom.yml:

---

- name: Custom playbook
  hosts: 'debops_all_hosts'

  tasks:
    - name: Message the user that we are in a custom playbook
      ansible.builtin.debug:
        msg: 'Hello from a custom playbook'

This playbook can be executed by the debops script very easily:

$ debops run custom

The script will look in the ansible/playbooks/ project subdirectory, where it will be found. Since users can specify multiple playbooks at once on the command line, both the site.yml playbook and the custom playbook can be executed together, in either order:

$ debops run site custom

Another place where we can import DebOps playbooks is the custom playbook itself, using the ansible.builtin.import_playbook Ansible module. The playbook can be imported either before or after the custom "play", so we can control the execution order.

For example, we can import the site.yml playbook before our custom tasks, so that DebOps codebase is executed first, mirroring the above example. Here, we import a playbook from the cloned DebOps git repository (take note how the path to the user's home directory is expanded from the $HOME environment variable):

---

- name: Import DebOps playbooks
  ansible.builtin.import_playbook: '{{ lookup("env", "HOME")
                                       + "/src/debops/ansible/playbooks/site.yml" }}'

- name: Custom playbook
  hosts: 'debops_all_hosts'

  tasks:
    - name: Message the user that we are in a custom playbook
      ansible.builtin.debug:
        msg: 'Hello from a custom playbook'

If DebOps Collection has been installed from Ansible Galaxy, or the Python package has been installed, the playbooks can be referenced using the Fully Qualified Collection Name of the playbook:

---

- name: Import DebOps playbooks
  ansible.builtin.import_playbook: 'debops.debops.site'

- name: Custom playbook
  hosts: 'debops_all_hosts'

  tasks:
    - name: Message the user that we are in a custom playbook
      ansible.builtin.debug:
        msg: 'Hello from a custom playbook'

This way the custom playbook is a lot more portable and doesn't depend on the location of the imported playbooks in the filesystem.

The above examples focus on using the site.yml "entry point" playbook, but this doesn't have to be the only one playbook that can be imported. Any playbook in the DebOps collection can be imported either by referencing it in the filesystem, or through its FQCN - for example, users can import the debops.debops.layer.common playbook to have only the common roles included in their custom playbooks. For more flexibility, the site.yml playbook can be copied to the ansible/playbooks/ subdirectory in the DebOps project directory and modified to import custom playbook at any point; just be sure to not use the same name, and rename the playbook to, for example, mysite.yml so that the scripts can correctly find it.

Footnotes

*

Not named after drybjed.