Image signing: - Ed25519 sign/verify package (pure Go stdlib, zero deps) - genkey and sign CLI subcommands for build system - Optional --pubkey flag for verifying updates on apply - Signature URLs in update metadata (latest.json) Portainer Edge Agent: - cloud-init portainer.go module writes K8s manifest - Auto-deploys Edge Agent when portainer.edge-agent.enabled - Full RBAC (ServiceAccount, ClusterRoleBinding, Deployment) - 5 Portainer tests in portainer_test.go Production tooling: - SSH debug extension builder (hack/build-ssh-extension.sh) - Boot performance benchmark (test/benchmark/bench-boot.sh) - Resource usage benchmark (test/benchmark/bench-resources.sh) - Deployment guide (docs/deployment-guide.md) Test results: 50 update agent tests + 22 cloud-init tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
147 lines
4.3 KiB
Bash
Executable File
147 lines
4.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# bench-resources.sh — Measure KubeSolo OS resource usage
|
|
#
|
|
# Connects to a running KubeSolo OS instance and measures:
|
|
# - Memory usage (total, used, available, per-process)
|
|
# - Disk usage (rootfs, data partition, containerd)
|
|
# - CPU usage under idle and load
|
|
# - Process count
|
|
# - Container count
|
|
# - Network overhead
|
|
#
|
|
# Usage:
|
|
# test/benchmark/bench-resources.sh [--ssh-port 2222]
|
|
#
|
|
# Prerequisites: KubeSolo OS running (e.g. via make dev-vm)
|
|
set -euo pipefail
|
|
|
|
SSH_PORT="${SSH_PORT:-2222}"
|
|
SSH_HOST="${SSH_HOST:-localhost}"
|
|
SSH_USER="${SSH_USER:-root}"
|
|
SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--ssh-port) SSH_PORT="$2"; shift 2 ;;
|
|
--ssh-host) SSH_HOST="$2"; shift 2 ;;
|
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
run_ssh() {
|
|
ssh $SSH_OPTS -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" "$@" 2>/dev/null
|
|
}
|
|
|
|
echo "=== KubeSolo OS Resource Benchmark ===" >&2
|
|
echo "Connecting to ${SSH_HOST}:${SSH_PORT}..." >&2
|
|
|
|
# Check connectivity
|
|
if ! run_ssh "true" 2>/dev/null; then
|
|
echo "ERROR: Cannot connect via SSH. Is KubeSolo OS running?" >&2
|
|
echo "Start with: make dev-vm" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "" >&2
|
|
|
|
# --- Memory ---
|
|
echo "--- Memory Usage ---" >&2
|
|
MEM_INFO=$(run_ssh "cat /proc/meminfo")
|
|
MEM_TOTAL=$(echo "$MEM_INFO" | sed -n 's/MemTotal: *\([0-9]*\).*/\1/p')
|
|
MEM_FREE=$(echo "$MEM_INFO" | sed -n 's/MemFree: *\([0-9]*\).*/\1/p')
|
|
MEM_AVAIL=$(echo "$MEM_INFO" | sed -n 's/MemAvailable: *\([0-9]*\).*/\1/p')
|
|
MEM_USED=$((MEM_TOTAL - MEM_FREE))
|
|
|
|
echo " Total: $((MEM_TOTAL / 1024)) MB" >&2
|
|
echo " Used: $((MEM_USED / 1024)) MB" >&2
|
|
echo " Available: $((MEM_AVAIL / 1024)) MB" >&2
|
|
echo " OS overhead: $((MEM_USED / 1024)) MB ($(( (MEM_USED * 100) / MEM_TOTAL ))%)" >&2
|
|
echo "" >&2
|
|
|
|
# Top memory consumers
|
|
echo "--- Top Processes (by RSS) ---" >&2
|
|
run_ssh "ps -o pid,rss,comm | sort -k2 -rn | head -10" 2>/dev/null | while read -r line; do
|
|
echo " $line" >&2
|
|
done
|
|
echo "" >&2
|
|
|
|
# --- Disk ---
|
|
echo "--- Disk Usage ---" >&2
|
|
run_ssh "df -h / /mnt/data 2>/dev/null || df -h /" | while read -r line; do
|
|
echo " $line" >&2
|
|
done
|
|
echo "" >&2
|
|
|
|
# Containerd data
|
|
CONTAINERD_SIZE=$(run_ssh "du -sh /var/lib/containerd 2>/dev/null | cut -f1" || echo "N/A")
|
|
KUBESOLO_SIZE=$(run_ssh "du -sh /var/lib/kubesolo 2>/dev/null | cut -f1" || echo "N/A")
|
|
echo " containerd data: $CONTAINERD_SIZE" >&2
|
|
echo " kubesolo data: $KUBESOLO_SIZE" >&2
|
|
echo "" >&2
|
|
|
|
# --- Processes ---
|
|
echo "--- Process Count ---" >&2
|
|
PROC_COUNT=$(run_ssh "ps | wc -l")
|
|
echo " Total processes: $PROC_COUNT" >&2
|
|
echo "" >&2
|
|
|
|
# --- K8s Status ---
|
|
echo "--- Kubernetes Status ---" >&2
|
|
NODE_STATUS=$(run_ssh "kubesolo kubectl get nodes -o wide --no-headers 2>/dev/null" || echo "N/A")
|
|
POD_COUNT=$(run_ssh "kubesolo kubectl get pods -A --no-headers 2>/dev/null | wc -l" || echo "0")
|
|
echo " Node: $NODE_STATUS" >&2
|
|
echo " Pod count: $POD_COUNT" >&2
|
|
echo "" >&2
|
|
|
|
# --- CPU (5-second sample) ---
|
|
echo "--- CPU Usage (5s idle sample) ---" >&2
|
|
CPU_IDLE=$(run_ssh "
|
|
read cpu user nice system idle rest < /proc/stat
|
|
sleep 5
|
|
read cpu user2 nice2 system2 idle2 rest2 < /proc/stat
|
|
total=\$((user2 + nice2 + system2 + idle2 - user - nice - system - idle))
|
|
idle_diff=\$((idle2 - idle))
|
|
if [ \$total -gt 0 ]; then
|
|
echo \$((idle_diff * 100 / total))
|
|
else
|
|
echo 0
|
|
fi
|
|
" 2>/dev/null || echo "N/A")
|
|
echo " CPU idle: ${CPU_IDLE}%" >&2
|
|
echo " CPU used: $((100 - ${CPU_IDLE:-0}))%" >&2
|
|
echo "" >&2
|
|
|
|
# --- OS Version ---
|
|
OS_VERSION=$(run_ssh "cat /etc/kubesolo-os-version 2>/dev/null" || echo "unknown")
|
|
|
|
# --- Output JSON ---
|
|
cat << EOF
|
|
{
|
|
"benchmark": "kubesolo-os-resources",
|
|
"os_version": "$OS_VERSION",
|
|
"memory": {
|
|
"total_kb": $MEM_TOTAL,
|
|
"used_kb": $MEM_USED,
|
|
"available_kb": ${MEM_AVAIL:-0},
|
|
"total_mb": $((MEM_TOTAL / 1024)),
|
|
"used_mb": $((MEM_USED / 1024)),
|
|
"available_mb": $((${MEM_AVAIL:-0} / 1024)),
|
|
"overhead_percent": $(( (MEM_USED * 100) / MEM_TOTAL ))
|
|
},
|
|
"disk": {
|
|
"containerd_size": "$CONTAINERD_SIZE",
|
|
"kubesolo_size": "$KUBESOLO_SIZE"
|
|
},
|
|
"processes": {
|
|
"total": $PROC_COUNT
|
|
},
|
|
"kubernetes": {
|
|
"pod_count": $POD_COUNT
|
|
},
|
|
"cpu": {
|
|
"idle_percent": ${CPU_IDLE:-0},
|
|
"used_percent": $((100 - ${CPU_IDLE:-0}))
|
|
}
|
|
}
|
|
EOF
|