Simplify playbook with roles

Posted by PHPHorizons on Mon, 03 Jan 2022 18:35:50 +0100

Simplify playbook with roles

Constructing an ansible playbook using roles

As more playbooks are developed, we may find many opportunities to reuse the code in the previously abbreviated playbook. Perhaps, a play used to configure a MySQL database for an application can change its purpose by using different host names, passwords and users to configure a MySQL database for another application.

But in reality, this play may be lengthy and complex, with many included or imported files, as well as tasks and handlers for managing various situations. Copying all this code into another playbook can be difficult.

The Ansible role provides a way for users to more easily reuse Ansible code in a common way. We can package all tasks, variables, files, templates, and other resources required to provision infrastructure or deploy applications in a standardized directory structure. Simply copy the roles from one project to another by copying the relevant directories. Then, just call the role from a play to execute it.

With the help of well written roles, you can pass variables to adjust their behavior from the playbook, and set all site related host names, IP addresses, user names, or other specific details required locally. For example, the role of deploying a database server may have been written to support multiple variables for setting the host name, database administrator user and password, and other parameters that need to be customized for installation. The role author can also ensure that reasonable default values are set for these variables when choosing not to set variable values in play.

The Ansible role has the following advantages:

  • Roles can group content to easily share code with others

  • You can write roles to define the basic elements of a system type: a Web server, a database server, a Git repository, or for other purposes

  • Roles make larger projects easier to manage

  • Roles can be developed in parallel by different administrators

    In addition to writing, using, reusing, and sharing roles yourself, you can also obtain roles from other sources. Some roles have been included in the RHEL system roles package, and users can also obtain many roles supported by the community from the Ansible Galaxy website.

Check the ansible role structure

The Ansible role is defined by the standardized structure of subdirectories and files. The top-level directory defines the name of the role itself. The files are sorted into subdirectories, which are named according to the purpose of each file in the role, such as tasks and handlers. The files and templates subdirectories contain files referenced by tasks in other YAML files.

site.yml
webservers.yml
fooservers.yml
roles/
    common/
        tasks/
        handlers/
        files/
        templates/
        vars/
        defaults/
        meta/
    webservers/
        tasks/
        defaults/
        meta/

The following tree command shows user Example directory structure of role:

[root@localhost roles]# tree user.example/
user.example/
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
    └── main.yml

Ansible role subdirectory

Subdirectoryfunction
defaultsMain. In this directory The YML file contains default values for role variables that can be overridden when using roles. These variables have low priority and should be changed and customized in play.
filesThis directory contains static files referenced by role tasks.
handlersMain. In this directory The YML file contains the handler definition for the role.
metaMain. In this directory The YML file contains role related information such as author, license, platform, and optional role dependencies.
tasksMain. In this directory The YML file contains the task definition of the role.
templatesThis directory contains Jinja2 templates referenced by role tasks.
testsThis directory can contain the manifest and the name test The playbook of YML can be used to test roles.
varsMain. In this directory The YML file defines variable values for roles. These variables are usually used for internal purposes of roles. These variables have higher priority and should not be changed when used in playbook.

Not every role has all of these directories.

Define variables and defaults

Role variables are created by creating vars / main with key value pairs in the role directory hierarchy YML file. Like other variables, these role variables are referenced in the role YAML file: {{VAR_NAME}}. These variables have high priority and cannot be overwritten by the list variables. These variables are intended to be used by the role's internal functions.

Default variables allow you to set default values for variables that can be used in play to configure roles or customize their behavior. They do this by creating defaults / main with key value pairs in the role directory hierarchy YML file. The default variable has the lowest priority of any available variable. They can easily be overridden by any other variable, including the manifest variable. These variables are designed to allow users to accurately customize or control the actions it will perform when writing a play using the role. They can be used to provide roles with the information they need to properly configure or deploy certain objects.

In vars / main YML or defaults / main Specific variables are defined in YML, but not in both. When you intend to override the value of a variable, you should use the default variable.

Note: roles should not contain site-specific data. They should never contain any secrets, such as passwords or private keys. This is because roles should be generic, reusable and freely shared. Site specific details should not be hard coded into roles. Confidentiality should be provided to the role through other means. This is one reason why users may want to set role variables when calling roles. The role variable set in play can provide a secret or point to an Ansible Vault encrypted file containing the secret.

Using the ansible role in playbook

Using roles in playbook is very simple. The following example demonstrates a way to call the Ansible role:

---
- hosts: remote.example.com
  roles:
    - role1
    - role2

For each specified role, role tasks, role handlers, role variables, and role dependencies are imported into the playbook in order. Any copy, script, template, or include in the role_ tasks/import_ Tasks tasks can reference related files, templates or task files in roles without relative or absolute path names. Ansible will look for them in the files, templates, or tasks subdirectories of the role, respectively.

If you import roles into a play using the roles section, these roles will run before any tasks defined by the user for the play.

The following example sets the values of the two role variables var1 and var2 of role2. When using role2, any defaults and vars variables are overwritten.

---
- hosts: remote.example.com
  roles:
    - role: role1
    - role: role2
      var1: val1
      var2: val2

The following is another equivalent YAML syntax that users may see in this case:

---
- hosts: remote.example.com
  roles:
    - role: role1
    - { role: role2, var1: val1, var2: val2 }

Although this writing is more concise, it is more difficult to read in some cases.

Important as shown in the previous example, the embedded role variables (role parameters) have a very high priority. They will override most other variables. Be careful not to reuse the name of any role variable embedded anywhere else in the play, because the value of the role variable will override the manifest variable and vars in any play.

Control execution sequence

For each play in the playbook, tasks are executed in the order in the task list. After all tasks are executed, the handler of the task notification will be executed.

After the role is added to the play, the role task will be added to the beginning of the task list. If the play contains a second role, its task list is added after the first role.

Role handlers are added to play in the same way that role tasks are added to play. Each play defines a list of handlers. The role handler is first added to the handler list, followed by any handler defined in the handlers section of play.

In some cases, you may need to perform some play tasks before the role. To support this scenario, you can configure pre for play_ Tasks section. All tasks listed in this section will be performed before performing any roles. If any of these tasks notifies the handler, these handler tasks are also executed before the role or normal task.

In addition, play also supports post_tasks keyword. These tasks are executed after the normal tasks of play and any handlers they notify run.

The following play demonstrates a with pre_tasks,roles,tasks,post_ Examples of tasks and handlers. A play usually does not contain all of these parts at the same time.

- name: Play to illustrate order of execution
  hosts: remote.example.com
  pre_tasks:
    - debug:
      msg: 'pre-task'
      notify: my handler
  roles:
    - role1
  tasks:
    - debug:
      msg: 'first task'
      notify: my handler
  post_tasks:
    - debug:
      msg: 'post-task'
      notify: my handler
  handlers:
    - name: my handler
      debug:
        msg: Running my handler

In the above example, the debug task is executed in each section to notify the my handler handler. The my handler task was executed three times:

  • After executing all pre_ After tasks
  • After performing all the role tasks and tasks in the tasks section
  • After all posts are executed_ After tasks

In addition to including roles in the roles section of the play, you can also add roles to the play using normal tasks. Use include_ The role module can dynamically include roles, using import_role module can statically import roles.

The following playbook demonstrates how to use include_role module to use tasks to include roles.

- name: Execute a role as a task
  hosts: remote.example.com
  tasks:
    - name: A normal task
      debug:
        msg: 'first task'
    - name: A task to include role2 here
      include_role: role2

be careful
include_ The role module is added in Ansible 2.3, while import_ The role module is added in Ansible 2.4.

Reuse content with system roles

Red Hat Enterprise Linux system role

From rhel7 Starting with 4, the operating system comes with multiple Ansible roles as part of the RHEL system roles package. In RHEL8, the package can be obtained from AppStream. The following is a brief description of each role:

RHEL system roles

namestateRole description
rhel-system-roles.kdumpFull supportConfigure kdump crash recovery service
rhel-system-roles.networkFull supportConfigure network interface
rhel-system-roles.selinuxFull supportConfigure and manage SELinux customizations, including SELinux mode, file and port context, Boolean settings, and SELinux users
rhel-system-roles.timesyncFull supportConfigure time synchronization using network time protocol or precise time protocol
rhel-system-roles.postfixTechnical Preview Configure each host as a mail transfer agent using the Postfix service
rhel-system-roles.firewallUnder developmentConfigure firewall for host
rhel-system-roles.tunedUnder developmentConfigure tuned services to tune system performance

The purpose of the system role is to standardize the configuration of the Red Hat Enterprise Linux subsystem among multiple versions. Use system roles to configure any Red Hat Enterprise Linux host version 6.10 and above.

Simplify configuration management

For example, RHEL7's recommended time synchronization service is chronyd service. However, in RHEL6, the recommended service is ntpd service. In an environment where RHEL6 and 7 hosts are mixed, the administrator must manage the configuration files of these two services.

With RHEL system role, the administrator no longer needs to maintain the configuration files of these two services. Administrators can use RHEL system roles The TimeSync role is used to configure the time synchronization of RHEL6 and 7 hosts. A simplified YAML file containing role variables can define time synchronization configurations for both types of hosts.

Install RHEL system roles

RHEL system roles are provided by the RHEL system roles package, which can be obtained from the AppStream stream stream. Install the package on the Ansible control node.

Install RHEL system roles

yum -y install rhel-system-roles

After installation, RHEL system roles are located in the * * / usr/share/ansible/roles * * Directory:

ls -l /usr/share/ansible/roles/
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.certificate
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.crypto_policies
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.ha_cluster
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.kdump
drwxr-xr-x. 10 root root 4096 8 January 3:53 rhel-system-roles.kernel_settings
drwxr-xr-x.  7 root root 4096 8 January 3:53 rhel-system-roles.logging
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.metrics
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.nbde_client
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.nbde_server
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.network
drwxr-xr-x.  7 root root 4096 8 January 3:53 rhel-system-roles.postfix
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.selinux
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.ssh
drwxr-xr-x.  9 root root  218 8 January 3:53 rhel-system-roles.sshd
drwxr-xr-x. 10 root root 4096 8 January 3:53 rhel-system-roles.storage
drwxr-xr-x. 10 root root 4096 8 January 3:53 rhel-system-roles.timesync
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.tlog
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.vpn

Default roles in Red Hat Enterprise Linux_ Path contains * * / usr/share/ansible/roles * * in the path, so Ansible can easily find these roles when playbook references them.

be careful
If roles are overwritten in the current Ansible configuration file_ Path, setting the environment variable ANSIBLE_ROLES_PATH, or roles_ If another role with the same name exists in the directory listed earlier in path, Ansible may not be able to find the system role.

Access documentation for RHEL system roles

[root@localhost ~]# ll /usr/share/ansible/roles/
Total dosage 68
......(Link above)
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.certificate
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.crypto_policies
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.ha_cluster
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.kdump
drwxr-xr-x. 10 root root 4096 8 January 3:53 rhel-system-roles.kernel_settings
drwxr-xr-x.  7 root root 4096 8 January 3:53 rhel-system-roles.logging
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.metrics
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.nbde_client
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.nbde_server
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.network
drwxr-xr-x.  7 root root 4096 8 January 3:53 rhel-system-roles.postfix
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.selinux
drwxr-xr-x.  8 root root 4096 8 January 3:53 rhel-system-roles.ssh
drwxr-xr-x.  9 root root  218 8 January 3:53 rhel-system-roles.sshd
drwxr-xr-x. 10 root root 4096 8 January 3:53 rhel-system-roles.storage
drwxr-xr-x. 10 root root 4096 8 January 3:53 rhel-system-roles.timesync
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.tlog
drwxr-xr-x.  9 root root 4096 8 January 3:53 rhel-system-roles.vpn

The document directory for each role contains a readme MD file. README. The MD file contains a description of the role and role usage information.

​ README. The MD file also describes the role variables that affect the role's behavior. Generally, readme The MD file contains a playbook code fragment to demonstrate the variable settings of common configuration scenarios.

Some role document directories contain sample playbooks. When you use a role for the first time, look at any additional example playbook in the documentation directory.

The role documentation of RHEL system roles matches that of Linux system roles. Use your Web browser to access the Web site at Ansible Galaxy https://galaxy.ansible.com/docs/ Role document on.

Time synchronization role example

Suppose you need to configure NTP time synchronization on the server. We can write our own automation to perform each necessary task. However, one of the RHEL system roles that can perform this operation is RHEL system roles timesync.

The detailed record of this role is located in readme.com under * * / usr / share / Doc / RHEL system roles / TimeSync directory MD * *. This file describes all the variables that affect the character's behavior and contains three playbook code snippets that demonstrate different time synchronization configurations.

In order to manually configure the NTP server, this role has a named TimeSync_ ntp_ Variable for servers. This variable takes a list of NTP servers to use as its value. Each item in the list consists of one or more properties. Two key attributes are as follows:

timesync_ntp_servers property

attributepurpose
hostnameThe host name of the NTP server to synchronize with.
iburstA Boolean value that enables or disables fast initial synchronization. The default is no in roles, but the attribute should usually be set to yes

Based on this information, the following example play uses RHEL system roles The TimeSync role configures the managed host to obtain time from three NTP servers using fast initial synchronization. In addition, a task has been added to set the time zone of the host to UTC using the timezone module.

- name: Time Synchronization Play
  hosts: servers
  vars:
    timesync_ntp_servers:
      - hostname: 0.rhel.pool.ntp.org
        iburst: yes
      - hostname: 1.rhel.pool.ntp.org
        iburst: yes
      - hostname: 2.rhel.pool.ntp.org
        iburst: yes
    timezone: UTC
    
  roles:
    - rhel-system-roles.timesync

  tasks:
    - name: Set timezone
      timezone:
        name: "{{ timezone }}"

be careful
If you want to set a different time zone, you can use the tzselect command to query other valid values. You can also use the timedatectl command to check the current clock setting.

Set time synchronization to Alibaba cloud

[root@localhost ~]# rpm -qa |grep chrony  
chrony-4.1-1.el8.x86_64                      # Check whether time synchronization is installed on the managed host. If not, perform the installation
[root@localhost ~]# yum -y install chrony

[root@localhost ~]# cat /etc/chrony.conf | head   # Check the configuration file
# # This is a comment

pool 2.centos.pool.ntp.org iburst                # This line is where it will change later. It will be synchronized to Alibaba cloud

# Allow the system clock to be stepped in the first three updates.
makestep 1.0 3

# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

stay ansible Control end installation RHEL System role
[root@localhost ~]# yum -y install rhel-system-roles

The post installation view path is
[root@localhost ~]# ls /usr/share/ansible/roles/

Time synchronization uses
rhel-system-roles.timesync

Create a role directory,Copy it to/etc/ansible/roles Next, and change to timesync
[root@localhost ~]# mkdir roles
[root@localhost ~]# cp -a /usr/share/ansible/roles/rhel-system-roles.timesync roles/timesync

Create a playbook Synchronizing time, using roles timesync,Execute this playbook
[root@localhost ansible]# cat mytime.yml 
---
- hosts: 192.168.220.8
  vars:
    timesync_ntp_servers:
        - hostname: time1.aliyun.com  # The synchronization time is Alibaba cloud
          iburst: yes
  roles:
    - timesync
    
directory structure
[root@localhost ansible]# tree /etc/ansible/
/etc/ansible/
├── mytime.yml   # The playbook is executed
├── roles       
│   └── timesync # This role is used
│       ├── ansible_pytest_extra_requirements.txt
│       ├── COPYING
│       ├── custom_requirements.txt
│       ├── defaults
│       │   └── main.yml
│       ├── handlers
│       │   └── main.yml
│       ├── library
│       │   └── timesync_provider.sh
│       ├── meta
│       │   └── main.yml
│       ├── molecule_extra_requirements.txt
│       ├── pylint_extra_requirements.txt
│       ├── pylintrc
│       ├── pytest_extra_requirements.txt
│       ├── README.html
│       ├── README.md                       # No, look at the sample in this file
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       │   ├── chrony.conf.j2
│       │   ├── chronyd.sysconfig.j2
│       │   ├── ntp.conf.j2
│       │   ├── ntpd.sysconfig.j2
│       │   ├── phc2sys.sysconfig.j2
│       │   ├── ptp4l.conf.j2
│       │   ├── ptp4l.sysconfig.j2
│       │   └── timemaster.conf.j2
│       ├── tests
│       │   ├── inventory.yaml.j2
│       │   ├── provision.fmf
│       │   ├── roles
│       │   ├── tests_chrony.yml
│       │   ├── tests_default_vars.yml
│       │   ├── tests_default_wrapper.yml
│       │   ├── tests_default.yml
│       │   ├── tests_ntp_provider1.yml
│       │   ├── tests_ntp_provider2.yml
│       │   ├── tests_ntp_provider3.yml
│       │   ├── tests_ntp_provider4.yml
│       │   ├── tests_ntp_provider5.yml
│       │   ├── tests_ntp_provider6.yml
│       │   ├── tests_ntp_ptp.yml
│       │   ├── tests_ntp.yml
│       │   ├── tests_ptp_multi.yml
│       │   └── tests_ptp_single.yml
│       ├── tox.ini
│       └── vars
│           ├── CentOS_6.yml
│           ├── CentOS_9.yml
│           ├── default.yml
│           ├── Fedora_33.yml
│           ├── main.yml
│           ├── RedHat_6.yml
│           └── RedHat_9.yml

Managed host viewing synchronization
[root@localhost ~]# cat /etc/chrony.conf | head 
# # This is a comment

server time1.aliyun.com iburst      # Synced to Alibaba cloud

# Allow the system clock to be stepped in the first three updates.
makestep 1.0 3

# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

This example sets role variables in the vars section of play, but it might be better to configure them as manifest variables for hosts or host groups.

For example, a playbook project has the following structure:

[root@localhost playbook-project]# tree
.
├── ansible.cfg
├── group_vars
│   └── servers
│       └── timesync.yml
├── inventory
└── timesync_playbook.yml

timesync.yml defines the time synchronization variable to override the default value of the role of the host in the servers group in the list. This file looks like:

timesync_ntp_servers:
  - hostname: 0.rhel.pool.ntp.org
    iburst: yes
  - hostname: 1.rhel.pool.ntp.org
    iburst: yes
  - hostname: 2.rhel.pool.ntp.org
    iburst: yes
timezone: UTC

timesync_ playbook. The content of YML is simplified to:

- name: Time Synchronization Play
  hosts: servers
  roles:
    - rhel-system-roles.timesync
  tasks:
    - name: Set timezone
      timezone:
        name: "{{ timezone }}"

This structure clearly separates roles, playbook code, and configuration settings. PlayBook code is easy to read and should not require complex refactoring. The role content is maintained and supported by red hat. All settings are handled as manifest variables.

The structure also supports dynamic heterogeneous environment. Hosts with new time synchronization requirements may be placed in a new host group. The corresponding variables are defined in the YAML file and placed in the corresponding group_vars (or host_vars) subdirectory.

SELINUX role example

rhel-system-roles.selinux role can simplify the management of SELinux configuration settings. It is implemented by using the Ansible module related to SELinux. The advantage of using this role over writing tasks yourself is that it frees users from the responsibility of writing these tasks. Instead, the user will provide variables to the role to configure it, and the code maintained in the role will ensure that the SELinux configuration required by the user is applied.

The tasks that this role can perform include:

  • Set the enforcing or permissive mode
  • Run restorecon on parts of the file system hierarchy
  • Set SELinux Boolean
  • Permanently set SELinux file context
  • Set SELinux user mapping

Call SELinux role

Sometimes, the SELinux role must ensure that the managed host is rebooted so that its changes can be fully applied. However, it never reboots the host itself. In this way, the user can control how the reboot is handled.

It works by placing a boolean variable SELinux in the role_ reboot_ Required is set to true and fails if a reboot is required. You can use the block / resume structure to recover from failures. The specific operations are: if the variable is not set to true, let play fail; if the value is true, reboot the managed host and re run the role. The blocks in play should look like:

First, the role/usr/share/ansible/roles/rhel-system-roles.selinux Copy to/etc/ansible/roles/The next name is selinux,Then in/etc/ansible/Create under selinux.yml of playbook,Write in it

Purpose: to transfer the managed host's selinux Set to enforcing
[root@localhost ansible]# cat selinux.yml 
---
- hosts: 192.168.220.8
  vars:
    selinux_state: enforcing 
  tasks:
    - name: selinux role
      block:
        - include_role:     # Include role
            name: selinux   # The role is selinux
      rescue:
        - name: If not started
          fail:
          when: not selinux_reboot_required

        - name: reboot
          reboot:

        - name: seseliunx
          include_role:
            name: selinux

# To use the include role after the task, use the following before the task:
# roles:
 #  -timesync

Configuring SELinux roles

Used to configure RHEL system roles The detailed record of the variables of the SELinux role is located in its readme MD file. The following example demonstrates some ways to use this role.

selinux_ The state variable sets the running mode of SELinux. It can be set to enforced, permitted, or disabled. If not set, the mode is not changed.

selinux_state: enforcing

selinux_ The boolean variable takes a list of SELinux Boolean values to be adjusted as the value. Each item in the list is a hash / dictionary of variables: the name and state of the Boolean value (should it be on or off), and whether the setting should be persistent after reboot.

This example will httpd_enable_homedirs is permanently set to on:

selinux_booleans:
  - name: 'httpd_enable_homedirs'
    state: 'on'
    persistent: 'yes'

selinux_ The fcontext variable takes a list of file contexts to be permanently set (or deleted) as its value. It works very similar to the selinux fcontent command.

The following example ensures that the policy contains a rule to set the default SELinux type of all files under / srv/www to httpd_sys_content_t.

selinux_fcontexts:
  - target: '/srv/www(/.*)?'
    setype: 'httpd_sys_content_t'
    state: 'present'

selinux_ restore_ The dirs variable specifies the list of directories on which to run restorecon:

selinux_restore_dirs:
  - /srv/www

selinux_ The ports variable takes the list of ports that should have a specific SELinux type as its value.

selinux_ports:
  - ports: '82'
    setype: 'http_port_t'
    proto: 'tcp'
    state: 'present'

Topics: Linux