你可以将所有东西都放到一个 playbook 文件中,但是随着文件越来越大,你修改起来也越来越麻烦。这时候可以把一些 playtaskhandler 放到其他文件中,然后通过 include指令包含进来,这样做完全没问题,但是 ansible 还提供了一个更好的解决方法,也就是roles,下面分别讲解

Ansible技术问答:http://linux.xyz/topic/Ansible

1、include 包含

playbook 可以包含其他 playbook 文件、task 文件和 handler 文件

1)包含 task 文件

如果有多个 play 都需要几个相同的 task,在每个 play 中都写一遍这些 task 就不明智了,聪明做法是将这些task 单独放到一个文件中,格式如下:

---
# possibly saved as tasks/foo.yml
- name: placeholder foo
 command: /bin/foo
- name: placeholder bar
 command: /bin/bar

然后在需要这些 task 的 play 中通过 include 包含上面的 tasks/foo.yml:

tasks:
 - include: tasks/foo.yml

还可以向 include 传递变量,如你部署了多个 wordpress 实例,通过向相同的wordprss.yml 文件传递不同的值来区分实例:

tasks:
 - include: wordpress.yml user=lipeibin #在 foo.yml 可以通过{{ user }}来使用这些变量
 - include: wordpress.yml user=liuzhiwei
 - include: wordpress.yml user=qubaoquan
 

如果使用 ansible1.4 及以上版本,include 还可以写成字典格式:

tasks:
- { include: wordpress.yml, user: lipeibin, ssh_keys: [ 'keys/one.txt',
'keys/two.txt' ] } #ssh_keys 是一个列表

从 ansible1.0 开始,还可以用如下格式向 include 传递变量:

tasks:
 - include: wordpress.yml
 vars:
 remote_user: timmy
 some_list_variable:
 - alpha
 - beta
 - gamma

2)包含 handler 文件

---
# this might be in a file like handlers/handlers.yml
- name: restart apache
 service: name=httpd state=restarted

在 play 末尾包含上面的 handler 文件:

handlers:
 - include: handlers/handlers.yml

3)包含 playbook 文件

- name: this is a play at the top level of a file
 hosts: all
 remote_user: root
 tasks:
 - name: say hi
 tags: foo
 shell: echo "hi..."
- include: load_balancers.yml #这些 playbook 文件中也至少定义了一个 play
- include: webservers.yml
- include: dbservers.yml

2、roles 角色

1)roles 组织结构

roles 用来组织 playbook 结构,以多层目录和文件将 playbook 更好的组织在一起,一个经过 roles 组织的 playbook 目录结构如下:

webservers.yml
roles/
 webservers/ # 这层目录是role角色的名字,下面的子目录都不是必须提供的,没有的目录会自动忽略,不会出现问题,所以你可以只有 tasks 子目录也没问题
 files/ # 这个目录是存放的是需要复制到远程主机的文件
 templates/ # 模板文件存放的目录
 tasks/ # 顾名思义,任务目录
 handlers/ # 触发器目录
 vars/ # 定义这个 role 所有 tasks 用到的变量
 meta/ # role 依赖关系定义

在 playbook 文件中包含 webservers 这个 role,webservers.yml 内容如下:

---
# file: webservers.yml
- hosts: all
 roles:
 - webservers

简单演示源码安装 Nginx 服务器,如下:

├── roles
│ └── nginx
│ ├── files
│ │ └── nginx-1.6.1.tar.gz
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ ├── basic.yml
│ │ ├── main.yml
│ │ └── nginx.yml
│ ├── templates
│ │ └── nginx.conf.j2
│ └── vars
│ └── main.yml
└── linuxxyz-nginx.yml

注:有些子目录并没有用到 ,如 meta, 因为没有其它依赖关 roles 可以不用创建

---
# file: linuxxz-nginx.yml
- hosts: nginxservers
 remote_user: root
 roles:
 - { role: nginx,tags:nginx }
---
# file: roles/nginx/vars/main.yml
worker_processes: 4
worker_connections: 65535
keepalive_timeout: 60

---
# file: roles/nginx/tasks/main.yml
- include: basic.yml
- include: nginx.yml
---
# file: roles/nginx/tasks/basic.yml
- name: install basic parket for nginx
yum: name={{ item }} state=latest enablerepo=epel
with_items:
 - pcre
 - pcre-devel
---
# file: roles/nginx/tasks/nginx.yml
- name: unzip nginx parket to remote hosts
 unarchive: src=nginx-1.6.1.tar.gz dest=/tmp
- name: make install nginx
 shell: cd /tmp/nginx-1.6.1/ && ./configure --prefix=/usr/local/nginx -
-with-http_ssl_module --with-http_stub_status_module && make && make in
stall
- name: copy nginx config file to remote hosts
 template: src=nginx.conf.j2 dest=/usr/local/nginx/conf/nginx.conf
 notify:
 - restart nginx service
- name: ensure nginx service is running
service: name=nginx state=started
---
# file: roles/nginx/handlers/main.yml
- name: restart nginx service
service: name=nginx state=restarted

2)roles 目录说明

假如有一个 play 包含了一个叫"x"的 role,则:

  • /path/roles/x/tasks/main.yml 中的 tasks 都将自动添加到该 play 中
  • /path/roles/x/handlers/main.yml 中的 handlers 都将添加到该 play 中
  • /path/roles/x/vars/main.yml 中的所有变量都将自动添加到该 play 中
  • /path/roles/x/meta/main.yml中的所有role 依赖关系都将自动添加到roles 列表
  • /path/roles/x/defaults/main.yml 中为一些默认变量值,具有最低优先权,在没有其他任何地方指定该变量的值时,才会用到默认变量值
  • task 中的 copy 模块和 script 模块会自动从/path/roles/x/files 寻找文件,也就是根本不需要指定文件绝对路径或相对路径,如 src=foo.txt 则自动转换为/path/roles/x/files/foo.txt
  • task 中的 template 模块会自动从/path/roles/x/templates/中加载模板文件,无需指定绝对路径或相对路径
  • 通过 include 包含文件会自动从/path/roles/x/tasks/中加载文件,无需指定绝对路径或相对路径

注:从 ansible1.4 开始可以在 ansible.cfg 配置文件中通过 roles_path 指令自定义roles 的路径

vim /etc/ansible/ansible.cfg
roles_path=/data/svndata/Code_base/other/svn_project/ansible/atom/roles

如同 include 一样,也可以为 role 传递变量,格式如下:

---
- hosts: webservers
 roles:
 - common
 - { role: foo_app_instance, dir: '/opt/a', port: 5000 } #在 foo_app_instance 这个role的task文件和模板文件中通过{{ dir }}和{{ port }}来使用变量
 - { role: foo_app_instance, dir: '/opt/b', port: 5001 }

如果一个 play 中,既有 tasks 也有 roles,那么 roles 会先于 tasks 执行,可以通过pre_tasks 和 post_tasks 指令指定某些 task 先于或晚于 roles 执行:

---
- hosts: webservers
 pre_tasks:
 - shell: echo 'hello' #最先执行
 roles:
 - { role: some_role } #第二个执行
  tasks:
 - shell: echo 'still busy'
 post_tasks:
 - shell: echo 'goodbye' #最后执行

3)roles 依赖关系

可以在一个 role 的 meta/main.yml 中定义该 role 依赖其他的 role,然后调用该 role的时候,会自动去拉取其他依赖的 role,如一个名为 myapp 的 role 的 meta/main.yml文件如下:

---
dependencies:
 - { role: common, some_parameter: 3 } #向依赖的 role 传递变量
 - { role: apache, port: 80 }
 - { role: postgres, dbname: blarg, other_parameter: 12 }

那么这些 role 的执行顺序为:

common
apache
postgres
myapp #会先把 myapp 依赖的其他所有 role 执行完再执行自己

默认一个 role 只能被其他 role 依赖一次,多次依赖不会执行,但是可以通过allow_duplicates 指令来改变这种行为: 名为 car 的 role 的 meta/main.yml:

---
dependencies:
 - { role: wheel, n: 1 }
 - { role: wheel, n: 2 }
 - { role: wheel, n: 3 }
 - { role: wheel, n: 4 }

名为 wheel 的 role 的 meta/main.yml:

---
allow_duplicates: yes
dependencies:
 - { role: tire }
 - { role: brake }

执行顺序如下:

tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
..