Kubernetes resolution (--resolve-k8s) enriches your ClickHouse query log analysis by resolving client IP addresses to Kubernetes service and pod names.
Services are identified only by IP address:
{
"ip": "10.0.1.100",
"k8s_service": "10.0.1.100", // Just the IP
"k8s_namespace": "",
"k8s_pod": "",
"tables_used": ["users", "orders"],
"query_count": 1000
}In the report UI: You see raw IPs like 10.0.1.100 which are hard to identify.
Services are identified by their Kubernetes names:
{
"ip": "10.0.1.100",
"k8s_service": "api-server", // Resolved!
"k8s_namespace": "production", // Namespace
"k8s_pod": "api-server-abc123", // Pod name
"tables_used": ["users", "orders"],
"query_count": 1000
}In the report UI: You see meaningful names like production/api-server instead of IPs.
ClickHouse Query Log Kubernetes API
┌─────────────────┐ ┌──────────────────┐
│ client_ip │ │ Pod │
│ 10.0.1.100 │─────────────>│ IP: 10.0.1.100 │
└─────────────────┘ │ Name: api-server │
│ NS: production │
│ Service: api-svc │
└──────────────────┘
Steps:
- ClickSpectre reads
initial_address(client IP) fromsystem.query_log - Strips IPv6-mapped prefix (
::ffff:→ clean IPv4) - Queries Kubernetes API:
kubectl get pods --all-namespaces --field-selector status.podIP=<IP> - Finds the pod and its owning service
- Caches the result (5 min TTL by default)
- Enriches the report with K8s metadata
To use K8s resolution, you need:
# Test your kubectl access
kubectl get pods --all-namespacesIf this works, ClickSpectre can resolve IPs.
# Default location
~/.kube/config
# Or specify custom path
clickspectre analyze --resolve-k8s --kubeconfig /path/to/configYour kubeconfig user needs at least:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: clickspectre-reader
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list"]clickspectre analyze \
--clickhouse-dsn "clickhouse://host:9000/default" \
--output ./report \
--resolve-k8s \
--verboseclickspectre analyze \
--clickhouse-dsn "clickhouse://host:9000/default" \
--resolve-k8s \
--kubeconfig ~/.kube/production-config \
--verbose# If you hit K8s API rate limits, reduce RPS
clickspectre analyze \
--resolve-k8s \
--k8s-rate-limit 5 \
--k8s-cache-ttl 10m# After analysis, check the report
cat ./report/report.json | jq '.metadata.k8s_resolution_enabled'
# Should output: true
# Check if services have resolved names
cat ./report/report.json | jq '.services[0:3] | .[] | {ip, k8s_service, k8s_namespace}'Expected output (working):
{
"ip": "::ffff:10.0.1.100",
"k8s_service": "api-server",
"k8s_namespace": "production"
}If NOT working (fallback to IP):
{
"ip": "::ffff:10.0.1.100",
"k8s_service": "::ffff:10.0.1.100", // Still just the IP
"k8s_namespace": ""
}Run with --verbose to see resolution details:
clickspectre analyze --resolve-k8s --verboseLook for these log messages:
✅ Success:
Stripped IPv6-mapped prefix: ::ffff:10.0.1.100 → 10.0.1.100
Resolved IP 10.0.1.100 → production/api-server (pod: api-server-abc123)
❌ Failure:
Failed to resolve IP 10.0.1.100: no pod found with IP 10.0.1.100 (falling back to raw IP)
Symptom:
{"k8s_service": "::ffff:10.0.1.100"}Causes:
- Kubeconfig not found or invalid
- No access to Kubernetes cluster
- IPs don't match any pods (queries from outside K8s)
- IPv6 mapping issue (fixed in latest version)
Fix:
# Test kubectl access
kubectl get pods --all-namespaces
# Try with explicit kubeconfig
clickspectre analyze --resolve-k8s --kubeconfig ~/.kube/config --verboseThis is normal! IPs from:
127.0.0.1- localhost (ClickHouse internal queries)0.0.0.0- System queries- External IPs - Queries from outside the cluster
These cannot be resolved to K8s services and will fall back to showing the IP.
Symptom:
rate limiter wait failed: context canceled
Fix:
# Reduce rate limit
clickspectre analyze --resolve-k8s --k8s-rate-limit 5
# Increase cache TTL to reduce lookups
clickspectre analyze --resolve-k8s --k8s-cache-ttl 15mSymptom:
failed to list pods: pods is forbidden: User "system:serviceaccount:default:default" cannot list resource "pods" in API group ""
Fix: Grant RBAC permissions (see Requirements section above)
K8s resolution adds minimal overhead:
| Configuration | Impact |
|---|---|
| Default (10 RPS, 5 min cache) | ~1-2 seconds for 100 unique IPs |
| Cached (repeated IPs) | ~0ms (instant cache hits) |
| High rate (50 RPS, 1 min cache) | Faster but may hit K8s API limits |
Best practices:
- Use default rate limit (10 RPS) for most cases
- Increase cache TTL (10-15 min) for large datasets
- Reduce rate limit if you see K8s API errors
Instead of: "IP 10.0.1.100 uses table X" You get: "production/api-server uses table X"
Find which service is causing issues:
# Without K8s: "Heavy query from 10.0.1.100"
# With K8s: "Heavy query from production/data-processor"See which namespace accesses which tables:
production/api-server→users,ordersstaging/api-server→users_stagingmonitoring/prometheus→ system tables
The D3.js bipartite graph shows:
- Nodes: Kubernetes service names (not IPs)
- Edges: Service → Table connections
- Colors: Namespace-based coloring
If you don't need K8s service names, omit the flag for faster analysis:
# Without K8s resolution (faster)
clickspectre analyze --clickhouse-dsn "..." --output ./reportThis is useful when:
- ClickHouse is not accessed from Kubernetes
- You only care about table usage, not which service uses them
- Faster analysis is needed
- No kubectl access available
clickspectre analyze \
--clickhouse-dsn "clickhouse://prod:9000/default" \
--output ./prod-report \
--lookback 30d \
--resolve-k8s \
--kubeconfig ~/.kube/prod-config \
--k8s-rate-limit 20 \
--verbose# Cluster 1
clickspectre analyze \
--clickhouse-dsn "$CLUSTER1_DSN" \
--output ./reports/cluster1 \
--resolve-k8s \
--kubeconfig ~/.kube/cluster1
# Cluster 2
clickspectre analyze \
--clickhouse-dsn "$CLUSTER2_DSN" \
--output ./reports/cluster2 \
--resolve-k8s \
--kubeconfig ~/.kube/cluster2# When you don't need service names
clickspectre analyze \
--clickhouse-dsn "clickhouse://host:9000/default" \
--output ./report \
--lookback 90d- Cache TTL: 5 minutes (configurable)
- Cache size: Unlimited (LRU eviction could be added)
- Cache hit rate: Typically >95% for repeated IPs
- Algorithm: Token bucket
- Default: 10 requests/second
- Burst: 20 requests
- Purpose: Protect Kubernetes API from overload
When K8s resolution fails:
- Log the error (if
--verbose) - Use raw IP as service name
- Cache the fallback result
- Continue analysis (no failure)
This ensures graceful degradation - analysis never fails due to K8s issues.
Q: Do I need K8s resolution? A: Only if your ClickHouse is accessed from Kubernetes pods and you want to see service names instead of IPs.
Q: Will analysis fail if kubectl doesn't work? A: No! It falls back to showing IPs. Analysis continues normally.
Q: Can I use K8s resolution with readonly ClickHouse users? A: Yes! K8s resolution is independent of ClickHouse permissions.
Q: Does it work with multiple Kubernetes clusters?
A: Yes, but you need to run separate analyses with different --kubeconfig files.
Q: What if some IPs are external (not in K8s)? A: Those IPs will show as-is. Only IPs matching K8s pods are resolved.
#!/bin/bash
# Test K8s resolution
echo "=== ClickSpectre K8s Resolution Test ==="
echo ""
echo "1. Testing kubectl access..."
kubectl get pods --all-namespaces | head -5 || echo "FAILED: kubectl not working"
echo ""
echo "2. Running analysis with K8s resolution..."
./bin/clickspectre analyze \
--clickhouse-dsn "$CLICKHOUSE_DSN" \
--output /tmp/k8s-test \
--lookback 1h \
--max-rows 1000 \
--resolve-k8s \
--verbose | grep -E "(Resolved|Failed to resolve|Stripped)"
echo ""
echo "3. Checking results..."
cat /tmp/k8s-test/report.json | jq '.services[0:3] | .[] | {ip, k8s_service, k8s_namespace}'
echo ""
echo "4. Summary:"
K8S_ENABLED=$(cat /tmp/k8s-test/report.json | jq '.metadata.k8s_resolution_enabled')
echo " K8s resolution enabled: $K8S_ENABLED"
RESOLVED_COUNT=$(cat /tmp/k8s-test/report.json | jq '[.services[] | select(.k8s_namespace != "")] | length')
TOTAL_COUNT=$(cat /tmp/k8s-test/report.json | jq '.services | length')
echo " Resolved services: $RESOLVED_COUNT / $TOTAL_COUNT"Save as test-k8s-resolution.sh and run to diagnose issues.