现代 IT 人一定要知道的 Ansible系列教程:Roles详解

现代 IT 人一定要知道的 Ansible系列教程:Roles详解

Roles

ansible 中角色允许我们根据已知的文件结构自动加载相关的变量、文件、任务、处理程序和其他 Ansible 工件。将内容分组到角色后,我们可以轻松地重复使用它们并与其他用户共享它们。

角色目录结构

Ansible 角色具有定义的目录结构,其中包含 8 个主要标准目录。我们必须在每个角色中至少包含其中一个目录。我们可以省略该角色不使用的任何目录。例如:

arduino 复制代码
# playbooks
site.yml
webservers.yml
fooservers.yml
python 复制代码
roles/
    common/               # 该层次结构表示一个"角色"
        tasks/            #
            main.yml      # 如果有必要,任务文件可以包括较小的文件
        handlers/         #
            main.yml      
        templates/        # 与模板资源一起使用的文件
            ntp.conf.j2   # 模板以.j2结尾
        files/            #
            bar.txt       # 用于复制资源的文件
            foo.sh        # 与脚本资源一起使用的脚本文件
        vars/             #
            main.yml      # 与此角色关联的变量
        defaults/         #
            main.yml      #  此角色的默认低优先级变量
        meta/             #
            main.yml      #  角色依赖关系
        library/          # 角色还可以包括自定义模块
        module_utils/     # 角色还可以包括自定义的module_utils
        lookup_plugins/   # 或者其他类型的插件,比如本例中的查找
​
    webtier/              # 与上面的"common"相同的结构,用于webtier角色
    monitoring/           
    fooapp/               

默认情况下,Ansible 将在角色的每个目录中查找 main.yml 相关内容(以及 main.yamlmain ):

  • tasks/main.yml - 角色执行的主要任务列表。
  • handlers/main.yml - 处理程序,可以在此角色内部或外部使用。
  • library/my_module.py - 模块,可以在此角色中使用(有关更多信息,请参阅在角色中嵌入模块和插件)。
  • defaults/main.yml - 角色的默认变量。这些变量在所有可用变量中具有最低的优先级,并且可以很容易地被任何其他变量(包括库存变量)覆盖。
  • vars/main.yml - 角色的其他变量。
  • files/main.yml - 角色部署的文件。
  • emplates/main.yml - 角色部署的模板。
  • meta/main.yml - 角色的元数据,包括角色依赖关系和可选的 Galaxy 元数据,例如支持的平台。

可以在某些目录中添加其他 YAML 文件。例如,我们可以将特定于平台的任务放在单独的文件中,并在 tasks/main.yml 文件中引用它们:

less 复制代码
# roles/example/tasks/main.yml
- name: Install the correct web server for RHEL
  import_tasks: redhat.yml
  when: ansible_facts['os_family']|lower == 'redhat'
​
- name: Install the correct web server for Debian
  import_tasks: debian.yml
  when: ansible_facts['os_family']|lower == 'debian'
​
# roles/example/tasks/redhat.yml
- name: Install web server
  ansible.builtin.yum:
    name: "httpd"
    state: present
​
# roles/example/tasks/debian.yml
- name: Install web server
  ansible.builtin.apt:
    name: "apache2"
    state: present

角色还可以在名为 library 的目录中包含模块和其他插件类型。

存储和查找角色

默认情况下,Ansible 在以下位置查找角色:

  • 如果我们使用了集合,可以在集合中查找
  • 在名为 roles/ 的目录中,相对于 playbook 文件
  • 在配置的roles_path中。默认搜索路径为 ~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
  • playbook 文件所在的目录中

如果我们将角色存储在其他位置,请设置 roles_path 配置选项,以便 Ansible 可以找到我们的角色。

将共享角色签入单个位置可使其更易于在多个 playbook 中使用。有关管理中的设置的详细信息,请参阅配置 ansible.cfg Ansible。

或者,我们可以使用完全限定的路径调用角色:

yaml 复制代码
---
- hosts: webservers
  roles:
    - role: '/path/to/my/roles/common'

使用角色

我们可以通过三种方式使用角色:

  • playbookroles 选项中,推荐使用这种方式。
  • task使用 include_role, 我们可以使用 在 playbooktasks 部分的任何位置动态重用角色 include_role
  • task使用 import_role , 我们可以使用 在 playbook tasks 部分的任何位置静态重用角色 import_role

在 playbook 使用角色

使用金典方式是针对给定 roles 进行选择:

yaml 复制代码
---
- hosts: webservers
  roles:
    - common
    - webservers

当我们在playbook级别使用该 roles 选项时,对于每个角色 x

  • 如果 roles/x/tasks/main.yml 存在,则 Ansible 会将该文件中的任务添加到playbook中。
  • 如果 roles/x/handlers/main.yml 存在,则 Ansible 会将该文件中的处理程序添加到playbook中。
  • 如果 roles/x/vars/main.yml 存在,Ansible 会将该文件中的变量添加到playbook中。
  • 如果 roles/x/defaults/main.yml 存在,则 Ansible 会将该文件中的变量添加到playbook中。
  • 如果 roles/x/meta/main.yml 存在,Ansible 会将该文件中的任何角色依赖项添加到角色列表中。
  • 任何副本、脚本、模板或包含任务(在角色中)都可以引用 roles/x/{files,templates,tasks}/ 中的文件(dir 取决于任务),而不必相对或绝对地路径它们。

我们可以将其他关键字传递给该 roles 选项:

yaml 复制代码
---
- hosts: webservers
  roles:
    - common
    - role: foo_app_instance
      vars:
        dir: '/opt/a'
        app_port: 5000
      tags: typeA
    - role: foo_app_instance
      vars:
        dir: '/opt/b'
        app_port: 5001
      tags: typeB

当我们向该 role 选项添加标签时,Ansible 会将该标签应用于角色中的所有任务。

在 playbook roles: 的部分中使用 vars: 时,变量将添加到剧本变量中,使它们可用于角色之前和之后剧本中的所有任务。此行为可以通过DEFAULT_PRIVATE_ROLE_VARS 更改。

动态重用角色

可以使用 include_roletasks 段中的任何位置动态重用角色。

虽然在 roles 中添加的角色在playbook的任何其他任务之前运行,但包含的角色则按定义的顺序运行。如果任务 include_role 之前有其他任务,则其他任务将首先运行。

要包含角色,请执行以下操作:

yaml 复制代码
---
- hosts: webservers
  tasks:
    - name: Print a message
      ansible.builtin.debug:
        msg: "this task runs before the example role"
​
    - name: Include the example role
      include_role:
        name: example
​
    - name: Print a message
      ansible.builtin.debug:
        msg: "this task runs after the example role"

在包含角色时,我们可以传递其他关键字,包括变量和标签:

yaml 复制代码
---
- hosts: webservers
  tasks:
    - name: Include the foo_app_instance role
      include_role:
        name: foo_app_instance
      vars:
        dir: '/opt/a'
        app_port: 5000
      tags: typeA
  ...

当我们向 include_role 任务添加标签时,Ansible 仅将标签应用于包含本身。

这意味着,如果角色中的选定任务本身具有与 include 语句相同的标记,则只能传递 --tags 以运行这些任务。

我们可以有条件地包含角色:

yaml 复制代码
---
- hosts: webservers
  tasks:
    - name: Include the some_role role
      include_role:
        name: some_role
      when: "ansible_facts['os_family'] == 'RedHat'"

导入角色

我们可以使用 import_role 在重用部分的 tasks 任意位置静态重用角色。该行为与使用 roles 关键字相同。例如:

yaml 复制代码
---
- hosts: webservers
  tasks:
    - name: Print a message
      ansible.builtin.debug:
        msg: "before we run our role"

    - name: Import the example role
      import_role:
        name: example

    - name: Print a message
      ansible.builtin.debug:
        msg: "after we ran our role"

导入角色时,我们可以传递其他关键字,包括变量和标签:

yaml 复制代码
---
- hosts: webservers
  tasks:
    - name: Import the foo_app_instance role
      import_role:
        name: foo_app_instance
      vars:
        dir: '/opt/a'
        app_port: 5000
  ...

当我们向 import_role 语句添加标签时,Ansible 会将该标签应用于角色中的所有任务。有关详细信息,请参阅标签继承:为多个任务添加标签。

角色参数验证

从版本 2.11 开始,我们可以选择基于参数规范启用角色参数验证。此规范在 meta/argument_specs.yml 文件(或 .yaml 文件扩展名)中定义。

定义此参数规范后,将在角色执行开始时插入一个新任务,该任务将根据规范验证为角色提供的参数。如果参数验证失败,则角色将无法执行。

角色参数规范必须在角色 meta/argument_specs.yml 文件的顶级 argument_specs 块中定义。所有字段均为小写。

示例:

yaml 复制代码
# roles/myapp/meta/argument_specs.yml
---
argument_specs:
  # roles/myapp/tasks/main.yml entry point
  main:
    short_description: Main entry point for the myapp role
    description:
      - This is the main entrypoint for the C(myapp) role.
      - Here we can describe what this entrypoint does in lengthy words.
      - Every new list item is a new paragraph. You can have multiple sentences
        per paragraph.
    author: Daniel Ziegenberg
    options:
      myapp_int:
        type: "int"
        required: false
        default: 42
        description:
          - "The integer value, defaulting to 42."
          - "This is a second paragraph."
​
      myapp_str:
        type: "str"
        required: true
        description: "The string value"
​
      myapp_list:
        type: "list"
        elements: "str"
        required: true
        description: "A list of string values."
        version_added: 1.3.0
​
      myapp_list_with_dicts:
        type: "list"
        elements: "dict"
        required: false
        default:
          - myapp_food_kind: "meat"
            myapp_food_boiling_required: true
            myapp_food_preparation_time: 60
          - myapp_food_kind: "fruits"
            myapp_food_preparation_time: 5
        description: "A list of dicts with a defined structure and with default a value."
        options:
          myapp_food_kind:
            type: "str"
            choices:
              - "vegetables"
              - "fruits"
              - "grains"
              - "meat"
            required: false
​
          myapp_food_boiling_required:
            type: "bool"
            required: false
            default: false
            description: "Whether the kind of food requires boiling before consumption."
​
          myapp_food_preparation_time:
            type: int
            required: true
            description: "Time to prepare a dish in minutes."
​
      myapp_dict_with_suboptions:
        type: "dict"
        required: false
        default:
          myapp_host: "bar.foo"
          myapp_exclude_host: true
          myapp_path: "/etc/myapp"
        description: "A dict with a defined structure and default values."
        options:
          myapp_host:
            type: "str"
            choices:
              - "foo.bar"
              - "bar.foo"
              - "ansible.foo.bar"
            required: true
            description: "A string value with a limited list of allowed choices."
​
          myapp_exclude_host:
            type: "bool"
            required: true
            description: "A boolean value."
​
          myapp_path:
            type: "path"
            required: true
            description: "A path value."
​
          original_name:
            type: list
            elements: "str"
            required: false
            description: "An optional list of string values."
​
  # roles/myapp/tasks/alternate.yml entry point
  alternate:
    short_description: Alternate entry point for the myapp role
    version_added: 1.2.0
    options:
      myapp_int:
        type: "int"
        required: false
        default: 1024
        description: "The integer value, defaulting to 1024."

传递不同的参数

Ansible 在一次play中只执行每个角色一次,即使我们多次定义它,除非每个定义在角色上定义的参数不同。

如果在每个角色定义中传递不同的参数,则 Ansible 会多次运行该角色。提供不同的变量值与传递不同的角色参数不同。

我们必须为此行为使用关键字 roles ,因为 import_role 并且 include_role 不接受角色参数。

yaml 复制代码
---
- hosts: webservers
  roles:
    - { role: foo, message: "first" }
    - { role: foo, message: "second" }

在这些示例中,Ansible 运行 foo 两次,因为每个角色定义都有不同的参数。

使用 allow_duplicates: true

llow_duplicates: true 添加到角色 meta/main.yml 的文件中:

yaml 复制代码
# playbook.yml
---
- hosts: webservers
  roles:
    - foo
    - foo
​
# roles/foo/meta/main.yml
---
allow_duplicates: true

在此示例中,Ansible 运行 foo 了两次,因为我们已明确启用它来执行此操作。

使用角色依赖关系

角色依赖关系允许我们在使用角色时自动拉入其他角色。

角色依赖关系是先决条件,而不是真正的依赖关系。这些角色没有父/子关系。Ansible 加载所有列出的角色,运行首先列出的 dependencies 角色,然后运行列出这些角色的角色。

角色依赖项存储在角色目录内的 meta/main.yml 文件中。此文件应包含要在指定角色之前插入的角色和参数的列表。例如:

yaml 复制代码
# roles/myapp/meta/main.yml
---
dependencies:
  - role: common
    vars:
      some_parameter: 3
  - role: apache
    vars:
      apache_port: 80
  - role: postgres
    vars:
      dbname: blarg
      other_parameter: 12

Ansible 始终在列出这些角色的角色之前执行 中 dependencies 列出的角色。当我们使用关键字时, roles Ansible 会递归地执行此模式。例如,如果在 下列出 role foo roles: ,则 role 在其 meta/main.yml 文件中列出 role,而 role 在其 meta/main.yml dependencies 中列出 role foo bar dependencies bar baz ,则 bar ,然后 foo baz

Ansible 将重复的角色依赖关系视为重复角色,例如: Ansible 只执行一次角色依赖关系,即使定义了多次, roles: 除非每个定义在角色上定义的参数、标签或 when 子句不同。如果一个角色中的两个角色都将第三个角色列为依赖项,则 Ansible 只会运行该角色依赖项一次,除非我们传递不同的参数、标签、when 子句或在要多次运行的角色 allow_duplicates: true 中使用。

例如,命名 car 的角色依赖于命名 wheel 如下的角色:

yaml 复制代码
---
dependencies:
  - role: wheel
    n: 1
  - role: wheel
    n: 2
  - role: wheel
    n: 3
  - role: wheel
    n: 4

角色 wheel 取决于两个角色: tirebrake .然后, meta/main.yml for 将包含以下内容:

yaml 复制代码
---
dependencies:
  - role: tire
  - role: brake

meta/main.yml for tirebrake 将包含以下内容:

yaml 复制代码
---
allow_duplicates: true

生成的执行顺序如下:

ini 复制代码
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car

若要与角色依赖项一起使用 allow_duplicates: true ,必须为 下 dependencies 列出的角色指定它,而不是为列出它的角色指定它。

在上面的示例中, allow_duplicates: true 出现在 tirebrake 角色中 meta/main.yml 。该 wheel 角色不需要 allow_duplicates: true ,因为 定义的 car 每个实例都使用不同的参数值。

在角色中嵌入模块和插件

如果我们编写自定义模块或插件,我们可能希望将其作为角色的一部分进行分发。

例如,如果我们编写了一个模块来帮助配置公司的内部软件,并且希望组织中的其他人使用此模块,但又不想告诉每个人如何配置其 Ansible 库路径,则可以将该模块包含在我们的internal_config角色中。

要向角色添加模块或插件:除了角色的"tasks"和"handlers"结构外,添加一个名为"library"的目录,然后将该模块直接包含在"library"目录中。

假设你有这个:

dart 复制代码
roles/
    my_custom_modules/
        library/
            module1
            module2

该模块将可用于角色本身,以及在此角色之后调用的任何角色,如下所示:

yaml 复制代码
---
- hosts: webservers
  roles:
    - my_custom_modules
    - some_other_role_using_my_custom_modules
    - yet_another_role_using_my_custom_modules

如有必要,我们还可以在角色中嵌入模块,以修改 Ansible 核心发行版中的模块。例如,通过复制模块并将副本嵌入角色,可以在生产版本中发布特定模块之前使用该模块的开发版本。请谨慎使用此方法,因为核心组件中的 API 签名可能会更改,并且不能保证此解决方法有效。

可以使用相同的机制在角色中嵌入和分发插件,使用相同的架构。例如,对于过滤器插件:

roles/
    my_custom_filter/
        filter_plugins
            filter1
            filter2

然后,这些过滤器可以在 Jinja 模板中以 my_custom_filter 之后调用的任何角色使用。

相关推荐
大霞上仙25 分钟前
Ubuntu系统电脑没有WiFi适配器
linux·运维·电脑
Karoku0661 小时前
【企业级分布式系统】Zabbix监控系统与部署安装
运维·服务器·数据库·redis·mysql·zabbix
为什么这亚子1 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
布值倒区什么name1 小时前
bug日常记录responded with a status of 413 (Request Entity Too Large)
运维·服务器·bug
。puppy2 小时前
HCIP--3实验- 链路聚合,VLAN间通讯,Super VLAN,MSTP,VRRPip配置,OSPF(静态路由,环回,缺省,空接口),NAT
运维·服务器
颇有几分姿色2 小时前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
光芒再现dev3 小时前
已解决,部署GPTSoVITS报错‘AsyncRequest‘ object has no attribute ‘_json_response_data‘
运维·python·gpt·语言模型·自然语言处理
AndyFrank3 小时前
mac crontab 不能使用问题简记
linux·运维·macos
成都古河云4 小时前
智慧场馆:安全、节能与智能化管理的未来
大数据·运维·人工智能·安全·智慧城市
算法与编程之美4 小时前
文件的写入与读取
linux·运维·服务器