540 lines
11 KiB
Markdown
540 lines
11 KiB
Markdown
|
|
# LibVirt + OpenTofu VM Provisioning Plan
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
This plan outlines how to use **LibVirt** (virtualization management toolkit) and **OpenTofu** (Infrastructure as Code tool) to provision and manage virtual machines declaratively.
|
||
|
|
|
||
|
|
**LibVirt** provides a unified API for managing different hypervisors (KVM, QEMU, Xen, etc.), while **OpenTofu** allows you to define infrastructure using declarative configuration files.
|
||
|
|
|
||
|
|
## Prerequisites
|
||
|
|
|
||
|
|
### System Requirements
|
||
|
|
- Linux host system (Ubuntu/Debian/RHEL/CentOS)
|
||
|
|
- KVM/QEMU support (check with `lscpu | grep Virtualization`)
|
||
|
|
- Sufficient RAM and storage for VMs
|
||
|
|
- Root/sudo access
|
||
|
|
|
||
|
|
### Software Installation
|
||
|
|
|
||
|
|
#### 1. Install LibVirt and KVM
|
||
|
|
```bash
|
||
|
|
# Ubuntu/Debian
|
||
|
|
sudo apt update
|
||
|
|
sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
|
||
|
|
|
||
|
|
# RHEL/CentOS/Fedora
|
||
|
|
sudo dnf install qemu-kvm libvirt libvirt-daemon-system libvirt-clients bridge-utils virt-manager
|
||
|
|
|
||
|
|
# Start and enable libvirt service
|
||
|
|
sudo systemctl start libvirtd
|
||
|
|
sudo systemctl enable libvirtd
|
||
|
|
|
||
|
|
# Add user to libvirt group
|
||
|
|
sudo usermod -aG libvirt $USER
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2. Install OpenTofu
|
||
|
|
```bash
|
||
|
|
# Download and install OpenTofu
|
||
|
|
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sh
|
||
|
|
|
||
|
|
# Or using package manager (Ubuntu/Debian)
|
||
|
|
sudo snap install opentofu --classic
|
||
|
|
|
||
|
|
# Verify installation
|
||
|
|
tofu version
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 3. Verify LibVirt Connection
|
||
|
|
```bash
|
||
|
|
# Test libvirt connection
|
||
|
|
virsh list --all
|
||
|
|
|
||
|
|
# Check available storage pools
|
||
|
|
virsh pool-list --all
|
||
|
|
|
||
|
|
# Check available networks
|
||
|
|
virsh net-list --all
|
||
|
|
```
|
||
|
|
|
||
|
|
## Project Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
vm-infrastructure/
|
||
|
|
├── main.tf # Main OpenTofu configuration
|
||
|
|
├── variables.tf # Input variables
|
||
|
|
├── outputs.tf # Output values
|
||
|
|
├── terraform.tfvars # Variable values
|
||
|
|
├── cloud-init/ # Cloud-init configuration files
|
||
|
|
│ ├── user-data.yaml
|
||
|
|
│ └── meta-data.yaml
|
||
|
|
├── images/ # VM base images
|
||
|
|
└── README.md # Project documentation
|
||
|
|
```
|
||
|
|
|
||
|
|
## Configuration Files
|
||
|
|
|
||
|
|
### 1. Main Configuration (`main.tf`)
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
terraform {
|
||
|
|
required_providers {
|
||
|
|
libvirt = {
|
||
|
|
source = "dmacvicar/libvirt"
|
||
|
|
version = "~> 0.7.0"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Configure the LibVirt Provider
|
||
|
|
provider "libvirt" {
|
||
|
|
uri = "qemu:///system"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create a storage pool
|
||
|
|
resource "libvirt_pool" "vm_pool" {
|
||
|
|
name = var.pool_name
|
||
|
|
type = "dir"
|
||
|
|
path = var.pool_path
|
||
|
|
}
|
||
|
|
|
||
|
|
# Download Ubuntu cloud image
|
||
|
|
resource "libvirt_volume" "base_image" {
|
||
|
|
name = "ubuntu-22.04-base.qcow2"
|
||
|
|
pool = libvirt_pool.vm_pool.name
|
||
|
|
source = "https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img"
|
||
|
|
format = "qcow2"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create cloud-init disk
|
||
|
|
resource "libvirt_cloudinit_disk" "cloudinit" {
|
||
|
|
name = "cloudinit.iso"
|
||
|
|
pool = libvirt_pool.vm_pool.name
|
||
|
|
user_data = file("${path.module}/cloud-init/user-data.yaml")
|
||
|
|
meta_data = file("${path.module}/cloud-init/meta-data.yaml")
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create VM volumes from base image
|
||
|
|
resource "libvirt_volume" "vm_disk" {
|
||
|
|
count = var.vm_count
|
||
|
|
name = "vm-${count.index + 1}-disk.qcow2"
|
||
|
|
pool = libvirt_pool.vm_pool.name
|
||
|
|
base_volume_id = libvirt_volume.base_image.id
|
||
|
|
size = var.disk_size
|
||
|
|
}
|
||
|
|
|
||
|
|
# Create VMs
|
||
|
|
resource "libvirt_domain" "vm" {
|
||
|
|
count = var.vm_count
|
||
|
|
name = "vm-${count.index + 1}"
|
||
|
|
memory = var.memory
|
||
|
|
vcpu = var.vcpu
|
||
|
|
|
||
|
|
cloudinit = libvirt_cloudinit_disk.cloudinit.id
|
||
|
|
|
||
|
|
network_interface {
|
||
|
|
network_name = "default"
|
||
|
|
wait_for_lease = true
|
||
|
|
}
|
||
|
|
|
||
|
|
disk {
|
||
|
|
volume_id = libvirt_volume.vm_disk[count.index].id
|
||
|
|
}
|
||
|
|
|
||
|
|
console {
|
||
|
|
type = "pty"
|
||
|
|
target_port = "0"
|
||
|
|
target_type = "serial"
|
||
|
|
}
|
||
|
|
|
||
|
|
graphics {
|
||
|
|
type = "spice"
|
||
|
|
listen_type = "address"
|
||
|
|
autoport = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Variables (`variables.tf`)
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
variable "pool_name" {
|
||
|
|
description = "Name of the storage pool"
|
||
|
|
type = string
|
||
|
|
default = "vm-pool"
|
||
|
|
}
|
||
|
|
|
||
|
|
variable "pool_path" {
|
||
|
|
description = "Path to the storage pool"
|
||
|
|
type = string
|
||
|
|
default = "/var/lib/libvirt/images/vm-pool"
|
||
|
|
}
|
||
|
|
|
||
|
|
variable "vm_count" {
|
||
|
|
description = "Number of VMs to create"
|
||
|
|
type = number
|
||
|
|
default = 3
|
||
|
|
}
|
||
|
|
|
||
|
|
variable "memory" {
|
||
|
|
description = "Memory allocation for each VM (MB)"
|
||
|
|
type = number
|
||
|
|
default = 2048
|
||
|
|
}
|
||
|
|
|
||
|
|
variable "vcpu" {
|
||
|
|
description = "Number of vCPUs for each VM"
|
||
|
|
type = number
|
||
|
|
default = 2
|
||
|
|
}
|
||
|
|
|
||
|
|
variable "disk_size" {
|
||
|
|
description = "Disk size for each VM (bytes)"
|
||
|
|
type = number
|
||
|
|
default = 21474836480 # 20GB
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Outputs (`outputs.tf`)
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
output "vm_ips" {
|
||
|
|
description = "IP addresses of the created VMs"
|
||
|
|
value = {
|
||
|
|
for i, vm in libvirt_domain.vm : vm.name => vm.network_interface[0].addresses[0]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
output "vm_names" {
|
||
|
|
description = "Names of the created VMs"
|
||
|
|
value = libvirt_domain.vm[*].name
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Variable Values (`terraform.tfvars`)
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
pool_name = "my-vm-pool"
|
||
|
|
pool_path = "/var/lib/libvirt/images/my-vm-pool"
|
||
|
|
vm_count = 3
|
||
|
|
memory = 4096 # 4GB
|
||
|
|
vcpu = 2
|
||
|
|
disk_size = 32212254720 # 30GB
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Cloud-Init Configuration
|
||
|
|
|
||
|
|
#### User Data (`cloud-init/user-data.yaml`)
|
||
|
|
```yaml
|
||
|
|
#cloud-config
|
||
|
|
users:
|
||
|
|
- name: ubuntu
|
||
|
|
groups: sudo
|
||
|
|
shell: /bin/bash
|
||
|
|
sudo: ['ALL=(ALL) NOPASSWD:ALL']
|
||
|
|
ssh_authorized_keys:
|
||
|
|
- ssh-rsa AAAAB3NzaC1yc2EAAAA... # Your SSH public key
|
||
|
|
|
||
|
|
packages:
|
||
|
|
- curl
|
||
|
|
- wget
|
||
|
|
- git
|
||
|
|
- htop
|
||
|
|
|
||
|
|
package_update: true
|
||
|
|
package_upgrade: true
|
||
|
|
|
||
|
|
# Set timezone
|
||
|
|
timezone: UTC
|
||
|
|
|
||
|
|
# Enable SSH
|
||
|
|
ssh_pwauth: false
|
||
|
|
|
||
|
|
# Run commands on first boot
|
||
|
|
runcmd:
|
||
|
|
- systemctl enable ssh
|
||
|
|
- systemctl start ssh
|
||
|
|
- echo "VM provisioned successfully" > /tmp/provision-complete
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Meta Data (`cloud-init/meta-data.yaml`)
|
||
|
|
```yaml
|
||
|
|
instance-id: vm-instance
|
||
|
|
local-hostname: vm-host
|
||
|
|
```
|
||
|
|
|
||
|
|
## Step-by-Step Workflow
|
||
|
|
|
||
|
|
### 1. Initial Setup
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Create project directory
|
||
|
|
mkdir vm-infrastructure
|
||
|
|
cd vm-infrastructure
|
||
|
|
|
||
|
|
# Create subdirectories
|
||
|
|
mkdir cloud-init images
|
||
|
|
|
||
|
|
# Generate SSH key if needed
|
||
|
|
ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Create Configuration Files
|
||
|
|
|
||
|
|
Create all the configuration files listed above in their respective locations.
|
||
|
|
|
||
|
|
### 3. Initialize OpenTofu
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Initialize the project
|
||
|
|
tofu init
|
||
|
|
|
||
|
|
# Validate configuration
|
||
|
|
tofu validate
|
||
|
|
|
||
|
|
# Plan the deployment
|
||
|
|
tofu plan
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. Deploy VMs
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Apply the configuration
|
||
|
|
tofu apply
|
||
|
|
|
||
|
|
# Confirm with 'yes' when prompted
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. Verify Deployment
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Check VM status
|
||
|
|
virsh list --all
|
||
|
|
|
||
|
|
# Get VM IP addresses
|
||
|
|
tofu output vm_ips
|
||
|
|
|
||
|
|
# SSH into a VM
|
||
|
|
ssh ubuntu@<vm-ip-address>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. Management Commands
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Show current state
|
||
|
|
tofu show
|
||
|
|
|
||
|
|
# Update configuration (modify .tf files then)
|
||
|
|
tofu plan
|
||
|
|
tofu apply
|
||
|
|
|
||
|
|
# Destroy infrastructure
|
||
|
|
tofu destroy
|
||
|
|
```
|
||
|
|
|
||
|
|
## Advanced Features
|
||
|
|
|
||
|
|
### Custom Networks
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
# Create custom network
|
||
|
|
resource "libvirt_network" "vm_network" {
|
||
|
|
name = "vm-network"
|
||
|
|
mode = "nat"
|
||
|
|
domain = "vm.local"
|
||
|
|
addresses = ["192.168.100.0/24"]
|
||
|
|
|
||
|
|
dhcp {
|
||
|
|
enabled = true
|
||
|
|
}
|
||
|
|
|
||
|
|
dns {
|
||
|
|
enabled = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Use custom network in VM
|
||
|
|
resource "libvirt_domain" "vm" {
|
||
|
|
# ... other configuration ...
|
||
|
|
|
||
|
|
network_interface {
|
||
|
|
network_id = libvirt_network.vm_network.id
|
||
|
|
wait_for_lease = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Multiple VM Types
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
# Web servers
|
||
|
|
resource "libvirt_domain" "web_servers" {
|
||
|
|
count = 2
|
||
|
|
name = "web-${count.index + 1}"
|
||
|
|
memory = 2048
|
||
|
|
vcpu = 2
|
||
|
|
# ... other configuration ...
|
||
|
|
}
|
||
|
|
|
||
|
|
# Database server
|
||
|
|
resource "libvirt_domain" "db_server" {
|
||
|
|
name = "database"
|
||
|
|
memory = 4096
|
||
|
|
vcpu = 4
|
||
|
|
# ... other configuration ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Storage Volumes
|
||
|
|
|
||
|
|
```hcl
|
||
|
|
# Additional data volume
|
||
|
|
resource "libvirt_volume" "data_volume" {
|
||
|
|
count = var.vm_count
|
||
|
|
name = "vm-${count.index + 1}-data.qcow2"
|
||
|
|
pool = libvirt_pool.vm_pool.name
|
||
|
|
size = 53687091200 # 50GB
|
||
|
|
}
|
||
|
|
|
||
|
|
# Attach to VM
|
||
|
|
resource "libvirt_domain" "vm" {
|
||
|
|
# ... other configuration ...
|
||
|
|
|
||
|
|
disk {
|
||
|
|
volume_id = libvirt_volume.data_volume[count.index].id
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Best Practices
|
||
|
|
|
||
|
|
### 1. Resource Organization
|
||
|
|
- Use consistent naming conventions
|
||
|
|
- Group related resources in modules
|
||
|
|
- Use variables for configurable values
|
||
|
|
- Document your infrastructure
|
||
|
|
|
||
|
|
### 2. State Management
|
||
|
|
- Store state files securely (consider remote backends)
|
||
|
|
- Use state locking to prevent concurrent modifications
|
||
|
|
- Backup state files regularly
|
||
|
|
|
||
|
|
### 3. Security
|
||
|
|
- Use SSH keys instead of passwords
|
||
|
|
- Configure proper firewall rules
|
||
|
|
- Keep base images updated
|
||
|
|
- Use cloud-init for secure initial configuration
|
||
|
|
|
||
|
|
### 4. Performance
|
||
|
|
- Size VMs appropriately for workload
|
||
|
|
- Use thin provisioning for storage
|
||
|
|
- Monitor resource usage
|
||
|
|
- Consider CPU topology for multi-vCPU VMs
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Common Issues
|
||
|
|
|
||
|
|
#### 1. Permission Denied
|
||
|
|
```bash
|
||
|
|
# Ensure user is in libvirt group
|
||
|
|
sudo usermod -aG libvirt $USER
|
||
|
|
# Logout and login again
|
||
|
|
|
||
|
|
# Check libvirt service status
|
||
|
|
sudo systemctl status libvirtd
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 2. Network Issues
|
||
|
|
```bash
|
||
|
|
# Check default network
|
||
|
|
virsh net-list --all
|
||
|
|
virsh net-info default
|
||
|
|
|
||
|
|
# Start default network if stopped
|
||
|
|
virsh net-start default
|
||
|
|
virsh net-autostart default
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 3. Storage Pool Issues
|
||
|
|
```bash
|
||
|
|
# Check storage pools
|
||
|
|
virsh pool-list --all
|
||
|
|
|
||
|
|
# Create default pool if missing
|
||
|
|
virsh pool-define-as default dir - - - - /var/lib/libvirt/images
|
||
|
|
virsh pool-build default
|
||
|
|
virsh pool-start default
|
||
|
|
virsh pool-autostart default
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 4. VM Won't Start
|
||
|
|
```bash
|
||
|
|
# Check VM configuration
|
||
|
|
virsh dumpxml vm-name
|
||
|
|
|
||
|
|
# Check libvirt logs
|
||
|
|
sudo journalctl -u libvirtd -f
|
||
|
|
|
||
|
|
# Validate domain XML
|
||
|
|
virsh validate vm-name
|
||
|
|
```
|
||
|
|
|
||
|
|
### Debug Commands
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# OpenTofu debugging
|
||
|
|
export TF_LOG=DEBUG
|
||
|
|
tofu plan
|
||
|
|
|
||
|
|
# LibVirt debugging
|
||
|
|
export LIBVIRT_DEBUG=1
|
||
|
|
virsh list
|
||
|
|
|
||
|
|
# Check VM console
|
||
|
|
virsh console vm-name
|
||
|
|
```
|
||
|
|
|
||
|
|
## Monitoring and Maintenance
|
||
|
|
|
||
|
|
### VM Monitoring
|
||
|
|
```bash
|
||
|
|
# Check VM stats
|
||
|
|
virsh domstats vm-name
|
||
|
|
|
||
|
|
# Monitor VM performance
|
||
|
|
virsh dominfo vm-name
|
||
|
|
virsh vcpuinfo vm-name
|
||
|
|
```
|
||
|
|
|
||
|
|
### Backup Strategy
|
||
|
|
```bash
|
||
|
|
# Backup VM configuration
|
||
|
|
virsh dumpxml vm-name > vm-name.xml
|
||
|
|
|
||
|
|
# Backup VM disk
|
||
|
|
cp /var/lib/libvirt/images/vm-disk.qcow2 /backup/location/
|
||
|
|
```
|
||
|
|
|
||
|
|
### Updates
|
||
|
|
```bash
|
||
|
|
# Update base images regularly
|
||
|
|
tofu apply -replace="libvirt_volume.base_image"
|
||
|
|
|
||
|
|
# Update OpenTofu provider
|
||
|
|
tofu init -upgrade
|
||
|
|
```
|
||
|
|
|
||
|
|
## Conclusion
|
||
|
|
|
||
|
|
This plan provides a comprehensive approach to using LibVirt and OpenTofu for VM provisioning. The Infrastructure as Code approach ensures reproducible, version-controlled virtual infrastructure that can be easily managed and scaled.
|
||
|
|
|
||
|
|
Key benefits:
|
||
|
|
- **Reproducible**: Infrastructure defined in code
|
||
|
|
- **Scalable**: Easy to add/remove VMs
|
||
|
|
- **Version Controlled**: Track infrastructure changes
|
||
|
|
- **Automated**: Minimal manual intervention required
|
||
|
|
- **Consistent**: Standardized VM configurations
|
||
|
|
|
||
|
|
Start with the basic configuration and gradually add advanced features as needed. Always test changes in a development environment before applying to production.
|