ansible系列(29)--ansible的Jinja2语法及应用



1. Ansible Jinja2

Jinja2Python 的全功能模板引擎

Ansible 需要使用 Jinja2 模板来修改被管理主机的配置文件。

ansible 使用 jinja2 模板需要借助 template 模块实现。

template 模块和 copy 模块完全一样,都是拷贝文件至远程主机,与copy的语法基本完全一致,区别在于template 模块会自动解析要拷贝的文件中变量的值,而 copy模块则是原封不动的将文件拷贝至被控端。

1.1 jinja2语法结构

  • 要想在配置文件中使用 jinj2playbook 中的 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使用jinja2if判断表达式渲染出keepalivedMasterSlave的配置文件。并推送至被控主机,实现方案如下:

  • 使用 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,检查 lvs01 Master 节点的 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 
    	}
    }
    
posted @ 2021-08-18 17:41  向往自由的独行者  阅读(620)  评论(0编辑  收藏  举报