A well-structured Ansible inventory is fundamental to an automated LINSTOR® cluster deployment. This blog post shows how to structure inventory groups for Day 1 installation and Day 2 operations. It assumes familiarity with Ansible and previous experience setting up a LINSTOR cluster.
Inventory group overview
Core LINSTOR inventory groups:
linstor_controllers: LINSTOR controller nodeslinstor_satellites: LINSTOR satellite nodes
Derived LINSTOR inventory group:
linstor_cluster: The union oflinstor_satellitesandlinstor_controllers
📝 NOTE: The built-in Ansible group
alloften matches every host inlinstor_cluster, but this is not guaranteed. Its scope depends on the hosts defined in your Ansible inventory.
A Proxmox inventory example
The following example walks through a typical LINSTOR Ansible inventory file for a Proxmox VE 9 cluster. The cluster is hyperconverged, with LINSTOR deployed alongside each Proxmox VE 9 host. Although this architecture is not required, it is commonly used in Proxmox environments and on other platforms that use LINBIT® software-defined storage (SDS), such as Kubernetes and CloudStack.
📝 NOTE: LINSTOR uses the term node to describe a cluster member. Ansible uses the term host. Proxmox also refers to cluster members as hosts. In this blog post, each term applies according to its context.
The example cluster has the following properties:
- Three hosts in total. All hosts are members of the
linstor_clusterinventory group. - Two Proxmox hosts (with LINSTOR storage pools). Both hosts are members of the
linstor_controllersandlinstor_satellitesinventory groups (combined LINSTOR node types). - One Proxmox QDevice host. The QDevice host is also a member of the
linstor_satellitesinventory group (no LINSTOR storage pool). - The LINSTOR controller service is a highly available service (supported on combined LINSTOR node types only). See details about making the LINSTOR controller service highly available in the LINSTOR User Guide.
- Three NVMe drives per Proxmox host, striped by the
linbit.linstor.storage_poolrole into an LVM thin pool for the LINSTOR storage pool. - Ansible SSH and LINSTOR management traffic share the Proxmox
vmbr0interface (ansible_host). - DRBD® replication uses a dedicated network defined by
replication_ipper host.
A complete inventory file for this example is available for download.
Defining the hosts
The following configuration defines each LINSTOR controller node as a member of the linstor_controllers inventory group. The host variables define typical networking addresses for each host.
# LINSTOR controller nodes
linstor_controllers:
hosts:
proxmox-0:
ansible_host: 192.168.223.130 # Ansible communication
linstor_ip: "{{ ansible_host }}" # LINSTOR management
proxmox_ip: "{{ ansible_host }}" # Proxmox vmbr0
replication_ip: 192.168.222.130 # DRBD replication
proxmox-1:
ansible_host: 192.168.223.131
linstor_ip: "{{ ansible_host }}"
proxmox_ip: "{{ ansible_host }}"
replication_ip: 192.168.222.131
Next, define the Proxmox QDevice inventory group (single host) along with a few IP address variables:
# Proxmox QDevice host (non-LINSTOR group)
pve_qdevice:
hosts:
proxmox-qdevice:
ansible_host: 192.168.223.138
linstor_ip: "{{ ansible_host }}"
proxmox_ip: "{{ ansible_host }}"
replication_ip: 192.168.222.138
📝 NOTE: The
pve_qdeviceinventory group becomes a member oflinstor_satellitesthrough group membership.
Defining shared cluster variables
Both storage nodes use identical NVMe device paths, so all nodes in the cluster can share the same linstor_storage_pools definition (sp0) under all:vars:
all:
vars:
linstor_sp: 'sp0'
linstor_storage_pools:
- name: "{{ linstor_sp }}"
type: lvmthin
vg: drbdpool
vg_thinpool: thinpool
physical_devices:
- /dev/nvme0n1
- /dev/nvme1n1
- /dev/nvme2n1
nodes:
- proxmox-0
- proxmox-1
📝 NOTE: A storage pool without a
nodesorgroupskey targets every host inlinstor_satellites. This pool sets an explicitnodeslist, deploying only on the two combined storage nodes and skipping the diskless QDevice.
đź’ˇ TIP: When device paths differ between hosts, reference a per-host variable instead of a literal list, for example
physical_devices: "{{ physical_devices_lvm_striped | default([]) }}". See centralized pool definitions with targeting in thestorage_poolrole README.
Defining nested groups
For this cluster, the LINSTOR controller nodes are combined node types, and the Proxmox QDevice doubles as a LINSTOR tiebreaker. To reflect this in Ansible inventory, add linstor_controllers and pve_qdevice as children of linstor_satellites, then group all LINSTOR node types into linstor_cluster.
# LINSTOR satellite nodes
# Includes linstor_controllers as children (combined node types)
# Includes pve_qdevice as children (diskless quorum node)
linstor_satellites:
children:
linstor_controllers:
pve_qdevice:
# All LINSTOR cluster members
linstor_cluster:
children:
linstor_controllers:
linstor_satellites:
đź’ˇ TIP: Proxmox high availability requires at least three nodes. LINSTOR also requires at least three nodes to enable quorum. A “2.5-node” deployment satisfies this requirement for both software stacks with two diskful storage nodes (Proxmox hypervisors) plus a dedicated diskless tiebreaker node (QDevice).
đź’ˇ TIP: For more complex deployments, you can optionally split LINSTOR satellite nodes into
linstor_diskful_satellitesandlinstor_diskless_satellitessub-groups.
Installing LINSTOR with the cluster initialization role
With a complete LINSTOR-centric Ansible inventory defined, run the following playbook to install LINSTOR by using the linbit.linstor.cluster_init role:
- name: Deploy LINSTOR
hosts: linstor_cluster
any_errors_fatal: true
become: true
tags: linstor
tasks:
- name: Install and initialize LINSTOR
ansible.builtin.import_role:
name: linbit.linstor.cluster_init
vars:
cluster_init_repo_access: public # Proxmox VE and Ubuntu LTS only
ha_database_pool: "{{ linstor_sp }}"
đź’ˇ TIP: The
linbit.linstor.cluster_initrole is a convenience role that invokes a chain of sub-roles. For reference, see the equivalent full initialization play in the role README.
This playbook performs the following actions:
- Either the
linbit.common.customer_reporole or thelinbit.common.public_reporole configures access to LINBIT package repositories on each host. Setcluster_init_repo_accesstocustomerorpublic.
âť— IMPORTANT: The
linbit.common.public_reporole only supports Proxmox VE and Ubuntu LTS. For LINBIT customers, thelinbit.common.customer_reporole supports all major enterprise Linux distributions, including Proxmox VE and Ubuntu LTS. - The
linbit.linstor.satellite_installrole and thelinbit.linstor.controller_installrole install LINBIT SDS components on every node inlinstor_cluster. - When
cluster_init_sslis true, thelinbit.linstor.ssl_initrole configures SSL/TLS encryption for LINSTOR connections. - The
linbit.linstor.cluster_membershiprole registers each node in the LINSTOR cluster based on its inventory group membership. - By default (
cluster_init_token_auth: true), thelinbit.linstor.auth_initrole enables token authentication for the LINSTOR REST API. - By default (
cluster_init_deploy_storage: true), thelinbit.linstor.storage_poolrole deploys the storage pools defined inlinstor_storage_pools. This step makes no changes when no pool targets a host. - By default (
cluster_init_ha_database: true), thelinbit.linstor.ha_databaserole migrates the controller database to highly available storage backed by DRBD replication. The role detects the diskful combined nodes from the deployed storage pools and converts the database when the cluster has the required two or three combined nodes. The role performs the same migration on an existing standalone controller database when run on its own.
LINSTOR cluster administration commands
The following Ansible commands use the LINSTOR inventory groups to administer a LINSTOR cluster.
Show LINSTOR cluster membership:
ansible linstor_controllers[0] -m shell -a "linstor node list" --become
đź’ˇ TIP: Most LINSTOR commands only need to run once (such as
run_once: true). Targetlinstor_controllers[0]to avoid redundant commands running on every host.
List all LINSTOR storage pools:
ansible linstor_controllers[0] -m shell -a "linstor storage-pool list" --become
List all LINSTOR resources:
ansible linstor_controllers[0] -m shell -a "linstor resource list" --become
List the loaded DRBD kernel module version:
ansible linstor_satellites -m shell -a "cat /sys/module/drbd/version" --become
List the installed DRBD kernel module version:
ansible linstor_satellites -m shell -a "modinfo drbd | grep ^version" --become
The derived linstor_cluster group also simplifies targeting commands. For example, the following commands are equivalent and both target every host in the LINSTOR cluster.
ansible linstor_controllers:linstor_satellites \
-m shell -a "dpkg -l 'drbd*' 'linstor*'" --become
ansible linstor_cluster -m shell -a "dpkg -l 'drbd*' 'linstor*'" --become
LINSTOR collection modules
The LINSTOR Ansible collection also includes modules for managing LINSTOR clusters.
For example, you can use the linbit.linstor.resource_group module to create a resource group, and use the linbit.linstor.resource module to spawn a new resource from it:
- name: Create a resource group on sp0
linbit.linstor.resource_group:
name: rg0
storage_pool: sp0
place_count: 2
delegate_to: "{{ linstor_api_delegate | default('localhost') }}"
run_once: true # noqa: run-once[task]
- name: Spawn a resource from the resource group
linbit.linstor.resource:
name: r0
mode: spawn
resource_group: rg0
size: 10G
delegate_to: "{{ linstor_api_delegate | default('localhost') }}"
run_once: true # noqa: run-once[task]
📝 NOTE: The example tasks delegate to
localhostby default, requiring thepython-linstorlibrary to exist on the Ansible control node. See Installing the LINSTOR Python API Client Library in the LINBIT knowledge base for installation instructions. To run LINSTOR API calls from a cluster node instead, set thelinstor_api_delegateinventory variable, for example to"{{ groups['linstor_controllers'][0] }}".
đź’ˇ TIP: When you create resources from a resource group, LINSTOR can automatically place them across the cluster. Automatic placement suffices for most use cases, but you can also manually place resources on specific nodes if needed.
The equivalent CLI commands are:
linstor resource-group create rg0 --storage-pool sp0 --place-count 2
linstor resource-group spawn-resources rg0 r0 10G
# List the resource group and resource
linstor resource-group list
linstor resource list
Conclusion
A well-structured inventory unlocks the full potential of the LINSTOR Ansible collection. This article covers considerable ground that might be overwhelming at first. Some important things to remember about using the collection are:
- The
linbit.linstor.cluster_initrole handles Day 1 installation. Additional playbooks, roles, and modules handle Day 2 operations, all from the same LINSTOR Ansible collection. - Various roles throughout the LINSTOR Ansible collection depend on the existence of
linstor_controllersandlinstor_satellitesgroups in your inventory. - Storage pool placement determines which satellites are diskful during cluster initialization. Scope any storage pool to specific hosts with either a
nodesorgroupskey, or omit both keys to target every host inlinstor_satellites. - Use
linstor_controllersto target controllers,linstor_satellitesto target all satellites, andlinstor_clusterto target every host in the LINSTOR cluster. - HA LINSTOR deployments require between two and three combined nodes (LINSTOR controller nodes with local storage pools).
- Diskless satellite nodes are useful at any scale, from serving as quorum tiebreakers in small clusters to running workloads with disklessly attached resources in larger ones.
One inventory file with three linstor_* groups and a playbook invoking linbit.linstor.cluster_init are all you need to deploy a LINSTOR cluster. Build the inventory. Run the playbook. Day 2 awaits.