TOC
Template模版
Ansible程序除了Palybook剧本之外,还提供了模版技术,这俩必须配合使用,在Palybook当中,可以采用模版的方式来进行文本处理,模版本身其实也是一个文本文件,但是之所以称之为模版文件,那是因为在这个文本文件中,有很多利用Mustache语法模版语法来表示的占位符,所以说,模版文件其实是一个并不完整的文件;
对于Ansible中的模版,常用于配置文件处理,根据不同的环境生成不同的配置文件,模版文件的后缀也有点特殊,它是采用".j2"的后缀作为模版文件,但是,需要知道的是,它必须借助Playbook才能被使用,我们无法直接使用ansible命令行调用,同时,模版文件必须存在于templates目录下,且templates目录需要和Palybook剧本文件平级;
模版渲染
对于Ansible的模版来讲,它是在依靠Python当中的Jinja2模版语言来实现的,在模版文件中,我们可以定义很多以Mustache语法表示的模版占位符,这些占位符最终都会替换成Playbook当中变量的值,这些变量可以来自Vars组件、主机变量、组变量以及变量配置清单;
当Ansible进行渲染时,会直接去这些来源里面去寻找对应的变量,然后将变量对应的Mustache语法表示的占位符进行替换,最终形成我们期望的一个完整的文本文件;
模版引用
如果我们希望在Palybook当中使用模版,来实现配置文件的动态渲染,必须借助一个模块,即template模块,该模块的主要作用和copy模块类似,都是用于将控制端主机的文件拷贝到远端被控主机的指定位置,只不过template模块,在正式复制文件到被控端主机之前,会进行一次模版渲染的过程,将模版文件渲染成一个完整的符合我们期望的文件,然后再将这个渲染后的内容,存储在被控端主机的指定的文件;
那么对于template模块,它的参数也和copy模块非常的像,大部分参数基本上就没改动过,但是这里唯一需要注意的是,就是src参数,它是一个相对路径,它相对的路径是templates文件夹,如下示例;
src:指定template模版文件相对路径,相对templates文件夹;
dest:要将文件复制到远程主机的哪个目录下面的哪个文件,绝对路径;
force:当远程主机的目标路径中已经存在同名文件,并且与ansible主机中的文件内容不同时,是否强制覆盖,可选值有yes和no,默认值为yes,表示覆盖,如果设置为no,则不会执行覆盖拷贝操作,远程主机中的文件保持不变;
backup:当远程主机的目标路径中已经存在同名文件,并且与ansible主机中的文件内容不同时,是否对远程主机的文件进行备份,可选值有yes和no,当设置为yes时,会先备份远程主机中的文件,然后再将ansible主机中的文件拷贝到远程主机;
owner:指定文件拷贝到远程主机后的属主,但是远程主机上必须有对应的用户,否则会报错;
group:指定文件拷贝到远程主机后的属组,但是远程主机上必须有对应的组,否则会报错;
mode:指定文件拷贝到远程主机后的权限,如果你想将权限设置为”rw-r--r--“,则可以使用mode=0644表示,如果你想要在user对应的权限位上添加执行权限,则可以使用mode=u+x表示;
模版数据类型
在Ansible的模版中,支持Python当中大多数的数据类型,如字符串、正数、列表、元组、字典和布尔值,但是需要注意的是,比如字典,它的值或value,我们都不需要使用引号将其包裹,同时,这些数据类型,都需使用Mustache语法来表示,即"{{ var_name }}",如下示例;
[cce@doorta /usr/local/Project/linux]# tree
├── palybook.yaml
└── templates
└── test.j2
# 定义模版文件
[cce@doorta /usr/local/Project/linux]# cat templates/test.j2
name: {{ name }}
age: {{ age }}
names: {{ names }}
user_info: {{ user_info }}
flag: {{ flag }}
# 给定palybook,并定义模版文件中需要的变量
[cce@doorta /usr/local/Project/linux]# cat palybook.yaml
- hosts: 172.16.1.1
remote_user: root
vars:
name: cce
age: 18
names: [ cce , cfj ]
user_info: { name: cce , age: 18 }
flag: true
tasks:
- name: copy template file
template: src=test.j2 dest=/tmp/test.txt%
# 最终结果
[root@node1 ~]# cat /tmp/test.txt
name: cce
age: 18
names: ['cce', 'cfj']
user_info: {'name': 'cce', 'age': 18}
flag: True
循环语句
在Template模版语言当中,也支持循环语句,它依旧采用的是Jinja2独特的语法,但是这里唯一需要注意的是,循环语句迭代的必须是一个容器,比如字符串、列表、字典、元祖,如下示例;
{% for i in containers %}
{{ i }} # 如果是一个字典,直接使用字典的方式取值即可
{% endfor %}
逻辑运算符
Ansible的模版语言中,支持众多的运算符,这些运算符主要是在进行逻辑判断的时候,我们会用到,一共有五种,算数运算符、逻辑运算符、比较运算符、布尔运算符以及is运算符,如下;
算数运算符:+、-、*、/、//、%、**
逻辑运算符:==、!=、>、>=、<、<=
比较运算符:and、or、not
布尔运算符:false、true
is defined:特殊运算符,主要用来判断指定变量是否定义;
逻辑判断
在Template模版语言当中,也支持所谓的逻辑判断,即IF语句,它和循环语句类似,语法较为特殊,它一般是用来配合循环语句来使用的,如下,通过循环来迭代一个数组内的元素,然后通过逻辑判断来判断哪些元素需要进行渲染,哪些元素不需要进行渲染;
{% for item in itens %}
{% if item.age > 10 %}
My Name Is {{ item.name }},and Age is {{ item.age }}
{% endif %}
{% endfor %}
Roles角色
角色是在Ansible 1.2版本引入的新特性,主要用于层次性、结构化地组织Playbook,同时,Roles还能够根据具有层次感的结构自动装载vars文件、task文件及handlers等文件的特性,简单来讲,Roles就是将Vars、Files、Tasks、Templates及Handlers放置于单独的目录中,并可以使用include关键字来便捷地引用它们的一种模块化机制;
那么一个Roles项目下面一般有如下几个目录,它们分别用来实现不同类型的模块化,这里唯一需要注意的是,除了files、templates文件夹之外,其他的所有文件夹内部,最少有一个main.yaml的文件,用于引入其他的模块;
files:存放由copy或script模块等调用的文件;
templates:template模块查找所需要模块文件的目录;
handlers:定义handlers,至少应该包含一个main.yml的文件,其他的文件需要在此文件中通过include进行包含;
tasks:定义tasks,至少应该包含一个名为main.yml的文件,其他的文件需要在此文件中通过include进行包含;
vars:定义变量,至少应该包含一个名为main.yml的文件,其他的文件需要在此文件中通过include进行包含;
meta:定义当前角色的特殊设定及其依赖关系,至少应该包含一个main.yml的文件,其他文件需在此文件中通过include进行包含;
default:设定默认变量时使用此目录中的main.yml文件,它比vars的优先级低;
Roles使用注意
Roles项目,必须存在于Ansible配置文件中中,指定的roles_path指定的目录下,如果存在多个目录,可以使用":"隔开,我们可以通过,ansible-galaxy role list命令来查看当前Ansible的Roles存放目录,如下;
[cce@doorta ~]# ansible-galaxy role list
# /etc/ansible/roles
# /usr/local/Project/linux
可以看到,一共有两个目录可以用于存放我们的Roles项目,我们只需要将我们的项目直接放在这两个目录的任何一个目录即可;
Roles目录解构
那么在了解来Roles常用的目录之后,我们先来看看Roles目录解构,首先,创建一个Roles项目有两种方式,第一种是通过Ansible所提供的ansible-galaxy命令行来创建一个Roles项目,也可以直接手动去组织整个Roles项目的目录解构,如下示例
[cce@doorta /usr/local/Project/linux]# ansible-galaxy role init cce
from cryptography.exceptions import InvalidSignature
- Role cce was created successfully
[cce@doorta /usr/local/Project/linux]# tree cce
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
# 如上,这就是通过ansible-galaxy命令来创建的一个Roles项目,它具备了一个Roles所需的所有目录解构;
可以看到,对于一个Roles项目来讲,很多目录下都有一个main.yaml的文件,该文件主要有两个作用,第一个作用,使用include语法将其他小模块引入到main.yaml文件中来,Ansible在执行这个Roles项目时,就可以直接在这个mian.yaml的文件中组织和获取palybook的内容;
第二个作用,就是执行的优先顺序,在mian.yaml文件中,主要是使用include语法将其他的小模块引入进来,那么Ansible在执行时,会自上而下执行,所以,我们也可以调整include的顺序,达到顺序执行的效果;
Roles基础实战
那么在熟悉了Roles基本结构之后,下面就通过一个小例子,来测试Roles的功能特性,如下,主要是通过Roles来一键安装,并配置、启动一个Nginx的服务,最终达到直接可以访问的效果;
# 创建基本目录结构
[cce@doorta /usr/local/Project/linux]# mkdir -p -v nginx/{files,templates,handlers,vars,tasks}
[cce@doorta /usr/local/Project/linux]# touch nginx/playbook.yaml
[cce@doorta /usr/local/Project/linux]# touch nginx/{handlers,vars,tasks}/main.yaml
[cce@doorta /usr/local/Project/linux]# tree nginx
├── files
├── handlers
│ └── main.yaml
├── playbook.yaml
├── tasks
│ └── main.yaml
├── templates
└── vars
└── main.yaml
# 创建网页源码文件
[cce@doorta /usr/local/Project/linux]# echo "<h1>Welcome To Nginx</h1>" > nginx/files/index.html
# 定义Nginx配置文件
[cce@doorta /usr/local/Project/linux]# cat nginx/templates/nginx.conf.j2
user nobody;
worker_processes 4;
events {
worker_connections 65535;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
keepalive_timeout 900;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css image/jpeg image/gif image/png;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
server_tokens off;
client_max_body_size 4096m;
limit_req_zone $binary_remote_addr zone=zone_web:50m rate=30r/s;
server {
listen {{ nginx_port }};
server_name {{ nginx_server_name }};
root /usr/share/nginx/html;
default_type 'text/html';
try_files $uri $uri/ /index.html;
charset utf-8;
underscores_in_headers on;
}
}
# 定义安装Nginx模块
[cce@doorta /usr/local/Project/linux]# cat nginx/tasks/install_nginx.yaml
- name: Install Nginx
yum: name=nginx state=present
# 定义复制网站源码文件模块
[cce@doorta /usr/local/Project/linux]# cat nginx/tasks/copy_code.yaml
- name: Copy Code
copy: src=index.html dest=/usr/share/nginx/html/index.html owner=nginx group=nginx mode=644
# 定义配置文件模块,并触发启动任务
[cce@doorta /usr/local/Project/linux]# cat nginx/tasks/changge_nginx_conf.yaml
- name: Change Nginx Config
template: src=nginx.conf.j2 dest=/etc/nginx/nginc.conf owner=nginx group=nginx mode=644
notify: Start Nginx
# 载入main.yaml配置文件
[cce@doorta /usr/local/Project/linux]# cat nginx/tasks/main.yaml
- include: install_nginx.yaml
- include: copy_code.yaml
- include: changge_nginx_conf.yaml
# 定义触发器任务
[cce@doorta /usr/local/Project/linux]# cat nginx/handlers/start_nginx.yaml
- name: Start Nginx
systemd: name=nginx state=started enabled=yes
# 载入main.yaml配置文件
[cce@doorta /usr/local/Project/linux]# cat nginx/handlers/main.yaml
- include: start_nginx.yaml
# 定义变量
[cce@doorta /usr/local/Project/linux]# cat nginx/vars/main.yaml # 变量直接在main.yaml中定义即可
nginx_port: 80
nginx_server_name: localhost
# 定义入口Playbook文件
[cce@doorta /usr/local/Project/linux]# cat playbook.yaml
- hosts: all
remote_user: root
gather_facts: no
roles:
- nginx
# Roles目录结构如下
[cce@doorta /usr/local/Project/linux]# tree
├── nginx
│ ├── files
│ │ └── index.html
│ ├── handlers
│ │ ├── main.yaml
│ │ └── start_nginx.yaml
│ ├── tasks
│ │ ├── changge_nginx_conf.yaml
│ │ ├── copy_code.yaml
│ │ ├── install_nginx.yaml
│ │ └── main.yaml
│ ├── templates
│ │ └── nginx.conf.j2
│ └── vars
│ ├── main.yaml
│ └── nginx_conf.yaml
└── playbook.yaml
# 测试Roles项目
[cce@doorta /usr/local/Project/linux]# ansible-playbook playbook.yaml
PLAY [all]
*****************************************************************************
TASK [nginx : Install Nginx]
*****************************************************************************
changed: [172.16.1.1]
changed: [172.16.1.2]
TASK [nginx : Copy Code]
*****************************************************************************
changed: [172.16.1.1]
changed: [172.16.1.2]
TASK [nginx : Change Nginx Config]
*****************************************************************************
changed: [172.16.1.1]
changed: [172.16.1.2]
RUNNING HANDLER [nginx : Start Nginx]
*****************************************************************************
changed: [172.16.1.1]
changed: [172.16.1.2]
PLAY RECAP
*****************************************************************************
172.16.1.1 : ok=4 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
172.16.1.2 : ok=4 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# 验证结果
[cce@doorta /usr/local/Project/linux]# curl 172.16.1.1
<h1>Welcome To Nginx</h1>
[cce@doorta /usr/local/Project/linux]# curl 172.16.1.2
<h1>Welcome To Nginx</h1>