自动化运维工具——YAML详解
YAML介绍
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者。
YAML Ain’t Markup Language,即YAML不是XML。不过,在开发的这种语言时,YAML的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)。其特性:
YAML的可读性好
YAML和脚本语言的交互性好
YAML使用实现语言的数据类型
YAML有一个一致的信息模型
YAML易于实现
YAML可以基于流来处理
YAML表达能力强,扩展性好
YAML语法
YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构(Structure)通过空格来展示,序列(Sequence)里的项用"-“来代表,Map里的键值对用”:"分隔。下面是一个示例。YAML是用键值对和缩进来表示的。
基本语法
1、大小写敏感
2、使用缩进表示层级关系
3、缩进时不允许使用Tab键,只能使用空格
4、缩进的空格数且不重要,只要相同层级的元素左侧对其即可
YAML支持的数据结构:
1.对象:键值对的集合,又称为映射(mapping)/哈希(hashes)/字典(dictionary)
例如:
name:Example Developer
键值
⒉数组:一组按次序排列的值,又称为序列(sequence)/列表(list)例如:-Apple
-Orange
3.纯量:单个的、不可再分的值
例如:number: 12.30
sure: true
yaml示例:
name:zhangsan
age:20
name:lisi
age:22
people:
-name:zhangsan
age:20
-name:lisiage:22
playbook的组成
Ansible的脚本—playbook剧本
通过task调用ansible的模板将多个play组织在一个
playbook中运行。playbooks本身由以下各部分组成
(1)Tasks:任务,即调用模块完成的某操作;
(2) Variables:变量
(3) Templates:模板
(4)Handlers:处理器,当某条件满足时,触发执行的操作;
(5) Roles:角色。
三要素
剧本的三要素
1、演出的场地
2、演员要到位
3、故事情节
playbook的三要素
1、场地—主机组hosts
2、演员—授权执行的用户 remote user
3、故事情节----执行的任务tasks (调用的是各种ansible模块)
playbook 语法
执行一个playbook
ansible-playbook [yaml文件名]
例如:ansible-playbook ping.yml
参数:-k(-ask-pass)用来交互输入ssh密码
-K(-ask-become-pass)用来交互输入sudo密码
-u指定用户
补充命令:
ansible-playbook nginx.yaml --syntax-check #检查yaml文件的语法是否正确
ansible-playbook nginx.yaml --list-task #检查tasks任务
ansible-playbook nginx.yaml --list-hosts #检查生效的主机
ansible-playbook nginx.yaml --start-at-task='Copy Nginx.conf’ #指定从某个task开始运行
hosts和users介绍
hosts: webserver #指定主机组,可以是一个或多个组。
remote user: root #指定远程主机执行的用户名
[root@server1 ~]# vim ping.yaml
- hosts: all #定义主机组
remote_user: root #指定root用户 全局变量
tasks:
- name: test connection
ping:
[root@server1 ~]# ansible-playbook ping.yaml --syntax-check //检查语法
playbook: ping.yaml
[root@server1 ~]# ansible-playbook ping.yaml //执行
PLAY [all] ************************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
ok: [192.168.188.20]
TASK [test connection] ************************************************************
ok: [192.168.188.20]
ok: [192.168.188.30]
PLAY RECAP ************************************************************************
192.168.188.20 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.188.30 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
指定远程主机sudo切换用户
[root@server1 ~]# vim sudo.yaml
- hosts: webservers
remote_user: root
become: yes //切换用户执行
become_user: lisi //指定sudo用户为lisi
tasks:
- name: copy text
copy: src=/etc/fstab dest=/home/lisi/fstab.bak
[root@server1 ~]# ansible-playbook sudo.yaml --syntax-check //查看语法
playbook: sudo.yaml
[root@server1 ~]# ansible-playbook sudo.yaml //执行
PLAY [webservers] *****************************************************************
TASK [Gathering Facts] ************************************************************
[WARNING]: Module remote_tmp /home/lisi/.ansible/tmp did not exist and was created
with a mode of 0700, this may cause issues when running as another user. To avoid
this, create the remote_tmp dir with the correct permissions manually
ok: [192.168.188.20]
TASK [copy text] ******************************************************************
changed: [192.168.188.20]
PLAY RECAP ************************************************************************
192.168.188.20 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
去node1验证
[root@server2 ~]# id lisi
uid=1001(lisi) gid=1001(lisi) 组=1001(lisi)
[root@server2 ~]# cd /home/lisi/
[root@server2 lisi]# ls
[root@server2 lisi]# ls
fstab.bak
小实验 练练手
实验要求:
安装Apcha
开启Apcha
关闭防火墙
给Apcha主页换成this is Apche
[root@server1 ~]# vim Apcha.yaml
- hosts: mysql
remote_user: root
tasks:
- name: yum httpd
yum: name=httpd
- name: start httpd
service: name=httpd state=started
- name: stop firewalld
service: name=firewalld state=stopped
- name: index.html
copy: content="this is Apcha" dest=/var/www/html/index.html
[root@server1 ~]# ansible-playbook Apcha.yaml --syntax-check
playbook: Apcha.yaml
[root@server1 ~]# ansible-playbook Apcha.yaml
PLAY [mysql] **********************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
TASK [yum httpd] ******************************************************************
changed: [192.168.188.30]
TASK [start httpd] ****************************************************************
changed: [192.168.188.30]
TASK [stop firewalld] *************************************************************
ok: [192.168.188.30]
TASK [index.html] *****************************************************************
changed: [192.168.188.30]
PLAY RECAP ************************************************************************
192.168.188.30 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2 验证
[root@server3 ~]# rpm -q httpd
httpd-2.4.6-67.el7.centos.x86_64
[root@server3 ~]# systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Active: active (running) since 四 2021-01-14 10:07:20 CST; 25s ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 42301 (httpd)
Status: "Total requests: 0; Current requests/sec: 0; Current traffic: 0 B/sec"
[root@server3 ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:firewalld(1)
如果一个hosts执行task失败,整个task就会回滚,请修正playbook错误,然后重新执行
[root@server1 ~]# vim Apcha.yaml
- hosts: webservers
remote_user: root
tasks:
- name: yum httpd
yum: name=httpd
- name: start httpd
service: name=httpd state=started
- name: stop firewalld
service: name=firewall state=stopped //firewalld 少d
- name: index.html
copy: content="this is Apcha" dest=/var/www/html/index.html
[root@server1 ~]# ansible-playbook Apcha.yaml --syntax-check //语法正确
playbook: Apcha.yaml
[root@server1 ~]# ansible-playbook Apcha.yaml //执行 到stop firewalld显示错误 后面不执行
PLAY [webservers] *****************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.20]
TASK [yum httpd] ******************************************************************
changed: [192.168.188.20]
TASK [start httpd] ****************************************************************
changed: [192.168.188.20]
TASK [stop firewalld] *************************************************************
fatal: [192.168.188.20]: FAILED! => {"changed": false, "msg": "Could not find the requested service firewall: host"}
PLAY RECAP ************************************************************************
192.168.188.20 : ok=3 changed=2 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
node1 验证
[root@server2 lisi]# id httpd
id: httpd: no such user
[root@server2 lisi]# systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Active: active (running) since 四 2021-01-14 10:48:41 CST; 1min 18s ago
Docs: man:httpd(8)
man:apachectl(8)
Main PID: 42757 (httpd)
Status: "Total requests: 0; Current requests/sec: 0; Current traffic: 0 B/sec"
[root@server2 lisi]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)
Active: active (running) since 四 2021-01-14 10:50:29 CST; 16s ago
Docs: man:firewalld(1)
Main PID: 42868 (firewalld)
CGroup: /system.slice/firewalld.service
└─42868 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid
在stop firewalld 下面添加ignore_errors: True(忽略错误,强制返回成功)
[root@server1 ~]# vim Apcha.yaml
- hosts: webservers
remote_user: root
tasks:
- name: yum httpd
yum: name=httpd
- name: start httpd
service: name=httpd state=started
- name: stop firewalld
service: name=firewall state=stopped
ignore_errors: True
- name: index.html
copy: content="this is Apcha" dest=/var/www/html/index.html
[root@server1 ~]# ansible-playbook Apcha.yaml --syntax-check
playbook: Apcha.yaml
[root@server1 ~]# ansible-playbook Apcha.yaml
PLAY [webservers] *****************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.20]
TASK [yum httpd] ******************************************************************
ok: [192.168.188.20]
TASK [start httpd] ****************************************************************
ok: [192.168.188.20]
TASK [stop firewalld] *************************************************************
fatal: [192.168.188.20]: FAILED! => {"changed": false, "msg": "Could not find the requested service firewall: host"}
...ignoring
TASK [index.html] *****************************************************************
changed: [192.168.188.20]
PLAY RECAP ************************************************************************
192.168.188.20 : ok=5 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 //有一条被忽略
Handlers介绍
Handlers也是一些task的列表,和一般的task并没有什么区别。
是由通知者进行的notify,如果没有被notify,则Handlers不会执行,假如被notify了,则Handlers被执行
不管有多少个通知者进行了notify,等到play中的所有task执行完成之后,handlers也只会被执行一次
1、在playbook直接使用变量
[root@server1 ~]# vim Apcha.yaml
- hosts: webservers
remote_user: root
vars:
- abc: httpd //把httpd变成abc
tasks:
- name: yum httpd
yum: name={{abc}}
- name: start httpd
service: name={{abc}} state=started
- name: stop firewalld
service: name=firewalld state=stopped
- name: index.html
copy: content="this is Apcha" dest=/var/www/html/index.html
[root@server1 ~]# ansible-playbook Apcha.yaml --syntax-check
playbook: Apcha.yaml
[root@server1 ~]# ansible-playbook Apcha.yaml
PLAY [webservers] *****************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.20]
TASK [yum httpd] ******************************************************************
changed: [192.168.188.20]
TASK [start httpd] ****************************************************************
changed: [192.168.188.20]
TASK [stop firewalld] *************************************************************
ok: [192.168.188.20]
TASK [index.html] *****************************************************************
ok: [192.168.188.20]
PLAY RECAP ************************************************************************
192.168.188.20 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2、通过ansible命令传递
[root@server1 ~]# vim Apcha.yaml
- hosts: webservers
remote_user: root
vars:
- abc: //空的 在外面用命令设置
tasks:
- name: yum httpd
yum: name={{abc}}
- name: start httpd
service: name={{abc}} state=started
- name: stop firewalld
service: name=firewalld state=stopped
- name: index.html
copy: content="this is Apcha" dest=/var/www/html/index.html
[root@server1 ~]# ansible-playbook Apcha.yaml -e 'abc=httpd'
PLAY [webservers] *****************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.20]
TASK [yum httpd] ******************************************************************
ok: [192.168.188.20]
TASK [start httpd] ****************************************************************
ok: [192.168.188.20]
TASK [stop firewalld] *************************************************************
ok: [192.168.188.20]
TASK [index.html] *****************************************************************
ok: [192.168.188.20]
PLAY RECAP ************************************************************************
192.168.188.20 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
3、直接在yaml中定义变量
[root@server1 ~]# vim test.yaml
- hosts: mysql
remote_user: root
tasks:
- name: copy file
copy: content="{{ansible_all_ipv4_addresses}}" dest=/opt/addr.txt
[root@server1 ~]# ansible-playbook test.yaml --syntax-check
playbook: test.yaml
[root@server1 ~]# ansible-playbook test.yaml
PLAY [mysql] **********************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
TASK [copy file] ******************************************************************
changed: [192.168.188.30]
PLAY RECAP ************************************************************************
192.168.188.30 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
到node2 验证
[root@server3 ~]# cd /opt/
[root@server3 opt]# ls
addr.txt rh
[root@server3 opt]# cat addr.txt
["192.168.122.1", "192.168.188.30"][root@server3 opt]#
4、直接引用主机变量
[root@server1 ~]# vim /etc/ansible/hosts
[webservers]
192.168.188.20
[mysql]
192.168.188.30 num="123456"
[root@server1 ~]# vim test.yaml
- hosts: mysql
remote_user: root
tasks:
- name: copy file
copy: content="{{ansible_all_ipv4_addresses}},{{num}}" dest=/opt/num.txt
[root@server1 ~]# ansible-playbook test.yaml --syntax-check
playbook: test.yaml
[root@server1 ~]# ansible-playbook test.yaml
PLAY [mysql] **********************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
TASK [copy file] ******************************************************************
changed: [192.168.188.30]
PLAY RECAP ************************************************************************
192.168.188.30 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
去node2验证
[root@server3 opt]# ls
addr.txt num.txt rh
[root@server3 opt]# cat num.txt
([u'192.168.122.1', u'192.168.188.30'], 123456)[root@server3 opt]#
条件测试
[root@server1 ~]# vim when.yaml
- hosts: mysql
remote_user: root
tasks:
- name: "shutdown CentOS"
command: /sbin/shutdown -h now
when: ansible_distribution == "CentOS" //判断系统CentOS
[root@server1 ~]# ansible-playbook when.yaml
PLAY [mysql] **********************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
TASK [shutdown CentOS] ************************************************************
fatal: [192.168.188.30]: FAILED! => {"msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.188.30 port 22: Connection refused"}
PLAY RECAP ************************************************************************
192.168.188.30 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
多条件判断
[root@server1 ~]# vim when.yaml
- hosts: mysql
remote_user: root
tasks:
- name: "shut down CentOS 7 systems"
command: /sbin/shutdown -r now
when:
- ansible_distribution == "CentOS" //判断CentOS系统
- ansible_distribution_major_version == "7" //7版本
[root@server1 ~]# ansible-playbook when.yaml
PLAY [mysql] **********************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
TASK [shut down CentOS 7 systems] *************************************************
fatal: [192.168.188.30]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Shared connection to 192.168.188.30 closed.", "unreachable": true}
PLAY RECAP ************************************************************************
192.168.188.30 : ok=1 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
组条件判断
[root@server1 ~]# vim when.yaml
- hosts: mysql
remote_user: root
tasks:
- name: "shut down CentOS 7 systems"
command: /sbin/shutdown -r now
when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6") or (ansible_distribution == "Debian " and ansible_distribution_major_version == "7") //判断你的系统是CentOS 版本是6 或者系统是Debian 7
[root@server1 ~]# ansible-playbook when.yaml --syntax-check
playbook: when.yaml
[root@server1 ~]# ansible-playbook when.yaml
迭代
当有需要重复性执行的任务时,可以使用迭代机制
[root@server1 ~]# vim yum.yaml
- hosts: all
remote_user: root
tasks:
- name: install httpd tomcat
yum: name={{item}}
with_items:
- httpd
- tomcat
[root@server1 ~]# ansible-playbook yum.yaml --syntax-check
playbook: yum.yaml
[root@server1 ~]# ansible-playbook yum.yaml
LAY [all] ************************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.30]
ok: [192.168.188.20]
TASK [install httpd tomcat] *******************************************************
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via
squash_actions is deprecated. Instead of using a loop to supply multiple items and
specifying `name: "{{item}}"`, please use `name: ['httpd', 'tomcat']` and remove
the loop. This feature will be removed in version 2.11. Deprecation warnings can
be disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via
squash_actions is deprecated. Instead of using a loop to supply multiple items and
specifying `name: "{{item}}"`, please use `name: ['httpd', 'tomcat']` and remove
the loop. This feature will be removed in version 2.11. Deprecation warnings can
be disabled by setting deprecation_warnings=False in ansible.cfg.
changed: [192.168.188.20] => (item=[u'httpd', u'tomcat'])
changed: [192.168.188.30] => (item=[u'httpd', u'tomcat'])
PLAY RECAP ************************************************************************
192.168.188.20 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.188.30 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@server2 ~]# rpm -q httpd
httpd-2.4.6-67.el7.centos.x86_64
[root@server2 ~]# rpm -q tomcat
tomcat-7.0.76-2.el7.noarch
[root@server3 ~]# rpm -q httpd
httpd-2.4.6-67.el7.centos.x86_64
[root@server3 ~]# rpm -q tomcat
tomcat-7.0.76-2.el7.noarch
也可以自己定义
[root@server1 ~]# vim user.yaml
- hosts: all
remote_user: root
tasks:
- name: "users"
user: name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name: 'song', groups: 'root' }
- { name: 'shufeng', groups: 'wheel' }
[root@server1 ~]# ansible-playbook user.yaml --syntax-check
playbook: user.yaml
[root@server1 ~]# ansible-playbook user.yaml
PLAY [all] ************************************************************************
TASK [Gathering Facts] ************************************************************
ok: [192.168.188.20]
ok: [192.168.188.30]
TASK [users] **********************************************************************
changed: [192.168.188.30] => (item={u'name': u'song', u'groups': u'root'})
changed: [192.168.188.20] => (item={u'name': u'song', u'groups': u'root'})
changed: [192.168.188.30] => (item={u'name': u'shufeng', u'groups': u'wheel'})
changed: [192.168.188.20] => (item={u'name': u'shufeng', u'groups': u'wheel'})
PLAY RECAP ************************************************************************
192.168.188.20 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
192.168.188.30 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@server2 ~]# id song
uid=1002(song) gid=1002(song) 组=1002(song),0(root)
[root@server2 ~]# id shufeng
uid=1003(shufeng) gid=1003(shufeng) 组=1003(shufeng),10(wheel)
[root@server3 ~]# id song
uid=1003(song) gid=1003(song) 组=1003(song),0(root)
[root@server3 ~]# id shufeng
uid=1004(shufeng) gid=1004(shufeng) 组=1004(shufeng),10(wheel)