Compare commits

...

7 Commits

Author SHA1 Message Date
Florian Paul Azim Hoberg
5101202f72 feature: Add possibility to sort and select balancing workloads by smaller/larger guest objects
- Allows operators to select if first larger or smaller workloads should be migrated

Fixes: #387
2025-12-08 15:44:38 +01:00
gyptazy
929390b288 Merge pull request #386 from gyptazy/docs/385-proxmox-offline-mirror-repo-support
docs: Add documentation about offline repor mirror and proxmox-offline-mirror suppot
2025-12-06 16:12:37 +01:00
gyptazy
d4560c3af4 docs: Add documentation about offline repor mirror and proxmox-offline-mirror support
* Offline mirror support (air-gapped envs)
        * Add new full Debian repository

Fixes: #385
2025-12-06 12:26:15 +01:00
gyptazy
55c885194e Merge pull request #382 from gyptazy/fix/275-add-overprovisioning-safety-guard
fix(calculations): Add safety guard to avoid overprovisioning of nodes by memory
2025-12-06 11:19:38 +01:00
gyptazy
3d9f0eb85e fix(calculations): Add safety guard to avoid overprovisioning of nodes by memory.
Fixes: #275
2025-12-02 09:59:51 +01:00
gyptazy
490fb55ee1 Merge pull request #376 from Thalagyrt/patch-affinity-rebalance
Fix enforce_affinity boolean inversion
2025-11-27 08:41:05 +01:00
James Riley
a70330d4c3 Fix enforce_affinity boolean inversion
During runs in which affinity checks determine balancing actions,
there was a small error in a boolean calculation that caused
ProxLB to always rebalance, as it exited the verification loop with
a failure the first time it saw a VM that actually passed affinity
checks.
2025-11-26 07:06:28 -07:00
7 changed files with 88 additions and 2 deletions

View File

@@ -0,0 +1,2 @@
fixed:
- Fixed missing overprovisioning safety guard to avoid node overprovisioning (@gyptazy). [#275]

View File

@@ -0,0 +1,2 @@
fixed:
- Fixed affinity matrix pre-validation by inverting validations (@Thalagyrt). [#335]

View File

@@ -0,0 +1,2 @@
added:
- Add possibility to sort and select balancing workloads by smaller/larger guest objects (@gyptazy). [#387]

View File

@@ -120,6 +120,8 @@ systemctl start proxlb
Afterwards, ProxLB is running in the background and balances your cluster by your defined balancing method (default: memory).
**Note**: If you want to use ProxLB with the proxmox-offline-mirror or any other APT mirror tool that does not support the flat repository architecture, please see the [docs](https://github.com/gyptazy/ProxLB/blob/main/docs/02_installation.md#Repo-Mirror-and-Proxmox-Offline-Mirror-Support) how you can add this by using ProxLB's fully repo.
#### Details
ProxLB provides two different repositories:
* https://repo.gyptazy.com/stable (only stable release)
@@ -287,6 +289,7 @@ The following options can be set in the configuration file `proxlb.yaml`:
| | memory_threshold | | 75 | `Int` | The maximum threshold (in percent) that needs to be hit to perform balancing actions. (Optional) |
| | method | | memory | `Str` | The balancing method that should be used. [values: `memory` (default), `cpu`, `disk`]|
| | mode | | used | `Str` | The balancing mode that should be used. [values: `used` (default), `assigned`, `psi` (pressure)] |
| | balance_larger_guests_first | | False | `Bool` | Option to prefer larger/smaller guests first |
| | psi | | { nodes: { memory: { pressure_full: 0.20, pressure_some: 0.20, pressure_spikes: 1.00 }}} | `Dict` | A dict of PSI based thresholds for nodes and guests |
| | pools | | pools: { dev: { type: affinity }, de-nbg01-db: { type: anti-affinity }} | `Dict` | A dict of pool names and their type for creating affinity/anti-affinity rules |
| `service` | | | | | |
@@ -334,6 +337,7 @@ balancing:
balanciness: 5
method: memory
mode: used
balance_larger_guests_first: False
# # PSI thresholds only apply when using mode 'psi'
# # PSI based balancing is currently in beta and req. PVE >= 9
# psi:

View File

@@ -32,6 +32,7 @@ balancing:
balanciness: 5 # Maximum delta of resource usage between highest and lowest usage node
method: memory # 'memory' | 'cpu' | 'disk'
mode: used # 'assigned' | 'used' | 'psi'
balance_larger_guests_first: False # Option to prioritize balancing of larger or smaller guests first
# # PSI thresholds only apply when using mode 'psi'
# psi:
# nodes:

View File

@@ -6,6 +6,7 @@
- [Quick-Start](#quick-start)
- [Details](#details)
- [Debian Packages (.deb files)](#debian-packages-deb-files)
- [Repo Mirror and Proxmox Offline Mirror Support](#repo-mirror-and-proxmox-offline-mirror-support)
- [RedHat Package](#redhat-package)
- [Container Images / Docker](#container-images--docker)
- [Overview of Images](#overview-of-images)
@@ -83,6 +84,27 @@ vi /etc/proxlb/proxlb.yaml
systemctl start proxlb
```
#### Repo Mirror and Proxmox Offline Mirror Support
ProxLB uses the supported flat mirror style for the Debian repository. Unfortunately, not all offline-mirror applications support it. One of the known ones is the official *proxmox-offline-mirror* which is unable to handle flat repositories (see also: [#385](https://github.com/gyptazy/ProxLB/issues/385)).
Therefore, we currently operate and support both ways to avoid everyone force switching to the new repository. As a result, you can simply use this repository:
```
deb https://repo.gyptazy.com/proxlb stable main
```
**Example Config for proxmox-offline-mirror:**
An example config for the proxmox-offline-mirror would look like:
```
mirror: proxlb
architectures amd64
base-dir /var/lib/proxmox-offline-mirror/mirrors/
key-path /etc/apt/trusted.gpg.d/proxlb.asc
repository deb https://repo.gyptazy.com/proxlb stable main
sync true
verify true
```
### RedHat Package
There's currently no official support for RedHat based systems. However, there's a dummy .rpm package for such systems in the pipeline which can be found here:
* https://github.com/gyptazy/ProxLB/actions/workflows/20-pipeline-build-rpm-package.yml

View File

@@ -377,7 +377,19 @@ class Calculations:
if proxlb_data["meta"]["balancing"].get("enforce_affinity", False):
logger.debug("Balancing of guests will be performed. Reason: enforce affinity balancing")
for group_name in proxlb_data["groups"]["affinity"]:
# Sort guests by used memory
# Allows processing larger guests first or smaller guests first
larger_first = proxlb_data.get("meta", {}).get("balancing", {}).get("balance_larger_guests_first", False)
if larger_first:
logger.debug("Larger guests will be processed first. (Sorting descending by memory used)")
else:
logger.debug("Smaller guests will be processed first. (Sorting ascending by memory used)")
sorted_guest_usage_groups = sorted(proxlb_data["groups"]["affinity"], key=lambda g: proxlb_data["groups"]["affinity"][g]["memory_used"], reverse=larger_first)
# Iterate over all affinity groups
for group_name in sorted_guest_usage_groups:
# We get initially the node with the most free resources and then
# migrate all guests within the group to that node to ensure the
@@ -387,6 +399,10 @@ class Calculations:
for guest_name in proxlb_data["groups"]["affinity"][group_name]["guests"]:
mode = proxlb_data["meta"]["balancing"].get("mode", "used")
if not Calculations.validate_node_resources(proxlb_data, guest_name):
logger.warning(f"Skipping relocation of guest {guest_name} due to insufficient resources on target node {proxlb_data['meta']['balancing']['balance_next_node']}. This might affect affinity group {group_name}.")
continue
if mode == 'psi':
logger.debug(f"Evaluating guest relocation based on {mode} mode.")
method = proxlb_data["meta"]["balancing"].get("method", "memory")
@@ -605,7 +621,7 @@ class Calculations:
logger.debug(f"Affinity for guest {guest} is {'valid' if balancing_state_affinity else 'NOT valid'}")
logger.debug(f"Anti-affinity for guest {guest} is {'valid' if balancing_state_anti_affinity else 'NOT valid'}")
balancing_ok = not balancing_state_affinity or not balancing_state_anti_affinity
balancing_ok = balancing_state_affinity and balancing_state_anti_affinity
if balancing_ok:
logger.debug(f"Rebalancing based on affinity/anti-affinity map is not required.")
@@ -707,3 +723,40 @@ class Calculations:
return False
return True
@staticmethod
def validate_node_resources(proxlb_data: Dict[str, Any], guest_name: str) -> bool:
"""
Validate that the target node has sufficient resources to host the specified guest.
This function checks if the target node, determined by the balancing logic,
has enough CPU, memory, and disk resources available to accommodate the guest.
Args:
proxlb_data (Dict[str, Any]): A dictionary containing the complete ProxLB state including:
- "nodes": Dictionary with node resource information
- "guests": Dictionary with guest resource requirements
- "meta": Dictionary with balancing information including target node
guest_name (str): The name of the guest to validate resources for
Returns:
bool: True if the target node has sufficient resources, False otherwise
"""
logger.debug("Starting: validate_node_resources.")
node_target = proxlb_data["meta"]["balancing"]["balance_next_node"]
node_memory_free = proxlb_data["nodes"][node_target]["memory_free"]
node_cpu_free = proxlb_data["nodes"][node_target]["cpu_free"]
node_disk_free = proxlb_data["nodes"][node_target]["disk_free"]
guest_memory_required = proxlb_data["guests"][guest_name]["memory_used"]
guest_cpu_required = proxlb_data["guests"][guest_name]["cpu_used"]
guest_disk_required = proxlb_data["guests"][guest_name]["disk_used"]
if guest_memory_required < node_memory_free:
logger.debug(f"Node '{node_target}' has sufficient resources for guest '{guest_name}'.")
logger.debug("Finished: validate_node_resources.")
return True
else:
logger.debug(f"Node '{node_target}' lacks sufficient resources for guest '{guest_name}'.")
logger.debug("Finished: validate_node_resources.")
return False