mirror of
https://github.com/gyptazy/ProxLB.git
synced 2026-04-06 04:41:58 +02:00
feature: Add possibility to pin guests to a specific hypervisor node.
Fixes: #218
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
added:
|
||||
- Add 1-to-1 relationships between guest and hypervisor node to ping a guest on a node (by @gyptazy) [#218]
|
||||
17
README.md
17
README.md
@@ -21,6 +21,7 @@
|
||||
1. [Affinity Rules](#affinity-rules)
|
||||
2. [Anti-Affinity Rules](#anti-affinity-rules)
|
||||
3. [Ignore VMs](#ignore-vms)
|
||||
4. [Pin VMs to Hypervisor Nodes](#pin-vms-to-hypervisor-nodes)
|
||||
7. [Maintenance](#maintenance)
|
||||
8. [Misc](#misc)
|
||||
1. [Bugs](#bugs)
|
||||
@@ -344,7 +345,7 @@ As a result, ProxLB will try to place the VMs with the `plb_anti_affinity_ntp` t
|
||||
|
||||
**Note:** While this ensures that ProxLB tries distribute these VMs across different physical hosts within the Proxmox cluster this may not always work. If you have more guests attached to the group than nodes in the cluster, we still need to run them anywhere. If this case occurs, the next one with the most free resources will be selected.
|
||||
|
||||
### Ignore VMs / CTs
|
||||
### Ignore VMs
|
||||
<img align="left" src="https://cdn.gyptazy.com/images/proxlb-ignore-vm-movement.jpg"/> Guests, such as VMs or CTs, can also be completely ignored. This means, they won't be affected by any migration (even when (anti-)affinity rules are enforced). To ensure a proper resource evaluation, these guests are still collected and evaluated but simply skipped for balancing actions. Another thing is the implementation. While ProxLB might have a very restricted configuration file including the file permissions, this file is only read- and writeable by the Proxmox administrators. However, we might have user and groups who want to define on their own that their systems shouldn't be moved. Therefore, these users can simpy set a specific tag to the guest object - just like the (anti)affinity rules.
|
||||
|
||||
To define a guest to be ignored from the balancing, users assign a tag with the prefix `plb_ignore_$TAG`:
|
||||
@@ -358,6 +359,20 @@ As a result, ProxLB will not migrate this guest with the `plb_ignore_dev` tag to
|
||||
|
||||
**Note:** Ignored guests are really ignored. Even by enforcing affinity rules this guest will be ignored.
|
||||
|
||||
### Pin VMs to Specific Hypervisor Nodes
|
||||
<img align="left" src="https://cdn.gyptazy.com/images/proxlb-tag-node-pinning.jpg"/> Guests, such as VMs or CTs, can also be pinned to specific nodes in the cluster. This might be usefull when running applications with some special licensing requirements that are only fulfilled on certain nodes. It might also be interesting, when some physical hardware is attached to a node, that is not available in general within the cluster.
|
||||
|
||||
To pin a guest to a specific cluster node, users assign a tag with the prefix `plb_pin_$nodename` to the desired guest:
|
||||
|
||||
#### Example for Screenshot
|
||||
```
|
||||
plb_pin_node03
|
||||
```
|
||||
|
||||
As a result, ProxLB will pin the guest `dev-vm01` to the node `virt03`.
|
||||
|
||||
**Note:** The given node names from the tag are validated. This means, ProxLB validated if the given node name is really part of the cluster. In case of a wrongly defined or unavailable node name it continous to use the regular processes to make sure the guest keeps running.
|
||||
|
||||
## Maintenance
|
||||
<img src="https://cdn.gyptazy.com/images/proxlb-rebalancing-demo.gif"/>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
2. [Anti-Affinity Rules](#anti-affinity-rules)
|
||||
3. [Affinity / Anti-Affinity Enforcing](#affinity--anti-affinity-enforcing)
|
||||
4. [Ignore VMs](#ignore-vms)
|
||||
5. [Pin VMs to Hypervisor Nodes](#pin-vms-to-hypervisor-nodes)
|
||||
2. [API Loadbalancing](#api-loadbalancing)
|
||||
3. [Ignore Host-Nodes or Guests](#ignore-host-nodes-or-guests)
|
||||
4. [IPv6 Support](#ipv6-support)
|
||||
@@ -124,6 +125,20 @@ As a result, ProxLB will not migrate this guest with the `plb_ignore_dev` tag to
|
||||
|
||||
**Note:** Ignored guests are really ignored. Even by enforcing affinity rules this guest will be ignored.
|
||||
|
||||
### Pin VMs to Specific Hypervisor Nodes
|
||||
<img align="left" src="https://cdn.gyptazy.com/images/proxlb-tag-node-pinning.jpg"/> Guests, such as VMs or CTs, can also be pinned to specific nodes in the cluster. This might be usefull when running applications with some special licensing requirements that are only fulfilled on certain nodes. It might also be interesting, when some physical hardware is attached to a node, that is not available in general within the cluster.
|
||||
|
||||
To pin a guest to a specific cluster node, users assign a tag with the prefix `plb_pin_$nodename` to the desired guest:
|
||||
|
||||
#### Example for Screenshot
|
||||
```
|
||||
plb_pin_node03
|
||||
```
|
||||
|
||||
As a result, ProxLB will pin the guest `dev-vm01` to the node `virt03`.
|
||||
|
||||
**Note:** The given node names from the tag are validated. This means, ProxLB validated if the given node name is really part of the cluster. In case of a wrongly defined or unavailable node name it continous to use the regular processes to make sure the guest keeps running.
|
||||
|
||||
### API Loadbalancing
|
||||
ProxLB supports API loadbalancing, where one or more host objects can be defined as a list. This ensures, that you can even operator ProxLB without further changes when one or more nodes are offline or in a maintenance. When defining multiple hosts, the first reachable one will be picked.
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ class Calculations:
|
||||
for guest_name in proxlb_data["groups"]["affinity"][group_name]["guests"]:
|
||||
proxlb_data["meta"]["balancing"]["balance_next_guest"] = guest_name
|
||||
Calculations.val_anti_affinity(proxlb_data, guest_name)
|
||||
Calculations.val_node_relationship(proxlb_data, guest_name)
|
||||
Calculations.update_node_resources(proxlb_data)
|
||||
|
||||
logger.debug("Finished: relocate_guests.")
|
||||
@@ -278,6 +279,37 @@ class Calculations:
|
||||
|
||||
logger.debug("Finished: val_anti_affinity.")
|
||||
|
||||
@staticmethod
|
||||
def val_node_relationship(proxlb_data: Dict[str, Any], guest_name: str):
|
||||
"""
|
||||
Validates and assigns guests to nodes based on defined relationships based on tags.
|
||||
|
||||
Parameters:
|
||||
proxlb_data (Dict[str, Any]): The data holding all content of all objects.
|
||||
guest_name (str): The name of the guest to be validated and assigned a node.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
logger.debug("Starting: val_node_relationship.")
|
||||
proxlb_data["guests"][guest_name]["processed"] = True
|
||||
|
||||
if proxlb_data["guests"][guest_name]["node_relationship"]:
|
||||
logger.info(f"Guest '{guest_name}' has a specific relationship defined to node: {proxlb_data['guests'][guest_name]['node_relationship']}. Pinning to node.")
|
||||
|
||||
# Validate if the specified node name is really part of the cluster
|
||||
if proxlb_data['guests'][guest_name]['node_relationship'] in proxlb_data["nodes"].keys():
|
||||
logger.info(f"Guest '{guest_name}' has a specific relationship defined to node: {proxlb_data['guests'][guest_name]['node_relationship']} is a known hypervisor node in the cluster.")
|
||||
# Pin the guest to the specified hypervisor node.
|
||||
proxlb_data["meta"]["balancing"]["balance_next_node"] = proxlb_data['guests'][guest_name]['node_relationship']
|
||||
else:
|
||||
logger.warning(f"Guest '{guest_name}' has a specific relationship defined to node: {proxlb_data['guests'][guest_name]['node_relationship']} but this node name is not known in the cluster!")
|
||||
|
||||
else:
|
||||
logger.info(f"Guest '{guest_name}' does not have any specific node relationships.")
|
||||
|
||||
logger.debug("Finished: val_node_relationship.")
|
||||
|
||||
@staticmethod
|
||||
def update_node_resources(proxlb_data):
|
||||
"""
|
||||
|
||||
@@ -90,6 +90,7 @@ class Guests:
|
||||
guests['guests'][guest['name']]['affinity_groups'] = Tags.get_affinity_groups(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['anti_affinity_groups'] = Tags.get_anti_affinity_groups(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['ignore'] = Tags.get_ignore(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['node_relationship'] = Tags.get_node_relationship(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['type'] = 'vm'
|
||||
else:
|
||||
logger.debug(f'Metric for VM {guest["name"]} ignored because VM is not running.')
|
||||
@@ -115,6 +116,7 @@ class Guests:
|
||||
guests['guests'][guest['name']]['affinity_groups'] = Tags.get_affinity_groups(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['anti_affinity_groups'] = Tags.get_anti_affinity_groups(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['ignore'] = Tags.get_ignore(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['node_relationship'] = Tags.get_node_relationship(guests['guests'][guest['name']]['tags'])
|
||||
guests['guests'][guest['name']]['type'] = 'ct'
|
||||
else:
|
||||
logger.debug(f'Metric for CT {guest["name"]} ignored because CT is not running.')
|
||||
|
||||
@@ -151,3 +151,29 @@ class Tags:
|
||||
|
||||
logger.debug("Finished: get_ignore.")
|
||||
return ignore_tag
|
||||
|
||||
@staticmethod
|
||||
def get_node_relationship(tags: List[str]) -> str:
|
||||
"""
|
||||
Get a node relationship tag for a guest from the Proxmox cluster by the API to pin
|
||||
a guest to a node.
|
||||
|
||||
This method retrieves a relationship tag between a guest and a specific
|
||||
hypervisor node to pin the guest to a specific node (e.g., for licensing reason).
|
||||
|
||||
Args:
|
||||
tags (List): A list holding all defined tags for a given guest.
|
||||
|
||||
Returns:
|
||||
Str: The related hypervisor node name.
|
||||
"""
|
||||
logger.debug("Starting: get_node_relationship.")
|
||||
node_relationship_tag = False
|
||||
|
||||
if len(tags) > 0:
|
||||
for tag in tags:
|
||||
if tag.startswith("plb_pin"):
|
||||
node_relationship_tag = tag.replace("plb_pin_", "")
|
||||
|
||||
logger.debug("Finished: get_node_relationship.")
|
||||
return node_relationship_tag
|
||||
|
||||
Reference in New Issue
Block a user