Ansible支持的两种重用机制是Roles和Includes。
Roles
是一种可重用的任务和变量的集合,是一种类似于编程语言中的包的概念
Includes
则是一种将一个Playbook分解成多个文件的方法,可以将一些常用的任务和变量放在单独的文件中,然后在需要的Playbook中引用这些文件。
在Ansible中,可以使用include关键字将一个或多个文件引入到一个Playbook中,从而实现代码重用
的目的。通过将常用的、通用的任务写在一个文件中,可以避免
在每个Playbook中重复编写
这些任务的代码,大大减少了工作量。此外,使用include语句还可以提高代码的可读性和可理解性
,因为将通用的代码抽象出来,使得Playbook的代码更加简洁清晰,易于维护和更新
。同时,使用include语句还有助于降低代码的耦合性
,因为将常用的任务抽象出来,可以使得不同的Playbook之间的依赖关系更加清晰明了,从而避免代码耦合度过高的问题。
以下是一个使用include语句的简单案例:
假设我们有一个ansible项目,其中使用了两个Playbook:web.yaml和db.yaml,它们分别用于部署Web服务器和数据库服务器。现在我们想要在这两个Playbook中引入一个共同的任务文件:common.yaml,以避免在两个Playbook中重复编写相同的任务代码。
首先,在项目的根目录下创建一个名为tasks的文件夹,用于存放共同的任务文件common.yml。然后在该文件夹下创建一个名为common.yml的文件,内容如下:
- name: install packages
remote_user: root
yum:
name: git,vim,curl,wget,unzip,zip,net-tools
state: present
这个任务用于安装一些常用的软件包,这些软件包在Web服务器和数据库服务器中都需要安装。
接下来,我们可以在web.yaml和db.yaml中使用include语句来引入该文件,代码如下:
web.yaml文件:
- name: Deploy web server
hosts: test1
tasks:
- include: tasks/common.yaml
- name: install and configure Apache
yum:
name: httpd
state: present
- service:
name: httpd
enabled: true
state: started
db.yaml文件:
- name: Deploy database server
hosts: test2
tasks:
- include: tasks/common.yaml
- name: install and configure Mysql
yum:
name: mariadb-server
state: present
- service:
name: mariadb
enabled: true
state: started
在上面的代码中,我们使用了include语句来引入tasks/common.yml文件中的任务,这样就避免了在web.yaml和db.yaml中重复编写安装常用软件包的任务代码。
role有比include更强大灵活的代码重用和分享机制。include类似于编程语言中的include,是重用单个文件的,重用的功能有限。
而role类似于编程语言中的 “Package”,可以重用一组文件,形成完整的功能。例如,安装和配置Apache,既需要任务实现安装包和复制模板,也需要httpd.conf 和 index.html 的模板文件,以及handler文件实现的重启功能。这些文件都可以放在一个role里面,以供不同的Playbook文件重用
定义role完整的目录结构
在Ansible中,通过遵循特定的目录结构,就可以实现对role定义。
那么如何创建角色目录呢?
ansible-galaxy init myrole
# 上述命令会在当前目录下创建一个名为myrole的目录,其默认包含了标准的角色目录结构。
一个标准的角色目录结构如下:
如果ansible.yaml 中要调用 role
---
- hosts: test1
remote_user: root
roles:
- myrole
Ansible 并不要求role包含上述所有的目录及文件,可以根据role的功能,加入对应的目录和文件即可。下面是每个目录和文件的功能:
main.yaml
是必须的,其他任务文件可按需添加。main.yaml
是必须的,其他变量文件可按需添加。main.yaml
是必须的,其他默认变量文件可按需添加。main.yaml
是必须的,其他处理程序可按需添加。inventory
:角色的测试用例的主机清单文件,定义了测试用例中需要使用的主机和主机组等信息。test.yaml
:角色的测试用例文件,定义了测试用例中需要执行的任务和测试方法等信息。此外,下面的文件不需要绝对或者是相对路径,和放在同一目录下的文件一样,直接使用即可
copy 或 scipt 使用 roles/x/files/下的文件
template使用roles/x/templates下的文件
include使用roles/x/tasks下的文件
而在写role的时候,一般要包含role入口文件 roles/x/tasks/main.yaml,其他的文件和目录,可以根据自己需求选择是否加入
带参数的role
下面定义了一个带参数的role,名字myrole,其目录结构如下
main.yaml
roles
myrole
tasks
main.yaml
在roles/myrole/tasks/main.yaml中,使用{{ }}定义的变量就可以了
---
- name: use param
debug:
msg="{{ param }}"
使用带有参数的role
在main.yaml中可以用如下的方法使用myrole了
---
- hosts: test1
remote_user: root
roles:
- role: myrole
param: 'Test ansible'
- role: myrole
param: 'Test ansible2'
role指定默认的参数
指定默认的参数后,如果在调用时传参数,那么就使用传入的参数值,如果在调用的时候没有传参数,那么就使用默认的参数值
指定默认的参数很简单,一上面的参数为例
main.yaml
roles
myrole
tasks
main.yaml
defaults
main.yaml
在roles/myrole/defaults/main.yaml 中
---
- hosts: test1
remote_user: root
roles:
- role: myrole
- role: myrole
param: "I am the value from external"
在前面的定义中,你会感到好奇,为什么文件夹default和vars下面的变量都会加到Play中,它们由上面区别吗?
default/main.yaml 中的是默认变量。优先级在所有的变量中是最低的,用于放置一些需要被覆盖的变量
vars/main.yaml 中的变量是role变量,优先级比较高,放置一些不想被覆盖的变量,所以变量在命名的时候一般都加入role的名字作为前缀,放置不小心被Playbook中定义的变量覆盖
任务是Playbook和role的核心,role是一种组织Playbook的方式,包含任务、模板、变量和文件等资源。在role中,任务文件tasks/main.yaml是入口文件,其中可以使用变量、静态文件和模板等资源。学会如何使用这些资源是编写role的关键。
role中的资源可以分为两类
使用x/*/main.yaml中的变量和handler
像使用同一个Playbook中的资源一样使用x/*/main.yaml中的变量和handler
使用x/{files,template}/下的文件
像使用同一个目录下的文件一样来使用放在这里的变量。需要模块和使用的文件类型要匹配
copy 、script对应files目录下的文件
template对应templates下的文件
比如,role x 的目录如下
[root@localhost ansible]# tree myrole/
myrole/
├── files
│ └── index.html
├── tasks
│ └── main.yml
├── templates
│ └── httpd.conf.j2
└── vars
└── main.yml
files下面的文件为
<!DOCTYPE html>
<html>
<head>
<title>hello world</title>
</head>
<body>
hi I am csq
</body>
</html>
vars下面的文件为
---
# http.conf vars
http_port: 9999
tasks下的文件为
---
- name: Deploy server web
yum:
name: httpd
state: present
- name: copy index.html
copy:
src: index.html
dest: /var/www/html/index.html
- name: copy http.conf
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
- name: service restart
service:
name: httpd
enabled: true
state: restarted
- name: Release Port
firewalld:
port: 9999/tcp
permanent: true
state: enabled
执行这个role
---
- hosts: test1
remote_user: root
roles:
- myrole
如果 role x 下面的内容比较复杂,需要对任务或者 vars 进一步2分类,可以使用除 main.yaml 以外的文件外。应如何使用其他文件中的任务或者 vars 呢?Ansible 提供了两个关键字:include 和 include_vars ,分别来引入 role 中非 main.yaml 其他文件包含的任务和vars
例如,在下面的role
myrole/
├── tasks
│ └── main.yml
│ └── http_install.yaml
│ └── http_configure.yaml
│ └── mysql_install.yaml
│ └── mysql_configure.yaml
├── templates
│ └── httpd.conf.j2
│ └── mysql.conf.j2
└── vars
└── httpd.yaml
└── mysql.yaml
那么,在 x/*/main.yaml中,通过 include_var来引入httpd的变量后,即可通过include来加载文件install.yaml和configure.yaml中的任务
---
- name: add the os specific varibles
include_vars: httpd.yaml
- name: install packages
yum:
name: {{packages}}
state: present
- include: http_install.yaml
- include: http_configure.yaml
安装一个Nginx 需要配置 yum仓库,如果不想在配置Nginx 的Playbook重新实现配置 yum 仓库的功能,那么就可以通过role的依赖来解决。role依赖关系的定义文件是x/meta/main.yaml。如果在 role x定义依赖 role y,那么在Playbook中调用 role x之前会先调用 role y。当多个 role 依赖同一个role时,Ansible会自动进行过滤,避免重复调用相同参数的role
在下面的例子中,role db 和Web都依赖role common。如果在Playbook中调用db和Web,那么Ansible会保证在role db和 Web运行前,先运行 role common ,并且只运行一次
playbook.yaml
roles
├── common
│ └── tasks
│ └── main.yaml
├── db
│ ├── meta
│ │ └── main.yaml
│ └── tasks
│ └── main.yaml
└── web
├── meta
│ └── main.yaml
└── tasks
└── main.yaml
在{web,db}/meta/main.yaml中添加
dependencies:
- { role: common }
common 下面的main.yaml 内容为
- name: xiugai quanxian
command: chattr -a csq.txt
web 下面的main.yaml 内容为
- name: shuru xinxi
shell: echo "I am web,hi csq" >> csq.txt
- name: jia quanxian
command: chattr +a csq.txt
db 下面的main.yaml 内容为
- name: shuru xinxi
shell: echo "I am db,hi csq" >> csq.txt
- name: jia quanxian
command: chattr +a csq.txt
playbook.yaml 内容为
---
- hosts: test1
remote_user: root
roles:
- common
- db
- web
执行结果
[root@localhost ansible]# ansible-playbook main.yaml
PLAY [test1] ***********************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************
ok: [192.168.200.30]
TASK [common : xiugai quanxian] ****************************************************************************************************************************
changed: [192.168.200.30]
TASK [db : shuru xinxi] ************************************************************************************************************************************
changed: [192.168.200.30]
TASK [db : jia quanxian] ***********************************************************************************************************************************
changed: [192.168.200.30]
TASK [web : shuru xinxi] ***********************************************************************************************************************************
changed: [192.168.200.30]
TASK [web : jia quanxian] **********************************************************************************************************************************
changed: [192.168.200.30]
PLAY RECAP *************************************************************************************************************************************************
192.168.200.30 : ok=6 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
然后查看一下 远程主机内容是否改变,以及隐藏权限是否还在
[root@localhost ansible]# ansible test1 -m shell -a "cat csq.txt && lsattr csq.txt"
192.168.200.30 | CHANGED | rc=0 >>
I am web,hi csq
I am web,hi csq
-----a---------- csq.txt