现代 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 之后调用的任何角色使用。

相关推荐
weixin_307779135 小时前
Clickhouse统计指定表中各字段的空值、空字符串或零值比例
运维·数据仓库·clickhouse
bubiyoushang8885 小时前
Windows11 WSL2 Ubuntu编译安装perf工具
linux·运维·ubuntu
xuanwojiuxin6 小时前
linux panic-propagation
linux·运维·服务器
藥瓿亭8 小时前
K8S认证|CKS题库+答案| 9. 网络策略 NetworkPolicy
linux·运维·docker·云原生·容器·kubernetes·cks
liuzhenghua669 小时前
Python任务调度模型
java·运维·python
黎相思9 小时前
应用层自定义协议与序列化
运维·服务器·网络
测试开发Kevin10 小时前
详解Jenkins Pipeline 中git 命令的使用方法
运维·jenkins
什么半岛铁盒10 小时前
Linux线程与进程关系及底层实现
java·linux·运维
langmeng11010 小时前
使用docker在3台服务器上搭建基于版本redis 6.x的一主两从模式
运维·redis·docker·容器·集群
jllllyuz10 小时前
如何为服务器生成TLS证书
运维·服务器·数据库