- Practical Ansible 2
- Daniel Oh James Freeman Fabio Alessandro Locati
- 1805字
- 2021-06-24 16:06:53
Understanding roles – the playbook organizer
Roles are designed to enable you to efficiently and effectively reuse Ansible code. They always follow a known structure and often will include sensible default values for variables, error handling, handlers, and so on. Taking our Apache installation example from the previous chapter, we know that this is something that we might want to do over and over again, perhaps with a different configuration file each time, and perhaps with a few other tweaks on a per-server (or per inventory group) basis. In Ansible, the most efficient way to support the reuse of this code in this way would be to create it as a role.
The process of creating roles is in fact very simple—Ansible will (by default) look within the same directory as you are running your playbook from for a roles/ directory, and in here, you will create one subdirectory for each role. The role name is derived from the subdirectory name—there is no need to create complex metadata or anything else—it really is that simple. Within each subdirectory goes a fixed directory structure that tells Ansible what the tasks, default variables, handlers, and so on are for each role.
Let's explore this in a little more detail. Consider the following directory structure:
site.yml frontends.yml dbservers.yml roles/ installapache/ tasks/ handlers/ templates/ vars/ defaults/ installtomcat/ tasks/ meta/
The preceding directory structure shows two roles defined in our hypothetical playbook directory, called installapache and installtomcat. Within each of these directories, you will notice a series of subdirectories. These subdirectories do not need to exist (more on what they mean in a minute, but for example, if your role has no handlers, then handlers/ does not need to be created). However, where you do require such a directory, you should populate it with a YAML file named main.yml. Each of these main.yml files will be expected to have certain contents, depending on the directory that contained them.
The subdirectories that can exist inside of a role are as follows:
- tasks: This is the most common directory to find in a role, and it contains all of the Ansible tasks that the role should perform.
- handlers: All handlers used in the role should go into this directory.
- defaults: All default variables for the role go in here.
- vars: These are other role variables—these override those declared in the defaults/ directory as they are higher up the precedence order.
- files: Files needed by the role should go in here—for example, any configuration files that need to be deployed to the target hosts.
- templates: Distinct from the files/ directory, this directory should contain all templates used by the role.
- meta: Any metadata needed for the role goes in here. For example, roles are normally executed in the order they are called from the parent playbook—however, sometimes a role will have dependency roles that need to be run first, and if this is the case, they can be declared within this directory.
For the examples we will develop in this part of this chapter, we will need an inventory, so let's reuse the inventory we used in the previous section (included in the following for convenience):
[frontends]
frt01.example.com https_port=8443
frt02.example.com http_proxy=proxy.example.com
[frontends:vars]
ntp_server=ntp.frt.example.com
proxy=proxy.frt.example.com
[apps]
app01.example.com
app02.example.com
[webapp:children]
frontends
apps
[webapp:vars]
proxy_server=proxy.webapp.example.com
health_check_retry=3
health_check_interal=60
Let's get started with some practical exercises to help you to learn how to create and work with roles. We'll start by creating a role called installapache, which will handle the Apache installation process we looked at in the previous section. However, here, we will expand it to cover the installation of Apache on both CentOS and Ubuntu. This is good practice, especially if you are looking to submit your roles back to the community as the more general purpose they are (and the wider the range of systems they will work on), the more useful they will be to people. Step through the following process to create your first role:
- Create the directory structure for the installapache role from within your chosen playbook directory—this is as simple as this:
$ mkdir -p roles/installapache/tasks
- Now, let's create the mandatory main.yml inside the tasks directory we just created. This won't actually perform the Apache installation—rather, it will call one of two external tasks files, depending on the operating system detected on the target host during the fact-gathering stage. We can use this special variable, ansible_distribution, in a when condition to determine which of the tasks files to import:
---
- name: import a tasks based on OS platform
import_tasks: centos.yml
when: ansible_distribution == 'CentOS'
- import_tasks: ubuntu.yml
when: ansible_distribution == 'Ubuntu'
- Create centos.yml in roles/installapache/tasks to install the latest version of the Apache web server via the yum package manager. This should contain the following content:
---
- name: Install Apache using yum
yum: name: "httpd" state: latest
- name: Start the Apache server
service:
name: httpd
state: started
- Create a file called ubuntu.yml in roles/installapache/tasks to install the latest version of the Apache web server via the apt package manager on Ubuntu. Notice how the content differs between CentOS and Ubuntu hosts:
---
- name: Install Apache using apt
apt: name: "apache2" state: latest
- name: Start the Apache server
service:
name: apache2
state: started
For now, we're keeping our role code really simple—however, you can see that the preceding tasks files are just like an Ansible playbook, except that they lack the play definition. As they do not come under a play, they are also at a lower indentation level than in a playbook, but apart from this difference, the code should look very familiar to you. In fact, this is part of the beauty of roles: as long as you pay attention to getting the indentation level right, you can more or less use the same code in a playbook or a role.
Now, roles don't run by themselves—we have to create a playbook to call them, so let's write a simple playbook to call our newly created role. This has a play definition just like we saw before, but then rather than having a tasks: section within the play, we have a roles: section where the roles are declared instead. Convention dictates that this file be called site.yml, but you are free to call it whatever you like:
---
- name: Install Apache using a role
hosts: frontends
become: true
roles:
- installapache
For clarity, your final directory structure should look like this:
.
├── roles
│ └── installapache
│ └── tasks
│ ├── centos.yml
│ ├── main.yml
│ └── ubuntu.yml
└── site.yml
With this completed, you can now run your site.yml playbook using ansible-playbook in the normal way—you should see output similar to this:
$ ansible-playbook -i hosts site.yml
PLAY [Install Apache using a role] *********************************************
TASK [Gathering Facts] *********************************************************
ok: [frt01.example.com]
ok: [frt02.example.com]
TASK [installapache : Install Apache using yum] ********************************
changed: [frt02.example.com]
changed: [frt01.example.com]
TASK [installapache : Start the Apache server] *********************************
changed: [frt01.example.com]
changed: [frt02.example.com]
TASK [installapache : Install Apache using apt] ********************************
skipping: [frt01.example.com]
skipping: [frt02.example.com]
TASK [installapache : Start the Apache server] *********************************
skipping: [frt01.example.com]
skipping: [frt02.example.com]
PLAY RECAP *********************************************************************
frt01.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
frt02.example.com : ok=3 changed=2 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
That's it—you have created, at the simplest possible level, your first role. Of course (as we discussed earlier), there is much more to a role than just simple tasks as we have added here, and we will see expanded examples as we work through this chapter. However, the preceding example is intended to show you how quick and easy it is to get started with roles.
Before we look at some of the other aspects relating to roles, let's take a look at some other ways to call your role. Ansible allows you to statically import or dynamically include roles when you write a playbook. The syntax between these importing or including a role is subtly different, and notably, both go in the tasks section of your playbook rather than in the roles section. The following is a hypothetical example that shows both options in a really simple playbook. The roles directory structure including both the common and approle roles would have been created in a similar manner as in the preceding example:
---
- name: Play to import and include a role
hosts: frontends
tasks: - import_role: name: common - include_role: name: approle
These features were not available in versions of Ansible earlier than 2.3, and their usage changed slightly in version 2.4 for consistency with the way that some other Ansible features work. We will not worry about the details of this here as Ansible is now on release 2.9, so unless you absolutely have to run a much earlier version of Ansible, it is sufficient to assume that these two statements work as we shall outline in the following.
Fundamentally, the import_role statement performs a static import of the role you specify at the time when all playbook code is parsed. Hence, roles brought into your playbook using the import_role statement are treated just as any other code in a play or role is when Ansible begins parsing. Using import_role is basically the same as declaring your roles after the roles: statement in site.yml, just as we did in the preceding example.
include_role is subtly but fundamentally different in that the role you specify is not evaluated when the playbook is parsed initially—rather, it is processed dynamically during the playbook run, at the point at which include_role is encountered.
Probably the most fundamental reason to choose between the include or import statements given in the preceding is looping—if you need to run a role within a loop, you cannot do so with import_role and so must use include_role. There are, however, both benefits and limitations to both, and you will need to choose the most appropriate one for your scenario—the official Ansible documentation (https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse.html#dynamic-vs-static) will help you to make the right decision.
As we have seen in this section, roles are incredibly simple to get started with and yet offer an incredibly powerful way in which to organize and reuse your Ansible code. In the next section, we will expand upon our simple task-based example by looking at adding role-specific variables and dependencies into your code.
- 課課通計算機原理
- Oracle SOA Governance 11g Implementation
- 21天學通PHP
- Getting Started with Clickteam Fusion
- Hands-On Data Science with SQL Server 2017
- 腦動力:PHP函數速查效率手冊
- 工業機器人工程應用虛擬仿真教程:MotoSim EG-VRC
- Multimedia Programming with Pure Data
- 水晶石精粹:3ds max & ZBrush三維數字靜幀藝術
- 簡明學中文版Photoshop
- 常用傳感器技術及應用(第2版)
- 工業控制系統安全
- Building Analytics Teams
- 單片機C語言編程實踐
- 計算機仿真技術