ansible系列(30)--ansible的role详解
1. Ansible Roles
ansilbe roles是自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。
1.1 roles目录结构
roles 官方目录结构,必须按如下方式定义。在每个目录中必须有 main.yml 文件,这些属于强制要求。

[root@xuzhichao /data/ansible/roles]$tree
.
├── playbooks.yml <==执行剧本
└── roles <==角色必须与执行剧本在同一级目录中
├── project <==项目名称
│ ├── default
│ ├── files
│ ├── handlers
│ ├── meta
│ ├── tasks
│ ├── templates
│ └── vars
└── project1 <==另一个项目
- tasks目录:角色需要执行的主任务文件放置在此目录中,默认的主任务文件名为
main.yml,当调用角色时,默认会执行main.yml文件中的任务,你也可以将其他需要执行的任务文件通过include的方式包含在tasks/main.yml文件中。 - handlers目录:当角色需要调用
handlers时,默认会在此目录中的main.yml文件中查找对应的handler。 - defaults目录:角色会使用到的变量可以写入到此目录中的
main.yml文件中,通常,defaults/main.yml文件中的变量都用于设置默认值,以便在你没有设置对应变量值时,变量有默认的值可以使用,定义在defaults/main.yml文件中的变量的优先级是最低的。 - vars目录:角色会使用到的变量可以写入到此目录中的
main.yml文件中,看到这里你肯定会有疑问,vars/main.yml文件和defaults/main.yml文件的区别在哪里呢?区别就是,defaults/main.yml文件中的变量的优先级是最低的,而vars/main.yml文件中的变量的优先级非常高,如果你只是想提供一个默认的配置,那么你可以把对应的变量定义在defaults/main.yml中,如果你想要确保别人在调用角色时,使用的值就是你指定的值,则可以将变量定义在vars/main.yml中,因为定义在vars/main.yml文件中的变量的优先级非常高,所以其值比较难以覆盖。 - meta目录:如果你想要赋予这个角色一些元数据,则可以将元数据写入到
meta/main.yml文件中,这些元数据用于描述角色的相关属性,比如 作者信息、角色主要作用等等,你也可以在meta/main.yml文件中定义这个角色依赖于哪些其他角色,或者改变角色的默认调用设定,在之后会有一些实际的示例。 - templates目录: 角色相关的模板文件可以放置在此目录中,当使用角色相关的模板时,如果没有指定路径,会默认从此目录中查找对应名称的模板文件。
- files目录:角色可能会用到的一些其他文件可以放置在此目录中,比如,当你定义
nginx角色时,需要配置https,那么相关的证书文件即可放置在此目录中。
当然,上述目录并不全是必须的,也就是说,如果你的角色并没有相关的模板文件,那么角色目录中并不用包含templates目录,同理,其他目录也一样,一般情况下,都至少会有一个tasks目录。
1.2 roles编写步骤
1.2.1 编写基本的roles
编写roles的步骤为:
- 创建
roles项目目录结构,手动创建或使用ansible-galaxy init test roles; - 编写
roles的功能,也就是tasks; - 最后
playbook引用roles编写好的tasks;
-
创建一个测试的
roles目录,就叫roles_test1,并创建下面的子目录,最基本的子目录需要有tasks目录:[root@xuzhichao ~]# mkdir /data/ansible/roles [root@xuzhichao ~]# cd /data/ansible/roles [root@xuzhichao roles]# mkdir roles_test1 [root@xuzhichao roles]# cd roles_test1 [root@xuzhichao roles_test1]# mkdir tasks -
然后在
tasks目录中编写相关的任务,在tasks目录中必须由一个main.yml文件,可以把需要编写的任务直接写到main.yml文件中,也可以写到其他文件中,然后在main.yml文件中通过include的方式引用这些文件。在tasks中的文件不需要写play部分,只需要写task部分。示例如下:[root@xuzhichao roles_test1]# cat tasks/main.yml - name: Print Vars debug: msg: "ansible_test_roles"或者:
[root@xuzhichao roles_test1]# cat tasks/task1.yml - name: Print Vars debug: msg: "ansible_test_roles" [root@xuzhichao roles_test1]# cat tasks/main.yml - include: task1.yml -
写好
task后,需要编写playbook调用roles,playbook编写如下:[root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost roles: - role: roles_test1 -
运行
playbook:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [Gathering Facts] **************************************************************************************************************************************** ok: [localhost] TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "ansible_test_roles" } PLAY RECAP **************************************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.2 roles的调用
在playbook调用roles时,如何找到roles所在的目录呢?有以下几种方式:
-
playbook文件会在同级目录中寻找与调用的角色同名的roles,例如在上节中rolestest1.yml文件中调用了roles_test1这个角色,会自动在同级目录中找到roles_test1这个目录,并执行其中的tasks。[root@xuzhichao roles]# ll total 4 drwxr-xr-x 3 root root 19 Aug 7 16:57 roles_test1 -rw-r--r-- 1 root root 46 Aug 7 17:14 rolestest1.yml -
playbook文件还会寻找同级目录中的roles目录,执行roles目录中同名的角色项目,例如:[root@xuzhichao roles]# tree . ├── roles │ └── roles_test1 │ └── tasks │ ├── main.yml │ └── task1.yml └── rolestest1.yml -
当前系统用户的家目录中的
.ansible/roles目录,即~/.ansible/roles目录中。 -
也可以修改
ansible配置文件中定义的roles的路径,多个路径之间使用冒号:隔开:[root@xuzhichao roles]# vim /etc/ansible/ansible.cfg roles_path = /etc/ansible/roles:/data/ansible/roles:/opt -
在
playbook文件中直接写roles的绝对路径也可以调用roles:[root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost roles: - role: /data/ansible/roles/roles_test1
1.2.3 roles中使用变量
-
修改一下
roles文件,引入变量:[root@xuzhichao roles]# cat roles_test1/tasks/task1.yml - name: Print Vars debug: msg: "{{ var1 }}" -
在调用这个角色时,则需要传入对应的变量,否则就会报错,调用上例角色的示例如下:
[root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost roles: - role: roles_test1 vars: var1: string1执行
palybook,结果如下:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string1" } -
也可以为变量设置默认值,这样即使在调用角色时没有传入任何参数,也有默认的值可以使用,同时也不会在调用时因为没有传入对应变量而报错,需要在
roles_test1目录中创建一个defaults目录,并且创建defaults/main.yml文件,defaults/main.yml文件内容如下:[root@xuzhichao roles]# cd roles_test1/ [root@xuzhichao roles_test1]# mkdir defaults [root@xuzhichao roles_test1]# cat defaults/main.yml var1: string2 -
此时运行
palybook,结果仍然是string1,说明playbook文件中定义的变量值比defaults/main.yml文件定义的变量优先级高:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string1" } -
删除
playbook文件中定义的变量后,运行playbook文件,显示的变量就是defaults/main.yml文件定义的变量:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string2" } -
接下来测试一下在
vars/main.yml文件中定义变量,定义在vars/main.yml文件中的变量优先级比较高,难以被覆盖,我们测试一下定义在这个文件中的变量的优先级到底有多高,为了使效果更加明显,我们在defaults/main.yml文件和vars/main.yml文件,以及vars_files中同时定义var1变量,并为其赋值不同的值,如下:#在vars/main.yml中定义变量: [root@xuzhichao roles_test1]# cat vars/main.yml var1: string3 #在defaults/main.yml中定义变量: [root@xuzhichao roles_test1]# cat defaults/main.yml var1: string2 #在vars_files中定义变量: [root@xuzhichao roles]# cat /data/ansible/roles/vars.yml var1: string1 #定义playbook: [root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost vars_files: ./vars.yml roles: - role: roles_test1 vars: var1: string4 -
运行
playbook文件,最终显示的变量是vars/main.yml中定义的变量:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string3" } -
变量优先级总结:前文中测试变量优先级时除了在命令行中定义的变量外,
vars_files中定义的变量优先级最高,这样我们可以得出结论,变量的优先级最高的仍然是ansible-playbook命令行定义的变脸,其次是vars/main.yml的变量,其他的变量的定义方式的优先级不变。 -
注意:在默认情况下,角色中的的变量是全局可访问的。就是说在同一个
playbook中为一个role定义了变量,那么这个playbook中其他的role也会自动使用这个变量,例如:[root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost roles: - role: roles_test1 vars: var1: string4 - role: roles_test2 [root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string4" } TASK [roles_test2 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string4" }如果想要解决上述问题,则可以将变量的访问域变成角色所私有的,如果想要将变量变成角色私有的,则需要设置
/etc/ansible/ansible.cfg文件,将private_role_vars的值设置为yes,默认情况下,”private_role_vars = yes”是被注释掉的,将前面的注释符去掉皆可,设置完成后,再次执行上例中的rolestest1.yml文件,输出结果如下:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string4" } TASK [roles_test2 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string2" } [root@xuzhichao roles]# cat roles_test2/defaults/main.yml var1: string2
1.2.4 多次调用同一个role
默认情况下,我们无法多次调用同一个角色,也就是说,如下playbook只会调用一次role角色:
# cat test.yml
- hosts: test
roles:
- role: testrole
- role: testrole
执行上例playbook会发现,testrole的debug模块只输出了一次,如果想要多次调用同一个角色,有两种方法,如下:
-
方法一:设置角色的
allow_duplicates属性 ,让其支持重复的调用。# cat testrole/meta/main.yml allow_duplicates: true -
方法二:调用角色时,传入的参数值不同。
# cat test.yml - hosts: test70 roles: - role: testrole vars: testvar: "123" - role: testrole vars: testvar: "abc"
1.2.5 roles管理模板文件
前文中提到,roles中把所有的模板文件放置到templates目录中,我们来测试一下模板文件的使用。
-
新增
templates目录,建立模板文件:[root@xuzhichao roles]# mkdir roles_test1/templates [root@xuzhichao roles]# cat roles_test1/templates/temp.j2 something in template; {{ template_var }} -
tasks/main.yml文件如下:[root@xuzhichao roles]# cat roles_test1/tasks/main.yml - name: Print Vars debug: msg: "{{ var1 }}" - name: Copy Templates template: src: temp.j2 dest: /tmp/temp -
变量文件如下:
[root@xuzhichao roles]# cat roles_test1/vars/main.yml var1: string1 template_var: tempvar -
playbook文件如下:[root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost roles: - role: roles_test1 -
运行
playbook文件:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* ok: [localhost] => { "msg": "string1" } TASK [roles_test1 : Copy Templates] *************************************************************************************************************************** changed: [localhost] PLAY RECAP **************************************************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [root@xuzhichao roles]# cat /tmp/temp something in template; tempvar
1.2.6 roles管理handlers文件
如果想要在角色中使用一些handlers以便进行触发,则可以直接将对应的handler任务写入到handlers/main.yml文件中,示例如下:
-
建立
handlers目录,建立main.yml文件:[root@xuzhichao roles]# mkdir roles_test1/templates [root@xuzhichao roles]# mkdir roles_test1/handlers [root@xuzhichao roles]# cd roles_test1/handlers [root@xuzhichao handlers]# vim main.yml - name: test_handler debug: msg: "this is a test handler" -
为了能够更加简单的触发对应的
handler,直接将tasks/main.yml中的debug任务的状态强行设置为”changed”,示例如下:[root@xuzhichao roles]# cat roles_test1/tasks/main.yml - name: Print Vars debug: msg: "{{ var1 }}" changed_when: true notify: test_handler - name: Copy Templates template: src: temp.j2 dest: /tmp/temp [root@xuzhichao roles]# cat rolestest1.yml - hosts: localhost roles: - role: roles_test1 -
运行
playbook文件,直接触发handlers:[root@xuzhichao roles]# ansible-playbook rolestest1.yml PLAY [localhost] ********************************************************************************************************************************************** TASK [roles_test1 : Print Vars] ******************************************************************************************************************************* changed: [localhost] => { "msg": "string1" } TASK [roles_test1 : Copy Templates] *************************************************************************************************************************** ok: [localhost] RUNNING HANDLER [roles_test1 : test_handler] ****************************************************************************************************************** ok: [localhost] => { "msg": "this is a test handler" } PLAY RECAP **************************************************************************************************************************************************** localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.7 roles的依赖关系
roles 允许在使用时自动引入其他 role , role 依赖关系存储在meta/main.yml 文件中。
例如: 安装 wordpress 项目时:
- 需要先确保
nginx与php-fpm的role都能正常运行; - 然后在
wordpress的role中定义,依赖关系; - 依赖的
role有nginx以及php-fpm;
-
在
wordpress的role中新建meta目录,编写main.yml文件:[root@m01 playbook]# cat /root/roles/wordpress/meta/main.yml --- dependencies: - { role: nginx } - { role: php-fpm } -
在
wodpress的playbook中就不需要写安装nginx和php-fpm的role了,只写一个wordpress就可以了,安装wordpress之前会自动先部署了nginx和php-fpm。[root@m01 playbook]# cat /root/roles/wordpress,yml --- - hosts: NginxWebs roles: - role: wordpress
1.2.8 roles的相互调用
roles中的各项目之间的文件是可以跨项目互相调用的。
跨项目调用时需要指明要调用的role的名称。
例如:同时部署了nginx和apache两个项目,两个项目中使用了相同的index.html主页文件,这个蛀牙文件已经在nginx项目中定义过了。若此时需要在apache项目中引用,可以使用如下方式:
[root@localhost /data/roles]$vim apache/tasks/html.yml <==页面配置
- name: Copy Index File
copy:
src:roles/nginx/files/index.html #跨项目调用路径要写roles的名称
dest: /var/www/nginx/html/
1.3 roles部署nginx实例
-
首先创建项目目录
nginx,并创建files、handlers、meta、tasks、templates和vars等目录;用不到的目录可以创建为空目录,也可以不创建,用到时再创建也可以:[root@xuzhichao roles]# mkdir nginx/{tasks,templates,handlers,files,vars} -pv -
编写
nginx的任务功能:[root@xuzhichao nginx]# cat tasks/Install_Nginx.yml - name: Install Nginx Server yum: name: nginx state: present [root@xuzhichao nginx]# cat tasks/Configure_Nginx.yml - name: Configure Nginx Conf Files template: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: "{{ item.user }}" group: "{{ item.group }}" mode: "{{ item.mode }}" loop: - { src: "nginx.conf.j2", dest: "/etc/nginx/nginx.conf", user: "root", group: "root", mode: "0644" } - { src: "ansible.web.conf.j2", dest: "/etc/nginx/conf.d/ansible.web.conf", user: "root", group: "root", mode: "0644" } notify: Restart Nginx Server [root@xuzhichao nginx]# cat tasks/Check_Configure.yml - name: Check Nginx Configure shell: cmd: "/usr/sbin/nginx -t" register: check_nginx changed_when: - check_nginx.stdout.find("ok") - false [root@xuzhichao nginx]# cat tasks/Init_Nginx.yml - name: Create Web Site Directory file: path: "{{ web_site_directory }}" state: directory owner: "{{ nginx_user }}" group: "{{ nginx_group }}" mode: "0755" - name: Init Nginx Server copy: src: "{{ nginxfile }}" dest: "{{ web_site_directory }}" owner: "{{ nginx_user }}" group: "{{ nginx_group }}" mode: "0644" [root@xuzhichao nginx]# cat tasks/Start_Nginx.yml - name: Start Nginx Server systemd: name: nginx state: started enabled: yes [root@xuzhichao nginx]# cat tasks/main.yml - include: Install_Nginx.yml - include: Configure_Nginx.yml - include: Check_Configure.yml - include: Init_Nginx.yml - include: Start_Nginx.yml -
编写
handlers文件:[root@xuzhichao nginx]# cat handlers/main.yml - name: Restart Nginx Server service: name: nginx state: restarted -
编写模板文件:
[root@xuzhichao nginx]# cat templates/nginx.conf.j2 user {{ nginx_user }}; worker_processes {{ ansible_processor_vcpus }}; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; access_log logs/access.log main; log_format access_json '{ "@timestamp": "$time_iso8601", ' '"remote_addr": "X-Forwarded_For", ' '"referer": "$http_referer", ' '"request": "$request", ' '"status": $status, ' '"bytes":$body_bytes_sent, ' '"agent": "$http_user_agent", ' '"x_forwarded": "$http_x_forwarded_for", ' '"upstr_addr": "$upstream_addr",' '"upstr_host": "$upstream_http_host",' '"upstreamtime": "$upstream_response_time" }'; server_tokens off; fastcgi_cache_path /data/nginx/fastcgi_cache levels=1:1:1 keys_zone=fastcgi_cache:250m inactive=10m max_size=1g; sendfile on; keepalive_timeout 65; gzip on; include /etc/nginx/conf.d/*.conf; } [root@xuzhichao nginx]# cat templates/ansible.web.conf.j2 server { listen 80; server_name {{ server_name }}; charset utf-8,gbk; #防盗链 valid_referers none blocked server_names *.b.com b.* ~\.baidu\. ~\.google\.; if ( $invalid_referer ) { return 403; } #浏览器图标 location = /favicon.ico { root {{ web_site_directory }}; } location / { root {{ web_site_directory }}; index index.html index.php; } } -
编写
nginx主页文件:[root@xuzhichao nginx]# cat files/index.html <h1>ansible web site</h1> -
编写变量文件:
[root@xuzhichao nginx]# cat vars/main.yml web_site_directory: /data/nginx/ansible nginxfile: index.html nginx_user: nginx nginx_group: nginx server_name: ansible.web.com -
编写
playbook文件:[root@xuzhichao nginx]# cat nginx.yml - hosts: 192.168.20.23 remote_user: root roles: - role: nginx -
整个
roles的目录结果如下:[root@xuzhichao nginx]# tree . ├── files │ └── index.html ├── handlers │ └── main.yml ├── nginx.yml ├── tasks │ ├── Check_Configure.yml │ ├── Configure_Nginx.yml │ ├── Init_Nginx.yml │ ├── Install_Nginx.yml │ ├── main.yml │ └── Start_Nginx.yml ├── templates │ ├── ansible.web.conf.j2 │ └── nginx.conf.j2 └── vars └── main.yml 5 directories, 12 files -
运行
palybook文件:[root@xuzhichao nginx]# ansible-playbook nginx.yml PLAY [192.168.20.23] ****************************************************************************************************************************************** TASK [nginx : Install Nginx Server] *************************************************************************************************************************** ok: [192.168.20.23] TASK [nginx : Configure Nginx Conf Files] ********************************************************************************************************************* ok: [192.168.20.23] => (item={u'dest': u'/etc/nginx/nginx.conf', u'src': u'nginx.conf.j2', u'group': u'root', u'user': u'root', u'mode': u'0644'}) ok: [192.168.20.23] => (item={u'dest': u'/etc/nginx/conf.d/ansible.web.conf', u'src': u'ansible.web.conf.j2', u'group': u'root', u'user': u'root', u'mode': u'0644'}) TASK [nginx : Check Nginx Configure] ************************************************************************************************************************** ok: [192.168.20.23] TASK [nginx : Create Web Site Directory] ********************************************************************************************************************** ok: [192.168.20.23] TASK [nginx : Init Nginx Server] ****************************************************************************************************************************** changed: [192.168.20.23] TASK [nginx : Start Nginx Server] ***************************************************************************************************************************** changed: [192.168.20.23] PLAY RECAP **************************************************************************************************************************************************** 192.168.20.23 : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 -
查看
nginx主机的相关信息:[root@nginx03 ~]# cat /data/nginx/ansible/index.html <h1>ansible web site</h1> [root@nginx03 ~]# cat /etc/nginx/conf.d/ansible.web.conf server { listen 80; server_name ansible.web.com; charset utf-8,gbk; #防盗链 valid_referers none blocked server_names *.b.com b.* ~\.baidu\. ~\.google\.; if ( $invalid_referer ) { return 403; } #浏览器图标 location = /favicon.ico { root /data/nginx/ansible; } location / { root /data/nginx/ansible; index index.html index.php; } }

浙公网安备 33010602011771号