Ansible之template模板

Posted 空白的Melody

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ansible之template模板相关的知识,希望对你有一定的参考价值。

模板是一个文本文件,可以做为生成文件的模版,并且模板文件中还可嵌套jinja2语法

Jinja2 是一个现代的,设计者友好的,仿照 Django 模板的 Python 模板语言。 它速度快,被广泛使用,并且提供了可选的沙箱模板执行环境保证安全:

特性:

沙箱中执行
强大的 html 自动转义系统保护系统免受 XSS
模板继承
及时编译最优的 python 代码
可选提前编译模板的时间
易于调试。异常的行数直接指向模板中的对应行。
可配置的语法

官方网站:

http://jinja.pocoo.org/
https://jinja.palletsprojects.com/en/2.11.x/

官方中文文档

http://docs.jinkan.org/docs/jinja2/
https://www.w3cschool.cn/yshfid/

jinja2 语言支持多种数据类型和操作:

字面量,如: 字符串:使用单引号或双引号,数字:整数,浮点数
列表:[item1, item2, ...]
元组:(item1, item2, ...)
字典:{key1:value1, key2:value2, ...}
布尔型:true/false
算术运算:+, -, *, /, //, %, **
比较操作:==, !=, >, >=, <, <=
逻辑运算:and,or,not
流表达式:For,If,When
字面量:

表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。如"Hello World" 双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个字符串(比如函数调用、过滤器或只是包含或继承一个模板的参数),如42,42.23数值可以为整数和浮点数。如果有小数点,则为浮点数,否则为整数。在 Python 里, 42 和 42.0 是不
一样的

算术运算:

Jinja 允许用计算值。支持下面的运算符

+:把两个对象加到一起。通常对象是素质,但是如果两者是字符串或列表,你可以用这 种方式来衔接 它们。无论如何这不是首选的连接字符串的方式!连接字符串见 ~ 运算符。 {{ 1 + 1 }} 等于 2

-:用第一个数减去第二个数。 {{ 3 - 2 }} 等于 1

/:对两个数做除法。返回值会是一个浮点数。 {{ 1 / 2 }} 等于 0.5

//:对两个数做除法,返回整数商。 {{ 20 // 7 }} 等于 2

%:计算整数除法的余数。 {{ 11 % 7 }} 等于 4

*:用右边的数乘左边的操作数。 {{ 2 * 2 }} 会返回 4 。也可以用于重 复一个字符串多次。 {{ \'=\' * 80 }}
会打印 80 个等号的横条\\

**:取左操作数的右操作数次幂。 {{ 2**3 }} 会返回 8

比较操作符

== 比较两个对象是否相等

!= 比较两个对象是否不等

> 如果左边大于右边,返回 true

>= 如果左边大于等于右边,返回 true

< 如果左边小于右边,返回 true

<= 如果左边小于等于右边,返回 true

逻辑运算符

对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式

and 如果左操作数和右操作数同为真,返回 true

or 如果左操作数和右操作数有一个为真,返回 true

not 对一个表达式取反

(expr)表达式组

true / false true 永远是 true ,而 false 始终是 false

template

template功能:可以根据和参考模块文件,动态生成相类似的配置文件

template文件必须存放于templates目录下,且命名为 .j2 结尾
yaml/yml 文件需和templates目录平级,目录结构如下示例:

./
├── temnginx.yml
└── templates
└── nginx.conf.j2

范例:利用template 同步nginx配置文件

# 准备templates/nginx.conf.j2文件
---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

执行

[root@centos8 ~]# ansible-playbook tempngix.yml

template变更替换 范例:

# 修改文件nginx.conf.j2

---
- hosts: websrvs
  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
    - name: start service
      service: name=nginx state=started enabled=yes

执行

[root@centos8 ~]# ansible-playbook temnginx2.yml
template算术运算

范例:

vim nginx.conf.j2
worker_processes {{ ansible_processor_vcpus**2 }};
worker_processes {{ ansible_processor_vcpus+2 }};

范例:

---
- hosts: websrvs
  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
      notify: restart Nginx
    - name: start service
      service: name=nginx state=started enabled=yes

  handlers:
    - name: restart Nginx
      service: name=nginx state=restarted

执行:目标主机只执行某一台

[root@centos8 ~]# ansible-playbook temp2nginx.yml  --limit 172.31.0.38

template中使用流程控制 for 和 if

template中也可以使用流程控制 for 循环和 if 条件判断,实现动态生成文件功能

for 循环

格式

{% for i in EXPR %}
  ...
{% endfor %}
示例:
{% for i in range(1,10) %}
  server_name web{{i}};
{% endfor %}

范例

# tem3nginx.yml
---
- hosts: websrvs
  remote_user: root
  vars:
    nginx_vhosts:
      - 81  
      - 82
      - 83
  tasks:
    - name: template config
      template: src=nginx.conf2.j2 dest=/data/nginx.conf

# templates/nginx.conf2.j2
{% for vhost in nginx_vhosts %}
server {
    listen {{ vhost }}
}
{% endfor %}

# 执行
[root@centos8 ~]# ansible-playbook tem3nginx.yml --limit 172.31.0.48

# 效果
[root@centos8 ~]# cat /data/nginx.conf 
server {
    listen 81
}
server {
    listen 82
}
server {
    listen 83
}

范例:

# tem4nginx.yml
---
- hosts: websrvs
  remote_user: root
  vars:
    nginx_vhosts:
      - listen 8080
  tasks:
    - name: config file
      template: src=nginx.conf3.j2 dest=/data/nginx3.conf

# templates/nginx.conf3.j2
{% for vhost in nginx_vhosts %}
server{
  listen {{vhost.listen}}
}
{% endfor %}

# 执行 注意:说是某台目标主机ip,但是必须是主机清单这个文件指定的这个组的ip才可以
[root@centos8 ~]# ansible-playbook tem4nginx.yml --limit 172.31.0.38

范例

- hosts: websrvs
  remote_user: root
  vars:
    nginx_vhosts:
      - listen: 8080
        server_name: "web1.longxuan.vip"
        root: "/var/www/nginx/web1/"
      - listen: 8081
        server_name: "web2.longxuan.vip"
        root: "/var/www/nginx/web2/"
      - {listen: 8082, server_name: "web3.longxuan.vip", root: "/var/www/nginx/web3/"}
  tasks:
    - name: template config
      template: src=nginx.conf4.j2 dest=/data/nginx4.conf

[root@centos8 /etc/ansible]# vim /root/templates/nginx.conf4.j2
{% for vhost in nginx_vhosts %}
server{
    listen {{ vhost.listen }}
    server_name {{ vhost.server_name }}
    root {{ vhost.root }}
}
{% endfor %}

# 执行
[root@centos8 /etc/ansible]# ansible-playbook tem5nginx.yml --limit 172.31.0.48

# 效果
[root@centos8 ~]# cat /data/nginx4.conf 
server{
    listen 8080
    server_name web1.longxuan.vip
    root /var/www/nginx/web1/
}
server{
    listen 8081
    server_name web2.longxuan.vip
    root /var/www/nginx/web2/
}
server{
    listen 8082
    server_name web3.longxuan.vip
    root /var/www/nginx/web3/
}

范例:

[root@centos8 /etc/ansible]# vim templates/nginx.conf5.j2
upstream webserver{
{% for in range(1,11) %}
  server 172.31.0.{{i}}:{{http_port}}
{% endfor %}
}

server{
   listen {{http_port}};
   server_name {{server_name}};
   location / {
      proxy_pass http://webserver;
   }
}

[root@centos8 /etc/ansible]# vim tem6nginx.yml
- hosts: websrvs
  vars:
    http_port: 80
    server_name: www.longxuan.vip
  tasks:
    - name: install nginx
      yum: name=nginx
    - name: config file
      template: src=nginx.conf5.j2 dest=/etc/nginx/conf.d/web_proxy.conf
    - name: start nginx
      service: name=nginx state=started

# 执行
[root@centos8 /etc/ansible]# ansible-playbook tem6nginx.yml

范例

# nginx.conf6.j2
upstream webservers {
{% for in groups[\'webservers] %}
  server {{i}}:{{http_port}}
{% endfor %}
vim hosts
[webservers]
172.31.0.101
172.31.0.102

if 条件判断

在模版文件中还可以使用if条件判断,决定是否生成相关的配置信息

范例:

#templnginx6.yml
- hosts: websrvs
  remote_user: root
  vars:
    nginx_vhosts:
      - web1:
        listen: 8080
        root: "/var/www/nginx/web1/"
      - web2:
        listen: 8080
        server_name: "web2.magedu.com"
        root: "/var/www/nginx/web2/"
      - web3:
        listen: 8080
        server_name: "web3.magedu.com"
        root: "/var/www/nginx/web3/"
  tasks:
    - name: template config to
      template: src=nginx.conf5.j2 dest=/data/nginx5.conf

#templates/nginx.conf6.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 %}

#生成的结果
server {
listen 8080
root /var/www/nginx/web1/
}
server {
listen 8080
server_name web2.magedu.com
root /var/www/nginx/web2/
}
server {
listen 8080
server_name web3.magedu.com
root /var/www/nginx/web3/
}

范例: 生成keepalived配置文件

vrrp_instrance VI_1 {
{% if ansible_fqdn == "ka1" %}
  state MASTER
  priority 100
{% elif ansible_fqdn == "ka2" %}
  state SLAVE
  priority 80
{% endif% }
......
}

使用循环迭代

迭代:当有需要重复性执行的任务时,可以使用迭代机制

迭代 with_items(loop)

对迭代项的引用,固定内置变量名为"item"

要在task中使用with_items给定要迭代的元素列表

注意: ansible2.5版本后,可以用loop代替with_items

列表元素格式:
字符串
字典

范例:

[root@centos8 /etc/ansible]# cat item.yml 
---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: add serveral user
      user: name={{item}} state=present group=wheel
      with_items:
        - testuser1
        - testuser2
        - testuser3
        
# 执行
[root@centos8 /etc/ansible]# ansible-playbook item.yml

范例:卸载 mariadb

---
# remove mariadb server
- hosts: appsrvs:!10.0.0.8
  remote_user: root
  tasks:
    - name: stop service
      shell: /etc/init.d/mysqld stop
    - name: delete files and dir
      file: path={{item}} state=absent
      with_items:
        - /usr/local/mysql
        - /usr/local/mariadb-10.2.27-linux-x86_64
        - /etc/init.d/mysqld
        - /etc/profile.d/mysql.sh
        - /etc/my.cnf
        - /data/mysql
    - name: delete user
      user: name=mysql state=absent remove=yes

范例:

---
- hosts:websrvs
  remote_user: root
  tasks
    - name: install some packages
      yum: name={{ item }} state=present
      with_items:
        - nginx
        - mysql
        - php-fpm

范例:

---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: copy file
      copy: src={{ item }} dest=/tmp/{{ item }}
      with_items:
        - file1
        - file2
        - file3
    - name: yum install httpd
      yum: name={{ item }} state=present
      with_items:
        - apr
        - apr-util
        - httpd

迭代嵌套子变量

在迭代中,还可以嵌套子变量,关联多个变量在一起使用

示例:

---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: add some groups
      group: name={{ item }} state=present
      with_items:
        - nginx
        - mysql
        - apache
    - name: add some users
      user: name={{ item.user }} group={{ item.group }} uid={{item.uid}} state=present
      with_items:
        - { user: \'nginx\', group: \'nginx\',uid: "80" }
        - { user: \'mysql\', group: \'mysql\' ,uid: "306" }
        - { user: \'apache\', group: \'apache\',uid: "8080" }

执行

[root@centos8 /etc/ansible]# ansible-playbook with_item.yml

范例:

[root@ansible ~]# cat with_item2.yml
---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: add some groups
      group: name={{ item }} state=present
      with_items:
        - g1
        - g2
        - g3
    - name: add some users
      user: name={{ item.name }} group={{ item.group }} home={{ item.home }} create_home=yes state=present
      with_items:
        - { name: \'user1\', group: \'g1\', home: \'/data/user1\' }
        - { name: \'user2\', group: \'g2\', home: \'/data/user2\' }
        - { name: \'user3\', group: \'g3\', home: \'/data/user3\' }

范例:

# ansible-doc file
- name: Create two hard links
  file:
    src: \'/tmp/{{ item.src }}\'
    dest: \'{{ item.dest }}\'
    state: hard
  loop:
    - { src: x, dest: y }
    - { src: m, dest: n }

范例:

- hosts: websrvs
  vars:
    rsyncd_conf: /etc/rsync.conf
    rsync_pass: /etc/rsync.pass
  tasks:
    - name: Configure Rsyncd Service
      template: src={{ item.src }} dest={{ item.dest }} mode={{ item.mode }}
      with items:
        - {src: \'./rsyncd.conf.j2\', dest: {{ rsyncd_conf }}, mode: 0644 }
        - {src: \'./rsync.pass.j2\', dest: {{ rsync_pass }}, mode: 0600 }

范例: 批量修改用户密码

---
- hosts: ssh-host    #这个主机清单一定要存在
  gather_facts: false
  tasks:
    - name: change user passwd
      user: name={{ item.name }} password={{ item.chpass | password_hash(\'sha512\')}} update_pass
word=always
      with_items:
        - { name: "root",chpass: \'123456\' }
        - { name: "app",chpass: "654321" }

执行

[root@centos8 /etc/ansible]# ansible-playbook ssh-host.yml

until 循环

范例: until 循环

# until为false时才会执行循环,为true则退出循环
[root@centos8 /etc/ansible]# vim until.yml
- hosts: websrvs
  gather_facts: false
  tasks:
    - debug: msg="until"
      until: false
      retries: 3  # 默认值即3次
      delay: 1

with_lines 逐行处理

范例: with_lines 逐行处理

[root@ansible ansible]#cat with_lines.yml
- hosts: localhost
  tasks:
    - debug: msg={{ item }}
      with_lines: ps aux

playbook使用 when

when语句可以实现条件测试。如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过在task后添加when子句即可使用条件测试,jinja2的语法格式

范例:条件判断

---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: "shutdown RedHat flavored systems"
      command: /sbin/shutdown -h now
      when: ansible_os_family=="RedHat"

范例: 判断服务状态决定是否重新启动

[root@localhost ~]# vim when2.yml
---
- hosts: websrvs
  tasks:
    - name: check nginx service  # 检查nginx服务是否是活动的
      command: systemctl is-active nginx
      ignore_errors: yes
      register: check_nginx
    - name: httpd restart     # 如果check nginx执行命令结果成功,即check_nginx.rc等于0,则执
行重启nginx,否则跳过
      service: name=nginx state=restarted
      when: check_nginx.rc == 0

范例: when的列表形式表示 and 关系

---
- hosts: all
  tasks:
    - name: "shutdown centos 7 systems"
      reboot:
      when:   
        - ansible_facts[\'distribution\'] == "CentOS"
        - ansible_facts["distribution_major_version"] == "7"

范例: 和循环一起使用

- hosts: websrvs
  tasks:
    - debug: msg="item > 3"
      with_items: [1,2,3,4,5,6]
      when: item > 3

范例: failed_when 满足条件时,使任务失败,和when功能相反

tasks:
  - command: echo failed
    register: result
    failed_when: "\'failed\' in result.stdout"
    #failed_when: false 不满足条件,任务正常执行
    #failed_when: true 满足条件,使用任务失败
  - debug: msg="echo failed_when"

范例:

---
- hosts: websrvs
  remote_user: root
  tasks:
    - name: install conf file to centos7
      template: src=nginx.conf.c7.j2 dest=/etc/nginx/nginx.conf
      when: ansible_distribution_major_version == "7"
    - name: install conf file to centos6
      template: src=nginx.conf.c6.j2 dest=/etc/nginx/nginx.conf
      when: ansible_distribution_major_version == "6"

分组 block

当想在满足一个条件下,执行多个任务时,就需要分组了。而不再每个任务都是用when

[root@ansible ansible]# cat block.yml
---
- hosts: localhost
  tasks:
    - block:
        - debug: msg="first"
        - debug: msg="second"
      when:
        - ansible_facts[\'distribution\'] == "CentOS"
        - ansible_facts[\'distribution_major_version\'] == "8"

changed_when

利用 changed_when 检查task返回结果

changed_when 检查task返回结果,决定是否继续向下执行

[root@ansible ansible]# cat test_changed_when.yml
---
- hosts: websrvs
  tasks:
    - name: install nginx
      yum: name=nginx
    - name: config file
      template: src="nginx.conf.j2" dest="/etc/nginx/nginx.conf"
      notify: restart nginx
    - name: check config
      shell: /usr/sbin/nginx -t
      register: check_nginx_config
      changed_when:
        - (check_nginx_config.stdout.find(\'successful\'))  #如果执行结果中有successful字符串,则继续执行,如果没有则停止向下执行
        - false   #nginx -t 每次成功执行是changed状态,关闭此changed状态
    - name: start service
      service: name=nginx state=started enabled=yes
  handlers:
    - name: restart nginx
      service: name=nginx state=restarted

关闭 changed 状态

当确定某个task不会对被控制端做修改时但执行结果却显示是黄色的changed状态,可以通过
changed_when: false 关闭changed状态

[root@ansible ansible]# cat test_changed.yml
---
- hosts: websrvs
  tasks:
    - name: check sshd service
      shell: ps aux| grep sshd
      changed_when: false  #关闭changed状态

滚动执行

管理节点过多导致的超时问题解决方法
默认情况下,Ansible将尝试并行管理playbook中所有的机器。对于滚动更新用例,可以使用serial关键字定义Ansible一次应管理多少主机,还可以将serial关键字指定为百分比,表示每次并行执行的主机数占总数的比例

范例:

[root@ansible ansible]# vim test_serial.yml
---
- hosts: all
  serial: 2    #每次只同时处理2个主机,将所有task执行完成后,再选下2个主机再执行所有task,直至所有主机
  gather_facts: False
  tasks:
    - name: task one
      comand: hostname
    - name: task two
      command: hostname

范例:

- name: test serail
  hosts: all
  serial: "20%"  #每次只同时处理20%的主机

范例:

[root@ansible ansible]# cat test_serial.yml
---
- hosts: websrvs
  serial: 1
  tasks:
    - name: task1
      shell: wall "{{ansible_nodename}} is running task1"
    - name: task2
      shell: wall "{{ansible_nodename}} is running task2"
    - name: task3
      shell: wall "{{ansible_nodename}} is running task3"

委派至其它主机执行

利用委托技术,可以在非当前被控主机的其它主机上执行指定操作

范例:

[root@ansible ~]# cat delegate.yml
#在172.31.0.8上执行hostname -I,而非当前主机localhost
- hosts: localhost
  tasks:
    - name: show ip address
      command: hostname -I
      delegate_to: 172.31.0.8

范例:

#在本地执行ifconfig,而非172.31.0.8
[root@ansible ~]# cat delegate2.yml
- hosts: 172.31.0.8
  tasks:
    - name: show ip address
      local_action: command ifconfig

只执行一次

利用 run_once 指令可以只执行一次,而非在所有被控主机都执行

[root@ansible ~]# cat run_once.yml
- hosts: websrvs
  tasks:
    - command: hostname
      run_once: true
      
[root@ansible ~]# ansible-playbook run_once.yml --list-hosts
playbook: run_once.yml
  play       #1 (websrvs): websrvs TAGS: []
    pattern: [\'websrvs\']
    hosts (2):
      172.31.0.8
      172.31.0.7
[root@ansible ~]# ansible-playbook run_once.yml

环境变量

临时修改环境变量

[root@ansible ~]# cat environment.yml
- hosts: localhost
  tasks:
    - shell: echo $PATH
      environment:
        PATH: /usr/local/app/bin:{{ ansible_env.PATH }}
        
[root@ansible ~]# ansible-playbook environment.yml -v

wait_for 等待条件再执行

#等待端口可用,才能执行任务
#暂停10s等待端口80打开,否则出错
wait_for: port=80 delay=10
#等待直到锁定文件被删除
wait_for: path=/var/lock/file.lock state=absent

yaml文件的相互调用

利用include 或 include_tasks可以在某个task中调用其它的只有task内容的yaml文件

[root@ansible ansible]# cat a.yml
---
- hosts: websrvs
  tasks:
    - name: run a job
      command: wall run a job
    - name: excute b.yml
      include: b.yml       #调用另一个yaml文件
      #include_tasks: b.yml #另一种写法
      
[root@ansible ansible]# cat b.yml
- name: run b job
  command: wall run b job

也可以将多个包含完整内容的yml文件由一个yml统一调用

[root@ansible ansible]# cat total_tasks.yml
- import_playbook: testks1.yml
- import_playbook: testks2.yml

[root@ansible ansible]# cat testks1.yml
---
- hosts: websrvs
  tasks:
    - name: run testk1 job
      command: wall run testk1 job

[root@ansible ansible]# cat testks2.yml
---
- hosts: websrvs
  tasks:
    - name: run testk2 job
      command: wall run testk2 job

以上是关于Ansible之template模板的主要内容,如果未能解决你的问题,请参考以下文章

Ansible之template模板

微信小程序开发之--"template模板“的应用

vscode之快速生成vue模板

微信小程序第七天WXML语法之模板用法

微信小程序第七天WXML语法之模板用法

Linux九阴真经之九阴白骨爪残卷5(ansible用法二之template及roles 角色)