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>

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注