Ansible的Playbook
Playbook简介
Playbook是一种简单的配置管理系统与多机器部署系统的基础.与现有的其他系统有不同之处,且非常适合于复杂应用的部署. 不同于我们前面使用的命令行方式, Playbook是可以做到一次编写,持久使用.
Playbook可以看做是我们用命令执行的条条命令的组合, 只不过这些组合在Playbook更加灵活罢了, 我们把这一条条执行命令称之为task.
Playbook特点:
1. 一次编写,持久使用
2. 结构清晰,方便维护
3. 可以处理一些复杂的逻辑
4. 编写简单,还可以配合角色使用
Playbook的核心元素
hosts: 执行剧本的远程主机或主机组列表
tasks: 任务集, 也就是需要执行的任务. 每个Playbook有一个或多个tasks
variables: 内置变量或自定义变量用于在Playbook使用,方便我们灵活的编写剧本
templates: 模板, 即使用模板语法的文件, 如配置文件.
handlers和notify: 一般两者都是结合使用, 前置是触发器,后者是触发器触发后需要完成的操作.
tags: 标签,可以指定剧本的单个task执行,也可以指定跳过那个task执行.
Playbook的语法格式
playbook的语法格式是yaml的格式, 后缀名一般是 yaml或 yml.
playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’ 为元素的列表.
- 开始行都应该是 ---
- 次行开始正常写
playbook
的内容,一般都会写上描述该playbook
的功能。 - 使用#号注释代码。
- 缩进必须统一,不能空格和
tab
混用。 - 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行实现的。
注意区分大小写.
k/v
的值可同行写也可以换行写。同行使用冒号分隔。v
可以是个字符串,也可以是一个列表- 一个完整的代码块功能需要最少元素包括
name: task
Playbook中的元素属性
主机与用户
你可以为 playbook 中的每一个 play,个别地选择操作的目标机器是哪些,以哪个用户身份去完成要执行的步骤(called tasks).
如下: hosts 行的内容是一个或多个组或主机的 patterns,以逗号为分隔符.
---
- hosts: 192.168.100.11,192.168.100.12
remote_user: root
在每个tasks中可以定义自己的用户,如下:
---
- hosts: 192.168.100.12
remote_user: root
tasks:
- name: test connection
ping:
remote_user: wallace
同时,支持sudo方式执行命令:
---
- hosts: 192.168.100.12
remote_user: wallace
sudo: yes
你也可以登陆后,sudo 到不同的用户身份,而不是使用 root:
---
- hosts: 192.168.100.12
remote_user: wallace
sudo: yes
sudo_user: nginx
tasks任务列表
每一个task
必须有一个名称name
,这样在运行playbook
时,从其输出的任务执行信息中可以很清楚的辨别是属于哪一个task
的,如果没有定义 name
,action
的值将会用作输出信息中标记特定的task
。
每一个playbook
中可以包含一个或者多个tasks
任务列表,每一个tasks
完成具体的一件事,(任务模块)比如创建一个用户或者安装一个软件等,在hosts
中定义的主机或者主机组都将会执行这个被定义的tasks
。
每个 task 的目标在于执行一个 moudle, 通常是带有特定的参数来执行.在参数中可以使用变量(variables).
tasks:
- name: create new file
file: path=/tmp/test.log state=touch
- name: create new user
user: name=wallace state=present
Handlers和Notify
很多时候当我们某一个配置发生改变,我们需要重启服务,(比如httpd配置文件文件发生改变了)这时候就可以用到handlers
和notify
了;
(当发生改动时)notify actions
会在playbook
的每一个task结束时被触发,而且即使有多个不同task通知改动的发生,notify actions
知会被触发一次;比如多个resources
指出因为一个配置文件被改动,所以apache
需要重启,但是重新启动的操作知会被执行一次。
[root@ansible ~]# cat anisble_httpd.yml
#用于安装httpd并配置启动
---
- hosts: 192.168.100.12
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=installed
- name: config httpd
template: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: start httpd
service: name=httpd state=started
handlers:
- name: restart httpd # 这个名字要和notify定义的一致才能生效
service: name=httpd state=restarted
这里我们修改了httpd配置文件,所以就需要从启服务. 而Handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作.除此以外很少用到了. notify 标识触发, handles是出发后
需要执行的操作.
Playbook的运行方式
通过ansible-playbook命令运行 格式:ansible-playbook <filename.yml> ... [options]
如下:
[root@ansible PlayBook]# ansible-playbook -h
#ansible-playbook常用选项:
--check or -C #只检测可能会发生的改变,但不真正执行操作
--list-hosts #列出运行任务的主机
--list-tags #列出playbook文件中定义所有的tags
--list-tasks #列出playbook文件中定义的所以任务集
--limit #主机列表 只针对主机列表中的某个主机或者某个组执行
-f #指定并发数,默认为5个
-t #指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags)
-v #显示过程 -vv -vvv更详细
Playbook中变量的使用
命令行指定变量
[root@ wallace PlayBook]# cat variables_test.yml
---
- hosts: 192.168.100.12
remote_user: root
tasks:
- name: create new file
file: path={{ dir }} state=touch
#执行playbook 指定pkg
[root@wallace PlayBook]# ansible-playbook -e "dir=/tmp/test.log" variables_test.yml
hosts文件中指定变量
在/etc/ansible/hosts
文件中定义变量,可以针对每个主机定义不同的变量,也可以定义一个组的变量,然后直接在playbook
中直接调用。注意,组中定义的变量没有单个主机中的优先级高。
# 编辑hosts文件定义变量
[root@ wallace playbook]# vim /etc/ansible/hosts
[httpd]
192.168.100.12 webdir=/opt/test01 #定义单个主机的变量
192.168.100.13
[httpd:vars] #定义整个组的统一变量
webdir=/tmp/www
[nginx]
192.168.100.1[4:5]
[nginx:vars]
webdir=/opt/test02
# 编辑playbook文件
[root@wallaceplaybook]# cat variables_test.yml
---
- hosts: all
remote_user: root
tasks:
- name: create webdir
file: name={{ webdir }} state=directory #引用变量
# 执行剧本
[root@wallace playbook]# ansible-playbook virables_test.yaml
playbook文件中指定变量
编写playbook
时,直接在里面定义变量,然后直接引用,可以定义多个变量;注意:如果在执行playbook
时,又通过-e
参数指定变量的值,那么会以-e
参数指定的为准。
# 编辑playbook
[root@wallace playw]# cat variables_test.yml
---
- hosts: all
remote_user: root
vars: #定义变量
pkg: httpd #变量1
dir: /tmp/httpd #变量2
tasks:
- name: install pkg
yum: name={{ pkg }} state=installed #引用变量
- name: create new dir
file: name={{ dir }} state=directory #引用变量
# 执行playbook
[root@wallace playbook]# ansible-playbook variables_test.yml
# -e的权限更高,以命令行的参数为准
[root@wallace playbook]# ansible-playbook -e "dir=/tmp/test2" variables_test.yml
调用setup模块方式获取变量
setup
模块默认是获取主机信息的,有时候在playbook
中需要用到,所以可以直接调用。常用的参数: 链接
# 编辑playbook文件
[root@wallace playbook]# cat variables_test.yml
---
- hosts: all
remote_user: root
tasks:
- name: create file
file: name={{ ansible_hostname}}.log state=touch #引用setup中的ansible_fqdn
# 执行playbook
[root@ansible PlayBook]# ansible-playbook variables.yml
常用setup模块如下:
过滤出指定的信息:例->ansible all -m setup -a "filter=ansible_os_family"
ansible_all_ipv4_addresses:仅显示ipv4的信息
ansible_devices:仅显示磁盘设备信息
ansible_distribution:显示是什么系统,例:centos,suse等
ansible_distribution_major_version:显示是系统主版本
ansible_distribution_version:仅显示系统版本
ansible_machine:显示系统类型,例:32位,还是64位
ansible_eth0:仅显示eth0的信息
ansible_hostname:仅显示主机名
ansible_kernel:仅显示内核版本
ansible_lvm:显示lvm相关信息
ansible_memtotal_mb:显示系统总内存
ansible_memfree_mb:显示可用系统内存
ansible_memory_mb:详细显示内存情况
ansible_swaptotal_mb:显示总的swap内存
ansible_swapfree_mb:显示swap内存的可用内存
ansible_mounts:显示系统磁盘挂载情况
ansible_processor:显示cpu个数(具体显示每个cpu的型号)
ansible_processor_vcpus:显示cpu个数(只显示总的个数)
ansible_python_version:显示python版本
独立变量
为了方便管理将所有的变量统一放在一个独立的变量YAML
文件中,laybook
文件直接引用文件调用变量即可。
# 定义存放变量的文件
[root@wallace playbook]# cat var.yml
var1: vsftpd
var2: httpd
# 编写playbook
[root@wallace playbook]# vim variables_test.yml
---
- hosts: all
remote_user: root
vars_files: #引用变量文件
- ./vars.yml #指定变量文件的path(这里可以是绝对路径,也可以是相对路径)
tasks:
- name: install package
yum: name={{ var1 }} #引用变量
- name: create file
file: name=/tmp/{{ var2 }}.log state=touch #引用变量
# 执行playbook
[root@wallace playbook]# ansible-playbook variables_test.yml
Playbook标签的使用
在 playbook
文件中,执行时如果想执行某一个任务,那么可以给每个任务集进行打标签,这样在执行的时候可以通过-t
选择指定标签执行,还可以通过--skip-tags跳过
某个标签执行等。
# 编写剧本
[root@wallace playbook]# vim tags_test.yml
---
- hosts: 192.168.100.12
remote_user: root
tasks:
- name: create file01
file: path=/tmp/tag01 state=touch
tags: file1
- name: create file02
file: path=/tmp/tag02 state=touch
tags: file2
- name: create file03
file: path=/tmp/tag03 state=touch
tags: file3
1. 正常执行结果
#正常执行结果
[root@wallace playbook]# ansible-playbook tags_test.yml
PLAY [192.168.100.12] *******************************************************************
TASK [Gathering Facts] ******************************************************************
ok: [192.168.100.12]
TASK [create file01] ********************************************************************
changed: [192.168.100.12]
TASK [create file02] ********************************************************************
changed: [192.168.100.12]
TASK [create file03] ********************************************************************
changed: [192.168.100.12]
PLAY RECAP ******************************************************************************
192.168.100.12 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2. 通过-t指定tags执行
通过-t指定tags名称,多个tags用逗号隔开
[root@wallace playbook]# ansible-playbook -t file1 tags_test.yml
PLAY [192.168.100.12] **************************************************************************************
TASK [Gathering Facts] *************************************************************************************
ok: [192.168.100.12]
TASK [create file01] ***************************************************************************************
changed: [192.168.100.12]
PLAY RECAP *************************************************************************************************
192.168.100.12 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3. 通过--skip-tags跳过某个tags执行
[root@wallace playbook]# ansible-playbook --skip-tags file1,file2 tags_test.yml
PLAY [192.168.100.12] **************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [192.168.100.12]
TASK [create file03] ***************************************************************************************************
changed: [192.168.100.12]
PLAY RECAP *************************************************************************************************************
192.168.100.12 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Playbook中模板的使用
template
模板为我们提供了动态配置服务,使用jinja2
语言,里面支持多种条件判断、循环、逻辑运算、比较操作等。其实说白了也就是一个文件,和之前配置文件使用copy
一样,只是使用copy
,不能根据服务器配置不一样进行不同动态的配置。这样就不利于管理。
说明:
1、多数情况下都将template
文件放在和playbook
文件同级的templates
目录下(手动创建),这样playbook
文件中可以直接引用,会自动去找这个文件。如果放在别的地方,也可以通过绝对路径去指定。
2、模板文件后缀名为.j2
。
示例: 通过template安装httpd
1.playbook
文件编写
[root@wallace playbook]# cat web.yml
#模板示例
---
- hosts: all
remote_user: root
vars:
- listen_port: 80 #定义变量
tasks:
- name: Install Httpd
yum: name=httpd state=installed
- name: Config Httpd
template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf #使用模板
notify: Restart Httpd
- name: Start Httpd
service: name=httpd state=started
handlers:
- name: Restart Httpd
service: name=httpd state=restarted
2.模板文件准备,httpd
配置文件准备,这里配置文件端口使用了变量
[root@wallace playbook]# cat templates/httpd.conf.j2 |grep ^Listen
Listen {{ listen_port }}
3. 查看目录结构
# 目录结构
[root@wallace playbook]# tree .
.
├── templates
│ └── httpd.conf.j2
└── testtmp.yml
1 directory, 2 files
4. 执行playbook
,由于192.168.100.14
那台机器是6
的系统,模板文件里面的配置文件是7
上面默认的httpd
配置文件,httpd
版本不一样(6默认版本为2.2.15,7默认版本为2.4.6
),所以拷贝过去后启动报错。下面使用playbook
中的判断语句进行处理;此处先略过
[root@wallace playbook]# ansible-playbook testtmp.yml
PLAY [all] ******************************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [192.168.100.12]
ok: [192.168.100.13]
ok: [192.168.100.14]
TASK [Install Httpd] ********************************************************************************
ok: [192.168.100.12]
ok: [192.168.100.13]
ok: [192.168.100.14]
TASK [Config Httpd] *********************************************************************************
changed: [192.168.100.12]
changed: [192.168.100.13]
changed: [192.168.100.14]
TASK [Start Httpd] **********************************************************************************
fatal: [192.168.100.14]: FAILED! => {"changed": false, "msg": "httpd: Syntax error on line 56 of
/etc/httpd/conf/httpd.conf: Include directory '/etc/httpd/conf.modules.d' not found
"}
changed: [192.168.100.12]
changed: [192.168.100.13]
RUNNING HANDLER [Restart Httpd] *********************************************************************
changed: [192.168.1.12]
changed: [192.168.1.13]
PLAY RECAP ******************************************************************************************
192.168.100.12 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.100.13 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.100.14 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
template之when
条件测试:如果需要根据变量、facts
或此前任务的执行结果来做为某task
执行与否的前提时要用到条件测试,通过when
语句执行,在task
中使用jinja2
的语法格式、
when语句:
在task
后添加when
子句即可使用条件测试;when
语句支持jinja2
表达式语法。
如下: 对实例进行修改.
1. 通过when语句完善上面的httpd配置, 准备两个配置文件,一个centos6
系统httpd
配置文件,一个centos7
系统httpd配置文件。
[root@wallace playbook]# tree templates/
templates/
├── httpd6.conf.j2 #6系统2.2.15版本httpd配置文件
└── httpd7.conf.j2 #7系统2.4.6版本httpd配置文件
0 directories, 2 files
2.修改playbook
文件,通过setup模块获取系统版本去判断。
[root@wallace playbook]# cat web.yml
#when示例
---
- hosts: all
remote_user: root
vars:
- listen_port: 80
tasks:
- name: Install Httpd
yum: name=httpd state=installed
- name: Config System6 Httpd
template: src=httpd6.conf.j2 dest=/etc/httpd/conf/httpd.conf
when: ansible_distribution_major_version == "6" #判断系统版本,为6便执行上面的template配置6的配置文件
notify: Restart Httpd
- name: Config System7 Httpd
template: src=httpd7.conf.j2 dest=/etc/httpd/conf/httpd.conf
when: ansible_distribution_major_version == "7" #判断系统版本,为7便执行上面的template配置7的配置文件
notify: Restart Httpd
- name: Start Httpd
service: name=httpd state=started
handlers:
- name: Restart Httpd
service: name=httpd state=restarted
3.执行playbook
[root@wallace playbook]# ansible-playbook web.yml
PLAY [all] ******************************************************************************************
TASK [Gathering Facts] ******************************************************************************
ok: [192.168.100.12]
ok: [192.168.100.13]
ok: [192.168.100.14]
TASK [Install Httpd] ********************************************************************************
ok: [192.168.100.12]
ok: [192.168.100.13]
ok: [192.168.100.14]
TASK [Config System6 Httpd] *************************************************************************
skipping: [192.168.100.12]
skipping: [192.168.100.13]
changed: [192.168.100.14]
TASK [Config System7 Httpd] *************************************************************************
skipping: [192.168.100.14]
changed: [192.168.100.12]
changed: [192.168.100.13]
TASK [Start Httpd] **********************************************************************************
ok: [192.168.100.12]
ok: [192.168.100.13]
ok: [192.168.100.14]
RUNNING HANDLER [Restart Httpd] *********************************************************************
ok: [192.168.100.12]
ok: [192.168.100.13]
ok: [192.168.100.14]
PLAY RECAP ******************************************************************************************
192.168.100.12 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.100.13 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.100.14 : ok=5 changed=2 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
template之with_items
with_items
迭代,当有需要重复性执行的任务时,可以使用迭代机制。
对迭代项的引用,固定变量名为“item”
,要在task中使用with_items给定要迭代的元素列表。
列表格式:
字符串
字典
示例1:通过with_items安装多个不同软件
[root@wallace playbook]# cat with_test.yml
# 示例with_items
---
- hosts: all
remote_user: root
tasks:
- name: Install Package
yum: name={{ item }} state=installed #引用item获取值
with_items: #定义with_items
- httpd
- nginx
- ansible
上面tasks
写法等同于:
---
- hosts: all
remote_user: root
tasks:
- name: Install Httpd
yum: name=httpd state=installed
- name: Install ansible
yum: name=ansible state=installed
- name: Install Nginx
yum: name=nginx state=installed
示例2:通过嵌套子变量创建用户并加入不同的组
1. 编写playbook
[root@ansible PlayBook]# cat testwith01.yml
# 示例with_items嵌套子变量
---
- hosts: all
remote_user: root
tasks:
- name: Create New Group
group: name={{ item }} state=present
with_items:
- group1
- group2
- group3
- name: Create New User
user: name={{ item.name }} group={{ item.group }} state=present
with_items:
- { name: 'user1', group: 'group1' }
- { name: 'user2', group: 'group2' }
- { name: 'user3', group: 'group3' }
2. 执行 playbook
# 执行playbook
[root@wallace playbook]# ansible-playbook with_test.yml
# 验证是否成功创建用户及组
[root@wallace playbook]# ansible all -m shell -a 'tail -3 /etc/passwd'
192.168.100.12 | CHANGED | rc=0 >>
user1:x:500:500::/home/user1:/bin/bash
user2:x:501:501::/home/user2:/bin/bash
user3:x:502:502::/home/user3:/bin/bash
192.168.100.13 | CHANGED | rc=0 >>
user1:x:1001:1001::/home/user1:/bin/bash
user2:x:1002:1002::/home/user2:/bin/bash
user3:x:1003:1003::/home/user3:/bin/bash
192.168.100.14 | CHANGED | rc=0 >>
user1:x:1002:1003::/home/user1:/bin/bash
user2:x:1003:1004::/home/user2:/bin/bash
user3:x:1004:1005::/home/user3:/bin/bash
template之for if
通过使用for
,if
可以更加灵活的生成配置文件等需求,还可以在里面根据各种条件进行判断,然后生成不同的配置文件、或者服务器配置相关等。
实例一:
1. 编写 playbook
[root@wallace playbook]# cat for_test.yml
# template for 示例
---
- hosts: all
remote_user: root
vars:
nginx_vhost_port:
- 81
- 82
- 83
tasks:
- name: Templage Nginx Config
template: src=nginx.conf.j2 dest=/tmp/nginx_test.conf
2. 模板文件编写
# 循环playbook文件中定义的变量,依次赋值给port
[root@wallace playbook]# cat templates/nginx.conf.j2
{% for port in nginx_vhost_port %}
server{
listen: {{ port }};
server_name: localhost;
}
{% endfor %}
3. 执行playbook
并查看生成结果
[root@wallace playbook]# ansible-playbook for_test.yml
# 去到一个节点看下生成的结果发现自动生成了三个虚拟主机
[root@wallace ~]# cat /tmp/nginx_test.conf
server{
listen: 81;
server_name: localhost;
}
server{
listen: 82;
server_name: localhost;
}
server{
listen: 83;
server_name: localhost;
}
实例二:
1. 编写playbook
[root@wallce wallace]# cat for_test.yml
# template for 示例
---
- hosts: all
remote_user: root
vars:
nginx_vhosts:
- web1:
listen: 8081
server_name: "web1.example.com"
root: "/var/www/nginx/web1"
- web2:
listen: 8082
server_name: "web2.example.com"
root: "/var/www/nginx/web2"
- web3:
listen: 8083
server_name: "web3.example.com"
root: "/var/www/nginx/web3"
tasks:
- name: Templage Nginx Config
template: src=nginx.conf.j2 dest=/tmp/nginx_vhost.conf
2. 模板文件编写
[root@wallace playbook]# cat templates/nginx.conf.j2
{% for vhost in nginx_vhosts %}
server{
listen: {{ vhost.listen }};
server_name: {{ vhost.server_name }};
root: {{ vhost.root }};
}
{% endfor %}
3. 执行playbook
并查看生成结果
[root@wallace playbook]# ansible-playbook for_test.yml
# 去到一个节点看下生成的结果发现自动生成了三个虚拟主机
[root@wallace ~]# cat /tmp/nginx_vhost.conf
server{
listen: 8081;
server_name: web1.example.com;
root: /var/www/nginx/web1;
}
server{
listen: 8082;
server_name: web2.example.com;
root: /var/www/nginx/web2;
}
server{
listen: 8083;
server_name: web3.example.com;
root: /var/www/nginx/web3;
}
实例三
在for循环中再嵌套if判断,让生成的配置文件更加灵活
1. 编写 playbook
[root@wallace playbook]# cat for_test.yml
# template for 示例
---
- hosts: all
remote_user: root
vars:
nginx_vhosts:
- web1:
listen: 8081
root: "/var/www/nginx/web1"
- web2:
server_name: "web2.example.com"
root: "/var/www/nginx/web2"
- web3:
listen: 8083
server_name: "web3.example.com"
root: "/var/www/nginx/web3"
tasks:
- name: Templage Nginx Config
template: src=nginx.conf.j2 dest=/tmp/nginx_vhost.conf