What happened? How can we reproduce this?
What happened:
If Tetragon is started with --metrics-server set to an address/port that's already occupied by another process, it logs "Starting metrics server" but never reports the bind error and never actually serves metrics. The main process continues on as if everything is fine.
This makes the failure hard to diagnose.
How to reproduce:
- Start another listener on port 2112:
python3 -m http.server 2112 --bind 0.0.0.0
- Start Tetragon with metrics enabled on the same
tcp/2112:
sudo tetragon --metrics-server=:2112
- Observe:
- Tetragon logs:
level=info msg="Starting metrics server" addr=:2112
- Tetragon continues and reaches
"Listening for events..."
ss -tlnp | grep 2112 shows only the Python process listening
curl http://127.0.0.1:2112/metrics returns the Python HTTP server's response, not Prometheus metrics
- No error or warning is logged by Tetragon
Code reference:
pkg/metricsconfig/root.go:
func EnableMetrics(address string) {
reg := GetRegistry()
logger.GetLogger().Info("Starting metrics server", "addr", address)
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
http.ListenAndServe(address, nil)
}
http.ListenAndServe runs in a goroutine (called via go metricsconfig.EnableMetrics() in cmd/tetragon/main.go) with its return value ignored entirely.
Environment:
- Tetragon version: v1.7.0
- Platform: bare metal / systemd (also affects any non-Kubernetes deployment that enables --metrics-server)
- Kernel: 6.17.13
Impact:
In production deployments managed by configuration management tools (e.g. Chef) that may restart Tetragon rapidly, a port conflict can leave the service running but without working metrics, leading to silent observability gaps.
Proposed fix:
I would like to propose the following, roughly in order of increasing scope, and would appreciate maintainer input on how far to take it:
- Log the bind error. Capture
http.ListenAndServe's return value and log it instead of discarding it. This alone makes the failure observable and requires no API changes.
- Accept a
context.Context. Thread ctx into EnableMetrics and switch from the package-level http.ListenAndServe to a dedicated *http.Server, so the metrics server can be gracefully stopped via Shutdown(ctx) on ctx.Done() (currently there's no shutdown path for this server at all).
- Optional: bind synchronously. If we want startup to fail loudly on a bad/occupied address (rather than only logging after the fact), move
net.Listen out of the goroutine and into the synchronous startup path, returning the bind error to the caller so main() can abort startup.
Personally I would think 1 and 2 above is sufficent, 3 seems extreme for a secondary metrics system.
Tetragon Version
v1.7.0
Kernel Version
6.17.13
Kubernetes Version
n/a we are not running in kubernetes, however this probably also affects kube deployments
Bugtool
No response
Relevant log output
Anything else?
Let me know if I'm understanding the problem correctly, happy to put up a PR!
What happened? How can we reproduce this?
What happened:
If Tetragon is started with
--metrics-serverset to an address/port that's already occupied by another process, it logs"Starting metrics server"but never reports the bind error and never actually serves metrics. The main process continues on as if everything is fine.This makes the failure hard to diagnose.
How to reproduce:
python3 -m http.server 2112 --bind 0.0.0.0tcp/2112:sudo tetragon --metrics-server=:2112level=info msg="Starting metrics server" addr=:2112"Listening for events..."ss -tlnp | grep 2112shows only the Python process listeningcurl http://127.0.0.1:2112/metricsreturns the Python HTTP server's response, not Prometheus metricsCode reference:
pkg/metricsconfig/root.go:http.ListenAndServe runs in a goroutine (called via go
metricsconfig.EnableMetrics()incmd/tetragon/main.go) with its return value ignored entirely.Environment:
Impact:
In production deployments managed by configuration management tools (e.g. Chef) that may restart Tetragon rapidly, a port conflict can leave the service running but without working metrics, leading to silent observability gaps.
Proposed fix:
I would like to propose the following, roughly in order of increasing scope, and would appreciate maintainer input on how far to take it:
http.ListenAndServe's return value and log it instead of discarding it. This alone makes the failure observable and requires no API changes.context.Context. ThreadctxintoEnableMetricsand switch from the package-levelhttp.ListenAndServeto a dedicated*http.Server, so the metrics server can be gracefully stopped viaShutdown(ctx)onctx.Done()(currently there's no shutdown path for this server at all).net.Listenout of the goroutine and into the synchronous startup path, returning the bind error to the caller so main() can abort startup.Personally I would think 1 and 2 above is sufficent, 3 seems extreme for a secondary metrics system.
Tetragon Version
v1.7.0
Kernel Version
6.17.13
Kubernetes Version
n/a we are not running in kubernetes, however this probably also affects kube deployments
Bugtool
No response
Relevant log output
Anything else?
Let me know if I'm understanding the problem correctly, happy to put up a PR!