Ansible的脚本---playbook剧本
YAML
YAML:是一种非标记语言。是用来写配置文件的语言,非常简洁和强大。YAML语法和其他语言类似,也可以表达散列表、标量等数据结构。
结构通过空格来展示;序列里配置项通过-来代表;Map里键值用:来分隔;YAML的扩展名为yaml
基本语法规则:
- 大小写敏感
- 使用缩进表示层级关系
- 缩进时不允许使用Tab键,只允许使用空格。
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
YAML支持的数据结构:
1.对象:键值对的集合,又称为映射(mapping)/哈希(hashes)/字典(dictionary)
例如:name: Example Developer
2.数组:一组按次序排列的值,又称为序列 (sequence)/列表(list)
例如:
- Apple
- Orange
3.纯量(变量):单个的、不可再分的值
例如:number: 12.30l
sure: true
yaml示例
name: zhangsan
age: 20
people:
- name: wangwu
age: 21
Ansible的脚本—playbook剧本
通过task调用ansible的模板将多个play组织在一个playbook中运行。
playbooks部分组成
(1)Tasks:任务,即调用模块完成的某操作
(2)Variables:变量
(3)Templates:模板
(4)Handlers:处理器,当某条件满足时,触发执行的操作
(5)Roles:角色
可将playbook看成一个剧本,剧本的内容包括:
剧本 | playbook |
---|---|
场地 | 主机组hosts |
演员 | 授权执行的用户remote_user |
故事情节 | 执行的任务 |
执行playbook
ansible-playbook [yaml文件名]
例如:
ansible-playbook ttt.yaml
参数:
-k(-ask-pass)用来交互输入密码
-K(-ask-become-pass)用来交互输入sudo密码
-u 指定用户
补充命令:
ansible-playbook ttt.yaml --syntax-check #检查yaml文件的语法是否正确
ansible-playbook ttt.yaml --list-task #检查tasks任务
ansible-playbook ttt.yaml --list-hosts #检查生效的主机
ansible-playbook ttt.yaml --start-at-task='abc' #指定从某个task开始运行
hosts和users介绍
- hosts: webserver #指定主机组,可以是一个或多个组
remote_user: root #指定远程主机执行的用户名
可以为每个任务定义远程执行用户
[root@server1 opt]# vim ping.yaml
- hosts: mysql
remote_user: root
tasks:
- name: ping #name为给接下来运行的任务起个名字
ping: #调用ping模块
remote_user: mysql #指定远程主机执行tasks的运行用户为mysql
执行playbook:
ansible-playbook ping.yaml -k #免密的情况下可省略-k
[root@server1 ~]# ansible-playbook ping.yaml #此时发现运行tasks:ping不成功
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [20.0.0.13]
TASK [useradd mysql] **************************************************************************
ok: [20.0.0.13]
TASK [ping] ***********************************************************************************
fatal: [20.0.0.13]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true}
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=2 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
分析错误原因:
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password)." #没有权限去执行操作
解决
给mysql用户提权
指定远程主机sudo切换用户:
[root@server1 opt]# vim ping.yaml
- hosts: mysql
remote_user: root
become:
tasks:
- name: ping
ping:
remote_user: mysql
[root@server1 ~]# ansible-playbook ping.yaml
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
fatal: [20.0.0.13]: FAILED! => {"ansible_facts": {}, "changed": false, "failed_modules": {"setup": {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "failed": true, "module_stderr": "Shared connection to 20.0.0.13 closed.\r\n", "module_stdout": ">>> /etc/sudoers: 语法错误 near line 1 <<<\r\nsudo: /etc/sudoers 中第 1 行附近有解析错误\r\nsudo: 没有找到有效的 sudoers 资源,退出\r\nsudo: 无法初始化策略插件\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}}, "msg": "The following modules failed to execute: setup\n"}
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
分析原因:
没有找到有效的 sudoers 资源
解决:
在被控制端的主机/etc/sudoers中添加mysql
[root@server1 ~]# vim ping.yaml
- hosts: mysql
remote_user: root
tasks:
- name: useradd mysql
user: name=mysql #创建一个mysql用户
- name: sudoers
shell: /usr/bin/echo "mysql ALL=(root) ALL" >> /etc/sudoers
- name: ping
become: yes #允许进行提权
become_user: mysql #提权账号
ping:
[root@server1 ~]# ansible-playbook ping.yaml
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [20.0.0.13]
TASK [useradd mysql] **************************************************************************
ok: [20.0.0.13]
TASK [sudoers] ********************************************************************************
changed: [20.0.0.13]
TASK [ping] ***********************************************************************************
ok: [20.0.0.13]
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tasks列表和action
- Play的主体部分是task列表,task列表中的各任务按次序逐个在hosts中指定的主机上执行,即在所有主机上完成第一个任务后再开始第二个任务
在运行playbook时(从上到下执行),如果一个host执行task失败,整个tasks都会回滚,请修正playbook 中的错误,然后重新执行即可。Task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量,模块执行时幂等的,这意味着多次执行是安全的,因为其结果一致。 - 每一个task必须有一个名称name,这样在运行playbook时,从其输出的任务执行信息中可以很好的辨别出是属于哪一个task的。
- 定义一个task,常见的格式:"module: options”例如: yum: name=httpd
- ansible的自带模块中,command模块和shell模块无需使用key=value格式
例如:
[root@server1 ~]# vim demo.yaml
- hosts: mysql
remote_user: root
tasks:
- name: useradd mysql
user: name=mysql
- name: sudoers
shell: usr/bin/echo "mysql ALL=(root) ALL" >> /etc/sudoers #这里去掉一个/,模拟运行失败
- name: ping
ping:
[root@server1 ~]# ansible-playbook demo.yaml
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [20.0.0.13]
TASK [useradd mysql] **************************************************************************
ok: [20.0.0.13]
TASK [sudoers] ********************************************************************************
fatal: [20.0.0.13]: FAILED! => {"changed": true, "cmd": "usr/bin/echo \"mysql ALL=(root) ALL\" >> /etc/sudoers", "delta": "0:00:00.002690", "end": "2021-01-14 21:59:18.256832", "msg": "non-zero return code", "rc": 127, "start": "2021-01-14 21:59:18.254142", "stderr": "/bin/sh: usr/bin/echo: 没有那个文件或目录", "stderr_lines": ["/bin/sh: usr/bin/echo: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=2 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
只要执行命令的返回值不为0,就为报错,tasks停止,此次任务回滚
修改
[root@server1 ~]# vim demo.yaml
- hosts: mysql
remote_user: root
tasks:
- name: useradd mysql
user: name=mysql
- name: sudoers
shell: usr/bin/echo "mysql ALL=(root) ALL" >> /etc/sudoers
ignore_errors: True #忽略错误,强制返回成功
- name: ping
ping:
[root@server1 ~]# ansible-playbook demo.yaml
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [20.0.0.13]
TASK [useradd mysql] **************************************************************************
ok: [20.0.0.13]
TASK [sudoers] ********************************************************************************
fatal: [20.0.0.13]: FAILED! => {"changed": true, "cmd": "usr/bin/echo \"mysql ALL=(root) ALL\" >> /etc/sudoers", "delta": "0:00:00.002509", "end": "2021-01-14 22:03:43.033818", "msg": "non-zero return code", "rc": 127, "start": "2021-01-14 22:03:43.031309", "stderr": "/bin/sh: usr/bin/echo: 没有那个文件或目录", "stderr_lines": ["/bin/sh: usr/bin/echo: 没有那个文件或目录"], "stdout": "", "stdout_lines": []}
...ignoring
TASK [ping] ***********************************************************************************
ok: [20.0.0.13]
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=4 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也只会被执行一次
[root@server1 ~]# vim demo.yaml
- hosts: mysql
remote_user: root
tasks:
- name: useradd mysql
user: name=mysql
- name: install apache
yum: name=httpd
notify:
- restart httpd
- name: restart httpd
service: name=httpd state=started
handlers:
- name: restart httpd
service: name=httpd state=restarted
[root@server1 ~]# ansible-playbook demo.yaml
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [20.0.0.13]
TASK [useradd mysql] **************************************************************************
ok: [20.0.0.13]
TASK [install apache] *************************************************************************
changed: [20.0.0.13]
TASK [restart httpd] **************************************************************************
changed: [20.0.0.13]
RUNNING HANDLER [restart httpd] ***************************************************************
changed: [20.0.0.13]
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=5 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
playbook变量
通过ansible命令传递
[root@server1 ~]# vim demo.yaml
- hosts: mysql
remote_user: root
vars:
- user:
tasks:
- name: useradd mysql
user: name={{user}}
[root@server1 opt]# ansible-playbook demo.yaml -e user=mysql #使用-e给变量赋值
PLAY [mysql] ********************************************************************
TASK [Gathering Facts] **********************************************************
ok: [20.0.0.13]
TASK [useradd mysql] ************************************************************
changed: [20.0.0.13]
PLAY RECAP **********************************************************************
20.0.0.13 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@server1 opt]# ansible mysql -m command -a 'tail /etc/passwd' #查看
20.0.0.13 | CHANGED | rc=0 >>
………………
mysql:x:1001:1001::/home/mysql:/bin/bash
直接在yaml中定义变量
[root@server1 ~]# vim demo.yaml
- hosts: mysql
remote_user: root
vars:
services: httpd
tasks:
- name: useradd mysql
user: name=mysql
- name: install apache
yum: name={{services}}
notify:
- restart httpd
- name: restart httpd
service: name={{services}} state=started
handlers:
- name: restart httpd
service: name={{services}} state=restarted
[root@server1 ~]# ansible-playbook demo.yaml
PLAY [mysql] **********************************************************************************
TASK [Gathering Facts] ************************************************************************
ok: [20.0.0.13]
TASK [useradd mysql] **************************************************************************
ok: [20.0.0.13]
TASK [install apache] *************************************************************************
ok: [20.0.0.13]
TASK [restart httpd] **************************************************************************
ok: [20.0.0.13]
PLAY RECAP ************************************************************************************
20.0.0.13 : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
直接引用一些变量
引用ansible的固定变量
[root@server1 opt]# vi demo.yaml
- hosts: mysql
remote_user: root
tasks:
- name: copy ipv4
copy: content="{{ansible_all_ipv4_addresses}}" dest=/opt/ipv4.txt
[root@server1 opt]# ansible-playbook demo.yaml
PLAY [mysql] ********************************************************************
TASK [Gathering Facts] **********************************************************
ok: [20.0.0.13]
TASK [copy ipv4] ****************************************************************
changed: [20.0.0.13]
PLAY RECAP **********************************************************************
20.0.0.13 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
mysql主机查看
[root@server1 opt]# cat ipv4.txt
["192.168.122.1", "20.0.0.13"]
条件测试
如果需要根据变量、facts(setup)或此前任务的执行结果来作为某task执行与否的前提时要用到条件测试,在playbook中条件测试使用when子句,在task后台添加when子句即可使用条件测试:when子句支持jinjia2表达式或语法(jinjia2:基于python语言,格式为{{}},是一种特殊的占位符)
[root@server1 opt]# vi when.yaml
- hosts: webserver
remote_user: root
tasks:
- name: shutdown
command: /sbin/shutdown -r now
when: #当系统版本为CentOS时,关闭webserver主机
- ansible_distribution == "CentOS"
多条件判断
[root@server1 opt]# vi when.yaml
- hosts: webserver
remote_user: root
tasks:
- name: shutdown
command: /sbin/shutdown -r now
when: #当系统版本为CentOS7时,关闭webserver主机
- ansible_distribution == "CentOS"
- ansible_distribution_major_version == "7"
组条件判断
[root@server1 opt]# vi when.yaml
- hosts: webserver
remote_user: root
tasks:
- name: shutdown CentOS6 and CentOS7
command: /sbin/shutdown -r now
when: #当系统版本为CentOS7时,关闭webserver主机
- ansible_distribution == "CentOS" and ansible_distribution_major_version == "6"
- ansible_distribution == "Debian" and ansible_distribution_major_version == "7"
迭代
当有需要重复性执行的任务时,可以使用迭代机制。其使用格式为item变量引用,并通过with_items语句指明迭代的元素
[root@server1 opt]# vi demo.yaml
hosts: mysql
remote_user: root
tasks:
- name: install
yum: name={{ item }} state=latest
with_items:
- httpd
- php
- tree
[root@server1 opt]# ansible-playbook demo.yaml
PLAY [mysql] ********************************************************************
TASK [Gathering Facts] **********************************************************
ok: [20.0.0.13]
TASK [install] ******************************************************************
[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', 'php']` 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: [20.0.0.13] => (item=[u'httpd', u'php'])
PLAY RECAP **********************************************************************
20.0.0.13 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
也可以自己定义
[root@server1 ~]# vim demo.yaml
- hosts: mysql
remote_user: root
tasks:
- name: useradd
user: name={{item.name}} state=present group={{item.groups}}
with_items:
- { name: test1, groups: wheel}
- { name: test2, groups: root}
[root@server1 opt]# ansible-playbook demo.yaml
PLAY [mysql] ********************************************************************
TASK [Gathering Facts] **********************************************************
ok: [20.0.0.13]
TASK [useradd] ******************************************************************
changed: [20.0.0.13] => (item={u'name': u'test1', u'groups': u'wheel'})
changed: [20.0.0.13] => (item={u'name': u'test2', u'groups': u'root'})
PLAY RECAP **********************************************************************
20.0.0.13 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@server1 opt]# ansible mysql -a " tail /etc/passwd"
20.0.0.13 | CHANGED | rc=0 >>
…………………………
test1:x:1002:10::/home/test1:/bin/bash
test2:x:1003:0::/home/test2:/bin/bash