Ansible Playbook-熟练运用ansible(二)

发布于:2022-12-13 ⋅ 阅读:(424) ⋅ 点赞:(0)

Ansible Playbook-熟练运用ansible(二)

在这里插入图片描述

Playbook概述

  • Ansible ad-hoc可以通过命令行形式远程管理其他主机
  • 适合执行一些临时性简单的任务
  • Ansible playbook中文名称叫剧本
  • 将经常需要执行的任务写入一个文件(剧本)
  • 剧本写后,我们随时根据剧本,执行相关的任务命令
  • playbook剧本要求按照YAML格式编写
  • 适合执行周期性经常执行的复杂任务

YAML简介

YAML(YAML Ain‘ t a Markup Language)是一个可读性高、用来表达数据序列的格式语言,YAML以数据为中心,重点描述数据的关系和结构。

YAML格式

  • "#"代表注释,一般第一行为三个横杠
  • 键值对使用“:”表示,数组使用 “-” 表示
  • 一般缩进由两个或以上空格组成
  • 相同层级的缩进必须对齐
  • 全文不可以使用tab键
  • 区分大小写、扩展名为yml或者yaml
  • 跨行数据需要使用>或者| (| 会保留换行符)

YAML格式(一)

  • YAML格式的键值对数据
    • key和value之间使用“:”分隔
    • “:”后面必须有空格
    • 缩进代表层级关系
      在这里插入图片描述

YAML格式(二)

  • YAML格式的数组数据
    • 使用短横杠和空格表示,一行表示数据格式[值,值,值. . .]
纯数组例子:
- 西瓜
- 苹果
- 香蕉
- 桃子

键值对和数组符合例子:
家人:
    - 母亲
    - 父亲
    - 哥哥
    
或者:
家人:[母亲,父亲,哥哥]

YAML格式(三)

  • 综合示例
 - 饭店:
    - 汤类      
        - 蘑菇扽鸡汤
        - 莲藕扽筒骨
          
    - 菜肴
        - 辣子鸡丁
        - 豆瓣鲫鱼

YAML格式(四)

  • 综合示例(注意要空格)
---  #喜欢的电影          ---   #人物描述
 - 功夫                    - 姓名:小红
 - 战狼                      年龄:28
 - 赌圣                      简介:活泼可爱
                            朋友:小明

YAML格式(五)

  • 综合示例(| 会保留换行符)
---   #跨行文本(计算机理解为一行)         ---   #跨行文本(计算机理解为多行)
个人简介:|                                 个人简介:>
      活泼可爱                                    活泼可爱
      青春明媚                                    青春明媚

YAML格式(六)

  • 综合示例(注意-和:后面一定要空格)
---   #一张发票
发票编号:33333
日期: 2022-10-19
商品:
    - 商品编号:JK999U
      描述:篮球鞋
      价格: 230
    - 商品编号:JK3773P
      描述:篮球
      价格:100
税费:12.00
总价:342.00
备注: >
    本次采购商品
    属于运动商品

Playbook剧本

  • 常用于复杂任务的管理,以及管理经常要完成的任务
  • playbook也是通过模块和它的参数,在特定主机上执行任务
  • playbook是一个文件,该文件中需要通过yaml格式进行书写

在写剧本之前设置一下vim方便写yaml语法

# 文件位置和名字是固定的,用于设置vim的格式
[root@control ansible]# vim ~/.vimrc
set ai        # 设置自动缩进
set ts=2      # 设置按tab键,缩进2个空格
set et        # 将tab转换成相应个数的空格

编写playbook

  • 一个剧本(即playbook),可以包含多个play
  • 每个play用于在指定的主机上,通过模块和参数执行相应的任务
  • 每个play可以包含多个任务。
  • 任务有模块和参数构成。
# 编写用于测试连通性的playbook,相当于执行ansible all -m ping
[root@control ansible]# vim test.yml
---
- hosts: all
  tasks:
    - ping:

[root@control ansible]# ansible-playbook test.yml  # 执行playbook

# 以上更规范的写法如下:
[root@control ansible]# vim test.yml
---
- name: test network    # play的名字,可选项
  hosts: all            # 作用于所有的主机
  tasks:                # 任务
    - name: task 1      # 第1个任务的名字,可选项
      ping:             # 第1个任务使用的模块

[root@control ansible]# ansible-playbook test.yml  # 执行playbook


# 在test组的主机和baikongji2上创建/tmp/demo目录,权限是0755。将控制端/etc/hosts拷贝到目标主机的/tmp/demo中
[root@control ansible]# vim fileop.yml
---
- name: create dir and copy file
  hosts: test,beikongji2    # 这里的名称,必须出现在主机清单文件中
  tasks:
    - name: create dir
      file:
        path: /tmp/demo
        state: directory
        mode: '0755'
    
    - name: copy file
      copy:
        src: /etc/hosts
        dest: /tmp/demo/hosts

# 执行playbook
[root@control ansible]# ansible-playbook fileop.yml


# 在test组中的主机上,创建用户bob,附加组是adm;在beikongji2主机上,创建/tmp/hi.txt,其内容为Hello World.
[root@control ansible]# vim two.yml
---
- name: create user
  hosts: test
  tasks:
    - name: create bob
      user:
        name: bob
        groups: adm

- name: create file
  hosts: node2
  tasks:
    - name: make file
      copy:
        dest: /tmp/hi.txt
        content: "Hello World"

[root@control ansible]# ansible-playbook two.yml
  • |>的区别:|它保留换行符,>把多行合并为一行
# 通过copy模块创建/tmp/1.txt,文件中有两行内容,分别是Hello World和ni hao
[root@control ansible]# vim f1.yml
---
- name: play 1
  hosts: test
  tasks:
    - name: mkfile 1.txt
      copy:
        dest: /tmp/1.txt
        content: |
          Hello World!
          ni hao.

[root@control ansible]# ansible-playbook f1.yml
# 查看结果
[root@beikongji1 ~]# cat /tmp/1.txt 
Hello World!
ni hao.


# 通过copy模块创建/tmp/2.txt,文件中有一行内容,分别是Hello World! ni hao
[root@control ansible]# vim f2.yml 
---
- name: play 1
  hosts: test
  tasks:
    - name: mkfile 2.txt
      copy:
        dest: /tmp/2.txt
        content: >
          Hello World!
          ni hao.

[root@control ansible]# ansible-playbook f2.yml
[root@beikongji1 ~]# cat /tmp/2.txt 
Hello World! ni hao.
  • playbook示例
# 在test组中的主机上创建john用户,它的uid是1040,主组是daemon,密码为123
[root@control ansible]# vim user_john.yml
---
- name: create user
  hosts: test
  tasks:
    - name: create user john
      user:
        name: john
        uid: 1040
        group: daemon
        password: "{{'123'|password_hash('sha512')}}"
[root@control ansible]# ansible-playbook user_john.yml

# 在test组中的主机上删除用户john
[root@control ansible]# vim del_john.yml
---
- name: delete user
  hosts: test
  tasks:
    - name: delete user john
      user:
        name: john
        state: absent
[root@control ansible]# ansible-playbook del_john.yml

硬盘管理

  • 常用的分区表类型有:MBR(主引导记录)、GPT(GUID分区表)
  • MBR最多支持4个主分区,或3个主分区加1个扩展分区。最大支持2.2TB左右的硬盘
  • GPT最多支持128个主分区。支持大硬盘

parted模块

  • 用于硬盘分区管理
  • 常用选项:
    • device:待分区的设备
    • number:分区编号
    • state:present表示创建,absent表示删除
    • part_start:分区的起始位置,不写表示从开头
    • part_end:表示分区的结束位置,不写表示到结尾
# 在test组中的主机上,对/dev/vdc进行分区,创建1个1GB的主分区
[root@control ansible]# vim disk.yml
---
- name: disk manage
  hosts: test
  tasks:
    - name: create a partition
      parted:
        device: /dev/vdc
        number: 1
        state: present
        part_end: 1GiB

[root@control ansible]# ansible-playbook disk.yml

# 在目标主机上查看结果
[root@beikongji1 ~]# lsblk 
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
.. ...
vdc    253:32   0   20G  0 disk 
`-vdc1 253:33   0 1023M  0 part 

# 继续编辑disk.yml,对/dev/vdc进行分区,创建1个新的5GB的主分区
[root@control ansible]# vim disk.yml 
... ...
    - name: add a new partition
      parted:
        device: /dev/vdc
        number: 2
        state: present
        part_start: 1GiB
        part_end: 6GiB

[root@control ansible]# ansible-playbook disk.yml 
[root@beikongji1 ~]# lsblk 
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
... ...
vdc    253:32   0   20G  0 disk 
|-vdc1 253:33   0 1023M  0 part 
`-vdc2 253:34   0    5G  0 part 

# 继续编辑disk.yml,创建名为my_vg的卷组,它由上面创建的vdc1和vdc2构成
[root@control ansible]# vim disk.yml 
... ...
    - name: create my_vg
      lvg:
        vg: my_vg
        pvs: /dev/vdc1,/dev/vdc2

# 继续编辑disk.yml,在my_vg卷组上创建名为my_lv的逻辑卷,大小1G
[root@control ansible]# vim disk.yml 
... ...
    - name: create my_lv
      lvol:
        vg: my_vg
        lv: my_lv
        size: 1G


# 继续编辑disk.yml,格式化my_lv为ext4
[root@control ansible]# vim disk.yml 
... ...
    - name: mkfs my_lv
      filesystem:
        dev: /dev/my_vg/my_lv
        fstype: ext4


# 继续编辑disk.yml,将my_lv挂载到/data
[root@control ansible]# vim disk.yml 
... ...
    - name: mount my_lv
      mount:
        path: /data
        src: /dev/my_vg/my_lv
        fstype: ext4
        state: mounted

# 完整的disk.yml如下
---
 - name: disk manage
  hosts: test
  tasks:
    - name: create a partition
      parted:
        device: /dev/vdc
        number: 1
        state: present
        part_end: 1GiB

    - name: add a new partition
      parted:
        device: /dev/vdc
        number: 2
        state: present
        part_start: 1GiB
        part_end: 6GiB

    - name: create my_vg
      lvg:
        vg: my_vg
        pvs: /dev/vdc1,/dev/vdc2

    - name: create my_lv
      lvol:
        vg: my_vg
        lv: my_lv
        size: 1G
        
    - name: mkfs my_lv
      filesystem:
        dev: /dev/my_vg/my_lv
        fstype: ext4

    - name: mount my_lv
      mount:
        path: /data
        src: /dev/my_vg/my_lv
        fstype: ext4
        state: mounted

特殊模块

setup模块

  • ansible_facts用于采集被管理设备的系统信息

  • 所有收集的信息都被保存在变量中

  • 每次执行playbook默认第一个任务就是Gathering Facts

  • 使用setup模块可以查看收集到的facts信息

[root@control ansible]# ansible test  -m setup
beikongji1 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.88.11",
            "192.168.122.1"
         ***省略下面内容***

debug模块

  • debug模块可以显示变量的值,可以辅助排错
[root@control ansible]# cat debug.yml 
---
- hosts: test
  tasks:
    - debug:
        msg: "主机名是:{{ansible_hostname}}"
    - debug:
        msg: "总内存大小:{{ansible_memtotal_mb}}"

[root@control ansible]# ansible-playbook debug.yml 

PLAY [test] ***************************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [beikongji1]

TASK [debug] **************************************************************************
ok: [beikongji1] => {
    "msg": "主机名是:beikongji1"
}

TASK [debug] **************************************************************************
ok: [beikongji1] => {
    "msg": "总内存大小:792"
}

PLAY RECAP ****************************************************************************
beikongji1                 : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


Ansible变量

facts变量

  • facts翻译过来就是事实。
  • facts变量是ansible自带的预定义变量,用于描述被控端软硬件信息。
  • facts变量通过setup模块获得。
# 通过setup模块查看所有facts变量
[root@control ansible]# ansible test -m setup
  • facts变量是一个大的由{}构成的键值对字典。在{}中,有很多层级的嵌套。可以通过参数过滤出第一个层级的内容。
# 查看所有的IPV4地址,filter是过滤的意思
[root@control ansible]# ansible test -m setup -a "filter=ansible_all_ipv4_addresses"

# 查看可用内存
[root@control ansible]# ansible test -m setup -a "filter=ansible_memfree_mb"
  • 常用的facts变量

    • ansible_all_ipv4_addresses:所有的IPV4地址
    • ansible_bios_version:BIOS版本信息
    • ansible_memtotal_mb:总内存大小
    • ansible_hostname:主机名
  • 在playbook中使用变量

# 显示远程主机的主机名和内存大小。在ansible中,变量使用{{}}表示
# debug模块用于输出信息,常用的参数是msg,用于输出指定内容
[root@control ansible]# vim debug.yml
---
- name: display host info
  hosts: test
  tasks:
    - name: display hostname and memory
      debug:    # debug是模块,它的选项msg可以输出指定信息
        msg: "hostname: {{ansible_hostname}}; mem: {{ansible_memtotal_mb}} MB"

[root@control ansible]# ansible-playbook debug.yml

自定义变量

  • 引入变量,可以方便Playbook重用。比如装包的playbook,包名使用变量。多次执行playbook,只要改变变量名即可,不用编写新的playbook。
  • ansible支持10种以上的变量定义方式。常用的变量来源如下:
    • inventory变量。变量来自于主机清单文件
    • facts变量。
    • playbook变量。变量在playbook中定义。
    • 变量文件。专门创建用于保存变量的文件。推荐变量写入单独的文件。
# 使用inventory变量。
[root@control ansible]# vim hosts
[test]
beikongji1 iname="nb"     # 主机变量定义的方法。iname是自定义名称

[proxy]
beikongji2

[webserver]
beikongji[3:4]

[database]
beikongji5

[cluster:children]
webserver
database

[webserver:vars]       # 组变量定义方法。:vars是固定格式
iname="dachui"

# 通过变量创建用户
[root@control ansible]# vim var1.yml
---
- name: test create user
  hosts: test
  tasks:
    - name: create user
      user:
        name: "{{iname}}"
        state: present
        
- name: create user in webserver
  hosts: webserver
  tasks:
    - name: create some users
      user:
        name: "{{iname}}"
        state: present

[root@control ansible]# ansible-playbook var1.yml

# 上述两个play也可以合并为一个,如下:
[root@control ansible]# vim var1.yml
---
- name: test create user
  hosts: test,webserver    # 指定执行的目标是test组和webserver组
  tasks:
    - name: create user
      user:
        name: "{{iname}}"
        state: present
        

# 在playbook中定义变量
# 在test组中的主机上创建用户jack,他的密码是123456
[root@control ansible]# vim user_jack.yml
---
- name: create user
  hosts: test
  vars:    # 固定格式,用于声明变量
    username: "jack"    # 此处引号可有可无
    mima: "123456"      # 此处引号是需要的,表示数字字符
  tasks:
    - name: create some users
      user:
        name: "{{username}}"   # {}出现在开头,必须有引号
        state: present
        password: "{{mima|password_hash('sha512')}}"

[root@control ansible]# ansible-playbook user_jack.yml


# 将变量定义在文件中
[root@control ansible]# vim vars.yml   # 文件名自定义
---
yonghu: rose
mima: abcd
[root@control ansible]# vim user_rose.yml 
---
- name: create user
  hosts: test
  vars_files: vars.yml   # vars_files用于声明变量文件
  tasks:
    - name: create some users
      user:
        name: "{{yonghu}}"   # 这里的变量来自于vars.yml
        state: present
        password: "{{mima|password_hash('sha512')}}"

[root@control ansible]# ansible-playbook user_rose.yml 

template模块

  • copy模块可以上传文件,但是文件内容固定
  • template模块可以上传具有特定格式的文件(如文件中包含变量)
  • 当远程主机接收到文件之后,文件中的变量将会变成具体的值
  • template模块上传的文件,使用的语法叫Jinja2。
  • 常用选项:
    • src:要上传的文件
    • dest:目标文件路径
# 使用template模块将含有变量的文件上传到test组中的主机
[root@control ansible]# vim index.j2
Welcome to {{ansible_hostname}} on {{ansible_eth0.ipv4.address}}

[root@control ansible]# vim templ.yml
---
- name: upload index
  hosts: test
  tasks:
    - name: create web index
      template:
        src: index.j2
        dest: /var/www/html/index.html

[root@control ansible]# ansible-playbook templ.yml
[root@control ansible]# curl http://192.168.88.11/
Welcome to node1 on 192.168.88.11
[root@beikongji1 ~]# cat /var/www/html/index.html 
Welcome to node1 on 192.168.88.11

进阶语法

错误处理

  • 当Playbook中包含很多任务时,当某一个任务遇到错误,它将崩溃,终止执行
# 在test组中的主机上启动mysqld服务,然后创建/tmp/service.txt
# 因为目标主机上没有mysqld服务,所以它将崩溃,终止执行。即,不会创建/tmp/service.txt文件
[root@control ansible]# vim myerr.yml
---
- name: my errors
  hosts: test
  tasks:
    - name: start mysqld service  # 通过service模块启动mysqld服务
      service:
        name: mysqld
        state: started
        enabled: yes
        
    - name: touch a file   # 通过file模块创建文件
      file:
        path: /tmp/service.txt
        state: touch

# 执行playbook,第1个任务就会失败
[root@control ansible]# ansible-playbook myerr.yml
# 到beikongji1上查看,因为第2个任务没有执行,所以文件不会创建
[root@beikongji1 ~]# ls /tmp/service.txt
ls: cannot access '/tmp/service.txt': No such file or directory
  • 可以指定某一个任务如果出现错误,则忽略它
# 编辑myerr.yml,如果myslqd服务无法启动,则忽略它
[root@control ansible]# vim myerr.yml
---
- name: my errors
  hosts: test
  tasks:
    - name: start mysqld service
      service:
        name: mysqld
        state: started
        enabled: yes
      ignore_errors: yes    # 即使这个任务失败了,也要继续执行下去

    - name: touch a file
      file:
        path: /tmp/service.txt
        state: touch

[root@control ansible]# ansible-playbook myerr.yml
[root@beikongji1 ~]# ls /tmp/service.txt   # 第2个任务已执行
/tmp/service.txt
  • 通过全局设置,无论哪个任务出现问题,都要忽略
[root@control ansible]# vim myerr.yml
---
- name: my errors
  hosts: test
  ignore_errors: yes
  tasks:
    - name: start mysqld service
      service:
        name: mysqld
        state: started
        enabled: yes

    - name: touch a file
      file:
        path: /tmp/mysql.txt
        state: touch

[root@control ansible]# ansible-playbook myerr.yml
[root@beikongji1 ~]# ls /tmp/mysql.txt 
/tmp/mysql.txt

触发执行任务

  • 通过handlers定义触发执行的任务
  • handlers中定义的任务,不是一定会执行的
  • 在tasks中定义的任务,通过notify关键通知handlers中的哪个任务要执行
  • 只有tasks中的任务状态是changed才会进行通知。
# 下载beikongji1上的/etc/httpd/conf/httpd.conf
[root@control ansible]# vim get_conf.yml
---
- name: download httpd.conf
  hosts: test
  tasks:
    - name: get httpd.conf
      fetch:
        src: /etc/httpd/conf/httpd.conf
        dest: ./
        flat: yes    # 直接下载文件,不要目录
[root@control ansible]# ansible-playbook get_conf.yml

# 修改httpd.conf的端口为变量
[root@control ansible]# vim +45 httpd.conf
... ...
Listen {{http_port}}
... ...

# 修改httpd服务的端口为8000,重启httpd
[root@control ansible]# vim trigger.yml
---
- name: configure httpd
  hosts: test
  vars:
    http_port: "8000"   # 定义httpd.conf中的变量和值
  tasks:
    - name: upload httpd.conf   # 上传httpd.conf
      template:
        src: ./httpd.conf
        dest: /etc/httpd/conf/httpd.conf

    - name: restart httpd    # 重启服务
      service:
        name: httpd
        state: restarted
# 第一次执行trigger.yml,上传文件和重启服务两个任务的状态都是黄色changed
[root@control ansible]# ansible-playbook trigger.yml
# 第二次执行trigger.yml,上传文件的任务状态是绿色的ok,重启服务任务的状态是黄色changed
[root@control ansible]# ansible-playbook trigger.yml

# 既然配置文件没有改变,那么服务就不应该重启
# 修改Playbook,只有配置文件变化了,才重启服务
[root@control ansible]# vim trigger.yml
---
- name: configure httpd
  hosts: test
  vars:
    http_port: "80"
  tasks:
    - name: upload httpd.conf
      template:
        src: ./httpd.conf
        dest: /etc/httpd/conf/httpd.conf
      notify: restart httpd   # 通知restart httpd需要执行

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted
# 第一次运行Playbook,因为第1个任务是黄色的changed,所以handlers中的任务也被触发执行
[root@control ansible]# ansible-playbook trigger.yml 
# 第二次运行Playbook,因为第1个任务是绿色的OK,也就不会再触发执行其他任务了
[root@control ansible]# ansible-playbook trigger.yml 

when条件

  • 只有满足某一条件时,才执行任务

  • 常用的操作符:

    • ==:相等
    • !=:不等
    • >:大于
    • <:小于
    • <=:小于等于
    • >=:大于等于
  • 多个条件或以使用and或or进行连接

  • when表达式中的变量,可以不使用`{{}}

# 当test组中的主机内存大于2G的时候,才安装mariadb-server
[root@control ansible]# vim when1.yml
---
- name: install mariadb
  hosts: test
  tasks:
    - name: install mariadb pkg
      yum:
        name: mariadb-server
        state: present
      when: ansible_memtotal_mb>2048

# 如果目标主机没有2GB内存,则不会安装mariadb-server
[root@control ansible]# ansible-playbook when1.yml



# 多条件。系统发行版是RedHat8才执行任务
# /etc/motd中的内容,将会在用户登陆时显示在屏幕上
[root@control ansible]# vim motd
 _____________
< hello world >
 -------------
     .=""=.
    / _  _ \
   |  d  b  |          
   \   /\   /                
  ,/'-=\/=-'\,       
 / /        \ \  
| /          \ | 
\/ \        / \/ 
    '.    .'     
    _|`~~`|_     
    /|\  /|\
————————————————   
[root@control ansible]# vim when2.yml
---
- name: when condition
  hosts: test
  tasks:
    - name: modify /etc/motd
      copy:
        dest: /etc/motd
        src: motd
      when: >     # 以下三行合并成一行
        ansible_distribution == "RedHat"
        and
        ansible_distribution_major_version == "8"

[root@control ansible]# ansible-playbook when2.yml