Playbook

playbook介绍

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

YAML 语言

YAML是一个可读性高的用来表达资料序列的格式 。

YAMLYAML Ain't Markup Language,即YAML不是XML。不过,在开发的这种语言时,YAML的意思

其实是:"Yet Another Markup Language"(仍是一种标记语言)

YAML语言特性

  • YAML的可读性好
  • YAML和脚本语言的交互性好
  • YAML使用实现语言的数据类型
  • YAML有一个一致的信息模型
  • YAML易于实现
  • YAML可以基于流来处理
  • YAML表达能力强,扩展性好

YAML语法介绍

  • 在单一文件第一行,用连续三个连字号"-" 开始,还有选择性的连续三个点号( ... )用来表示文件的
  • 结尾
  • 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
  • 使用#号注释代码
  • 缩进必须是统一的,不能空格和tab混用
  • 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换
  • 行来实现的
  • YAML文件内容是区别大小写的,key/value的值均需大小写敏感
  • 多个key/value可同行写也可换行写,同行使用,分隔
  • v可是个字符串,也可是另一个列表
  • YAML文件扩展名通常为yml或yaml

YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构
Structure)通过空格来展示,序列(Sequence)里的项用"-"来代表,Map里的键值对用":"分隔,下
面介绍常见的数据结构。

List列表:

列表由多个元素组成,每个元素放在不同行,且元素前均使用"-"打头,并且 - 后有一个空格, 或者将所
有元素用 [ ] 括起来放在同一行
范例

#不同行,行以-开头,后面有一个空格
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango
#同一行
[Apple,Orange,Strawberry,Mango]

Dictionary字典 :
字典由多个keyvalue构成,keyvalue之间用 :分隔, 并且 : 后面有一个空格,所有k/v可以放在一
行,或者每个 k/v 分别放在不同行
范例

#不同行
# An employee record
name: Example Developer
job: Developer
skill: Elite
#同一行,也可以将key:value放置于{}中进行表示,用,分隔多个key:value
# An employee record
{name: "Example Developer", job: "Developer", skill: "Elite"}

Playbook 核心组件

一个playbook 中由列表组成,其中所用到的常见组件类型如下:

  • Hosts 执行的远程主机列表
  • Tasks 任务集,由多个task的元素组成的列表实现,每个task是一个字典
  • Variables 内置变量或自定义变量在playbook中调用
  • Templates 模板,可替换模板文件中的变量并实现一些简单逻辑的文件
  • Handlers 和 notify 结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
  • tags 标签 指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此
  • 会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地
  • 长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
  • 一个完整的代码块功能需最少元素需包括 name 和 task,一个name只能包括一个task

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

one.example.com
one.example.com:two.example.com
192.168.10.203
192.168.10.*
webservers:dbsrvs #或者,两个组的并集
webservers:&dbsrvs #与,两个组的交集
webservers:!phoenix #在webservers组,但不在phoenix组

案例:

- hosts: webservers:appsrvs

remote_user 组件
remote_user: 可用于Hosttask中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可
用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户

- hosts: websrvs
  remote_user: root
  tasks:
    - name: test connection
      ping:
      remote_user: magedu
      sudo: yes #默认sudo为root
      sudo_user:meng #sudo为meng

task列表和action组件
play的主体部分是task listtask list中有一个或多个task,各个task 按次序逐个在hosts中指定的所有主
机上执行,即在所有主机上完成第一个task后,再开始第二个task
task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着
多次执行是安全的,因为其结果均一致
每个task都应该有其name,用于playbook的执行结果输出,建议其内容能清晰地描述任务执行步骤。
如果未提供name,则action的结果将用于输出
注意:shellcommand模块后面跟命令,而非key=value
范例

---
- hosts: webservers
  remote_user: root
  tasks:
    - name: install httpd
      yum: name=httpd
    - name: start httpd
      service: name=httpd state=started enabled=yes

其它组件
某任务的状态在运行后为changed时,可通过"notify"通知给相应的handlers
任务可以通过"tags"打标签,可在ansible-playbook命令上使用-t指定进行调用
ShellScripts VS Playbook 案例

#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,并设置开机启动
systemctl enable --now httpd

#Playbook实现
---
- hosts: webservers
  remote_user: root
  tasks:
    - name: install httpd
      yum: name=httpd
    - name: "复制配置文件"
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
- name: "复制配置文件"
copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d
- name: "启动apache,设置开机启动" service: name=httpd state=started enabled=yes

playbook 命令

格式

ansible-playbook <filename.yml> ... [options]

常见选项

-C --check #只检测可能会发生的改变,但不真正执行操作
--list-hosts #列出运行任务的主机
--list-tags #列出tag
--list-tasks #列出task
--limit 主机列表 #只针对主机列表中的特定主机执行
-v -vv -vvv #显示过程

范例1

[root@ansible ansible]#cat hello.yml
---
- hosts: websrvs
  tasks:
    - name: hello
      command: echo "hello ansible"
[root@ansible ansible]#ansible-playbook hello.yml
[root@ansible ansible]#ansible-playbook -v hello.yml

范例2

[root@ansible-1 ansible]# ansible-playbook file.yml --check #只检测
[root@ansible-1 ansible]# ansible-playbook file.yml
[root@ansible-1 ansible]# ansible-playbook file.yml --limit websrvs

Playbook 初步使用

利用 playbook 创建 mysql 用户
范例:


[root@ansible-1 playbook]# cat mysql_user.yml --- - hosts: webservers remote_user: root tasks: - name: create user user: name=mysql shell=/sbin/nologin system=yes group=mysql uid=305 home=/data/mysql create_home=no
[root@ansible-1 playbook]# ansible-playbook mysql_user.yml 

PLAY [webservers] **************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************
ok: [192.168.10.203]
ok: [192.168.10.202]

TASK [create user] *************************************************************************************************************
changed: [192.168.10.202]
changed: [192.168.10.203]

PLAY RECAP *********************************************************************************************************************
192.168.10.202             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.10.203             : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

利用 playbook 安装 nginx
范例:

[root@ansible-1 playbook]# cat install_nginx.yml 
---
#install nginx
- hosts: webservers
  remote_user: root
  tasks:
    - name: add group nginx
      group: name=nginx state=present 
    - name: add user nginx
      user: name=nginx1 state=present group=nginx
    - name: install nginx
      yum: name=nginx state=present
    - name: web page
      copy: src=/root//index.html dest=/usr/share/nginx/html/index.html
    - name: start nginx
      service: name=nginx state=started enabled=yes
[root@ansible-1 playbook]# 

Playbook中使用handlersnotify

Handlers本质是task list ,类似于MySQL中的触发器触发的行为,其中的task与前述的task并没有本质
上的不同,主要用于当关注的资源发生变化时,才会采取一定的操作。而Notify对应的action可用于在
每个play的最后被触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完
成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的
操作
范例:

---
- hosts: webservers
  remote_user: root
  gather_facts: no
  tasks:
    - name: install httpd
      yum: name=httpd state=present
    - name: install configure file
      copy: src=/root/httpd.conf dest=/etc/httpd/conf/
      notify: restart httpd
    - name: ensure apache is running
      service: name=httpd state=started enabled=yes
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

 

Playbook中使用tags组件

playbook文件中,可以利用tags组件,为特定 task 指定标签,当在执行playbook时,可以只执行特
tagstask,而非整个playbook文件
案例

[root@ansible-1 playbook]# cat 2.yml 
---
- hosts: webservers
  remote_user: root
  gather_facts: no
  tasks:
    - name: install httpd
      yum: name=httpd state=present
    - name: install configure file
      copy: src=/root/httpd.conf dest=/etc/httpd/conf/
      tags: conf
    - name: ensure apache is running
      service: name=httpd state=started enabled=yes
      tags: service
    - name: restart httpd
      service: name=httpd state=restarted
[root@ansible-1 playbook]# ansible-playbook 2.yml -t conf,service

Playbook中使用变量

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

变量定义:

variable=value

范例:

http_port=80

变量调用方式:
通过{{ variable_name }} 调用变量,且变量名前后建议加空格,有时用"{{ variable_name }}"才生效
变量来源:
1. ansible setup facts 远程主机的所有变量都可直接调用
2. 通过命令行指定变量,优先级最高

ansible-playbook -e varname=value test.yml

3. playbook文件中定义

vars:
  - var1: value1
  - var2: value2

4. 在独立的变量YAML文件中定义

- hosts: all
  vars_files:
    - vars.yml

5. /etc/ansible/hosts 中定义
主机(普通)变量:主机组中主机单独定义,优先级高于公共变量
组(公共)变量:针对主机组中所有主机定义统一变量
6. role中定义 

使用 setup 模块中变量

本模块自动在playbook调用,不要用ansible命令调用

案例:使用setup变量

[root@ansible-1 playbook]# ansible 192.168.10.202 -m setup -a 'filter="ansible_default_ipv4"'
192.168.10.202 | SUCCESS => {
    "ansible_facts": {
        "ansible_default_ipv4": {
            "address": "192.168.10.202", 
            "alias": "bond0", 
            "broadcast": "192.168.10.255", 
            "gateway": "192.168.10.2", 
            "interface": "bond0", 
            "macaddress": "00:0c:29:1b:38:1c", 
            "mtu": 1500, 
            "netmask": "255.255.255.0", 
            "network": "192.168.10.0", 
            "type": "bonding"
        }, 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

playbook 命令行中定义变量

[root@ansible-1 playbook]# cat 4.yml 
---
- hosts: webservers
  remote_user: root
  gather_facts: no
  tasks:
    - name: install httpd
      yum: name={{ pkname }} state=present
[root@ansible-1 playbook]# ansible-playbook -e pkname=httpd 4.yml 

playbook文件中定义变量

[root@ansible-1 playbook]# cat 5.yml 
---
- hosts: webservers
  remote_user: root
  gather_facts: no
  vars: 
    - username: user1
    - groupname: group1
  tasks:
    - name: create group
      group: name={{ groupname }} state=present
    - name: create user
      user: name={{ username }} group={{ groupname }} state=present

[root@ansible-1 playbook]# ansible-playbook 5.yml
[root@ansible-1 playbook]# ansible-playbook -e "username=user2 groupname=group2" 5.yml 
#通过命令行指定变量,优先级最高,以下是执行结果
[root@ansible-3 conf]# more /etc/passwd |grep user1
user1:x:2048:1004:test user:/app/user1:/bin/bash
[root@ansible-3 conf]# more /etc/group |grep group1
group1:x:1004:
[root@ansible-3 conf]# 
[root@ansible-3 conf]# more /etc/passwd |grep user2
user2:x:2050:1005::/home/user2:/bin/bash
[root@ansible-3 conf]# more /etc/group |grep group2
group2:x:1005:
[root@ansible-3 conf]# 

使用变量文件
可以在一个独立的playbook文件中定义变量,在另一个playbook文件中引用变量文件中的变量,比
playbook中定义的变量优先级高

[root@ansible-1 playbook]# cat vars.yml 
---
# variables file
package_name: mariadb-server
service_name: mariadb
[root@ansible-1 playbook]# cat 6.yml 
---
#install package and start service
- hosts: webservers
  remote_user: root
  vars_files: 
    - vars.yml
  
  tasks:
    - name: install package
      yum: name={{ package_name }}
      tags: install
    - name: start service
      service: name={{ service_name }} state=started enabled=yes
[root@ansible-1 playbook]# 

主机清单文件中定义变量
主机变量
inventory 主机清单文件中为指定的主机定义变量以便于在playbook中使用
范例:

[webservers]
192.168.10.202 http_port=80 maxRequestsPerChild=808
192.168.10.203 http_port=8080 maxRequestsPerChild=909

组(公共)变量

inventory 主机清单文件中赋予给指定组内所有主机上的在playbook中可用的变量,如果和主机变是
同名,优先级低于主机变量
范例:

[webservers]
192.168.10.202 http_port=8080
192.168.10.203
[webservers:vars]
http_port=80
ntp_server=ntp.xx.com
nfs_server=nfs.xxx.com

template 模板

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

jinja2语言

http://jinja.pocoo.org/
jinja2 语言使用字面量,有下面形式:
字符串:使用单引号或双引号
数字:整数,浮点数
列表:[item1, item2, ...]
元组:(item1, item2, ...)
字典:{key1:value1, key2:value2, ...}
布尔型:true/false
算术运算:+, -, *, /, //, %, **
比较操作:==, !=, >, >=, <, <=
逻辑运算:andornot
流表达式:ForIfWhen
字面量:
表达式最简单的形式就是字面量。字面量表示诸如字符串和数值的 Python 对象。如"Hello World"
双引号或单引号中间的一切都是字符串。无论何时你需要在模板中使用一个字符串(比如函数调用、过
滤器或只是包含或继承一个模板的参数),如4242.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文件
vim temnginx.yml
---
- hosts: websrvs
  remote_user: root
  tasks:
  - name: template config to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
[root@ansible-1 templates]# ansible-playbook temnginx.yml

template变更替换
范例:

#修改文件nginx.conf.j2
mkdir templates
vim templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }};
vim temnginx2.yml
---
- 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

template算术运算
范例:

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

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

[root@ansible-1 playbook]# cat temlnginx2.yml 
---
- hosts: webservers
  remote_user: root
  vars:
    nginx_vhosts:
    - 81
    - 82
    - 83
  tasks:
    - name: template config
      template: src=nginx.conf.j2 dest=/data/nginx.conf

[root@ansible-1 templates]# cat nginx.conf2.j2 
{% for vhost in nginx_vhosts %}
server {
  listen {{ vhost }}
} 
{% endfor %}
[root@ansible-1 templates]# 
#生成的结果:
server {
  listen 81
}
server {
  listen 82
}
server {
  listen 83
}

 

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"

 

playbook 使用迭代 with_items

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

--
#remove mariadb server
- hosts: webservices
  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

 

管理节点过多导致的超时问题解决方法

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

#vim test_serial.yml
---
- hosts: all
  serial: 2 #每次只同时处理2个主机
  gather_facts: False
  tasks:
    - name: task one
      comand: hostname
    - name: task two
      command: hostname

范例:

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

 

posted @ 2021-02-08 14:01  menglingqian  阅读(353)  评论(0编辑  收藏  举报