- Create comprehensive RESOURCE_LIMITS.md with memory, CPU, and disk guidance - Provide three configuration profiles (conservative, moderate, unbounded) - Include complete Kubernetes deployment examples with resource limits - Add Prometheus monitoring queries and recommended alerts - Document tuning guidelines and troubleshooting procedures - Update README.md to reference new documentation - Mark Milestone 3 as complete in plan.md Milestone 3 (Production Hardening) is now complete. Next: Milestone 4 (Kubernetes Integration) Signed-off-by: Jason Hall <jason@chainguard.dev>
14 KiB
Resource Limits and Recommendations
This document provides guidance on configuring resource limits for the snoop sidecar in production environments.
Overview
Snoop is designed to be lightweight and have minimal impact on application performance. However, proper resource limits ensure predictable behavior and protect against resource exhaustion under unusual conditions.
Memory Usage
Components
Snoop's memory usage consists of several components:
-
eBPF Maps (Kernel Memory)
- Ring buffer: 256 KB (fixed)
- Per-CPU heap: 1 entry × ~300 bytes × CPU count (negligible)
- Traced cgroups map: 64 entries × ~16 bytes = ~1 KB
- Dropped events counter: negligible
- Total kernel memory: ~300 KB (plus per-CPU overhead)
-
Userspace Deduplication Cache
- Each file path entry: ~256 bytes (path string + map/list overhead)
- Default (unbounded): memory grows with unique files seen
- With
max-unique-files=N: capped at approximatelyN × 256 bytes
Examples:
- 10,000 files: ~2.5 MB
- 100,000 files: ~25 MB
- 1,000,000 files: ~250 MB
-
Go Runtime Overhead
- Base runtime: ~5-10 MB
- Goroutines: minimal (4-5 goroutines)
- HTTP server (metrics/health): ~1-2 MB
-
Report Buffer
- JSON serialization buffer: proportional to unique files
- Temporary, released after each report write
- Peak usage: ~2× the deduplication cache size during report generation
Recommendations
Conservative (Most Applications)
For applications with typical file access patterns (thousands of unique files):
resources:
requests:
memory: 32Mi
limits:
memory: 128Mi
Configuration:
-max-unique-files=50000 # Cap at 50K unique files (~12 MB)
Moderate (Large Applications)
For applications that access many files (e.g., monorepos, data processing):
resources:
requests:
memory: 64Mi
limits:
memory: 256Mi
Configuration:
-max-unique-files=200000 # Cap at 200K unique files (~50 MB)
Unbounded (Long-Running Observability)
For long-term observation where you want complete data:
resources:
requests:
memory: 128Mi
limits:
memory: 512Mi
Configuration:
-max-unique-files=0 # Unbounded, monitor snoop_unique_files metric
Important: When using unbounded mode, actively monitor the snoop_unique_files metric to detect unexpected growth.
CPU Usage
Characteristics
Snoop's CPU usage is primarily driven by:
-
Event Processing: Proportional to syscall frequency
- Each event: path normalization, deduplication lookup, metric updates
- Typical overhead: <50 µs per event
-
Report Writing: Periodic JSON serialization
- Frequency: configurable via
-interval(default: 30s) - Duration: 1-10ms for typical workloads
- Frequency: configurable via
-
eBPF Overhead: Kernel-side filtering and event emission
- Per-syscall overhead: <1 µs
- Negligible for most workloads
Expected CPU Usage
| Workload | File Accesses/sec | Expected CPU | Notes |
|---|---|---|---|
| Idle | <10 | <0.1% | Background syscalls only |
| Light | 10-100 | 0.1-0.5% | Typical web services |
| Moderate | 100-1000 | 0.5-2% | Active applications |
| Heavy | 1000-10000 | 2-5% | High I/O workloads |
| Extreme | >10000 | 5-10% | May hit ring buffer limits |
Recommendations
Conservative (Most Applications)
resources:
requests:
cpu: 10m # 10 millicores (1% of 1 CPU)
limits:
cpu: 100m # 100 millicores (10% of 1 CPU)
Moderate (High I/O Applications)
resources:
requests:
cpu: 20m # 20 millicores (2% of 1 CPU)
limits:
cpu: 200m # 200 millicores (20% of 1 CPU)
High-Throughput (Data Processing)
resources:
requests:
cpu: 50m # 50 millicores (5% of 1 CPU)
limits:
cpu: 500m # 500 millicores (50% of 1 CPU)
Note: CPU limits should be generous to avoid throttling during burst activity (e.g., application startup).
Disk I/O
Snoop writes reports to disk periodically. The I/O pattern is:
- Frequency: Every
-intervalseconds (default: 30s) - Write size: Proportional to unique files (typical: 10-500 KB per report)
- Method: Atomic write via temp file + rename
- Peak I/O: 2× the report size (temp file + rename)
Recommendations
-
Report Interval:
- Default (30s) is appropriate for most workloads
- Increase for large file counts to reduce I/O frequency:
-interval=60s # For 100K+ unique files -interval=120s # For 500K+ unique files
-
Volume Type:
- Any volume type is suitable (even NFS)
- Atomic rename is used for crash safety
- No special I/O performance requirements
-
Volume Size:
- Minimum: 100 MB
- Recommended: 1 GB (allows for log rotation, multiple reports)
- Report size: typically 10-500 KB, max ~5 MB for 1M files
Complete Examples
Example 1: Small Web Service
Application characteristics:
- 2-3 containers per pod
- ~5,000 unique files accessed
- Light file I/O (<100 accesses/sec)
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: app
image: myapp:latest
# ... app config ...
- name: snoop
image: snoop:latest
args:
- -cgroup=/sys/fs/cgroup/kubepods/pod$(POD_UID)/$(CONTAINER_ID)
- -report=/data/snoop-report.json
- -interval=30s
- -max-unique-files=10000
- -log-level=info
securityContext:
capabilities:
add: [SYS_ADMIN, BPF, PERFMON]
readOnlyRootFilesystem: true
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 128Mi
volumeMounts:
- name: snoop-data
mountPath: /data
- name: cgroup
mountPath: /sys/fs/cgroup
readOnly: true
volumes:
- name: snoop-data
emptyDir:
sizeLimit: 100Mi
- name: cgroup
hostPath:
path: /sys/fs/cgroup
Example 2: Data Processing Application
Application characteristics:
- Single container, processing large datasets
- ~100,000 unique files accessed
- Heavy file I/O (1,000+ accesses/sec)
apiVersion: v1
kind: Pod
metadata:
name: data-processor
spec:
containers:
- name: app
image: data-processor:latest
# ... app config ...
- name: snoop
image: snoop:latest
args:
- -cgroup=/sys/fs/cgroup/kubepods/pod$(POD_UID)/$(CONTAINER_ID)
- -report=/data/snoop-report.json
- -interval=60s
- -max-unique-files=200000
- -log-level=info
securityContext:
capabilities:
add: [SYS_ADMIN, BPF, PERFMON]
readOnlyRootFilesystem: true
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: snoop-data
mountPath: /data
- name: cgroup
mountPath: /sys/fs/cgroup
readOnly: true
volumes:
- name: snoop-data
emptyDir:
sizeLimit: 500Mi
- name: cgroup
hostPath:
path: /sys/fs/cgroup
Example 3: Long-Running Observability
Application characteristics:
- Production service, running for weeks
- Unknown file access patterns (new deployment)
- Want complete observability without data loss
apiVersion: v1
kind: Pod
metadata:
name: production-service
spec:
containers:
- name: app
image: production-service:latest
# ... app config ...
- name: snoop
image: snoop:latest
args:
- -cgroup=/sys/fs/cgroup/kubepods/pod$(POD_UID)/$(CONTAINER_ID)
- -report=/data/snoop-report.json
- -interval=60s
- -max-unique-files=0 # Unbounded - monitor snoop_unique_files
- -log-level=warn # Reduce log noise in production
- -metrics-addr=:9090
securityContext:
capabilities:
add: [SYS_ADMIN, BPF, PERFMON]
readOnlyRootFilesystem: true
resources:
requests:
cpu: 20m
memory: 128Mi
limits:
cpu: 200m
memory: 1Gi # Generous limit for unbounded mode
volumeMounts:
- name: snoop-data
mountPath: /data
- name: cgroup
mountPath: /sys/fs/cgroup
readOnly: true
ports:
- name: metrics
containerPort: 9090
volumes:
- name: snoop-data
persistentVolumeClaim:
claimName: snoop-data-pvc # Persistent storage for long-term observation
- name: cgroup
hostPath:
path: /sys/fs/cgroup
Monitoring and Alerts
Use Prometheus metrics to monitor snoop resource usage:
Memory Monitoring
# Current unique files being tracked
snoop_unique_files
# Memory estimate (bytes): unique_files × 256
snoop_unique_files * 256
# Alert when approaching memory limits
snoop_unique_files * 256 > 100000000 # 100 MB
CPU Monitoring
# Event processing rate
rate(snoop_events_received_total[5m])
# Events dropped (ring buffer overflow)
rate(snoop_events_dropped_total[5m])
# Cache evictions (memory pressure)
rate(snoop_events_evicted_total[5m])
Recommended Alerts
# Alert when ring buffer is dropping events
- alert: SnoopRingBufferOverflow
expr: rate(snoop_events_dropped_total[5m]) > 10
for: 5m
annotations:
summary: "Snoop is dropping events due to ring buffer overflow"
description: "Consider increasing CPU limits or reducing file access rate"
# Alert when cache eviction is occurring
- alert: SnoopCacheEviction
expr: rate(snoop_events_evicted_total[5m]) > 0
for: 5m
annotations:
summary: "Snoop is evicting cached file paths"
description: "Consider increasing max-unique-files or memory limits"
# Alert when memory usage is high
- alert: SnoopHighMemoryUsage
expr: snoop_unique_files > 300000
for: 10m
annotations:
summary: "Snoop is tracking a large number of unique files"
description: "Consider setting max-unique-files limit or investigating unusual file access patterns"
Tuning Guidelines
Reducing Memory Usage
If memory usage is higher than expected:
-
Set a limit: Use
-max-unique-filesto cap memory growth-max-unique-files=50000 # Limit to ~12 MB -
Increase exclusions: Filter out unnecessary paths
-exclude=/proc/,/sys/,/dev/,/tmp/ # Add /tmp/ if temp files aren't relevant -
Monitor evictions: Check if LRU evictions are affecting data completeness
rate(snoop_events_evicted_total[1h])
Reducing CPU Usage
If CPU usage is higher than expected:
-
Increase report interval: Reduce JSON serialization frequency
-interval=60s # or 120s -
Check event rate: Verify application file access patterns
rate(snoop_events_received_total[5m]) -
Verify filtering: Ensure cgroup filtering is working correctly
# Check that only target container events are being processed # Log level debug will show which cgroup IDs are traced -log-level=debug
Handling Ring Buffer Overflow
If snoop_events_dropped_total is increasing:
- This is expected under extreme load (>10,000 file accesses/sec)
- Options:
- Increase CPU limits to process events faster
- Accept data loss during burst periods (best-effort design)
- For critical observability, consider increasing ring buffer size (requires recompilation)
Performance Impact on Application
Snoop's design minimizes impact on the traced application:
- Syscall overhead: <1 µs per syscall (eBPF filtering is extremely fast)
- No application changes: Zero code changes required
- Kernel-side filtering: Only relevant cgroups emit events
- Ring buffer: Asynchronous event delivery, no blocking
Expected application performance impact: <0.1% for typical workloads.
Verification
To verify snoop is not impacting your application:
- Before/after benchmarks: Run application benchmarks with and without snoop
- Monitor application metrics: Watch application-specific performance metrics
- Syscall latency: Use
perfto measure syscall latency changes
# Measure syscall latency without snoop
sudo perf stat -e 'syscalls:sys_enter_openat' -p $(pidof myapp)
# Compare with snoop enabled
Troubleshooting
High Memory Usage (Unbounded Mode)
Symptom: Memory grows continuously beyond expected levels
Diagnosis:
snoop_unique_files # Check current file count
Solutions:
- Verify application isn't accessing an unusual number of files
- Check for symlink loops or recursive directory traversal
- Add exclusions for problematic paths
- Set
-max-unique-fileslimit
OOMKilled
Symptom: Snoop container is killed by OOM
Diagnosis: Check kubectl describe pod or container logs
Solutions:
- Set or increase
-max-unique-files - Increase memory limits
- Reduce
-intervalso reports are written more frequently (releases memory during GC) - Add more path exclusions
High CPU Usage
Symptom: Snoop using more CPU than expected
Diagnosis:
rate(snoop_events_received_total[5m]) # Check event rate
Solutions:
- Verify event rate is reasonable for application
- Check if filtering is correct (only target cgroup should be traced)
- Increase CPU limits if processing legitimate high event rate
- Reduce log level from
debugtoinfoorwarn
Events Dropped
Symptom: snoop_events_dropped_total is increasing
Diagnosis:
rate(snoop_events_dropped_total[5m]) # Check drop rate
Solutions:
- This is expected under extreme load (>10K events/sec)
- Increase CPU limits to process events faster
- Accept data loss (snoop is designed as best-effort)
- For critical observability, consider tuning ring buffer size (code change required)
Summary
Default Configuration (suitable for 80% of workloads):
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 128Mi
args:
- -max-unique-files=50000
- -interval=30s
- -log-level=info
Key Principles:
- Start conservative, scale up based on metrics
- Always monitor
snoop_unique_filesin unbounded mode - CPU limits should be generous to handle burst activity
- Ring buffer drops are acceptable under extreme load
- Snoop is designed for best-effort observability, not guaranteed delivery