Bootstrap

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
;