Ansible-playbook

Playbook

playbook介绍

一. 为什么引入playbook

我们完成一个任务,例如安装部署一个httpd服务,我们需要多个模块(一个模块也可以称之为task)提供功能来完成。而playbook就是组织多个task的容器,他的实质就是一个文件,有着特定的组织格式,它采用的语法格式是YAML(Yet Another Markup Language)。YAML语法能够简单的表示散列表,字典等数据结构。具体请参考YAML详细语法

playbook是由一个或多个"play"组成的列表
play的主要功能在于将预定义的一组主机,装扮成事先通过ansible中的task定义好的角色。
Task实际是调用ansible的一个module,将多个play组织在一个playbook中,
即可以让它们联合起来,按事先编排的机制执行预定义的动作
Playbook采用YAML语言编写

二. playbook基础组件

  • Hosts:运行执行任务(task)的目标主机

  • remote_user:在远程主机上执行任务的用户

  • tasks:任务列表

  • handlers:任务,与tasks不同的是只有在接受到通知时才会被触发

  • templates:使用模板语言的文本文件,使用jinja2语法。

  • variables:变量,变量替换{{ variable_name }}

  • tags标签: 指定某条任务执行,用于选择运行playbook中的部分代码。
    ansible具有幂等性,因此会自动跳过没有变化的部分,
    即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。
    此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
    ansible-playbook -t tagsname useradd.yml

整个playbook是以task为中心,表明要执行的任务。hosts和remote_user表明在哪些远程主机以何种身份执行。其他组件让其能够更加灵活。

下面详细介绍某些组件

playbook基础组件

Hosts:

    > playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务。
      hosts用于指定要执行指定任务的主机,须事先定义在主机清单中

    > 可以是如下形式:
        one.example.com
        one.example.com:two.example.com
        192.168.1.50
        192.168.1.*
    > Websrvs:dbsrvs       或者,两个组的并集
    > Websrvs:&dbsrvs      与,两个组的交集
    > webservers:!phoenix  在websrvs组,但不在dbsrvs组
    示例: - hosts: websrvs:dbsrvs

remote_user:

    可用于Host和task中。
    也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;
    此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户
    - hosts: websrvs
        remote_user: root   (可省略,默认为root)  以root身份连接
      tasks:    指定任务
    - name: test connection
        ping:
        remote_user: magedu
        sudo: yes           默认sudo为root
        sudo_user:wang      sudo为wang

task列表和action:

    可用于Host和task中。
    也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;
    此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户
    - hosts: websrvs
        remote_user: root   (可省略,默认为root)  以root身份连接
      tasks:    指定任务
    - name: test connection
        ping:
        remote_user: magedu
        sudo: yes           默认sudo为root
        sudo_user:wang      sudo为wang

tasks:任务列表

两种格式:
    (1) action: module arguments
    (2) module: arguments 建议使用  模块: 参数
    注意:shell和command模块后面跟命令,而非key=value

某任务的状态在运行后为changed时,可通过"notify"通知给相应的handlers

任务可以通过"tags"打标签,可在ansible-playbook命令上使用-t指定进行调用
示例:
tasks:
  - name: disable selinux   描述
    command: /sbin/setenforce 0   模块名: 模块对应的参数
    
如果命令或脚本的退出码不为零,可以使用如下方式替代
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand || /bin/true  
    转错为正  如果命令失败则执行 true

或者使用ignore_errors来忽略错误信息
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand
    ignore_errors: True  忽略错误

variable

变量定义在资产(inventory)中:

主机变量:
192.168.200.136 http_port=808 maxRequestsPerChild=808
192.168.200.137 http_port=8080 maxRequestsPerChild=909

主机组变量:
[websers]
192.168.200.136
192.168.200.137

[websers:vars]  
ntp_server=ntp.exampl.com
proxy=proxy.exampl.com

定义在playbook中

- hosts: webservers   
  vars:     
    http_port: 80

运行playbook

运行playbook的方式
    ansible-playbook <filename.yml> ... [options]

常见选项
    --check -C       只检测可能会发生的改变,但不真正执行操作 
                     (只检查语法,如果执行过程中出现问题,-C无法检测出来)
                     (执行playbook生成的文件不存在,后面的程序如果依赖这些文件,也会导致检测失败)
    --list-hosts     列出运行任务的主机
    --list-tags      列出tag  (列出标签)
    --list-tasks     列出task (列出任务)
    --limit 主机列表 只针对主机列表中的主机执行
    -v -vv -vvv      显示过程

示例
    ansible-playbook hello.yml --check 只检测
    ansible-playbook hello.yml --list-hosts  显示运行任务的主机
    ansible-playbook hello.yml --limit websrvs  限制主机

Playbook VS ShellScripts

使用playbook安装httpd

SHELL脚本
#!/bin/bash
# 安装Apache
yum install --quiet -y httpd
# 复制配置文件
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp/tmp/vhosts.conf /etc/httpd/conf.d/
# 启动Apache,并设置开机启动
service httpd start
chkconfig httpd on
Playbook定义
---
- hosts: all
  remote_user: root
  
  tasks:
    - name: "安装Apache"
      yum: name=httpd       yum模块:安装httpd
    - name: "复制配置文件"
      copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/  copy模块: 拷贝文件
    - name: "复制配置文件"
      copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/  
    - name: "启动Apache,并设置开机启动"
      service: name=httpd state=started enabled=yes   service模块: 启动服务 

示例:Playbook 创建用户

示例:sysuser.yml
---
- hosts: all
  remote_user: root

  tasks:
    - name: create mysql user
      user: name=mysql system=yes uid=36
    - name: create a group
      group: name=httpd system=yes

Playbook示例 安装httpd服务

示例:httpd.yml
- hosts: websrvs
  remote_user: root

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
    - name: start service
      service: name=httpd state=started enabled=yes

Playbook示例 安装nginx服务

示例 nginx.yml
- hosts: all
  remote_user: root

  tasks:
    - name: add group nginx
      user: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: Start Nginx
      service: name=nginx state=started enabled=yes

===================================

Playbook中handlers使用

===================================

handlers和notify结合使用触发条件

Handlers 实际上就是一个触发器
是task列表,这些task与前述的task并没有本质上的不同,用于当关注的资源发生变化时,才会采取一定的操作

Notify此action可用于在每个play的最后被触发,
这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。
在notify中列出的操作称为handler,也即notify中调用handler中定义的操作
当你的配置文件发生改变的时候,但是你的服务并没有检测到配置文件发生改变而重启服务,此时的hanlers就会检测你的更改配置文件下所标识的notify来重启服务。

Playbook中handlers使用

[root@ansible work]# cat playbook.yaml 
- hosts: server 
  remote_user: root
  tasks: 
    - name: Install httpd 
      yum: name=httpd state=present 
    - name: Install config 
      copy: src=/root/work/httpd.conf dest=/etc/httpd/conf/ backup=yes          # 原先的端口是80 被我改成了8080之后标识notify
      notify: restart service                 #标识必须和handlers一样
    - name: copy config 
      copy: src=/root/work/index.html dest=/var/www/html/
    - name: start service 
      service: name=httpd state=started enabled=yes 

  handlers:								   #空一行分开写 采取措施,采取使用service重启服务的措施
    - name: restart service
      service: name=httpd state=restarted

触发多个notify的handlers用法

- hosts: websrvs
  remote_user: root
  
  tasks:
    - name: add group nginx
      tags: user
      user: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: config
      copy: src=/root/config.txt dest=/etc/nginx/nginx.conf
      notify:
        - Restart Nginx 			# 重启服务
        - Check Nginx Process		 # 检查进程是否启动
  
  handlers:
    - name: Restart Nginx
      service: name=nginx state=restarted enabled=yes
    - name: Check Nginx process
      shell: killall -0 nginx > /tmp/nginx.log

===================================

Playbook中tags使用

===================================

tage: 添加标签

  • 可以指定某一个任务添加一个标签,添加标签以后,想执行某个动作可以做出挑选来执行
  • 多个动作可以使用同一个标签
  • 执行playbookl的时候,假设前面的都执行过了,只是单独的服务没起来,可以加个tags,执行的时候单独 -t [ tags ] 就可以只执行tags的那部分。
[root@ansible work]# cat playbook.yaml  
- hosts: server 
  remote_user: root
  tasks: 
    - name: Install httpd 
      yum: name=httpd state=present 
      tags: installed 
    - name: Install config 
      copy: src=/root/work/httpd.conf dest=/etc/httpd/conf/ backup=yes
      notify: restart service
    - name: copy config 
      copy: src=/root/work/index.html dest=/var/www/html/
    - name: start service 
      service: name=httpd state=started enabled=yes 
      tags: restart 

  handlers:
    - name: restart service
      service: name=httpd state=restarted

只执行tags的 restart部分

[root@ansible work]# ansible-playbook -t restart playbook.yaml  

PLAY [server] *******************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************
ok: [192.168.1.113]
ok: [192.168.1.112]
ok: [192.168.1.110]

TASK [start service] ************************************************************************************************************************************************************************
changed: [192.168.1.113]
changed: [192.168.1.112]
changed: [192.168.1.110]

PLAY RECAP **********************************************************************************************************************************************************************************
192.168.1.110              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.113              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

===================================

Playbook中变量的使用

===================================

变量名:仅能由字母、数字和下划线组成,且只能以字母开头

变量来源:
    1> ansible setup facts 远程主机的所有变量都可直接调用 (系统自带变量)
       setup模块可以实现系统中很多系统信息的显示
                可以返回每个主机的系统信息包括:版本、主机名、cpu、内存
       ansible all -m setup -a 'filter="ansible_nodename"'     查询主机名
       ansible all -m setup -a 'filter="ansible_memtotal_mb"'  查询主机内存大小
       ansible all -m setup -a 'filter="ansible_distribution_major_version"'  查询系统版本
       ansible all -m setup -a 'filter="ansible_processor_vcpus"' 查询主机cpu个数
    
    2> 在/etc/ansible/hosts(主机清单)中定义变量
        普通变量:主机组中主机单独定义,优先级高于公共变量(单个主机 )
        公共(组)变量:针对主机组中所有主机定义统一变量(一组主机的同一类别)
    
    3> 通过命令行指定变量,优先级最高
       ansible-playbook –e varname=value
    
    4> 在playbook中定义
       vars:
        - var1: value1
        - var2: value2
    
    5> 在独立的变量YAML文件中定义
    
    6> 在role中定义

变量命名:
    变量名仅能由字母、数字和下划线组成,且只能以字母开头

变量定义:key=value
    示例:http_port=80

变量调用方式:
    1> 通过{{ variable_name }} 调用变量,且变量名前后必须有空格,有时用“{{ variable_name }}”才生效

    2> ansible-playbook –e 选项指定
       ansible-playbook test.yml -e "hosts=www user=magedu"

在主机清单中定义变量,在ansible中使用变量

[root@ansible work]# vim /etc/ansible/hosts
[appsrvs]
192.168.38.17 http_port=817 name=www
192.168.38.27 http_port=827 name=web

调用变量
ansible appsrvs -m hostname -a'name={{name}}'  更改主机名为各自被定义的变量 

针对一组设置变量
[appsrvs:vars]
make="-"

ansible appsrvs -m hostname -a 'name={{name}}{{mark}}{{http_port}}'  ansible调用变量

将变量写进单独的配置文件中引用

[root@ansible work]# vim vars.yaml 
pack: vsftpd 
service: vsftpd 

引用变量
vars_files:
  - vars.yaml 

使用vars变量安装和启动httpd

  • 执行命令的时候赋值变量
[root@ansible work]# vim playbook.yaml 
- hosts: server
  remote_user: root
  tasks:
    - name: Install httpd
      yum: name={{ pkname }}
    - name: start service
      service: name={{ pkname }} state=started enabled=yes

#使用执行命令的时候给变量赋值
[root@ansible work]# ansible-playbook  -e  'pkname=httpd' playbook.yaml  

PLAY [server] *****************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************
ok: [192.168.1.112]
ok: [192.168.1.113]
ok: [192.168.1.110]

TASK [Install httpd] **********************************************************************************************************
changed: [192.168.1.110]
changed: [192.168.1.112]
changed: [192.168.1.113]

TASK [start service] **********************************************************************************************************
changed: [192.168.1.112]
changed: [192.168.1.113]
changed: [192.168.1.110]

PLAY RECAP ********************************************************************************************************************
192.168.1.110              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.113              : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   



  • 通过vars.yaml指定一个变量文件
[root@ansible work]# vim vars.yaml 
pkname1: httpd
pkname2: vim

[root@ansible work]# vim playbook.yaml 
- hosts: server
  remote_user: root
  vars_files:
    - vars.yaml 
  tasks:
    - name: Install httpd
      yum: name={{ pkname1 }}
    - name: start service
      service: name={ {pkname1} } state=started enabled=yes
    - name: Install vim
      yum: name={{ pkname2 }} 
  • 直接在playbooke的里面定义变量
[root@ansible work]# vim playbook.yaml 
- hosts: server
  remote_user: root
  vars:
    - pkname1: httpd
    - pkname2: vim
  tasks:
    - name: Install httpd
      yum: name={{ pkname1 }}
    - name: start service
      service: name={{ pkname1 }} state=started enabled=yes
    - name: Install vim
      yum: name={{ pkname2 }}

[root@ansible work]# ansible-playbook playbook.yaml  

PLAY [server] *****************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************
ok: [192.168.1.113]
ok: [192.168.1.112]
ok: [192.168.1.110]

TASK [Install httpd] **********************************************************************************************************
changed: [192.168.1.110]
changed: [192.168.1.112]
changed: [192.168.1.113]

TASK [start service] **********************************************************************************************************
changed: [192.168.1.112]
changed: [192.168.1.113]
changed: [192.168.1.110]

TASK [Install vim] ************************************************************************************************************
ok: [192.168.1.110]
ok: [192.168.1.112]
ok: [192.168.1.113]

PLAY RECAP ********************************************************************************************************************
192.168.1.110              : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.113              : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
  • 通过修改主机清单的变量
[root@ansible work]# vim /etc/ansible/hosts 
[server]

192.168.1.110 http_port=81
192.168.1.112 http_port=82

[root@ansible work]# vim hostname.yaml 
---
- hosts: server
  remote_user: root
  tasks:
    - name: hostname
      hostname: name=www{{http_port}}.magedu.com

[root@ansible work]# ansible-playbook  hostname.yaml 
PLAY [server] *****************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************
ok: [192.168.1.110]
ok: [192.168.1.112]

TASK [hostname] ***************************************************************************************************************
ok: [192.168.1.112]
ok: [192.168.1.110]

PLAY RECAP ********************************************************************************************************************
192.168.1.110              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[root@ansible work]# ansible all -m shell -a "hostname"
192.168.1.110 | CHANGED | rc=0 >>
www81.magedu.com
192.168.1.112 | CHANGED | rc=0 >>
www82.magedu.com
[root@ansible work]# vim /etc/ansible/hosts 
[server]

192.168.1.110 http_port=81
192.168.1.112 http_port=82

[server:vars]
nodename=www
domainname=magedu.com

[root@ansible work]# vim hostname.yaml  
---
- hosts: server
  remote_user: root

  tasks:
    - name: hostname
      hostname: name={{nodename}}{{http_port}}.{{domainname}}

[root@ansible work]# ansible
ansible             ansible-connection  ansible-doc         ansible-inventory   ansible-pull        
ansible-config      ansible-console     ansible-galaxy      ansible-playbook    ansible-vault       

[root@ansible work]# ansible-playbook hostname.yaml  
PLAY [server] *************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************
ok: [192.168.1.110]
ok: [192.168.1.112]

TASK [hostname] ***********************************************************************************************************************************
changed: [192.168.1.112]
ok: [192.168.1.110]

PLAY RECAP ****************************************************************************************************************************************
192.168.1.110              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

===================================

invertory参数

===================================

invertory参数:用于定义ansible远程连接目标主机时使用的参数,而非传递给playbook的变量

    ansible_ssh_host
    ansible_ssh_port
    ansible_ssh_user
    ansible_ssh_pass
    ansbile_sudo_pass

示例:
    cat /etc/ansible/hosts
    [websrvs]
    192.168.0.1 ansible_ssh_user=root ansible_ssh_pass=magedu
    192.168.0.2 ansible_ssh_user=root ansible_ssh_pass=magedu

invertory参数

inventory参数
ansible基于ssh连接inventory中指定的远程主机时,还可以通过参数指定其交互方式;
这些参数如下所示:
ansible_ssh_host
The name of the host to connect to, if different from the alias you wishto give to it.

ansible_ssh_port
The ssh port number, if not 22

ansible_ssh_user
The default ssh user name to use.

ansible_ssh_pass
The ssh password to use (this is insecure, we strongly recommendusing --ask-pass or SSH keys)

ansible_sudo_pass
The sudo password to use (this is insecure, we strongly recommendusing --ask-sudo-pass)

ansible_connection
Connection type of the host. Candidates are local, ssh or paramiko.
The default is paramiko before Ansible 1.2, and 'smart' afterwards which
detects whether usage of 'ssh' would be feasible based on whether
ControlPersist is supported.

ansible_ssh_private_key_file
Private key file used by ssh. Useful if using multiple keys and you don't want to use SSH agent.

ansible_shell_type
The shell type of the target system. By default commands are formatted
using 'sh'-style syntax by default. Setting this to 'csh' or 'fish' will cause
commands executed on target systems to follow those shell's syntax instead.

ansible_python_interpreter
The target host python path. This is useful for systems with more
than one Python or not located at "/usr/bin/python" such as \*BSD, or where /usr/bin/python

is not a 2.X series Python. We do not use the "/usr/bin/env" mechanism as that requires the remote user's

path to be set right and also assumes the "python" executable is named python,where the executable might

be named something like "python26".
ansible\_\*\_interpreter

Works for anything such as ruby or perl and works just like ansible_python_interpreter.

This replaces shebang of modules which will run on that host.

===================================

模板templates

===================================

jinja2的语法

文本文件,嵌套有脚本(使用模板编程语言编写) 借助模板生成真正的文件
Jinja2语言,使用字面量,有下面形式
    字符串:使用单引号或双引号
    数字:整数,浮点数
    列表:[item1, item2, ...]
    元组:(item1, item2, ...)
    字典:{key1:value1, key2:value2, ...}
    布尔型:true/false
算术运算:+, -, *, /, //, %, **
比较操作:==, !=, >, >=, <, <=
逻辑运算:and,or,not
流表达式:For,If,When

Jinja2相关

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

Jinja2:算术运算

算术运算
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

Jinja2的基本用法

比较操作符
== 比较两个对象是否相等
!= 比较两个对象是否不等
> 如果左边大于右边,返回 true
>= 如果左边大于等于右边,返回 true
< 如果左边小于右边,返回 true
<= 如果左边小于等于右边,返回 true

逻辑运算符
对于 if 语句,在 for 过滤或 if 表达式中,它可以用于联合多个表达式
and
    如果左操作数和右操作数同为真,返回 true
or
    如果左操作数和右操作数有一个为真,返回 true
not
    对一个表达式取反(见下)
(expr)
    表达式组

['list', 'of', 'objects']:
一对中括号括起来的东西是一个列表。列表用于存储和迭代序列化的数据。
例如 你可以容易地在 for循环中用列表和元组创建一个链接的列表
    <ul>
    {% for href, caption in [('index.html', 'Index'), ('about.html', 'About'), ('downloads.html',
'Downloads')] %}
        <li><a href="{{ href }}">{{ caption }}</a></li>
    {% endfor %}
    </ul>
    ('tuple', 'of', 'values'):

元组与列表类似,只是你不能修改元组。
如果元组中只有一个项,你需要以逗号结尾它。
元组通常用于表示两个或更多元素的项。更多细节见上面的例子
    {'dict': 'of', 'key': 'and', 'value': 'pairs'}:

Python 中的字典是一种关联键和值的结构。
键必须是唯一的,并且键必须只有一个 值。
字典在模板中很少使用,罕用于诸如 xmlattr() 过滤器之类
    true / false:
    true 永远是 true ,而 false 始终是 false

===================================

template 的使用

===================================

template功能:根据模块文件动态生成对应的配置文件
   > template文件必须存放于templates目录下,且命名为 .j2 结尾
   > yaml/yml 文件需和templates目录平级,目录结构如下:
    ./
     ├── temnginx.yml
     └── templates
        └── nginx.conf.j2

template示例

示例:利用template 同步nginx配置文件
准备templates/nginx.conf.j2文件
[root@ansible work]# vim playbook.yaml  
- hosts: server
  remote_user: root
  vars_files:
    - vars.yaml
  tasks:
    - name: Install httpd
      yum: name={{ pkname1 }}
    - name: copy templatest
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf 
    - name: start service
      service: name={{ pkname1 }} state=started enabled=yes
      
[root@ansible work]# ansible-playbook  playbook.yaml  
  • template的j2文件的变量替换
[root@ansible work]# vim 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**2 }} ;    #设置auto改成ansible_processor_vcpus**2 16线程 Playbook中template算术运算
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

  • playbook的j2替换和handlers使用
[root@ansible work]#  vim playbook.yaml  
- hosts: server
  remote_user: root
  vars_files:
    - vars.yaml
  tasks:
    - name: Install httpd
      yum: name={{ pkname1 }}
    - name: copy templatest
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
      notify: restart nginx
    - name: start service
      service: name={{ pkname1 }} state=started enabled=yes

  handlers:
    - name: restart nginx
      service: name=nginx state=restarted
  • 执行playbook的结果
[root@ansible work]# ansible-playbook playbook.yaml  

PLAY [server] *****************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************
ok: [192.168.1.110]
ok: [192.168.1.112]

TASK [Install httpd] *****************************************************************************************************************************************
ok: [192.168.1.112]
ok: [192.168.1.110]

TASK [copy templatest] *****************************************************************************************************************************************
changed: [192.168.1.110]
changed: [192.168.1.112]

TASK [start service] *****************************************************************************************************************************************
ok: [192.168.1.110]
ok: [192.168.1.112]

RUNNING HANDLER [restart nginx] *****************************************************************************************************************************************
changed: [192.168.1.110]
changed: [192.168.1.112]

PLAY RECAP *****************************************************************************************************************************************
192.168.1.110              : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

===================================

Playbook中使用when

===================================

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

when语句
    在task后添加when子句即可使用条件测试;when语句支持Jinja2表达式语法
示例:
tasks:
  - name: "shutdown RedHat flavored systems"
    command: /sbin/shutdown -h now
    when: ansible_os_family == "RedHat"  当系统属于红帽系列,执行command模块 
 
when语句中还可以使用Jinja2的大多"filter",
例如要忽略此前某语句的错误并基于其结果(failed或者success)运行后面指定的语句,
可使用类似如下形式:
tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

此外,when语句中还可以使用facts或playbook中定义的变量

示例:when条件判断

  • 利用when的条件判断,判断变量为主机名的分别安装httpd和安装nginx
[root@ansible work]# vim playbook.yaml  
---
- hosts: server
  remote_user: root

  tasks:
    - name: install httpd
      yum: name=httpd state=present
      when: ansible_hostname == "www81"
    - name: install nginx
      yum: name=nginx state=present
      when: ansible_hostname == "www82"
    - name: restart httpd
      service: name=httpd state=started enabled=yes
      when: ansible_hostname == "www81"
    - name: restart nginx
      service: name=nginx state=started enabled=yes
      when: ansible_hostname == "www82"
  • 利用when判断变量主机版本为7或者6的进行更改配置文件
示例:
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"

===================================

迭代:with_items、loop

===================================

迭代:当有需要重复性执行的任务时,可以使用迭代机制
    > 对迭代项的引用,固定变量名为"item"
    > 要在task中使用with_items给定要迭代的元素列表
    > 列表格式:
         字符串
         字典

示例

示例: 创建用户
- name: add several users
  user: name={{ item }} state=present groups=wheel   #{{ item }} 系统自定义变量
  with_items:       # 定义{{ item }} 的值和个数
    - testuser1
    - testuser2

上面语句的功能等同于下面的语句:
- name: add user testuser1
  user: name=testuser1 state=present groups=wheel
- name: add user testuser2
  user: name=testuser2 state=present groups=wheel
  
with_items中可以使用元素还可为hashes
示例:
- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

ansible的循环机制还有更多的高级功能,具体请参见官方文档
http://docs.ansible.com/playbooks_loops.html

旧循环语句

在Ansible 2.5以前,playbook通过不同的循环语句以实现不同的循环,这些语句使用with_作为前缀。这些语法目前仍然兼容,但在未来的某个时间点,会逐步废弃。

Playbook使用循环迭代示例

#ansible2.9.5 避免循环重复使用建议使用loop
[root@ansible work]# ansible --version 
ansible 2.9.25
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.6/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.6.8 (default, Dec  5 2019, 15:45:45) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]

#with_items循环
[root@ansible work]# vim playbook.yaml  
---
- hosts: server
  remote_user: root

  tasks:
    - name: add serverl users
      user: name={{ item }} state=present  # 定义{{ item }} 的变量
      with_items:
        - testuser1111
        - testuser2222
        
        
#loop循环
[root@ansible work]# vim playbook.yaml  
---
- hosts: server
  remote_user: root

  tasks:
    - name: add serverl users
      user: name={{ item }} state=present   # 定义{{ item }} 的变量
      loop:
        - testuser1111
        - testuser2222
        
 [root@ansible work]# ansible-playbook  playbook.yaml  

PLAY [server] *****************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************
ok: [192.168.1.112]
ok: [192.168.1.110]

TASK [add serverl users] *****************************************************************************************************************************************
changed: [192.168.1.110] => (item=testuser1111)
changed: [192.168.1.112] => (item=testuser1111)
changed: [192.168.1.110] => (item=testuser2222)
changed: [192.168.1.112] => (item=testuser2222)

PLAY RECAP *****************************************************************************************************************************************
192.168.1.110              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.112              : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

items迭代示例

将多个文件进行copy到被控端

---
- hosts: testsrv
  remote_user: root
  tasks
  - name: Create rsyncd config
    copy: src={{ item }} dest=/etc/{{ item }}
    with_items:
  - rsyncd.secrets
  - rsyncd.conf

items迭代示例

安装多个软件使用item迭代

- hosts: websrvs
  remote_user: root
  tasks:
   - name: yum install httpd
     yum: name={{ item }} state=present
     with_items:
       - apr
       - apr-util
       - httpd

迭代嵌套子变量

使用item循环迭代创建组,再使用子变量将创建的用户加入到组里面。

[root@ansible work]# vim playbook.yaml 
---
- hosts: server
  remote_user: root
  
  tasks:
    - name: add some groups
      group: name={{item}} state=present
      with_items:
        - group1
        - group2
        - group3
    - name: add some users
      user: name={{item.name}} group{{item.group}} state=present
      with_items:
        - {name: 'user1',group: 'group1'}
        - {name: 'user2',group: 'group2'}
        - {name: 'user3',group: 'group3'}

===================================

template for if when循环

===================================

for循环简单应用

[root@ansible work]# vim testfor.yaml 
---
- hosts: server
  remote_user: root
  vars:
    ports:
      - 81
      - 83
      - 82
  tasks:
    - name: mkdir file
      file: path=/data1 state=directory
    - name: copy conf
      template: src=for1.conf.j2 dest=/data1/for1.conf
      
# 自定义templates
[root@linux-node1 ansible]# cat templates/for1.conf.j2 
{% for port in ports %}
server{
        listen {{ port }}
}
{% endfor %}

#和shellfor循环类似

[root@ansible work]# ansible server -m shell -a "cat /data1/*"
10.100.35.13 | CHANGED | rc=0 >>
server{
	listen 81
}
server{
	listen 83
}
server{
	listen 82
}

剧本中是单个键值对

[root@ansible work]# vim testfor.yaml 
---
- hosts: server
  remote_user: root
  vars:
    ports:
      - listen_port: 81
      - listen_port: 83
      - listen_port: 82
  tasks:
    - name: mkdir file
      file: path=/data1 state=directory
    - name: copy conf
      template: src=for1.conf.j2 dest=/data1/for1.conf

[root@ansible work]# vim templates/for1.conf.j2 
{% for port in ports %}
server{
        listen {{port.listen_port}}
}
{% endfor  %}

剧本中是多个键值对

[root@ansible work]# vim testfor.yaml 
---
- hosts: server
  remote_user: root
  vars:
    ports:
      - web1:
        port: 81
        name: www.zjy1.com
        rootdir: /data1/zjy1
      - web2:
        port: 82
        name: www.zjy2.com
        rootdir: /data1/zjy2
      - web3:
        port: 83
        name: www.zjy3.com
        rootdir: /data1/zjy3
  tasks:
    - name: mkdir file
      file: path=/data1 state=directory
    - name: copy conf
      template: src=for1.conf.j2 dest=/data1/for1.conf

# template自定义多个值
[root@ansible work]# vim templates/for1.conf.j2 
{% for p in ports %}
server{
        listen {{p.port}}
        servername {{p.name}}
        documentroot{{p.rootdir}}
}
{% endfor  %}

剧本中是多个键值对。加if判断(比如把name注释掉)

[root@ansible work]# vim testfor.yaml 
---
- hosts: server
  remote_user: root
  vars:
    ports:
      - web1:
        port: 81
        # name: www.zjy1.com
        rootdir: /data1/zjy1
      - web2:
        port: 82
        #  name: www.zjy2.com
        rootdir: /data1/zjy2
      - web3:
        port: 83
        # name: www.zjy3.com
        rootdir: /data1/zjy3
  tasks:
    - name: mkdir file
      file: path=/data1 state=directory
    - name: copy conf
      template: src=for1.conf.j2 dest=/data1/for1.conf

判断自定义templates,加上if判断语句

[root@linux-node1 ansible]# cat templates/for4.conf.j2 
{% for p in ports %}
server{
        listen {{ p.port }}
{% if  p.name is defined %}
        servername {{ p.name }}
{% endif %}
        documentroot {{ p.rootdir }}
}
{% endfor %}

[root@ansible work]# ansible server -m shell -a "cat /data1/*"
10.100.35.12 | CHANGED | rc=0 >>
server{
	listen 81
	documentroot/data1/zjy1   #if的判断语句生效了
}
server{
	listen 82
	documentroot/data1/zjy2
}
server{
	listen 83
	documentroot/data1/zjy3
}
posted @ 2021-09-19 23:47  isicman  阅读(92)  评论(0编辑  收藏  举报