Author: Jun Zhang License: MIT License Version: 1.0
Adaptive CPU and Bandwidth load generator for LXD containers with intelligent monitoring and control.
This system generates synthetic CPU and network bandwidth load while monitoring organic system usage. It automatically adjusts synthetic load to maintain target levels without exceeding safety limits.
┌─────────────────────────────────────────────────────────────┐
│ Load Generator Service │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────┐ ┌──────────────┐ ┌────────────┐│
│ │ Monitor │─────▶│ Controller │────▶│ Actuator ││
│ │ Module │ │ Module │ │ Module ││
│ └───────────────┘ └──────────────┘ └────────────┘│
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Collect Metrics Calculate Gap Generate Load │
│ - CPU Usage - Target vs Actual - stress-ng │
│ - Bandwidth - Organic Load - wget/curl │
│ - Network I/O - Needed Synthetic │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Reporting Module │ │
│ │ - Real-time stats │ │
│ │ - Historical data │ │
│ │ - Organic vs Synthetic breakdown │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Responsibilities:
- Collect system CPU usage from
/proc/stat - Collect network bandwidth from
/proc/net/dev - Track PIDs of synthetic load generators
- Calculate organic vs synthetic load breakdown
Key Functions:
get_cpu_usage()- Returns total CPU percentageget_bandwidth_mbps()- Returns current network throughputget_organic_load()- Calculates load excluding synthetic processes
Responsibilities:
- Compare current vs target load
- Calculate required synthetic load adjustments
- Implement safety limits and thresholds
- Decide when to adjust load generators
Key Functions:
calculate_cpu_gap()- Determines CPU adjustment neededcalculate_bandwidth_gap()- Determines bandwidth adjustment neededapply_safety_limits()- Ensures load doesn't exceed maximumsshould_adjust()- Determines if adjustment threshold met
Logic:
gap = target - (organic + current_synthetic)
if gap > threshold:
increase synthetic load
elif gap < -threshold:
decrease synthetic load
else:
maintain current load
Responsibilities:
- Start/stop/adjust CPU load generators (stress-ng)
- Start/stop/adjust bandwidth generators (wget/curl)
- Track running process PIDs
- Graceful cleanup on shutdown
Key Functions:
start_cpu_load(workers, load_percent)- Start stress-ngstop_cpu_load()- Stop stress-ng processesstart_bandwidth_load(rate_mbps, urls)- Start download loopsstop_bandwidth_load()- Stop bandwidth processes
Responsibilities:
- Generate human-readable status reports
- Log CSV statistics for analysis
- Calculate load distribution percentages
Key Functions:
generate_report()- Create formatted status reportlog_stats_csv()- Append to time-series CSVshow_current_status()- Display real-time stats
Responsibilities:
- Generate cyberpunk-themed HTML dashboard
- Display 7-day historical charts with Chart.js
- Show real-time generator status
- Auto-refresh every 60 seconds
Key Functions:
generate_html_dashboard()- Create HTML dashboardgenerate_chart_data()- Extract 7-day historical data from CSV
Configuration file: /etc/loadgen.conf
[TARGETS]
CPU_TARGET_PERCENT=50 # Target overall CPU usage %
BANDWIDTH_TARGET_MBPS=100 # Target bandwidth in Mbps
[MONITORING]
MONITOR_INTERVAL=5 # Seconds between monitoring checks
ADJUSTMENT_INTERVAL=10 # Seconds between load adjustments
REPORT_INTERVAL=60 # Seconds between report generation
[SAFETY]
MAX_CPU_PERCENT=90 # Never exceed this CPU%
MAX_BANDWIDTH_MBPS=2000 # Never exceed this bandwidth (tested up to 1800 Mbps)
MIN_ADJUSTMENT_THRESHOLD=5 # Don't adjust if within 5% of target
[BANDWIDTH_SOURCES]
# Public speedtest servers for download testing (space-separated)
# These high-speed servers can deliver 700-900 Mbps each, use multiple for 1000+ Mbps aggregate
DOWNLOAD_URLS="https://speedtest.atlanta.linode.com/1GB-atlanta.bin https://speedtest.newark.linode.com/1GB-newark.bin https://speedtest.dallas.linode.com/1GB-dallas.bin https://speed.cloudflare.com/__down?bytes=1000000000"
[LOGGING]
LOG_LEVEL=INFO
REPORT_FILE=/var/log/loadgen/report.log
STATS_FILE=/var/log/loadgen/stats.csv
[WEB_DASHBOARD]
ENABLE_WEB_DASHBOARD=true # Enable web dashboard
HTML_REPORT_INTERVAL=60 # Generate HTML every 60 seconds
WEB_ROOT=/var/www/loadgen # Directory to store HTML files
WEB_PORT=80 # HTTP server port- Install on container:
# From host
lxc file push -r ./loadgenerator your-container/root/
# In container
lxc exec your-container -- bash
cd /root/loadgenerator
./install.sh- Configure targets:
vim /etc/loadgen.conf
# Edit CPU_TARGET_PERCENT and BANDWIDTH_TARGET_MBPS- Start services:
# Start main load generator service
systemctl start loadgen
systemctl enable loadgen
# Start web dashboard (optional, if ENABLE_WEB_DASHBOARD=true)
systemctl start loadgen-web
systemctl enable loadgen-websystemctl start loadgen # Start load generator
systemctl stop loadgen # Stop load generator
systemctl restart loadgen # Restart service
systemctl reload loadgen # Re-read config without restart
systemctl status loadgen # Show service statusloadgen-status # Show current load breakdown
tail -f /var/log/loadgen/report.log # Watch reports
tail -f /var/log/loadgen/stats.csv # Watch CSV stats
journalctl -u loadgen -f # Watch service logsvim /etc/loadgen.conf # Edit configuration
systemctl reload loadgen # Apply changesIf enabled in configuration (ENABLE_WEB_DASHBOARD=true):
-
Access Dashboard:
- Direct:
http://<container-ip>:80/ - Via reverse proxy: Configure nginx to proxy to container
- Direct:
-
Features:
- Real-time CPU and bandwidth metrics
- 7-day historical charts with Chart.js
- Active generator counts and status
- Auto-refresh every 60 seconds
- Cyberpunk-themed dark UI
-
Nginx Reverse Proxy (for production):
location / {
# CRITICAL: Disable caching for real-time updates
proxy_cache_bypass 1;
proxy_no_cache 1;
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
proxy_pass http://container-ip:80;
proxy_http_version 1.1;
proxy_set_header Connection "";
}=== Load Generator Report ===
Timestamp: 2025-10-15 10:00:00
CPU Status:
Target: 50%
Organic: 30% (60% of target)
Synthetic: 20% (40% of target)
Total: 50% (✓ ON TARGET)
Bandwidth Status:
Target: 100 Mbps
Organic: 45 Mbps (45% of target)
Synthetic: 50 Mbps (50% of target)
Total: 95 Mbps (95% of target)
Active Generators:
- stress-ng: 2 workers @ 50% load
- bandwidth: 3 downloaders @ 16.7 Mbps each
System Health:
- Load Average: 2.5, 2.3, 2.1
- Memory: 512MB / 2GB
- Network: eth0 active
timestamp,cpu_target,cpu_organic,cpu_synthetic,cpu_total,bw_target,bw_organic,bw_synthetic,bw_total
2025-10-15 10:00:00,50,30,20,50,100,45,50,95
2025-10-15 10:01:00,50,35,15,50,100,60,40,100
loadgenerator/
├── LICENSE # MIT License
├── README.md # This file
├── CLAUDE.md # Claude Code guidance
├── FAST_SERVERS.md # High-speed bandwidth server specs
├── TEST_RESULTS.md # Comprehensive test validation
├── install.sh # Installation script
├── loadgen.conf.example # Example configuration
├── bin/
│ ├── loadgen.sh # Main service script
│ ├── loadgen-status # Status utility
│ └── loadgen-webserver # Web dashboard HTTP server
├── lib/
│ ├── monitor.sh # Monitoring functions
│ ├── controller.sh # Control logic
│ ├── actuator.sh # Load generation
│ ├── reporter.sh # Reporting functions
│ └── web_reporter.sh # Web dashboard HTML generation
└── systemd/
├── loadgen.service # Main systemd unit
└── loadgen-web.service # Web dashboard systemd unit
- Hard Limits: Never exceed MAX_CPU_PERCENT or MAX_BANDWIDTH_MBPS
- Graceful Degradation: Automatically reduces synthetic load when organic load increases
- Threshold Hysteresis: MIN_ADJUSTMENT_THRESHOLD prevents oscillation
- Monitoring Failure Protection: Shuts down generators if metrics unavailable
- Graceful Shutdown: Properly cleans up all child processes on service stop
-
Service Start
- Read configuration
- Initialize monitoring
- Start with zero synthetic load
-
Monitoring Loop (every MONITOR_INTERVAL)
- Sample CPU usage
- Sample network bandwidth
- Identify organic vs synthetic load
- Update current state
-
Control Loop (every ADJUSTMENT_INTERVAL)
- Get latest metrics
- Calculate gaps (target - current)
- Apply safety limits
- Determine if adjustment needed
- Send commands to actuator
-
Actuation
- Adjust CPU load (kill old stress-ng, start new)
- Adjust bandwidth load (kill old wget, start new)
-
Reporting (every REPORT_INTERVAL)
- Generate human-readable report
- Append CSV statistics
- Log to files
-
Loop back to step 2
stress-ng- CPU load generationwgetorcurl- Bandwidth load generationsysstat(optional) - Enhanced CPU monitoringbc- Floating point calculations
- Check if safety limits are too low
- Verify network connectivity for bandwidth tests
- Check available CPU cores
- Reduce MIN_ADJUSTMENT_THRESHOLD for faster response
- Check for other processes consuming resources
- Check logs:
journalctl -u loadgen -xe - Verify configuration syntax
- Ensure dependencies installed
Author: Jun Zhang
License: MIT License - see LICENSE file for details
Copyright (c) 2025 Jun Zhang
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
- README.md: This file - project overview and usage
- CLAUDE.md: Development guidance for Claude Code
- FAST_SERVERS.md: Bandwidth server performance specifications
- TEST_RESULTS.md: Comprehensive testing and validation results