第十七节

ansible实战

创建和使用逻辑卷

创建一个能批量、自动管理逻辑卷设备的剧本,不但能大大提高硬盘设备的管理效率,而且还能避免手动创建带来的错误。例如,我们想在每台受管主机上都创建出一个名为data的逻辑卷设备,大小为150MB,归属于research卷组。如果创建成功,则进一步用Ext4文件系统进行格式化操作;如果创建失败,则给用户输出一条报错提醒,以便排查原因。

在这种情况下,使用Ansible剧本要比使用Shell脚本的优势大,原因主要有下面两点。

Ansible模块化的功能让操作更标准,只要在执行过程中无报错,那么便会依据远程主机的系统版本及配置自动做出判断和操作,不用担心因系统变化而导致命令失效的问题。

Ansible服务在执行剧本文件时会进行判断:如果该文件或该设备已经被创建过,或是某个动作(play)已经被执行过,则绝对不会再重复执行;而使用Shell脚本有可能导致设备被重复格式化,导致数据丢失。

通过回忆学习过的逻辑卷的知识,我们应该让剧本文件依次创建物理卷(PV)、卷组(VG)及逻辑卷(LV)。需要先使用lvg模块让设备支持逻辑卷技术,然后创建一个名为research的卷组。lvg模块的帮助信息如下:

[root@linuxprobe ~]# ansible-doc lvg
> LVG    (/usr/lib/python3.6/site-packages/ansible/modules/system/lvg.py)

        This module creates, removes or resizes volume groups.

  * This module is maintained by The Ansible Community

………………省略部分输出信息………………

EXAMPLES:

- name: Create a volume group on top of /dev/sda1 with physical extent size = 3>
  lvg:
    vg: vg.services
    pvs: /dev/sda1
    pesize: 32

- name: Create a volume group on top of /dev/sdb with physical extent size = 12>
  lvg:
    vg: vg.services
    pvs: /dev/sdb
    pesize: 128K

通过输出信息可得知,创建PV和VG的lvg模块总共有3个必备参数。其中,vg参数用于定义卷组的名称,pvs参数用于指定硬盘设备的名称,pesize参数用于确定最终卷组的容量大小(可以用PE个数或容量值进行指定)。这样一来,我们先创建出一个由/dev/sdb设备组成的名称为research、大小为150MB的卷组设备。

[root@linuxprobe ~]# vim lv.yml
---
- name: 创建和使用逻辑卷
  hosts: all
  tasks:
          - name: one
            lvg:
                    vg: research
                    pvs: /dev/sdb
                    pesize: 150M

由于刚才只在prod组的两台主机上添加了新硬盘设备文件,因此在执行上述操作时其余3台主机会提示未创建成功,这属于正常情况。接下来使用lvol模块创建出逻辑卷设备。还是按照惯例,先查看模块的帮助信息:

[root@linuxprobe ~]# ansible-doc lvol
> LVOL    (/usr/lib/python3.6/site-packages/ansible/modules/system/lvol.py)

        This module creates, removes or resizes logical volumes.

  * This module is maintained by The Ansible Community

………………省略部分输出信息………………

EXAMPLES:

- name: Create a logical volume of 512m
  lvol:
    vg: firefly
    lv: test
    size: 512

- name: Create a logical volume of 512m with disks /dev/sda and /dev/sdb
  lvol:
    vg: firefly
    lv: test
    size: 512
    pvs: /dev/sda,/dev/sdb

通过输出信息可得知,lvol是用于创建逻辑卷设备的模块。其中,vg参数用于指定卷组名称,lv参数用于指定逻辑卷名称,size参数则用于指定最终逻辑卷设备的容量大小(不用加单位,默认为MB)。填写好参数,创建出一个大小为150MB、归属于research卷组且名称为data的逻辑卷设备:

[root@linuxprobe ~]# vim lv.yml
---
- name: 创建和使用逻辑卷
  hosts: all
  tasks:
          - name: one
            lvg:
                    vg: research
                    pvs: /dev/sdb
                    pesize: 150M
          - name: two
            lvol:
                    vg: research
                    lv: data
                    size: 150M

这样还不够好,如果还能将创建出的/dev/research/data逻辑卷设备自动用Ext4文件系统进行格式化操作,则又能帮助运维管理员减少一些工作量。可使用filesystem模块来完成设备的文件系统格式化操作。该模块的帮助信息如下:

[root@linuxprobe ~]# ansible-doc filesystem
> FILESYSTEM    (/usr/lib/python3.6/site-packages/ansible/modules/system/filesy>

        This module creates a filesystem.

  * This module is maintained by The Ansible Community

………………省略部分输出信息………………

EXAMPLES:

- name: Create a ext2 filesystem on /dev/sdb1
  filesystem:
    fstype: ext2
    dev: /dev/sdb1

filesystem模块的参数真是简练,fstype参数用于指定文件系统的格式化类型,dev参数用于指定要格式化的设备文件路径。继续编写:

[root@linuxprobe ~]# vim lv.yml
---
- name: 创建和使用逻辑卷
  hosts: all
  tasks:
          - name: one
            lvg:
                    vg: research
                    pvs: /dev/sdb
                    pesize: 150M
          - name: two
            lvol:
                    vg: research
                    lv: data
                    size: 150M
          - name: three
            filesystem:
                    fstype: ext4
                    dev: /dev/research/data 

这样按照顺序执行下来,逻辑卷设备就能够自动创建好了。等一下,还有个问题没有解决。现在只有prod组的主机上添加了新的硬盘设备文件,其余主机是无法按照既定模块顺利完成操作的。这时就要使用类似于if条件语句的方式进行判断—如果失败……,则……。

首先用block操作符将上述的3个模块命令作为一个整体(相当于对这3个模块的执行结果作为一个整体进行判断),然后使用rescue操作符进行救援,且只有block块中的模块执行失败后才会调用rescue中的救援模块。其中,debug模块的msg参数的作用是,如果block中的模块执行失败,则输出一条信息到屏幕,用于提醒用户。完成编写后的剧本是下面这个样子:

[root@linuxprobe ~]# vim lv.yml
---
- name: 创建和使用逻辑卷
  hosts: all
  tasks:
          - block:
                  - name: one
                    lvg:
                            vg: research
                            pvs: /dev/sdb
                            pesize: 150M
                  - name: two
                    lvol:
                            vg: research
                            lv: data
                            size: 150M
                  - name: three
                    filesystem:
                            fstype: ext4
                            dev: /dev/research/data
            rescue:
                    - debug:
                            msg: "Could not create logical volume of that size"

YAML语言对格式有着硬性的要求,既然rescue是对block内的模块进行救援的功能代码,因此recue和block两个操作符必须严格对齐,错开一个空格都会导致剧本执行失败。确认无误后,执行lv.yml剧本文件检阅一下效果:

[root@linuxprobe ~]# ansible-playbook lv.yml 

PLAY [创建和使用逻辑卷] *********************************************************

TASK [Gathering Facts] *********************************************************
ok: [192.168.10.20]
ok: [192.168.10.21]
ok: [192.168.10.22]
ok: [192.168.10.23]
ok: [192.168.10.24]

TASK [one] *********************************************************************
fatal: [192.168.10.20]: FAILED! => {"changed": false, "msg": "Device /dev/sdb not found."}
fatal: [192.168.10.21]: FAILED! => {"changed": false, "msg": "Device /dev/sdb not found."}
changed: [192.168.10.22]
changed: [192.168.10.23]
fatal: [192.168.10.24]: FAILED! => {"changed": false, "msg": "Device /dev/sdb not found."}

TASK [two] *********************************************************************
changed: [192.168.10.22]
changed: [192.168.10.23]

TASK [three] *********************************************************************
changed: [192.168.10.22]
changed: [192.168.10.23]

TASK [debug] *******************************************************************
ok: [192.168.10.20] => {
    "msg": "Could not create logical volume of that size"
}
ok: [192.168.10.21] => {
    "msg": "Could not create logical volume of that size"
}
ok: [192.168.10.24] => {
    "msg": "Could not create logical volume of that size"
}

PLAY RECAP *********************************************************************
192.168.10.20  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=1  ignored=0   
192.168.10.21  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=1  ignored=0   
192.168.10.22  : ok=4  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
192.168.10.23  : ok=4  changed=3  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
192.168.10.24  : ok=2  changed=0  unreachable=0  failed=0  skipped=0  rescued=1  ignored=0 

在剧本运行完毕后的执行记录(PLAY RECAP)中可以很清晰地看到只有192.168.10.22及192.168.10.23这两台prod组中的主机执行成功了,其余3台主机均触发了rescue功能。登录到任意一台prod组的主机上,找到新建的逻辑卷设备信息:

[root@linuxprobe ~]# lvdisplay 
  --- Logical volume ---
  LV Path                /dev/research/data
  LV Name                data
  VG Name                research
  LV UUID                EOUliC-tbkk-kOJR-8NaH-O9XQ-ijrK-TgEYGj
  LV Write Access        read/write
  LV Creation host, time linuxprobe.com, 2021-04-23 11:00:21 +0800
  LV Status              available
  # open                 0
  LV Size                5.00 GiB
  Current LE             1
  Segments               1
  Allocation             inherit
  Read ahead sectors     auto
  - currently set to     8192
  Block device           253:2
………………省略部分输出信息………………

判断主机组名

在上面的剧本实验中,我们可以让不同的主机根据自身不同的变量信息而生成出独特的网站主页文件,但却无法对某个主机组进行针对性的操作。其实,在每个客户端中都会有一个名为inventory_hostname的变量,用于定义每台主机所对应的Ansible服务的主机组名称,也就是/etc/ansible/hosts文件中所对应的分组信息,例如dev、test、prod、balancers。

inventory_hostname是Ansible服务中的魔法变量,这意味着无法使用setup模块直接进行查询,诸如ansible all -m setup -a 'filter="*关键词*"'这样的命令将对它失效。魔法变量需要在执行剧本文件时的Gathering Facts阶段进行搜集,直接查询是看不到的,只能在剧本文件中进行调用。

在获得了存储主机组名称的变量名称后,接下来开始实战。这里的需求如下:

若主机在dev分组中,则修改/etc/issue文件内容为Development;

若主机在test分组中,则修改/etc/issue文件内容为Test;

若主机在prod分组中,则修改/etc/issue文件内容为Production。

copy模块的主要作用是新建、修改及复制文件,更符合当前的需要,此时便派上了用场。先查询copy模块的帮助信息:

[root@linuxprobe ~]# ansible-doc copy
> COPY    (/usr/lib/python3.6/site-packages/ansible/modules/files/copy.py)

        The `copy' module copies a file from the local or remote
        machine to a location on the remote machine. Use the [fetch]
        module to copy files from remote locations to the local box.
        If you need variable interpolation in copied files, use the
        [template] module. Using a variable in the `content' field
        will result in unpredictable output. For Windows targets, use
        the [win_copy] module instead.

  * This module is maintained by The Ansible Core Team
  * note: This module has a corresponding action plugin.

………………省略部分输出信息………………

EXAMPLES:

- name: Copy file with owner and permissions
  copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: '0644'

- name: Copy using inline content
  copy:
    content: '# This file was moved to /etc/other.conf'
    dest: /etc/mine.conf

在输出信息中列举了两种管理文件内容的示例。第一种用于文件的复制行为,第二种是通过content参数定义内容,通过dest参数指定新建文件的名称。显然,第二种更加符合当前的实验场景。编写剧本文件如下:

[root@linuxprobe ~]# vim issue.yml
---
- name: 修改文件内容
  hosts: all
  tasks:
          - name: one
            copy:
                    content: 'Development'
                    dest: /etc/issue
          - name: two
            copy:
                    content: 'Test'
                    dest: /etc/issue
          - name: three
            copy:
                    content: 'Production'
                    dest: /etc/issue

但是,如果按照这种顺序执行下去,每一台主机的/etc/issue文件都会被重复修改3次,最终定格在“Production”字样,这显然缺少了一些东西。我们应该依据inventory_hostname变量中的值进行判断。若主机为dev组,则执行第一个动作;若主机为test组,则执行第二个动作;若主机为prod组,则执行第三个动作。因此,要进行3次判断。

when是用于判断的语法,我们将其用在每个动作的下方进行判断,使得只有在满足条件才会执行:

[root@linuxprobe ~]# vim issue.yml
---
- name: 修改文件内容
  hosts: all
  tasks:
          - name: one
            copy:
                    content: 'Development'
                    dest: /etc/issue
            when: "inventory_hostname in groups.dev"
          - name: two
            copy:
                    content: 'Test'
                    dest: /etc/issue
            when: "inventory_hostname in groups.test"
          - name: three
            copy:
                    content: 'Production'
                    dest: /etc/issue
            when: "inventory_hostname in groups.prod"

执行剧本文件,在过程中可清晰地看到由于when语法的作用,未在指定主机组中的主机将被跳过(skipping):

[root@linuxprobe ~]# ansible-playbook issue.yml 

PLAY [修改文件内容] ************************************************************************

TASK [Gathering Facts] ********************************************************************
ok: [192.168.10.20]
ok: [192.168.10.21]
ok: [192.168.10.22]
ok: [192.168.10.23]
ok: [192.168.10.24]

TASK [one] ********************************************************************************
changed: [192.168.10.20]
skipping: [192.168.10.21]
skipping: [192.168.10.22]
skipping: [192.168.10.23] 
skipping: [192.168.10.24]

TASK [two] ********************************************************************************
skipping: [192.168.10.20]
changed: [192.168.10.21]
skipping: [192.168.10.23]
skipping: [192.168.10.24]
skipping: [192.168.10.25]

TASK [three] ******************************************************************************
skipping: [192.168.10.20]
skipping: [192.168.10.21]
changed: [192.168.10.22]
changed: [192.168.10.23]
skipping: [192.168.10.24]

PLAY RECAP ********************************************************************************
192.168.10.20   : ok=2  changed=1  unreachable=0  failed=0  skipped=2  rescued=0  ignored=0   
192.168.10.21   : ok=2  changed=1  unreachable=0  failed=0  skipped=2  rescued=0  ignored=0   
192.168.10.22   : ok=2  changed=1  unreachable=0  failed=0  skipped=2  rescued=0  ignored=0   
192.168.10.23   : ok=2  changed=1  unreachable=0  failed=0  skipped=2  rescued=0  ignored=0 
192.168.10.24   : ok=1  changed=0  unreachable=0  failed=0  skipped=3  rescued=0  ignored=0 

登录到dev组的192.168.10.20主机上,查看文件内容:

[root@linuxprobe ~]# cat /etc/issue 
Development

登录到test组的192.168.10.21主机上,查看文件内容:

[root@linuxprobe ~]# cat /etc/issue 
Test

登录到prod组的192.168.10.22/23主机上,查看文件内容:

[root@linuxprobe ~]# cat /etc/issue 
Production

管理文件属性

我们学习剧本的目的是为了满足日常的工作需求,把重复的事情写入到脚本中,然后再批量执行下去,从而提高运维工作的效率。其中,创建文件、管理权限以及设置快捷方式几乎是每天都用到的技能。尤其是文件的一般权限、特殊权限、隐藏权限时,往往还会因命令的格式问题而导致出错。这么多命令该怎么记呢?

Ansible服务将常用的文件管理功能都合并到了file模块中,大家不用再为了寻找模块而“东奔西跑”了。先来看一下file模块的帮助信息:

[root@linuxprobe ~]# ansible-doc file
> FILE    (/usr/lib/python3.6/site-packages/ansible/modules/files/file.py)

        Set attributes of files, symlinks or directories.
        Alternatively, remove files, symlinks or directories. Many
        other modules support the same options as the `file' module -
        including [copy], [template], and [assemble]. For Windows
        targets, use the [win_file] module instead.

  * This module is maintained by The Ansible Core Team

………………省略部分输出信息………………

EXAMPLES:

- name: Change file ownership, group and permissions
  file:
    path: /etc/foo.conf
    owner: foo
    group: foo
    mode: '0644'

- name: Create a symbolic link
  file:
    src: /file/to/link/to
    dest: /path/to/symlink
    owner: foo
    group: foo
    state: link

- name: Create a directory if it does not exist
  file:
    path: /etc/some_directory
    state: directory
    mode: '0755'

- name: Remove file (delete file)
  file:
    path: /etc/foo.txt
    state: absent

通过上面的输出示例,大家已经能够了解file模块的基本参数了。其中,path参数定义了文件的路径,owner参数定义了文件所有者,group参数定义了文件所属组,mode参数定义了文件权限,src参数定义了源文件的路径,dest参数定义了目标文件的路径,state参数则定义了文件类型。

可见,file模块基本上把管理文件权限的功能都包含在内了。我们来就来挑战下面的实验吧:

请创建出一个名为/linuxprobe的新目录,所有者及所属组均为root管理员身份;

设置所有者和所属于组拥有对文件的完全控制权,而其他人则只有阅读和执行权限;

给予SGID特殊权限;

仅在dev主机组的主机上实施。

第二条要求是算术题,即将权限描述转换为数字表示法,即可读为4、可写为2、可执行为1。大家可以先自行默默计算一下答案。此前在编写剧本文件时,hosts参数对应的一直是all,即全体主机,这次需要修改为仅对dev主机组成员生效,请小心谨慎。编写模块代码如下:

[root@linuxprobe ~]# vim chmod.yml
---
- name: 管理文件属性
  hosts: dev
  tasks:
          - name: one
            file:
                    path: /linuxprobe
                    state: directory 
                    owner: root
                    group: root
                    mode: '2775'

一不小心把题目出简单了,这里没能完全展示出file模块的强大之处。我们临时添加一个需求:请再创建一个名称为/linuxcool的快捷方式文件,指向刚刚建立的/linuxprobe目录。这样用户在访问两个目录时就能有相同的内容了。在使用file模块设置快捷方式时,不需要再单独创建目标文件,Ansible服务会帮我们完成:

[root@linuxprobe ~]# vim chmod.yml
---
- name: 管理文件属性
  hosts: dev
  tasks:
          - name: one
            file:
                    path: /linuxprobe
                    state: directory 
                    owner: root
                    group: root
                    mode: '2775'
          - name: two
            file:
                    src: /linuxprobe
                    dest: /linuxcool
                    state: link

剧本文件的执行过程如下所示:

[root@linuxprobe ~]# ansible-playbook chmod.yml 

PLAY [管理文件属性] ***************************************************************

TASK [Gathering Facts] ***********************************************************
ok: [192.168.10.20]
ok: [192.168.10.21]
ok: [192.168.10.22]
ok: [192.168.10.23]
ok: [192.168.10.24]

TASK [one] ***********************************************************************
changed: [192.168.10.20]
skipping: [192.168.10.21]
skipping: [192.168.10.22]
skipping: [192.168.10.23]
skipping: [192.168.10.24]

TASK [two] ***********************************************************************
changed: [192.168.10.20]
skipping: [192.168.10.21]
skipping: [192.168.10.22]
skipping: [192.168.10.23]
skipping: [192.168.10.24]

PLAY RECAP ***********************************************************************
192.168.10.20   : ok=3  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
192.168.10.22   : ok=1  changed=0  unreachable=0  failed=0  skipped=3  rescued=0  ignored=0
192.168.10.22   : ok=1  changed=0  unreachable=0  failed=0  skipped=3  rescued=0  ignored=0
192.168.10.22   : ok=1  changed=0  unreachable=0  failed=0  skipped=3  rescued=0  ignored=0
192.168.10.22   : ok=1  changed=0  unreachable=0  failed=0  skipped=3  rescued=0  ignored=0

进入到dev组的主机中,可以看到/linuxprobe目录及/linuxcool的快捷方式均已经被顺利创建:

[root@linuxprobe ~]# ls -ld /linuxprobe
drwxrwsr-x. 2 root root 6 Apr 20 09:52 /linuxprobe
[root@linuxprobe ~]# ls -ld /linuxcool
lrwxrwxrwx. 1 root root 11 Apr 20 09:52 /linuxcool -> /linuxprobe

管理密码库文件

自Ansible 1.5版本发布后,vault作为一项新功能进入到了运维人员的视野。它不仅能对密码、剧本等敏感信息进行加密,而且还可以加密变量名称和变量值,从而确保数据不会被他人轻易阅读。使用ansible-vault命令可以实现内容的新建(create)、加密(encrypt)、解密(decrypt)、修改密码(rekey)及查看(view)等功能。

下面通过示例来学习vault的具体用法。

第1步:创建出一个名为locker.yml的配置文件,其中保存了两个变量值:

[root@linuxprobe ~]# vim locker.yml
---
pw_developer: Imadev
pw_manager: Imamgr

第2步:使用ansible-vault命令对文件进行加密。由于需要每次输入密码比较麻烦,因此还应新建一个用于保存密码值的文本文件,以便让ansible-vault命令自动调用。为了保证数据的安全性,在新建密码文件后将该文件的权限设置为600,确保仅管理员可读可写:

[root@linuxprobe ~]# vim /root/secret.txt
whenyouwishuponastar
[root@linuxprobe ~]# chmod 600 /root/secret.txt

在Ansible服务的主配置文件中,在第140行的vault_password_file参数后指定密码值保存的文件路径,准备进行调用:

[root@linuxprobe ~]# vim /etc/ansible/ansible.cfg
137 
138 # If set, configures the path to the Vault password file as an alternative to
139 # specifying --vault-password-file on the command line.
140 vault_password_file = /root/secret.txt
141 

第3步:在设置好密码文件的路径后,Ansible服务便会自动进行加载。用户也就不用在每次加密或解密时都重复输入密码了。例如,在加密刚刚创建的locker.yml文件时,只需要使用encrypt参数即可:

[root@linuxprobe ~]# ansible-vault encrypt locker.yml
Encryption successful

文件将使用AES 256加密方式进行加密,也就是意味着密钥有2256种可能。查看到加密后的内容为:

[root@linuxprobe ~]# cat locker.yml 
$ANSIBLE_VAULT;1.1;AES256
38653234313839336138383931663837333533396161343730353530313038313631653439366335
3432346333346239386334663836643432353434373733310a306662303565633762313232663763
38366334316239376262656230643531656665376166663635656436363338626464333430343162
6664643035316133650a333331393538616130656136653630303239663561663237373733373638
62383234303061623865633466336636363961623039343236356336356361613736333739623961
6334303865663838623363333339396637363061626363383266

如果不想使用原始密码了呢?也可以使用rekey参数手动对文件进行改密操作,同时应结合--ask-vault-pass参数进行修改,否则Ansible服务会因接收不到用户输入的旧密码值而拒绝新的密码变更请求:

[root@linuxprobe ~]# ansible-vault rekey --ask-vault-pass locker.yml 
Vault password: 输入旧的密码
New Vault password: 输入新的密码
Confirm New Vault password: 再输入新的密码
Rekey successful

第4步:如果想查看和修改加密文件中的内容,该怎么操作呢?对于已经加密过的文件,需要使用ansible-vault命令的edit参数进行修改,随后用view参数即可查看到修改后的内容。ansible-vault命令对加密文件的编辑操作默认使用的是Vim编辑器,在修改完毕后请记得执行wq操作保存后退出:

[root@linuxprobe ~]# ansible-vault edit locker.yml
---
pw_developer: Imadev
pw_manager: Imamgr
pw_production: Imaprod

最后,再用view参数进行查看,便是最新的内容了:

[root@linuxprobe ~]# ansible-vault view locker.yml
Vault password: 输入密码后敲击回车确认
--- 
pw_developer: Imadev 
pw_manager: Imamgr 
pw_production: Imaprod
posted @ 2022-02-28 16:19  小蟋帅  阅读(126)  评论(0编辑  收藏  举报