# Deploy Linux VM with Post-Config LVM - Quick Reference ## Playbook `plays/deploy-linux-vm-lvm.yml` ## Description Multi-distribution Linux VM deployment with post-installation LVM configuration. This playbook deploys a VM using cloud images and then configures LVM on a second disk to meet CLAUDE.md requirements. ## Quick Deployment ### Debian 12 with LVM ```bash ansible-playbook plays/deploy-linux-vm-lvm.yml \ -e "os_distribution=debian-12" \ -e "vm_name=debian-lvm" ``` ### Ubuntu 22.04 with LVM ```bash ansible-playbook plays/deploy-linux-vm-lvm.yml \ -e "os_distribution=ubuntu-22.04" \ -e "vm_name=ubuntu-lvm" ``` ### AlmaLinux 9 with LVM ```bash ansible-playbook plays/deploy-linux-vm-lvm.yml \ -e "os_distribution=almalinux-9" \ -e "vm_name=alma-lvm" ``` ### Custom Resources ```bash ansible-playbook plays/deploy-linux-vm-lvm.yml \ -e "os_distribution=rocky-9" \ -e "vm_name=prod-server" \ -e "vm_vcpus=8" \ -e "vm_memory_mb=16384" \ -e "vm_disk_size_gb=100" ``` ## Supported Distributions ### Debian Family - `debian-11`, `debian-12` - `ubuntu-20.04`, `ubuntu-22.04`, `ubuntu-24.04` ### RHEL Family - `rhel-8`, `rhel-9` (manual download required) - `centos-stream-8`, `centos-stream-9` - `rocky-8`, `rocky-9` - `almalinux-8`, `almalinux-9` ### SUSE Family - `sles-15` (manual download required) - `opensuse-leap-15.5`, `opensuse-leap-15.6` ## LVM Configuration This playbook creates a **30GB secondary disk** (`/dev/vdb`) with LVM: ``` Physical Volume: /dev/vdb (30GB) Volume Group: vg_system Logical Volumes (CLAUDE.md compliant): ├── lv_opt 3G /opt ├── lv_tmp 1G /tmp (noexec,nosuid,nodev) ├── lv_home 2G /home ├── lv_var 5G /var ├── lv_var_log 2G /var/log ├── lv_var_tmp 5G /var/tmp (noexec,nosuid,nodev) ├── lv_var_audit 1G /var/log/audit └── lv_swap 2G swap Primary disk (/dev/vda): OS installation (unchanged) ``` ## Variables | Variable | Default | Description | |----------|---------|-------------| | `os_distribution` | **REQUIRED** | Distribution identifier | | `vm_name` | linux-guest | VM name | | `vm_hostname` | linux-vm | VM hostname | | `vm_vcpus` | 2 | Number of vCPUs | | `vm_memory_mb` | 2048 | RAM in MB | | `vm_disk_size_gb` | 20 | Primary disk size | | `lvm_disk_size_gb` | 30 | LVM disk size | | `lvm_vg_name` | vg_system | Volume group name | | `lvm_pv_device` | /dev/vdb | Physical volume device | ## Tag-Based Execution ```bash # Pre-flight checks ansible-playbook plays/deploy-linux-vm-lvm.yml --tags preflight # Deploy VM only (no LVM) ansible-playbook plays/deploy-linux-vm-lvm.yml --skip-tags lvm # Configure LVM only (existing VM) ansible-playbook plays/deploy-linux-vm-lvm.yml \ -e "vm_ip=192.168.122.50" \ --tags lvm # Full deployment ansible-playbook plays/deploy-linux-vm-lvm.yml ``` ### Available Tags - `preflight`, `validate` - Pre-flight validation - `install` - Install hypervisor packages - `download`, `verify` - Download and verify cloud images - `storage` - Create VM disks (including LVM disk) - `cloud-init` - Generate cloud-init configuration - `deploy` - Deploy and start VM - `lvm` - Configure LVM on deployed VM - `post-deploy` - Post-deployment tasks - `cleanup` - Remove temporary files ## Deployment Process ### Stage 1: VM Deployment (~2-3 minutes) 1. Download cloud image (if not cached) 2. Create primary disk from cloud image 3. Create secondary 30GB disk for LVM 4. Generate cloud-init configuration 5. Deploy VM with both disks attached 6. Wait for VM to boot ### Stage 2: LVM Configuration (~3-5 minutes) 1. Install LVM packages on VM 2. Create physical volume on /dev/vdb 3. Create volume group vg_system 4. Create logical volumes 5. Format filesystems 6. Copy existing data to LVM volumes 7. Update /etc/fstab 8. **Reboot required to activate** ### Total Time: ~5-8 minutes ## Post-Deployment ### Get VM IP ```bash ssh grokbox "virsh domifaddr " ``` ### Access VM ```bash ssh -J grokbox ansible@ ``` ### Verify LVM Configuration ```bash # Check LVM status ssh -J grokbox ansible@ "sudo pvs && sudo vgs && sudo lvs" # Check disk layout ssh -J grokbox ansible@ "lsblk" # View fstab ssh -J grokbox ansible@ "cat /etc/fstab" ``` Expected lsblk output: ``` NAME SIZE TYPE MOUNTPOINTS vda 20G disk ├─vda1 19.9G part / └─vda14 4M part vdb 30G disk ├─vg_system-lv_opt 3G lvm /opt ├─vg_system-lv_tmp 1G lvm /tmp ├─vg_system-lv_home 2G lvm /home ├─vg_system-lv_var 5G lvm /var ├─vg_system-lv_var_log 2G lvm /var/log ├─vg_system-lv_var_tmp 5G lvm /var/tmp ├─vg_system-lv_var_audit 1G lvm /var/log/audit └─vg_system-lv_swap 2G lvm [SWAP] ``` ### Reboot to Activate LVM Mounts ```bash # IMPORTANT: Reboot required! ssh -J grokbox ansible@ "sudo reboot" # Wait ~1 minute, then verify mounts ssh -J grokbox ansible@ "df -h" ``` After reboot, verify all LVM volumes are mounted: ```bash ssh -J grokbox ansible@ "df -h | grep vg_system" ``` ## Distribution-Specific Features ### Debian/Ubuntu - Package manager: `apt` - Firewall: `ufw` (enabled with SSH allowed) - Security: AppArmor enabled - Auto-updates: `unattended-upgrades` - User group: `sudo` ### RHEL/AlmaLinux/Rocky - Package manager: `dnf` - Firewall: `firewalld` (enabled with SSH allowed) - Security: SELinux enforcing - Auto-updates: `dnf-automatic` - User group: `wheel` ### SUSE/openSUSE - Package manager: `zypper` - Firewall: `firewalld` - User group: `wheel` ## LVM Management ### Extend Logical Volumes ```bash # Extend lv_var by 5GB ssh ansible@ "sudo lvextend -L +5G /dev/vg_system/lv_var" ssh ansible@ "sudo resize2fs /dev/vg_system/lv_var" # Use all remaining free space ssh ansible@ "sudo lvextend -l +100%FREE /dev/vg_system/lv_var" ssh ansible@ "sudo resize2fs /dev/vg_system/lv_var" ``` ### Create New Logical Volume ```bash ssh ansible@ "sudo lvcreate -L 5G -n lv_app vg_system" ssh ansible@ "sudo mkfs.ext4 /dev/vg_system/lv_app" ssh ansible@ "sudo mkdir -p /opt/app" ssh ansible@ "sudo mount /dev/vg_system/lv_app /opt/app" # Add to fstab ssh ansible@ "echo '/dev/vg_system/lv_app /opt/app ext4 defaults 0 2' | sudo tee -a /etc/fstab" ``` ### LVM Snapshots ```bash # Create snapshot ssh ansible@ "sudo lvcreate -L 2G -s -n lv_var_snapshot /dev/vg_system/lv_var" # Mount snapshot ssh ansible@ "sudo mkdir -p /mnt/snapshot" ssh ansible@ "sudo mount /dev/vg_system/lv_var_snapshot /mnt/snapshot" # Remove snapshot ssh ansible@ "sudo umount /mnt/snapshot" ssh ansible@ "sudo lvremove /dev/vg_system/lv_var_snapshot" ``` ## Troubleshooting ### LVM Configuration Failed ```bash # Check if second disk is attached ssh grokbox "virsh domblklist " # Check disk visibility on VM ssh ansible@ "lsblk" # Manually run LVM setup ssh ansible@ "sudo pvcreate /dev/vdb" ssh ansible@ "sudo vgcreate vg_system /dev/vdb" ``` ### Mounts Not Active After Reboot ```bash # Check fstab entries ssh ansible@ "cat /etc/fstab | grep vg_system" # Manually mount all ssh ansible@ "sudo mount -a" # Check for errors ssh ansible@ "dmesg | tail -30" ``` ### Data Migration Issues ```bash # Check data was copied ssh ansible@ "sudo du -sh /var" ssh ansible@ "sudo du -sh /opt" # If data missing, restore from rsync backup # (Data should be preserved on original locations until reboot) ``` ### SELinux Issues (RHEL Family) ```bash # Check SELinux status ssh ansible@ "getenforce" # View denials ssh ansible@ "sudo ausearch -m avc -ts recent" # Relabel filesystem ssh ansible@ "sudo restorecon -R /opt /tmp /home /var" ``` ## Security Features All deployed VMs include: ✅ **Authentication** - ansible user with passwordless sudo - SSH key-based authentication only - Root login disabled via SSH ✅ **Firewall** (enabled and configured) - Debian/Ubuntu: UFW - RHEL/SUSE: firewalld ✅ **Automatic Updates** - Debian/Ubuntu: unattended-upgrades - RHEL: dnf-automatic - Security updates only, no auto-reboot ✅ **Security Hardening** - RHEL: SELinux enforcing - Debian: AppArmor enabled - Audit daemon enabled - Time synchronization (chrony) ✅ **Essential Packages** - System tools: vim, htop, tmux - Network: curl, wget, rsync - Development: git, python3 - Security: aide, auditd - Storage: lvm2, parted ## VM Management ```bash # Start/Stop ssh grokbox "virsh start " ssh grokbox "virsh shutdown " ssh grokbox "virsh destroy " # Status ssh grokbox "virsh dominfo " ssh grokbox "virsh list --all" # Console ssh grokbox "virsh console " # Delete ssh grokbox "virsh destroy " ssh grokbox "virsh undefine --remove-all-storage" ``` ## Validation Checklist After deployment and reboot: - [ ] VM running: `virsh list | grep ` - [ ] IP assigned: `virsh domifaddr ` - [ ] SSH accessible: `ssh -J grokbox ansible@` - [ ] Cloud-init complete: `cloud-init status` - [ ] LVM configured: `sudo vgs && sudo lvs` - [ ] All volumes mounted: `df -h | grep vg_system` - [ ] Firewall enabled: `sudo ufw status` or `sudo firewall-cmd --state` - [ ] SELinux enforcing (RHEL): `getenforce` - [ ] Swap active: `free -h | grep Swap` ## Comparison with Other Playbooks | Feature | deploy-debian12-vm | deploy-debian-lvm-netinst | deploy-linux-vm-lvm | deploy_linux_vm role | |---------|-------------------|---------------------------|---------------------|---------------------| | Multi-distro | ❌ No | ❌ No | ✅ Yes | ✅ Yes | | LVM Support | ❌ No | ✅ Yes (native) | ✅ Yes (post-config) | ✅ Yes (post-config) | | Deploy Time | 2-3 min | 15-20 min | 5-8 min | 5-8 min | | CLAUDE.md LVM | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes | | Reboot Required | No | No | Yes | Yes | | Modular | No | No | No | Yes | ## Important Files ### On Hypervisor (grokbox) - Cloud images: `/var/lib/libvirt/images/*.qcow2` - VM primary disk: `/var/lib/libvirt/images/.qcow2` - VM LVM disk: `/var/lib/libvirt/images/-lvm.qcow2` - Cloud-init ISO: `/var/lib/libvirt/images/-cloud-init.iso` ### On Guest VM - SSH config: `/etc/ssh/sshd_config.d/99-security.conf` - Sudoers: `/etc/sudoers.d/ansible` - Fstab: `/etc/fstab` (LVM mounts) - LVM config: `/etc/lvm/lvm.conf` ## Related Documentation - Playbook: `plays/deploy-linux-vm-lvm.yml` - Role (recommended): `roles/deploy_linux_vm/` - Multi-distro without LVM: `plays/deploy-linux-vm.yml` - Debian native LVM: `plays/deploy-debian-lvm-netinst.yml` - CLAUDE.md: LVM specifications ## Support For issues or questions: - Check playbook output during LVM configuration - View `/var/log/cloud-init-output.log` on VM - Consult CLAUDE.md for LVM requirements - Use role version for advanced features