Ansible role

定义

Ansible 角色 (Roles) 是一种用于组织和复用 Ansible Playbooks 的方式。它们提供了一种标准化的方法来打包和管理配置任务、文件、模板、变量等。

基本结构

一个典型的 Ansible 角色目录结构如下所示:

myrole/
├── tasks/
│   └── main.yml
├── files/
│   └── example.conf
├── templates/
│   └── example.conf.j2
├── vars/
│   └── main.yml
├── defaults/
│   └── main.yml
├── handlers/
│   └── main.yml
└── meta/
    └── galaxy.yml
  • tasks/: 包含任务定义的 YAML 文件,通常是 main.yml
  • files/: 存储要复制到远程主机的文件。
  • templates/: 存储 Jinja2 模板文件,这些模板可以用来生成配置文件。
  • vars/: 存储角色相关的变量定义。
  • defaults/: 存储角色的默认变量值。
  • handlers/: 定义 handler 任务,这些任务在被通知时才会执行。
  • meta/: 存储元数据文件,如依赖关系和 Galaxy 元数据。

可以使用ansible-galaxy init --help查看更多选项。比如,使用--init-path选项指定创建的Role路径:

$ ansible-galaxy init --init-path /etc/ansible/roles first_role

角色的使用场合

  • 当你需要在多个环境中重复使用相同的配置时。
  • 当你需要将复杂的配置分解成更小的、可管理的部分时。
  • 当你需要与其他团队成员共享配置时。

Ansible role 入口文件

ansible-playbook执行的入口playbook文件(就像main()函数一样),在这个入口文件中引入一个或多个roles目录下的Role。入口文件的名称可以随意,比如www.yml、site.yml、main.yml等,但注意它们和roles目录在同一个目录下。

例如:

├── enter.yml
└── roles
    ├── first_role/
    └── second_role/

上面和roles同目录的enter.yml文件内容如下,此文件中使用roles指令引入了roles目录内的两个Role。

---
- name: play with role
  hosts: nginx
  gather_facts: false
  roles: 
    - first_role
    - second_role

如果遵循了Role规范,入口文件中可以直接使用Role名称来引入roles目录下的Role(正如上面的示例),也可以指定Role的路径来引入。

定义Role的task

Role的任务定义在roles/xxx/tasks/main.yml文件中,main.yml是该Role任务的入口,在执行Role的时候会自动执行main.yml中的任务。可以直接将所有任务定义在此文件中,也可以定义在其它文件中,然后在main.yml文件中去引入。

定义Role的handler

handler和task类似,它定义在roles/xxx/handlers/main.yml中,当Role的task触发了对应的handler,会自动来此文件中寻找。

仍然要说的是,可以将handler定义在其它文件中,然后在roles/xxx/handlers/main.yml使用include_tasksimport_tasks指令来引入,而且前面也提到过这两者在handler上的区别和注意事项。

例如,roles/first_role/handlers/main.yml中定义了如下简单的handler:

例如,roles/first_role/handlers/main.yml中定义了如下简单的handler:

---
- name: handler for test
  debug: 
    msg: "a simple handler for test"

roles/first_role/tasks/main.yml中通过notify触发该Handler:

---
- name: task in main.yml
  debug:
    msg: "task in main.yml"
  changed_when: true
  notify: handler for test

定义Role的变量

Role中有两个地方可以定义变量:

  • (1).roles/xxx/vars/main.yml
  • (2).roles/xxx/defaults/main.yml

从目录名称中可以看出,defaults/main.yml中用于定义Role的默认变量,那么显然,vars/main.yml用于定义其它变量。

这两个文件之间的区别在于,defaults/main.yml中定义的变量优先级低于vars/main.yml中定义的变量。事实上,defaults/main.yml中的变量优先级几乎是最低的,基本上其它任何地方定义的变量都可以覆盖它。

Role中定义的模块和插件

对于绝大多数需求,使用Ansible已经提供的模块和插件就能解决问题,但有时候确实有些需求需要自己去写模块或插件,Ansible也支持用户自定义的模块和插件。

对于Role来说,如果这个Role需要额外使用自己编写的模块或插件,则模块放在roles/xxx/librarys/目录下,而插件放在各自对应类型的目录下:

定义Role的依赖关系

很多时候,一个系统化配置管理的需求中并不仅仅只有一个Role。一个典型的案例是部署LAMP,这里面涉及到了部署Apache、MySQL、PHP,但通常不会将它们全部定义到单个Role中,而是按照一些分类逻辑进行Role的划分,比如MySQL作为DB,单独定义成一个Role,apache和php作为一个webserver整体定义成一个Role。如何对功能进行划分由我们自己决定,正如上面的apache和php完全可以定义成两个Role。

有了多个Role,这些Role可能会存在依赖关系。例如,在新结点上部署集群服务的时候,通常都要对这些新结点进行一些初始化配置,比如设置时间同步,只有满足了这个条件,才会去启动集群服务。

换句话说,有些任务必须先行,这些先行任务就是被依赖的任务。

按照Role规范,被依赖的先行任务都定义在roles/xxx/meta/main.yml文件中。

例如:

---
dependencies: 
  - second_role
  - third_role

使用Role:roles、include_role和import_role

写好Role之后就是使用Role,即在一个入口playbook文件中去加载Role。

加载Role的方式有多种:

  • (1).roles指令:play级别的指令,在playbook解析阶段加载对应文件,这是传统的引入Role的方式
  • (2).import_role指令:task级别的指令,在playbook解析阶段加载对应文件
  • (3).include_role指令:task级别的指令,在遇到该指令的时候才加载Role对应文件

例如前面使用的是roles,如下:

---
- name: play1
  hosts: localhost
  gather_facts: false
  roles: 
    - first_role

上面通过roles指令来定义要解析和执行的Role,可以同时指定多个Role,且也可以加上role:参数,例如:

roles: 
  - first_role
  - role: seconde_role
  - role: third_role

也可以使用include_roleimport_role来引入Role,但需注意,这两个指令是tasks级别的,也正因为它们是task级别,使得它们可以和其它task共存。

---
- hosts: localhost
  gather_facts: false
  tasks:
  - debug:
      msg: "before first role"
  - import_role:
      name: first_role
  - include_role:
      name: second_role
  - debug:
      msg: "after second role"

这三种引入Role的方式都可以为对应的Role传递参数,例如:

---
- hosts: localhost
  gather_facts: false
  roles: 
    - role: first_role
      varvar: "valuevalue"
      vars: 
        var1: value1

  tasks:
  - import_role:
      name: second_role
    vars: 
      var1: value1
  - include_role:
      name: third_role
    vars: 
      var1: value1

查看任务和打标签tags

每个Role的任务有可能分布在多个文件中,还有可能会在入口文件中加载多个Role,这时想要知道执行playbook时具体会执行哪些任务,可以使用ansible-playbook命令的--list-tasks选项,它会列出某个playbook中的所有任务。

$ ansible-playbook --list-tasks enter.yml

playbook: enter.yml

  play #1 (localhost): play1      TAGS: []
    tasks:
      first_role : task1          TAGS: []
      first_role : task in t.yml  TAGS: []

上面的结果表示,enter.yml这个playbook文件中只有一个play,这个play中包含了两个任务。

从结果中还看到play和task的后面都带有TAGS: [],它是标签。当在play或task级别使用tags指令后就表示为此play或task打了标签。

1.可以在task级别为单个任务打一个或多个标签,多个任务可以打同一个标签名

例如:

- name: yum install ntp
  yum: 
    name: ntp
    state: present
  tags: 
    - initialize
    - pkginstall
    - ntp

- name: started ntpd
  service:
    name: ntpd
    state: started
  tags: 
    - ntp
    - initialize

当任务具有了标签之后,就可以在ansible-playbook命令行中使用--tags来指定只有带有某标记的任务才执行,也可以使用--skip-tags选项明确指定不要执行某个任务。

# 只执行第一个任务
$ ansible-playbook test.yml --tags "pkginstall"

# 两个任务都执行
$ ansible-playbook test.yml --tags "ntp,initialize"

# 第一个任务不执行
$ ansible-playbook test.yml --skip-tags "pkginstall"

如果想要确定tag筛选之后会执行哪些任务,加上--list-tasks即可:

2.可以在play级别打标签,这等价于对play中的所有任务都打上标签

例如:

- name: play1
  hosts: localhost
  gather_facts: false
  tags: 
    - tag1
    - tag2
  pre_tasks:
    - debug: "msg='pre_task1'"
    - debug: "msg='pre_task2'"
  tasks: 
    - debug: "msg='task1'"
    - debug: "msg='task2'"
Index