Compute Resource Consolidation Pattern
Combine workloads into fewer compute resources to reduce cost, improve utilization, and simplify operations.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
The Compute Resource Consolidation Pattern combines workloads into fewer compute resources to improve utilization, reduce cost, and simplify operations. Instead of running one workload per instance, you group compatible workloads based on resource profiles, availability needs, and security boundaries.
This pattern is common in cloud cost optimization, batch processing, and legacy consolidation projects where idle capacity is expensive and management overhead is high.
When to Use
Use this pattern when:
- You have many small workloads with low individual utilization
- Cloud costs are rising due to over-provisioned or idle instances
- You want to reduce the number of servers, containers, or nodes to manage
- Workloads have complementary resource profiles (e.g., CPU-bound and memory-bound)
- You can safely share infrastructure without violating security or compliance boundaries
Solution
# Simplified resource profile analyzer for consolidation decisions
workloads = [
{'name': 'report-generator', 'cpu_avg': 0.2, 'mem_avg': 0.8, 'peak_hours': [2, 3]},
{'name': 'notification-sender', 'cpu_avg': 0.6, 'mem_avg': 0.2, 'peak_hours': [9, 10, 11]},
{'name': 'data-cleaner', 'cpu_avg': 0.1, 'mem_avg': 0.1, 'peak_hours': [0, 1]},
]
def can_consolidate(a, b):
overlapping_peaks = set(a['peak_hours']) & set(b['peak_hours'])
combined_cpu = a['cpu_avg'] + b['cpu_avg']
combined_mem = a['mem_avg'] + b['mem_avg']
return not overlapping_peaks and combined_cpu < 0.9 and combined_mem < 0.9
# Example: report-generator and notification-sender have complementary peaks
print(can_consolidate(workloads[0], workloads[1])) # True
# Kubernetes pod with multiple containers sharing a node
apiVersion: v1
kind: Pod
spec:
containers:
- name: worker-a
resources:
requests:
cpu: "250m"
memory: "128Mi"
- name: worker-b
resources:
requests:
cpu: "250m"
memory: "128Mi"
Explanation
Consolidation works by analyzing the resource demands and scheduling patterns of each workload. Compatible workloads are placed on the same compute resource as long as their combined peak usage stays below capacity. The goal is to maximize utilization without introducing contention or violating isolation requirements.
The pattern often involves:
- Profiling: measure CPU, memory, disk, and network usage over time
- Bin packing: group workloads so total resource needs fit the available capacity
- Scheduling: place time-shifted workloads on the same resource
- Monitoring: watch for resource contention after consolidation
- Fallback: keep burst capacity ready for unexpected load
Variants
| Variant | Approach | Best For |
|---|---|---|
| Container consolidation | Multiple containers on one host or pod | Microservices with low utilization |
| VM consolidation | Multiple workloads on one virtual machine | Legacy applications |
| Serverless bundling | Combine functions into a single process or runtime | Event-driven workloads |
| Batch scheduling | Run jobs at different times on shared compute | Cron jobs and ETL pipelines |
What Works
- Profile workloads for average and peak usage before consolidating
- Keep security boundaries clear; do not mix sensitive and public workloads
- Leave headroom for bursts and failovers
- Use resource quotas and limits to prevent one workload from starving others
- Monitor latency and error rates after consolidation to detect contention
- Document fallback plans for splitting workloads again if needed
Common Mistakes
- Consolidating workloads with overlapping peak hours, causing contention
- Ignoring noisy neighbor effects on shared CPU, memory, or disk
- Mixing workloads with different compliance or security requirements
- Removing too much capacity, leaving no room for scaling or failures
- Forgetting to update monitoring and alerting thresholds after consolidation
Troubleshooting
- High latency between services: trace the request path. Look for synchronous chains, missing caching, and oversized payloads that cross network boundaries.
- Single point of failure: identify components without redundancy. Add replicas, failover, or circuit breakers before scaling traffic.
- Unexpected coupling between services: review shared databases, libraries, and schemas. Bound contexts should own their data and expose stable interfaces.
- Cost spikes after scaling: right-size instances and use autoscaling with limits. Reserved capacity or spot instances can reduce steady-state spend.
- Difficult to reason about the system: maintain architecture decision records and service dependency maps.
Further Reading
- Official documentation: check the current reference for the framework or tool used.
- Related guides: explore the pattern and cost-optimization guides for deeper coverage.
- Complementary patterns: review design patterns applicable to your technology stack.
- Public postmortems: study real incidents from teams that faced similar production issues.
Production Notes
- Deploy gradually using canary or blue-green to catch regressions early.
- Configure alerts for error rate, p99 latency, and failure rate before enabling in production.
- Document the rollback in the runbook; test the procedure in staging at least once per quarter.
- Review structured logs with correlation IDs to trace requests end-to-end during incidents.
Key Takeaways
- Apply compute resource consolidation pattern when you need a practical solution for your use case.
- Monitor performance after implementation; measure latency, errors, and resource usage before and after.
- Check the Troubleshooting section for common failures; most have documented root causes with fixes.
- Keep dependencies updated and run tests in CI to prevent production regressions.
Advanced Solutions
Dynamic bin packing with Kubernetes scheduler
Use Kubernetes custom schedulers or plugins to implement intelligent bin packing:
# Kubernetes scheduler config for bin packing optimization
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: bin-packing-scheduler
pluginConfig:
- name: NodeResourcesFit
args:
scoringStrategy:
type: LeastAllocated
resources:
- name: cpu
weight: 1
- name: memory
weight: 1
# Custom scoring plugin for complementary resource profiles
def score_node(pod, node):
node_cpu_used = sum(cpu for c in node.pods)
node_mem_used = sum(mem for c in node.pods)
node_cpu_total = node.allocatable['cpu']
node_mem_total = node.allocatable['memory']
pod_cpu = pod.spec.containers[0].resources.requests.cpu
pod_mem = pod.spec.containers[0].resources.requests.memory
# Prefer nodes where pod fills gaps in resource profile
cpu_fill = (node_cpu_used + pod_cpu) / node_cpu_total
mem_fill = (node_mem_used + pod_mem) / node_mem_total
# Higher score for better resource balance
return 100 - abs(cpu_fill - mem_fill) * 50
Time-based consolidation with spot instances
Use spot instances with time-shifted workloads for maximum cost savings:
import boto3
import datetime
def schedule_spot_consolidation(workloads, region='us-east-1'):
ec2 = boto3.client('ec2', region_name=region)
# Group workloads by time windows
time_windows = {}
for w in workloads:
window = (w['start_hour'], w['end_hour'])
if window not in time_windows:
time_windows[window] = []
time_windows[window].append(w)
# Launch spot instances for each time window
for window, ws in time_windows.items():
instance_type = 'm5.large' # Balance of CPU and memory
# Calculate total resource requirements
total_cpu = sum(w['cpu'] for w in ws)
total_mem = sum(w['mem'] for w in ws)
# Request spot instance
response = ec2.request_spot_instances(
InstanceCount=1,
Type='one-time',
InstanceInterruptionBehavior='terminate',
LaunchSpecification={
'ImageId': 'ami-12345678',
'InstanceType': instance_type,
'UserData': f'#cloud-config\nruncmd:\n - docker run -d {total_cpu}m {total_mem}Mi'
}
)
print(f"Launched spot instance for window {window}: {response['SpotInstanceRequests'][0]['SpotInstanceRequestId']}")
Container resource isolation with cgroups
Prevent noisy neighbor effects using Linux cgroups:
# Create cgroup for CPU isolation
sudo cgcreate -g cpu,memory:/workload-a
# Set CPU quota (50% of one core)
sudo cgset -r cpu.cfs_quota_us=50000 /workload-a
sudo cgset -r cpu.cfs_period_us=100000 /workload-a
# Set memory limit (512MB)
sudo cgset -r memory.limit_in_bytes=536870912 /workload-a
# Run workload in cgroup
sudo cgexec -g cpu,memory:workload-a python workload-a.py
# Create cgroup for workload-b with different limits
sudo cgcreate -g cpu,memory:/workload-b
sudo cgset -r cpu.cfs_quota_us=50000 /workload-b
sudo cgset -r memory.limit_in_bytes=536870912 /workload-b
sudo cgexec -g cpu,memory:workload-b python workload-b.py
Additional Best Practices
- For a deeper guide, see Content Delivery Network (CDN) Pattern.
-
Use resource quotas at multiple levels. Apply quotas at the cluster level, namespace level, and pod level to enforce limits hierarchically. This prevents one team or application from consuming all resources.
-
Implement burst capacity strategies. Keep a small pool of on-demand instances ready for spot instance interruptions or unexpected load spikes. Use Kubernetes cluster autoscaler to add nodes when pod scheduling fails.
-
Monitor resource utilization continuously. Use Prometheus and Grafana to track CPU, memory, disk, and network utilization at 1-minute granularity. Set alerts for sustained high utilization (>80%) which indicates consolidation may be too aggressive.
Common Production Pitfalls
- Applying the pattern where no abstraction is needed, adding accidental complexity.
- Letting the pattern leak into unrelated modules and blur ownership boundaries.
- Over-engineering the first implementation instead of starting simple and measuring pain.
- Skipping contract tests, so refactors silently break consumers.
- Ignoring failure modes that the pattern does not cover.
- Using the pattern as a default instead of choosing the right tool for the current scale.
- Forgetting to document when to stop using the pattern and what replaces it.
- Missing observability around the pattern’s performance and error propagation.
Frequently Asked Questions
- Is consolidation the same as autoscaling?
No. Consolidation reduces the number of resources you use. Autoscaling adjusts the number of resources based on demand. They can work together.
- Should I consolidate production and development workloads?
Generally no. Production workloads should be isolated from non-production environments for stability and security.
- How do I know when consolidation has gone too far?
Watch for increased latency, higher error rates, memory pressure, or CPU throttling. These are signs that workloads are competing for resources.
Related Resources
Content Delivery Network (CDN) Pattern
Distribute static and live content through geographically dispersed edge servers to reduce latency, improve availability, and offload origin infrastructure.
PatternGateway Routing Pattern
Route requests to multiple backend services through a single entry point that handles cross-cutting concerns.
PatternAnti-Corruption Layer: Isolate Legacy with Adapters
How to isolate legacy systems with translation adapters. Covers ACL facade, domain translation, bidirectional mapping, and gradual legacy replacement.