ansible系列(29)--ansible的Jinja2语法及应用
1. Ansible Jinja2
Jinja2
是 Python
的全功能模板引擎
Ansible
需要使用 Jinja2
模板来修改被管理主机的配置文件。
ansible
使用 jinja2
模板需要借助 template
模块实现。
template
模块和 copy
模块完全一样,都是拷贝文件至远程主机,与copy
的语法基本完全一致,区别在于template
模块会自动解析要拷贝的文件中变量的值,而 copy
模块则是原封不动的将文件拷贝至被控端。
1.1 jinja2语法结构
- 要想在配置文件中使用
jinj2
,playbook
中的tasks
必须使用template
模块。 - 配置文件里面使用变量,比如
{{ PORT }}
或使用{{ facts 变量 }}
。 - {% %} :用来装载控制语句,比如
if
控制结构,for
循环控制结构。 - {# #} :用来装载注释,模板文件被渲染后,注释不会包含在最终生成的文件中。
jinja2
文件的后缀一般为.j2
。
jinja2
的判断和循环的语法如下:
-
if
判断:{% if EXPR %} ... {% endif %} 或: {% if EXPR %} ... {% else %} ... {% endif %} 或: {% if EXPR %} ... {% elif EXPR %} ... {% endif %} 或: {% if EXPR %} ... {% elif EXPR %} ... {% else %} ... {% endif %}
-
循环:
{% for i in EXPR %} ... {% endfor %}
循环示例如下:
# cat test.j2 jinja2 test {% for i in [3,1,7,8,2] %} {{ i }} {% endfor %} 渲染效果如下: # cat /opt/test jinja2 test 3 1 7 8 2
-
在
jinja2
中,波浪符”~”
就是字符串连接符,它会把所有的操作数转换为字符串,并且连接它们:# cat test.j2 jinja2 test {% for i in [3,1,7,8,2] -%} {{ i~' ' }} {%- endfor %} 渲染效果如下: # cat test jinja2 test 3 1 7 8 2
1.2 jinja2中{{ }}中的运算符
-
比较表达式的相关示例如下:
#模板文件内容如下: # cat test.j2 jinja2 test {{ 1 == 1 }} {{ 2 != 2 }} {{ 2 > 1 }} {{ 2 >= 1 }} {{ 2 < 1 }} {{ 2 <= 1 }} #生成文件内容如下: # cat test jinja2 test True False True True False False
-
逻辑运算的相关示例如下:
#模板文件内容 # cat test.j2 jinja2 test {{ (2 > 1) or (1 > 2) }} {{ (2 > 1) and (1 > 2) }} {{ not true }} {{ not True }} {{ not false }} {{ not False }} #生成文件内容 # cat test jinja2 test True False False False True True
-
算数运算的相关示例如下:
模板文件内容 # cat test.j2 jinja2 test {{ 3 + 2 }} {{ 3 - 4 }} {{ 3 * 5 }} {{ 2 ** 3 }} {{ 7 / 5 }} {{ 7 // 5 }} {{ 17 % 5 }} 生成文件内容 # cat test jinja2 test 5 -1 15 8 1.4 1 2
-
成员运算的相关示例如下:
模板文件内容 # cat test.j2 jinja2 test {{ 1 in [1,2,3,4] }} {{ 1 not in [1,2,3,4] }} 生成文件内容 # cat test jinja2 test True False
-
向{{ }}中传递变量:
#jinja2文件如下: jinja2 test {{ teststr }} {{ testnum }} {{ testlist[1] }} {{ testlist1[1] }} {{ testdic['name'] }} #playbook文件如下: # cat temptest.yml --- - hosts: test70 remote_user: root gather_facts: no vars: teststr: 'tstr' testnum: 18 testlist: ['aA','bB','cC'] testlist1: - AA - BB - CC testdic: name: bob age: 18 tasks: - template: src: /testdir/ansible/test.j2 dest: /opt/test #最终生成后的文件如下: # cat test jinja2 test tstr 18 bB BB bob
1.3 jinja2中for循环和if判断示例
-
目标:生成如下的配置文件:
{ server_name www.example.com; listen 80; } { server_name app.example.com; listen 81; } { server_name blog.example.com; listen 82; }
-
首先要定义一个模板文件,在模板文件中来调用这些值,例如上例中的
server
名字和端口各不相同,需要通过定义变量将不同的值区分出来,我们首先来解决端口的问题:[root@xuzhichao playbook]# cat jinja2_example1.yml - hosts: localhost vars: ports: - 81 - 82 - 83 tasks: - name: template: src: file/jinja2_test1 dest: /tmp/jinja2_test1
-
模板文件如下:
[root@xuzhichao playbook]# cat file/jinja2_test1.j2 {% for p in ports %} <==此处的ports需要与定义的变量名保持一致 server { listen {{ p }} } {% endfor %}
-
运行
playbook
,最终得到的文件如下:[root@xuzhichao playbook]# ansible-playbook jinja2_example1.yml [root@xuzhichao playbook]# cat /tmp/jinja2_test1 server { listen 81 } server { listen 82 } server { listen 83 }
-
然后我们再来达到最终的效果,修改一下
playbook
文件,变量定义使用字典的形式:[root@xuzhichao playbook]# cat jinja2_example1.yml - hosts: localhost vars: host: - server: www.example.com port: 81 - server: app.example.com port: 82 - server: blog.example.com port: 83 tasks: - name: template: src: file/jinja2_test1.j2 dest: /tmp/jinja2_test1
-
模板文件如下:
[root@xuzhichao playbook]# cat file/jinja2_test1.j2 {% for var in host %} server { server_name {{ var.server }}; listen {{ var.port }}; } {% endfor %}
-
运行
playbook
,最终得到的文件如下:[root@xuzhichao playbook]# ansible-playbook jinja2_example1.yml [root@xuzhichao playbook]# cat /tmp/jinja2_test1 server { server_name www.example.com; listen 81; } server { server_name app.example.com; listen 82; } server { server_name blog.example.com; listen 83; }
-
这样得到了最终的目标,此时若某一个
nginx
虚拟主机需要使用默认的端口80
(即没有定义port
这个变量),或者虚拟主机没有定义server_name
,这样上面的模板就无法实现了,此时我们可以修改一下模板文件,增加判断机制,如下:[root@xuzhichao playbook]# cat file/jinja2_test1.j2 {% for var in host %} server { {% if var.server is defined %} <==判断一下变量是否定义,只有定义了才会写到配置文件中 server_name {{ var.server }}; {% endif %} {% if var.port is defined %} listen {{ var.port }}; {% endif %} } {% endfor %}
-
测试,修改
playbook
文件如下:[root@xuzhichao playbook]# cat jinja2_example1.yml - hosts: localhost vars: host: - server: www.example.com - server: app.example.com port: 82 - port: 83 tasks: - name: template: src: file/jinja2_test1.j2 dest: /tmp/jinja2_test1
-
运行
playbook
,生成的文件如下:[root@xuzhichao playbook]# ansible-playbook jinja2_example1.yml [root@xuzhichao playbook]# cat /tmp/jinja2_test1 server { server_name www.example.com; } server { server_name app.example.com; listen 82; } server { listen 83; }
1.4 Jinja2管理Nginx负载均衡
-
需求:在
nginx
的负载均衡场景中,配置文件中会定义后端WEB主机列表,如果新增加了一个后端服务器,就需要为ansible添加一个主机清单,并且手动去配置文件中添加这个WEB主机,我们需要使用模板功能实现在ansible的添加一个主机清单后,nginx的配置文件就自动把这个主机添加到后端WEB服务器中。 -
首先定义
playbook
文件:[root@xuzhichao playbook]# cat proxy_test.yml - hosts: localhost tasks: - name: Copy Template Nginx Configure template: src: conf/proxy.conf.j2 dest: /tmp/proxy.conf notify: Restart Nginx handlers: - name: Restart Nginx service: name: nginx state: restarted
-
模板文件如下:
upstream ansible_php { {% for i in groups['NginxWebs'] %} server {{ i }}:80; {% endfor %} } server { listen 80; server_name ansible.example.com; location / { proxy_pass http://ansible_php; proxy_set_header Host $http_hosts; } }
-
运行
playbook
,生成的文件如下:[root@xuzhichao playbook]# ansible-playbook jinja2_example1.yml [root@xuzhichao playbook]# cat /tmp/proxy.conf upstream ansible_php { server 192.168.20.22:80; server 192.168.20.23:80; } server { listen 80; server_name ansible.example.com; location / { proxy_pass http://ansible_php; proxy_set_header Host $http_hosts; } }
1.5 Jinja2管理Keepalived
ansible
使用jinja2
的if
判断表达式渲染出keepalived
的Master
和Slave
的配置文件。并推送至被控主机,实现方案如下:
-
使用
playbook
推送keeplaived
配置文件:[root@xuzhichao playbook]# cat keepalived.yml - hosts: lvs tasks: - name: Copy Template Keepalived Configure template: src: keepalived.conf.j2 dest: /etc/keepalived/keepalived.conf notify: Restart Keepalived Server handlers: - name: Restart Keepalived Server service: name: keepalived state: restarted
-
准备
keepalived.conf.j2
配置文件:[root@xuzhichao playbook]# cat keepalived.conf.j2 global_defs { router_id {{ ansible_fqdn }} } vrrp_instance VI_1 { {% if ansible_fqdn == 'lvs01' %} #如果主机名为lb01则使用如下配置 state MASTER priority 150 {% elif ansible_fqdn == 'lvs02' %} #如果主机名为lb02则使用如下配置 state Backup priority 100 {% endif %} #相同配置 interface eth0 virtual_router_id 51 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 10.0.0.3 } }
-
运行
palybook
,检查 lvs01Master
节点的keepalived
配置文件:[root@lvs01 playbook]# cat keepalived.conf.j2 global_defs { router_id {{ ansible_fqdn }} } vrrp_instance VI_1 { state MASTER priority 150 interface eth0 virtual_router_id 51 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 10.0.0.3 } }
-
检查 lvs02
Backup
节点的keepalived
配置文件:[root@lvs02 playbook]# cat keepalived.conf.j2 global_defs { router_id {{ ansible_fqdn }} } vrrp_instance VI_1 { state Backup priority 100 interface eth0 virtual_router_id 51 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 10.0.0.3 } }