Ansible之templates模板
一、jinja2简介解
Jinja2是Python下一个被广泛应用的模版引擎,他的设计思想来源于Djanjo的模板引擎,并扩展了其语法和一系列强大的功能。ansible的模板配置文件就是用jinja2这种模板编程语言写的,其中jinja2使用字面量有如下形式
1)字符串:使用单引号或双引号引起来的部分
2)数字:支持整数,浮点数(小数)
3)列表:和python里的列表一样,用中括号括起来,每个元素之间用逗号隔开,如[item1,item2,....]
4)元组:和python里的元组一样,小括号括起来,每个元素之间用逗号隔开,如(item1,item2,....)
5)字典:同python里的字典一样,大括号括起来,每个k/v(键值对)用逗号隔开,键和值用":"冒号隔开,如{key1:value1,key2:value2,....}
6)布尔型:同其他语言布尔型一样,ture/false
7)支持算数运算:+,-,*,/,//(整除,地板除),%(取模,取余数),**(幂运算)
8)支持比较操作:==(比较两个值是否相等),!=(不等),>,>=(大于等于),<,<=(小于等于)
9)支持逻辑运算:and(与),or(或),not(非)
10)支持for(循环) ,if(判断),when(当某一条件满足才开始执行when所在的代码块,作用类似if)
除此以外,jinja2还支持“test”测试语句,比如我们要判断某个变量是否定义,可以使用defined来判断,如 vars is defined ,如果vars定义则表达式返回true,相反返回false。类似的还有undefined(与defined相反),equalto(与“==”作用等效),even(判断对象是否是偶数),iterable(判断对象是否可迭代)
二、jinja2语法
jinja2有如下语法:
1)控制结构,它和我们写别的语言的控制结构类似,唯一不同的是它的for循环要用”{%%}“给括起来,且必须写在两个百分号之间,后面结束有一个{%endfor$}来表示for循环的结束,同样if语句也是,必须写在"{%%}" 两个百分号之间,后面结束也有个{%endif%}来表示if语句的结束,当然if语句支持elif ,else 这个同python里的if用法一样。
2)变量引用,jinja2的变量引用同ansible的变量应用类似,都是用"{{}}" 双大括号来表示中间括起来的内容表示变量
3)注释用"{#"开始,中间部分表示注释,以"#}"结束表示注释结束,支持多行注释和当行注释
示例:
1)for循环使用
{% for vhost in nginx_vhosts %} server { listen {{ vhost.listen }} } {% endfor %}
说明:以上模板表示,从nginx_vhosts列表里去循环,nginx_vhosts里有几个元素,它将循环几次,每次循环取的值用vhost变量代替,它的用法类型shell里的for循环和python里的for循环
2)if单分支选择使用
{% if vhost.server_name is defined %} server_name {{ vhost.server_name }} {% endif %}
3)if多分支选择使用
{%if vhost.port is undefined %} http_port=80 {%elif vhost.port == 81%} http_port=81 {%else%} http_port = 83 {%endif%}
4)单行注释
{#% for i in list %#}
5)多行注释
{# {% for i in list %} i+=i {% endfor %} #}
三、playbook中使用模板
templates是ansible的一个模块,其功能是根据模板文件动态生成配置文件,templates文件必须存放于templates目录下,且命名为".j2"结尾,yaml/yml文件需要和templates目录平级,这样我们在yml文件中调用模板的时候,就不需要写模板文件的路径,否则需要描述模板文件的路径,因为template模块会自动去找templates目录下的模板文件。目录结构如下
. ├── temnginx.yml └── templates └── nginx.conf.j2
示例:playbook中template变量替换
利用tamplates同步nginx配置文件,其中修改nginx.conf.j2(worker_processes {{ ansible_processor_vcpus }};)指定其worker进程的个数由远程主机的cpu个数决定
[root@test ~]#ll 总用量 4 -rw-r--r-- 1 root root 212 11月 18 14:08 temnginx.yml drwxr-xr-x 2 root root 27 11月 18 14:05 templates [root@test ~]#tree . ├── temnginx.yml └── templates └── nginx.conf.j2 1 directory, 2 files [root@test ~]#head templates/nginx.conf.j2 # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes {{ ansible_processor_vcpus }}; error_log /var/log/nginx/error.log; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/nginx/README.dynamic. [root@test ~]#cat temnginx.yml --- - hosts: websers remote_user: root tasks: - name: install nginx yum: name=nginx - name: template config to remote hosts template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf [root@test ~]#
说明:以上playbook里是使用template模板去动态生成的配置文件,不是拷贝,拷贝是将文件原封不动的拷贝过去,还需要注意的是template模块不能在ad-hoc命令行里使用,只能在playbook里使用,且模板文件必须是".j2"结尾的
示例:playbook中template算数运算
还是以上的示例,我们可以这样写
vim nginx.conf.j2 worker_processes {{ ansible_processor_vcpus**2 }};
说明:我们可以用ansible内置变量做算术运算后的值作为模板文件的值,在playbook中还是用template模块去调用就好了
示例:模板文件中 for循环的用法
[root@test ~]#tree . ├── temnginx.yml └── templates └── nginx.conf.j2 1 directory, 2 files [root@test ~]#cat templates/nginx.conf.j2 {% for vhost in nginx_vhosts %} server { listen {{ vhost.listen }} } {% endfor %} [root@test ~]#cat temnginx.yml --- - hosts: websers remote_user: root vars: nginx_vhosts: - listen: 8080 tasks: - name: install nginx yum: name=nginx - name: template config to remote hosts template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf [root@test ~]#
生成结果:
[root@test ~]#ansible-playbook temnginx.yml PLAY [websers] ****************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************** ok: [192.168.0.128] ok: [192.168.0.218] TASK [install nginx] ************************************************************************************************ changed: [192.168.0.128] changed: [192.168.0.218] TASK [template config to remote hosts] ****************************************************************************** changed: [192.168.0.128] changed: [192.168.0.218] PLAY RECAP ********************************************************************************************************** 192.168.0.128 : ok=3 changed=2 unreachable=0 failed=0 192.168.0.218 : ok=3 changed=2 unreachable=0 failed=0 [root@test ~]#ansible websers -m shell -a 'cat /etc/nginx/nginx.conf' 192.168.0.128 | SUCCESS | rc=0 >> server { listen 8080 } 192.168.0.218 | SUCCESS | rc=0 >> server { listen 8080 } [root@test ~]#
示例:模板文件中if的用法
[root@test ~]#tree . ├── temnginx.yml └── templates └── nginx.conf.j2 1 directory, 2 files [root@test ~]#cat temnginx.yml --- - hosts: websers remote_user: root vars: nginx_vhosts: - web1: listen: 8080 server_name: "web1.test.com" root: "/var/www/nginx/web1/" - web2: listen: 8080 root: "/var/www/nginx/web2/" - web3: listen: 8080 server_name: "web2.test.com" root: "var/www/nginx/web3/" tasks: - name: template config to remote hosts template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf [root@test ~]#cat templates/nginx.conf.j2 {% for vhost in nginx_vhosts %} server { listen {{ vhost.listen }} {% if vhost.server_name is defined %} server_name {{ vhost.server_name }} {% endif %} root {{ vhost.root }} } {% endfor %} [root@test ~]#
生成结果:
[root@test ~]#ansible-playbook temnginx.yml PLAY [websers] ****************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************** ok: [192.168.0.128] ok: [192.168.0.218] TASK [template config to remote hosts] ****************************************************************************** changed: [192.168.0.128] changed: [192.168.0.218] PLAY RECAP ********************************************************************************************************** 192.168.0.128 : ok=2 changed=1 unreachable=0 failed=0 192.168.0.218 : ok=2 changed=1 unreachable=0 failed=0 [root@test ~]#ansible websers -m shell -a 'cat /etc/nginx/nginx.conf' 192.168.0.128 | SUCCESS | rc=0 >> server { listen 8080 server_name web1.test.com root /var/www/nginx/web1/ } server { listen 8080 root /var/www/nginx/web2/ } server { listen 8080 server_name web2.test.com root var/www/nginx/web3/ } 192.168.0.218 | SUCCESS | rc=0 >> server { listen 8080 server_name web1.test.com root /var/www/nginx/web1/ } server { listen 8080 root /var/www/nginx/web2/ } server { listen 8080 server_name web2.test.com root var/www/nginx/web3/ } [root@test ~]#
说明:可以看到第二个server里是没有server_name,因为定义的变量列表里web2没有定义server_name
四、playbook中使用when条件测试
条件测试:如果需要根据变量、facts或此前任务的执行结果来作为某task执行与否的前提时需要用到条件测试,通过when语句实现,在task中使用jinja2的语法格式,when语句支持jinja2表达式语法;
示例:
[root@test ~]#cat test.yml --- - hosts: all remote_user: root tasks: - name: set hostname centos6 hostname: name=ansible_centos6 when: ansible_distribution_major_version == "6" - name: set hostname centos7 hostname: name=ansible_centos7 when: ansible_distribution_major_version == "7" [root@test ~]#ansible all -m shell -a 'hostname' 192.168.0.128 | SUCCESS | rc=0 >> localhost 192.168.0.218 | SUCCESS | rc=0 >> localhost.localdomain 192.168.0.217 | SUCCESS | rc=0 >> centos7 [root@test ~]#ansible-playbook test.yml PLAY [all] ********************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************** ok: [192.168.0.128] ok: [192.168.0.218] ok: [192.168.0.217] TASK [set hostname centos6] ***************************************************************************************** skipping: [192.168.0.217] changed: [192.168.0.218] changed: [192.168.0.128] TASK [set hostname centos7] ***************************************************************************************** skipping: [192.168.0.128] skipping: [192.168.0.218] changed: [192.168.0.217] PLAY RECAP ********************************************************************************************************** 192.168.0.128 : ok=2 changed=1 unreachable=0 failed=0 192.168.0.217 : ok=2 changed=1 unreachable=0 failed=0 192.168.0.218 : ok=2 changed=1 unreachable=0 failed=0 [root@test ~]#ansible all -m shell -a 'hostname' 192.168.0.128 | SUCCESS | rc=0 >> ansible_centos6 192.168.0.218 | SUCCESS | rc=0 >> ansible_centos6 192.168.0.217 | SUCCESS | rc=0 >> ansible_centos7 [root@test ~]#
说明:以上playbook利用when语句判断系统版本号,实现了通过不同的系统版本号,设置不同的主机名。
五、playbook迭代with_items
迭代:当有需要重复性执行的任务时,可使用迭代机制;对迭代项的引用,ansible有固定不变的变量,名为“item”;要在task中使用with_items给定要迭代的元素列表,列表格式可以为字符串、字典。
示例:批量创建用户
[root@test ~]#cat adduser.yml --- - hosts: websers remote_user: root tasks: - name: add users user: name={{ item }} state=present home=/home/{{ item }} groups=root,bin,wheel with_items: - user1 - user2 - user3 [root@test ~]#ansible-playbook adduser.yml PLAY [websers] ****************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************** ok: [192.168.0.128] ok: [192.168.0.218] TASK [add users] **************************************************************************************************** changed: [192.168.0.128] => (item=user1) changed: [192.168.0.218] => (item=user1) changed: [192.168.0.128] => (item=user2) changed: [192.168.0.218] => (item=user2) changed: [192.168.0.128] => (item=user3) changed: [192.168.0.218] => (item=user3) PLAY RECAP ********************************************************************************************************** 192.168.0.128 : ok=2 changed=1 unreachable=0 failed=0 192.168.0.218 : ok=2 changed=1 unreachable=0 failed=0 [root@test ~]#ansible websers -m shell -a 'tail -3 /etc/passwd' 192.168.0.128 | SUCCESS | rc=0 >> user1:x:503:503::/home/user1:/bin/bash user2:x:504:504::/home/user2:/bin/bash user3:x:505:505::/home/user3:/bin/bash 192.168.0.218 | SUCCESS | rc=0 >> user1:x:1213:1213::/home/user1:/bin/bash user2:x:1214:1214::/home/user2:/bin/bash user3:x:1215:1215::/home/user3:/bin/bash [root@test ~]#ansible websers -m shell -a 'id user1' 192.168.0.218 | SUCCESS | rc=0 >> uid=1213(user1) gid=1213(user1) 组=1213(user1),0(root),1(bin),10(wheel) 192.168.0.128 | SUCCESS | rc=0 >> uid=503(user1) gid=503(user1) 组=503(user1),0(root),1(bin),10(wheel) [root@test ~]#
示例:迭代嵌套子变量
[root@test ~]#cat adduser2.yml --- - hosts: websers remote_user: root tasks: - name: create groups group: name={{ item }} with_items: - group1 - group2 - group3 - name: create users user: name={{ item.name }} group={{ item.group }} state=present with_items: - {name: 'test1',group: 'group1'} - {name: 'test2',group: 'group2'} - {name: 'test3',group: 'group3'} [root@test ~]#ansible-playbook adduser2.yml PLAY [websers] ****************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************** ok: [192.168.0.128] ok: [192.168.0.218] TASK [create groups] ************************************************************************************************ changed: [192.168.0.128] => (item=group1) changed: [192.168.0.218] => (item=group1) changed: [192.168.0.128] => (item=group2) changed: [192.168.0.218] => (item=group2) changed: [192.168.0.128] => (item=group3) changed: [192.168.0.218] => (item=group3) TASK [create users] ************************************************************************************************* changed: [192.168.0.128] => (item={u'group': u'group1', u'name': u'test1'}) changed: [192.168.0.218] => (item={u'group': u'group1', u'name': u'test1'}) changed: [192.168.0.128] => (item={u'group': u'group2', u'name': u'test2'}) changed: [192.168.0.218] => (item={u'group': u'group2', u'name': u'test2'}) changed: [192.168.0.128] => (item={u'group': u'group3', u'name': u'test3'}) changed: [192.168.0.218] => (item={u'group': u'group3', u'name': u'test3'}) PLAY RECAP ********************************************************************************************************** 192.168.0.128 : ok=3 changed=2 unreachable=0 failed=0 192.168.0.218 : ok=3 changed=2 unreachable=0 failed=0 [root@test ~]#ansible websers -m shell -a 'tail -3 /etc/passwd' 192.168.0.128 | SUCCESS | rc=0 >> test1:x:506:506::/home/test1:/bin/bash test2:x:507:507::/home/test2:/bin/bash test3:x:508:508::/home/test3:/bin/bash 192.168.0.218 | SUCCESS | rc=0 >> test1:x:1216:1216::/home/test1:/bin/bash test2:x:1217:1217::/home/test2:/bin/bash test3:x:1218:1218::/home/test3:/bin/bash [root@test ~]#ansible websers -m shell -a 'id test1' 192.168.0.218 | SUCCESS | rc=0 >> uid=1216(test1) gid=1216(group1) 组=1216(group1) 192.168.0.128 | SUCCESS | rc=0 >> uid=506(test1) gid=506(group1) 组=506(group1) [root@test ~]#ansible websers -m shell -a 'id test2' 192.168.0.128 | SUCCESS | rc=0 >> uid=507(test2) gid=507(group2) 组=507(group2) 192.168.0.218 | SUCCESS | rc=0 >> uid=1217(test2) gid=1217(group2) 组=1217(group2) [root@test ~]#ansible websers -m shell -a 'id test3' 192.168.0.128 | SUCCESS | rc=0 >> uid=508(test3) gid=508(group3) 组=508(group3) 192.168.0.218 | SUCCESS | rc=0 >> uid=1218(test3) gid=1218(group3) 组=1218(group3) [root@test ~]#
说明:以上playbook实现了对应用户创建时加入对应的组里