LVM provides a layer of abstraction over physical storage and allows you to create logical storage volumes. With a logical volume, there is no restriction to physical disk sizes. In addition, the hardware storage configuration is hidden from the software enabling for ease of resizing and moving the need to stop applications or unmount the file systems.
Some of the benefits of Logical volumes are:
- Easy naming of devices: Logical storage volumes can be managed in user-defined and custom named groups.
- Disk striping: You can create logical volume that stripes data across two or more disks giving you better throughput.
- Mirroring of volumes: With logical volumes you can provide a convenient way to configure data mirroring
- Ease of resizing storage pools: Being able to extend logical volumes or reduce logical volumes in size without reformatting and repartitioning underlying disk devices
- Flexibility in capacity extension: You can extend file systems across multiple disks through disks aggregation and partitioning into a single logical volume.
- And much more
In this article we look at how you can create a logical volume using Ansible playbook with the following considerations:
- Creating a logical volume in an existing volume group.
- Creating PV, VG then Logical Volume(LV)
LVM structure:
- Physical Volumes: Block devices (physical disks or partitions) initialized.
- Volume Group: Physical volumes combined into Volume group. It represents a single storage pool of available storage space.
- Logical Volume: Created on top of Volume Group (Similar to creating simple partitions on disk partition)
1. Create Logical Volume with existing Volume Group
Here is a sample playbook that can be modified when creating a logical volume from existing volume group.
---
- name: Playbook to create LV, Filesystem and Mount it
hosts: myservers
remote_user: debian
become: yes
become_method: sudo
vars:
vg_name: vg0
lv_name: container_data
lv_size: 20g
fs_type: xfs
mount_path: /data
tasks:
- name: Create Logical Volume for data persistence
community.general.lvol:
vg: "{{ vg_name }}"
lv: "{{ lv_name }}"
size: "{{ lv_size }}"
- name: Create filesystem on LV
community.general.filesystem:
fstype: "{{ fs_type }}"
dev: /dev/mapper/{{ vg_name }}-{{ lv_name }}
- name: Get LV UUID
ansible.builtin.command: lsblk /dev/mapper/{{ vg_name }}-{{ lv_name }} -no UUID
register: lv_uuid
- name: Mount created filesystem
ansible.posix.mount:
path: "{{ mount_path }}"
src: UUID={{ lv_uuid.stdout }}
state: mounted
fstype: "{{ fs_type }}"
Variables used:
- vg_name: The name of the existing volume group in your server(s)
- lv_name: The name of the logical volume to be created.
- lv_size: The size of logical volume being created. t for terabyte, g for gigabyte, m for megabytes.
- fs_type: The filesystem type to be used in LV partitioning
- mount_path: Directory where disk is mounted. It will be created if doesn’t exist.
Create inventory file with the your servers.
$ vim hosts
[myservers]
192.168.1.20
192.168.1.21
192.168.1.22
Run playbook after customizing it create logical volume.
$ ansible-playbook -i hosts lvm-partition_data.yml
PLAY [Playbook to create LV, Filesystem and Mount it] ************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************
ok: [192.168.1.20]
TASK [Create Logical Volume for data persistence] ****************************************************************************************************************************************************
changed: [192.168.1.20]
TASK [Create filesystem on LV] ***********************************************************************************************************************************************************************
changed: [192.168.1.20]
TASK [Get LV UUID] ***********************************************************************************************************************************************************************************
changed: [192.168.1.20]
TASK [Mount created filesystem] **********************************************************************************************************************************************************************
changed: [192.168.1.20]
PLAY RECAP *******************************************************************************************************************************************************************************************
192.168.1.20 : ok=5 changed=4 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
2. Create Physical Volume, Volume Group and Logical Volume
In this example we’re using raw disks to create PV, then VG and LV creation.
Raw disks used in this example are:
- /dev/vdb
- /dev/vdc
Check your server to see available raw disk devices.
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
loop0 7:0 0 63.5M 1 loop /snap/core20/2015
loop1 7:1 0 111.9M 1 loop /snap/lxd/24322
loop2 7:2 0 40.8M 1 loop /snap/snapd/20092
sr0 11:0 1 4M 0 rom
vda 252:0 0 30G 0 disk
├─vda1 252:1 0 29.9G 0 part /
├─vda14 252:14 0 4M 0 part
└─vda15 252:15 0 106M 0 part /boot/efi
vdb 252:16 0 20G 0 disk
vdc 252:32 0 20G 0 disk
Let’s create playbook file.
vim lvm_pv_vg_lv.yml
Customize below contents to be used in the ansible playbook.
---
- name: Playbook to create PV, VG, LV, Filesystem and Mount it
hosts: myservers
remote_user: ubuntu
become: yes
become_method: sudo
vars:
pv_disks: /dev/vdb
#pv_disks: /dev/vdb,/dev/vdc
vg_name: vg1
lv_name: container_data
lv_size: +100%FREE
#lv_size: 10g
fs_type: ext4
mount_path: /data
tasks:
- name: Create a volume group
community.general.lvg:
vg: "{{ vg_name }}"
pvs: "{{ pv_disks }}"
- name: Create Logical Volume for data persistence
community.general.lvol:
vg: "{{ vg_name }}"
lv: "{{ lv_name }}"
size: "{{ lv_size }}"
- name: Create filesystem on LV
community.general.filesystem:
fstype: "{{ fs_type }}"
resizefs: true
dev: /dev/mapper/{{ vg_name }}-{{ lv_name }}
- name: Get LV UUID
ansible.builtin.command: lsblk /dev/mapper/{{ vg_name }}-{{ lv_name }} -no UUID
register: lv_uuid
- name: Mount created filesystem
ansible.posix.mount:
path: "{{ mount_path }}"
src: UUID={{ lv_uuid.stdout }}
state: mounted
fstype: "{{ fs_type }}"
Where:
- pv_disks: List of one or more raw disk devices path
- vg_name: The name of the volume group to be created
- lv_name: The name of the logical volume to be created
- lv_size: The size of logical volume to be created. t for terabyte, g for gigabyte, m for megabytes.
- fs_type: The filesystem type to be used in device partitioning
- mount_path: Directory where disk is mounted. It will be created if doesn’t exist.
Once done run the Playbook to create PV, VG and LV.
$ ansible-playbook -i hosts lvm_pv_vg_lv.yml
PLAY [Playbook to create LV, Filesystem and Mount it] ************************************************************************************************************************************************
TASK [Gathering Facts] *******************************************************************************************************************************************************************************
ok: [192.168.56.50]
TASK [Create a volume group] *************************************************************************************************************************************************************************
changed: [192.168.56.50] => (item=/dev/vdb)
TASK [Create Logical Volume for data persistence] ****************************************************************************************************************************************************
changed: [192.168.56.50]
TASK [Create filesystem on LV] ***********************************************************************************************************************************************************************
changed: [192.168.56.50]
TASK [Get LV UUID] ***********************************************************************************************************************************************************************************
changed: [192.168.56.50]
TASK [Mount created filesystem] **********************************************************************************************************************************************************************
changed: [192.168.56.50]
PLAY RECAP *******************************************************************************************************************************************************************************************
192.168.56.50 : ok=6 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Confirm PV, VG, and LV creations.
$ sudo pvs
PV VG Fmt Attr PSize PFree
/dev/vdb vg1 lvm2 a-- <20.00g 0
$ sudo vgs
VG #PV #LV #SN Attr VSize VFree
vg1 1 1 0 wz--n- <20.00g 0
$ sudo lvs
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
container_data vg1 -wi-ao---- <20.00g
$ df -hT /data
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/vg1-container_data ext4 20G 24K 19G 1% /data
$ cat /etc/fstab
LABEL=cloudimg-rootfs / ext4 discard,errors=remount-ro 0 1
LABEL=UEFI /boot/efi vfat umask=0077 0 1
UUID=e7e6a6f6-e85e-46a2-b070-c3ee5fb571f5 /data ext4 defaults 0 0
From the commands output we can confirm the playbook execution was successful. This is a basic ansible usage example of LVM management. You can check official Ansible documentation pages for more modules usage guidance.