Ansible Inventory Groups for LINSTOR

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 nodes
  • linstor_satellites: LINSTOR satellite nodes

Derived LINSTOR inventory group:

  • linstor_cluster: The union of linstor_satellites and linstor_controllers

📝 NOTE: The built-in Ansible group all often matches every host in linstor_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_cluster inventory group.
  • Two Proxmox hosts (with LINSTOR storage pools). Both hosts are members of the linstor_controllers and linstor_satellites inventory groups (combined LINSTOR node types).
  • One Proxmox QDevice host. The QDevice host is also a member of the linstor_satellites inventory 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_pool role into an LVM thin pool for the LINSTOR storage pool.
  • Ansible SSH and LINSTOR management traffic share the Proxmox vmbr0 interface (ansible_host).
  • DRBD® replication uses a dedicated network defined by replication_ip per 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_qdevice inventory group becomes a member of linstor_satellites through 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 nodes or groups key targets every host in linstor_satellites. This pool sets an explicit nodes list, 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 the storage_pool role 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_satellites and linstor_diskless_satellites sub-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_init role 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:

  1. Either the linbit.common.customer_repo role or the linbit.common.public_repo role configures access to LINBIT package repositories on each host. Set cluster_init_repo_access to customer or public.

    âť— IMPORTANT: The linbit.common.public_repo role only supports Proxmox VE and Ubuntu LTS. For LINBIT customers, the linbit.common.customer_repo role supports all major enterprise Linux distributions, including Proxmox VE and Ubuntu LTS.

  2. The linbit.linstor.satellite_install role and the linbit.linstor.controller_install role install LINBIT SDS components on every node in linstor_cluster.
  3. When cluster_init_ssl is true, the linbit.linstor.ssl_init role configures SSL/TLS encryption for LINSTOR connections.
  4. The linbit.linstor.cluster_membership role registers each node in the LINSTOR cluster based on its inventory group membership.
  5. By default (cluster_init_token_auth: true), the linbit.linstor.auth_init role enables token authentication for the LINSTOR REST API.
  6. By default (cluster_init_deploy_storage: true), the linbit.linstor.storage_pool role deploys the storage pools defined in linstor_storage_pools. This step makes no changes when no pool targets a host.
  7. By default (cluster_init_ha_database: true), the linbit.linstor.ha_database role 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). Target linstor_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 localhost by default, requiring the python-linstor library 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 the linstor_api_delegate inventory 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_init role 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_controllers and linstor_satellites groups in your inventory.
  • Storage pool placement determines which satellites are diskful during cluster initialization. Scope any storage pool to specific hosts with either a nodes or groups key, or omit both keys to target every host in linstor_satellites.
  • Use linstor_controllers to target controllers, linstor_satellites to target all satellites, and linstor_cluster to 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.

Picture of Ryan Ronnander

Ryan Ronnander

Ryan Ronnander is a Solutions Architect at LINBIT with over 15 years of Linux experience. While studying computer science at Oregon State University he developed a passion for open source software that continues to burn just as brightly today. Outside of tech, he's also an avid guitar player and musician. You'll find him often immersed in various hobbies including, but not limited to occasional music projects, audio engineering, wrenching on classic cars, and finding balance in the great outdoors.

Talk to us

LINBIT is committed to protecting and respecting your privacy, and we’ll only use your personal information to administer your account and to provide the products and services you requested from us. From time to time, we would like to contact you about our products and services, as well as other content that may be of interest to you. If you consent to us contacting you for this purpose, please tick above to say how you would like us to contact you.

You can unsubscribe from these communications at any time. For more information on how to unsubscribe, our privacy practices, and how we are committed to protecting and respecting your privacy, please review our Privacy Policy.

By clicking submit below, you consent to allow LINBIT to store and process the personal information submitted above to provide you the content requested.

Talk to us

LINBIT is committed to protecting and respecting your privacy, and we’ll only use your personal information to administer your account and to provide the products and services you requested from us. From time to time, we would like to contact you about our products and services, as well as other content that may be of interest to you. If you consent to us contacting you for this purpose, please tick above to say how you would like us to contact you.

You can unsubscribe from these communications at any time. For more information on how to unsubscribe, our privacy practices, and how we are committed to protecting and respecting your privacy, please review our Privacy Policy.

By clicking submit below, you consent to allow LINBIT to store and process the personal information submitted above to provide you the content requested.