Playbook是由一个或多个“play”组成的列表,可以让它们联同起来按事先编排的机制执行;所谓task无非是调用ansible的一个module,而在模块参数中可以使用变量;模块执行是幂等的,这就意味着多次执行是安全的,因为其结果均一致。
就好像一个剧本一样,安排每个演员(每天被控制的主机)什么时候做什么事情(执行命令命令)
YAML的可读性好
YAML和脚本语言的交互性好
YAML使用实现语言的数据类型
YAML有一个一致的信息模型
YAML易于实现
YAML可以基于流来处理
YAML表达能力强,扩展性好
YAML不像其他的信息传输格式。json具有标记简单,体积小的优点但是不容易读。xml具有可读性好的优点,但是标记冗长,体积大。YAML融合了这两者的优点,用缩进和空格表示。下面是一个json和xml实例对比。
1。基本格式要求
YAML大小写敏感;
使用缩进代表层级关系;
缩进只能使用空格,不能使用TAB,不要求空格个数,只需要相同层级左对齐(一般2个或4个空格)
yaml用#作为注释
(详见这个链接)
https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-syntax
2.我们这里只注重yaml里对playbook最重要的两部分:列表,字典
(1)列表,每个列表项前面有一个“-”和空格符号。
例如,表示一个个水果的数组,相当于[apple,orange,pear]
- apple
- orange
- pear
(2)字典,每个字典项只要对齐就行了。
例如,表示一个个水果和他颜色的字典{apple:red,orange:orange,pear:yellow}
apple:red
orang:orange
pear:yellow
(3)看起来简单的数据格式yaml好像规定这么多,好麻烦,但是当数据关系复杂了之后,这种格式就非常清晰了。在后面的例子里我们将会发现他的好处。
(4)yaml也可以用流式数据作为文件数据的交换格式(这就丧失了他简单明了的优越性,所以不推荐。
1.我们之前的ansible命令都是一个个功能一条命令,想要重复使用命令还得一条条去找。用playbook把所有的ansible命令放到一个文件里,啥时候用直接到用这个文件就行了
2.不过这和普通的脚本文件不同,因为这个文件把每个对每个节点分开设置命令,享用哪个节点的设置就直接传入相应的参数就行。
3.标记tag一些命令,当传入tag时就只执行被tag过后的命令。
创建后缀名为.yaml的文件,并且按照yaml语法指定文件。
1.这是最基础的文件格式
相当于在命令行里执行了
ansible 选择的主机 -m 使用的模块名 -a 参数1=参数的值1 参数2= 参数的值2
- hosts:选择的主机组
remote_user:以什么身份在被控制的主机上执行命令
tasks:
- name: 第一条命令的描述
使用的模块名:
参数1: 参数的值1
参数2: 参数的值2
- name: 第二条命令的描述
.....
2.注意:
(1)参数1: 参数的值1之间的: 可以用=代替。
(2)像shell,command,script这样的模块可以直接给参数值,就不需要:分割了
测试yaml文件语法错误和预先排练一遍
[root@node1 ~]# ansible-playbook --syntax-check test.yaml
[root@node1 ~]# ansible-playbook --check test.yaml
ansible-playbook
其他参数
[options]: 选项
-C, --check:并不在远程主机上执行,只是测试。
-i PATH, --inventory=PATH:资产的文件路径
–flush-cache:清楚fact缓存
–list-hosts:列出匹配的远程主机,并不执行任何动作
-t, TAGS, --tags=TAGS:运行指定的标签任务
–skip-tags:跳过指定的notify,后面详细介绍。
1.下面以创建用户为例,写一个playbook在node2上设置hostname,上创建一个user1用户,并且把该用户的信息输出到1.txt里。
2.步骤
(1)创建test.yaml
[root@node1 ~]# cat test.yaml
- hosts: node2
remote_user: root
tasks:
- name: hostname
command: hostnamectl set-hostname node2
- name: create user
user: name:user1
- name: show user
shell: id user1 >> 1.txt
如果把他写成流式的形式非常难看,yaml使得我们我们这样可以非常清晰地看到数据项之间的关系
[{hosts: node2,remote_user: root,tasks: [{name: hostname,command: hostnamectl set-hostname node2},{name: create user,user: name:user1},{- name: show user,shell: id user1 >> 1.txt}]]
(2)检查语法错误,并且预先排练一遍
(3)正式执行成功
[root@node1 ~]# ansible-playbook test.yaml
(如果有同名情况,优先级从上往下)
1.在调用playbook时传入参数。通过-e 传入参数
[root@node1 ~]# ansible-playbook -e test=2 test.yaml
(1)在/etc/ansible/hosts 定义变量,在主机组中的主机单独定义
(2)在/etc/ansible/hosts 定义变量,针对主机组中的所有主机集中定义变量
3.在yaml文件里var。
4.调用被控制机器的信息。
要用{{ 变量名 }}来使用,要注意空格
- name: use var
shell: echo {{ var_in_yaml }} > 2.txt
如果有一条task没有改变(changed)对应被控制主机的结果。那么就notify指向handler中的某一条处理。经常使用在看一个文件的配置文件改变了没有,改变的话就重启服务。
- name: 任务名称
使用的模块名:
参数1: 参数的值1
参数2: 参数的值2
notify:
- restart apache
handler:
- name: 处理名称
使用的模块名:
参数1: 参数的值1
参数2: 参数的值2
实例
我们先在node2节点上开启httpd服务,然后在node1上运行这个程序,发现
- hosts: node2
remote_user: root
tasks:
- name: start service
service:
name: httpd
state: started
notify:
- show
handler:
- name: show
shell: echo "http服务第一启动" > message.txt
当满足某个条件时,才执行这条任务
- name: 任务名称
使用的模块名:
参数1: 参数的值1
参数2: 参数的值2
when:条件
任务中可以通过{{ item }}使用这个变量
- name: 任务名称
使用的模块名:
参数1: 参数的值1
参数2: 参数的值2
with_itmes:
- 元素1
- 元素2
- 元素3
思考这样的情景:我们小时候学校发奖状,会从头到尾手写奖状内容吗?当然不会。
一般会是这样:先制作一个模板奖状,把名字,日期,奖项空出来。然后复印这个模板,等到发奖状的时候把需要填的内容填上就可以把奖状发给同学了。
因此模板文件也有这样的功能,提供一个模板,在分发任务的时候根据具体的变量值把模板填充完整。
需要使用变量名的时候用:{{ 变量名 }}
下面我们还是使用这个发奖状的例子,给每个主机发一个奖状,对不同的主机需要写不同的名字。
[root@node1 test_dir]# cat jiangzhuang.j2
host {{ hostname }}:
恭喜你获得了”最棒的主机"称号
在yaml文件里需要为模板文件提供变量,对node2和node3写yaml文件。执行这个yaml文件。会发现node2,node3收到了文件。
[root@node1 test_dir]# cat test.yaml
- hosts: node2
remote_user: root
vars:
hostname: "主机2"
tasks:
- name: send jiangzhuang
template:
src=jiangzhuang.j2 dest=/root/jiangzhuang.txt
- hosts: node3
remote_user: root
vars:
hostname: "主机3"
tasks:
- name: send jiangzhuang
template:
src=jiangzhuang.j2 dest=/root/jiangzhuang.txt
- name: 任务名称
使用的模块名:
参数1: 参数的值1
参数2: 参数的值2
tags:标记名
仅执行被标记的task,跳过执行的task
ansible-playbook --tags 标记名 testtag.yml
ansible-playbook --skip-tags 标记名 testtag.ymll
把每个功能模块化,需要的时候在site里调用这个功能
创建这样的文件结构。
roles/
├── http
│ ├── defaults
│ │ └── main.yaml
│ ├── files
│ │ └── index.html
│ ├── headlers
│ │ └── main.yaml
│ ├── meta
│ │ └── main.yaml
│ ├── tasks
│ │ └── main.yaml
│ ├── templates
│ │ └── httpd.conf.j2
│ └── vars
│ └── main.yaml
└── site.yaml
tasks/:此目录中至少应该有一个名为main.yml的文件,用于定义各task;其它的文件需要由main.yml进行“包含”调用;
handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler;其它的文件需要由main.yml进行“包含”调用;
vars/:用于存放数据文件;
templates/:存储由template模块调用的模板文本;
meta/:此目录中至少应该有一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要由main.yml进行“包含”调用;
default/:此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;
files目录:存放由copy或script等模块调用的文件;
1.创建文件目录,并且在site里写主任务
[root@node1 ansible]# tree roles/
roles/
├── http
│ ├── defaults
│ │ └── main.yaml
│ ├── files
│ │ └── index.html
│ ├── headlers
│ │ └── main.yaml
│ ├── meta
│ │ └── main.yaml
│ ├── tasks
│ │ └── main.yaml
│ ├── templates
│ │ └── httpd.conf.j2
│ └── vars
│ └── main.yaml
└── site.yaml
[root@node1 roles]# cat site.yaml
- hosts: node2
remote_user: root
# vars:
# httpd_ports: 8080
roles:
- http
2.在files/index.html文件
[root@node1 http]# cat files/index.html
hello eagleslab!
3.在tasks/main.yaml文件设置任务
[root@node1 http]# cat tasks/main.yaml
- name: installed httpd service
yum: name=httpd state=latest
when: ansible_os_family == "RedHat"
- name: start httpd server
service: name=httpd state=restarted
- name: write httpd config
template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
notify:
- restart httpd
- name: config httpd index
copy: src=index.html dest=/var/www/html/index.html
4.写模板文件。
[root@node1 http]# cat templates/httpd.conf.j2 | grep -Ev "^[[:space:]]|^#"
ServerRoot "/etc/httpd"
Listen {{ httpd_ports }}
Include conf.modules.d/*.conf
User apache
Group apache
ServerAdmin root@localhost
<Directory />
</Directory>
DocumentRoot "/var/www/html"
<Directory "/var/www">
</Directory>
<Directory "/var/www/html">
</Directory>
<IfModule dir_module>
</IfModule>
<Files ".ht*">
</Files>
ErrorLog "logs/error_log"
LogLevel warn
<IfModule log_config_module>
</IfModule>
<IfModule alias_module>
</IfModule>
<Directory "/var/www/cgi-bin">
</Directory>
<IfModule mime_module>
</IfModule>
AddDefaultCharset UTF-8
<IfModule mime_magic_module>
</IfModule>
EnableSendfile on
IncludeOptional conf.d/*.conf
[root@node1 http]# cat headlers/main.yaml
- name: restart httpd
service: name=httpd state=restarted
5.在vars/main.yaml文件里写端口号
[root@node1 http]# cat vars/main.yaml
httpd_ports: 8000
6.运行
[root@node1 http]# ansible site.yaml