mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 08:16:39 +00:00
add basic metrics for stress testing
This commit is contained in:
@@ -300,14 +300,24 @@ func NewChecker(logger *log.Logger, provider clientProvider) *Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new health probe server.
|
// NewServer creates a new health probe server.
|
||||||
func NewServer(addr string, checker *Checker, logger *log.Logger) *Server {
|
// If metricsHandler is non-nil, it is mounted at /metrics on the same port.
|
||||||
|
func NewServer(addr string, checker *Checker, logger *log.Logger, metricsHandler http.Handler) *Server {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.StandardLogger()
|
logger = log.StandardLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler := checker.Handler()
|
||||||
|
if metricsHandler != nil {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/metrics", metricsHandler)
|
||||||
|
mux.Handle("/", checker.Handler())
|
||||||
|
handler = mux
|
||||||
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
server: &http.Server{
|
server: &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: checker.Handler(),
|
Handler: handler,
|
||||||
ReadTimeout: 5 * time.Second,
|
ReadTimeout: 5 * time.Second,
|
||||||
WriteTimeout: 5 * time.Second,
|
WriteTimeout: 5 * time.Second,
|
||||||
},
|
},
|
||||||
|
|||||||
56
proxy/internal/metrics/metrics.go
Normal file
56
proxy/internal/metrics/metrics.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Metrics struct {
|
||||||
|
requestsTotal prometheus.Counter
|
||||||
|
requestDuration prometheus.Histogram
|
||||||
|
activeRequests prometheus.Counter
|
||||||
|
backendDuration prometheus.Histogram
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(reg prometheus.Registerer) *Metrics {
|
||||||
|
return &Metrics{
|
||||||
|
requestsTotal: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "netbird_proxy_requests_total",
|
||||||
|
Help: "Total number of requests made to the netbird proxy",
|
||||||
|
}),
|
||||||
|
requestDuration: promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
|
||||||
|
Name: "netbird_proxy_request_duration_seconds",
|
||||||
|
Help: "Duration of requests made to the netbird proxy",
|
||||||
|
Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10},
|
||||||
|
}),
|
||||||
|
activeRequests: promauto.With(reg).NewCounter(prometheus.CounterOpts{
|
||||||
|
Name: "netbird_proxy_active_requests_total",
|
||||||
|
Help: "Current in-flight requests handled by the netbird proxy",
|
||||||
|
}),
|
||||||
|
backendDuration: promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
|
||||||
|
Name: "netbird_proxy_backend_duration_seconds",
|
||||||
|
Help: "Duration of peer round trip time from the netbird proxy",
|
||||||
|
Buckets: []float64{0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) Middleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
m.requestsTotal.Inc()
|
||||||
|
m.activeRequests.Inc()
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
m.activeRequests.Desc()
|
||||||
|
m.requestDuration.Observe(time.Since(start).Seconds())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) CompleteRoundTrip(t time.Duration) {
|
||||||
|
m.backendDuration.Observe(t.Seconds())
|
||||||
|
}
|
||||||
@@ -55,6 +55,8 @@ type managementClient interface {
|
|||||||
CreateProxyPeer(ctx context.Context, req *proto.CreateProxyPeerRequest, opts ...grpc.CallOption) (*proto.CreateProxyPeerResponse, error)
|
CreateProxyPeer(ctx context.Context, req *proto.CreateProxyPeerRequest, opts ...grpc.CallOption) (*proto.CreateProxyPeerResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type backendMetricRecorder func(duration time.Duration)
|
||||||
|
|
||||||
// NetBird provides an http.RoundTripper implementation
|
// NetBird provides an http.RoundTripper implementation
|
||||||
// backed by underlying NetBird connections.
|
// backed by underlying NetBird connections.
|
||||||
// Clients are keyed by AccountID, allowing multiple domains to share the same connection.
|
// Clients are keyed by AccountID, allowing multiple domains to share the same connection.
|
||||||
@@ -70,6 +72,8 @@ type NetBird struct {
|
|||||||
clients map[types.AccountID]*clientEntry
|
clients map[types.AccountID]*clientEntry
|
||||||
initLogOnce sync.Once
|
initLogOnce sync.Once
|
||||||
statusNotifier statusNotifier
|
statusNotifier statusNotifier
|
||||||
|
|
||||||
|
recordBackendDuration backendMetricRecorder
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientDebugInfo contains debug information about a client.
|
// ClientDebugInfo contains debug information about a client.
|
||||||
@@ -375,6 +379,10 @@ func (n *NetBird) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||||||
resp, err := transport.RoundTrip(req)
|
resp, err := transport.RoundTrip(req)
|
||||||
duration := time.Since(start)
|
duration := time.Since(start)
|
||||||
|
|
||||||
|
if n.recordBackendDuration != nil {
|
||||||
|
n.recordBackendDuration(duration)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.logger.Debugf("roundtrip: method=%s host=%s url=%s account=%s duration=%s err=%v",
|
n.logger.Debugf("roundtrip: method=%s host=%s url=%s account=%s duration=%s err=%v",
|
||||||
req.Method, req.Host, req.URL.String(), accountID, duration.Truncate(time.Millisecond), err)
|
req.Method, req.Host, req.URL.String(), accountID, duration.Truncate(time.Millisecond), err)
|
||||||
@@ -483,19 +491,20 @@ func (n *NetBird) ListClientsForStartup() map[types.AccountID]*embed.Client {
|
|||||||
// NewNetBird creates a new NetBird transport. Set wgPort to 0 for a random
|
// NewNetBird creates a new NetBird transport. Set wgPort to 0 for a random
|
||||||
// OS-assigned port. A fixed port only works with single-account deployments;
|
// OS-assigned port. A fixed port only works with single-account deployments;
|
||||||
// multiple accounts will fail to bind the same port.
|
// multiple accounts will fail to bind the same port.
|
||||||
func NewNetBird(mgmtAddr, proxyID, proxyAddr string, wgPort int, logger *log.Logger, notifier statusNotifier, mgmtClient managementClient) *NetBird {
|
func NewNetBird(mgmtAddr, proxyID, proxyAddr string, wgPort int, logger *log.Logger, notifier statusNotifier, mgmtClient managementClient, metric backendMetricRecorder) *NetBird {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.StandardLogger()
|
logger = log.StandardLogger()
|
||||||
}
|
}
|
||||||
return &NetBird{
|
return &NetBird{
|
||||||
mgmtAddr: mgmtAddr,
|
mgmtAddr: mgmtAddr,
|
||||||
proxyID: proxyID,
|
proxyID: proxyID,
|
||||||
proxyAddr: proxyAddr,
|
proxyAddr: proxyAddr,
|
||||||
wgPort: wgPort,
|
wgPort: wgPort,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
clients: make(map[types.AccountID]*clientEntry),
|
clients: make(map[types.AccountID]*clientEntry),
|
||||||
statusNotifier: notifier,
|
statusNotifier: notifier,
|
||||||
mgmtClient: mgmtClient,
|
mgmtClient: mgmtClient,
|
||||||
|
recordBackendDuration: metric,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (m *mockMgmtClient) CreateProxyPeer(_ context.Context, _ *proto.CreateProxy
|
|||||||
// mockNetBird creates a NetBird instance for testing without actually connecting.
|
// mockNetBird creates a NetBird instance for testing without actually connecting.
|
||||||
// It uses an invalid management URL to prevent real connections.
|
// It uses an invalid management URL to prevent real connections.
|
||||||
func mockNetBird() *NetBird {
|
func mockNetBird() *NetBird {
|
||||||
return NewNetBird("http://invalid.test:9999", "test-proxy", 0, nil, nil, &mockMgmtClient{})
|
return NewNetBird("http://invalid.test:9999", "test-proxy", "localhost", 0, nil, nil, &mockMgmtClient{}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetBird_AddPeer_CreatesClientForNewAccount(t *testing.T) {
|
func TestNetBird_AddPeer_CreatesClientForNewAccount(t *testing.T) {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cenkalti/backoff/v4"
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
@@ -38,6 +40,7 @@ import (
|
|||||||
proxygrpc "github.com/netbirdio/netbird/proxy/internal/grpc"
|
proxygrpc "github.com/netbirdio/netbird/proxy/internal/grpc"
|
||||||
"github.com/netbirdio/netbird/proxy/internal/health"
|
"github.com/netbirdio/netbird/proxy/internal/health"
|
||||||
"github.com/netbirdio/netbird/proxy/internal/k8s"
|
"github.com/netbirdio/netbird/proxy/internal/k8s"
|
||||||
|
"github.com/netbirdio/netbird/proxy/internal/metrics"
|
||||||
"github.com/netbirdio/netbird/proxy/internal/proxy"
|
"github.com/netbirdio/netbird/proxy/internal/proxy"
|
||||||
"github.com/netbirdio/netbird/proxy/internal/roundtrip"
|
"github.com/netbirdio/netbird/proxy/internal/roundtrip"
|
||||||
"github.com/netbirdio/netbird/proxy/internal/types"
|
"github.com/netbirdio/netbird/proxy/internal/types"
|
||||||
@@ -146,6 +149,10 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) {
|
|||||||
s.Logger = log.StandardLogger()
|
s.Logger = log.StandardLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start up metrics gathering
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
meter := metrics.New(reg)
|
||||||
|
|
||||||
// The very first thing to do should be to connect to the Management server.
|
// The very first thing to do should be to connect to the Management server.
|
||||||
// Without this connection, the Proxy cannot do anything.
|
// Without this connection, the Proxy cannot do anything.
|
||||||
mgmtURL, err := url.Parse(s.ManagementAddress)
|
mgmtURL, err := url.Parse(s.ManagementAddress)
|
||||||
@@ -192,7 +199,7 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) {
|
|||||||
|
|
||||||
// Initialize the netbird client, this is required to build peer connections
|
// Initialize the netbird client, this is required to build peer connections
|
||||||
// to proxy over.
|
// to proxy over.
|
||||||
s.netbird = roundtrip.NewNetBird(s.ManagementAddress, s.ID, s.ProxyURL, s.WireguardPort, s.Logger, s, s.mgmtClient)
|
s.netbird = roundtrip.NewNetBird(s.ManagementAddress, s.ID, s.ProxyURL, s.WireguardPort, s.Logger, s, s.mgmtClient, meter.CompleteRoundTrip)
|
||||||
|
|
||||||
// When generating ACME certificates, start a challenge server.
|
// When generating ACME certificates, start a challenge server.
|
||||||
tlsConfig := &tls.Config{}
|
tlsConfig := &tls.Config{}
|
||||||
@@ -271,7 +278,7 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) {
|
|||||||
if healthAddr == "" {
|
if healthAddr == "" {
|
||||||
healthAddr = "localhost:8080"
|
healthAddr = "localhost:8080"
|
||||||
}
|
}
|
||||||
s.healthServer = health.NewServer(healthAddr, s.healthChecker, s.Logger)
|
s.healthServer = health.NewServer(healthAddr, s.healthChecker, s.Logger, promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
|
||||||
healthListener, err := net.Listen("tcp", healthAddr)
|
healthListener, err := net.Listen("tcp", healthAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("health probe server listen on %s: %w", healthAddr, err)
|
return fmt.Errorf("health probe server listen on %s: %w", healthAddr, err)
|
||||||
@@ -285,7 +292,7 @@ func (s *Server) ListenAndServe(ctx context.Context, addr string) (err error) {
|
|||||||
// Start the reverse proxy HTTPS server.
|
// Start the reverse proxy HTTPS server.
|
||||||
s.https = &http.Server{
|
s.https = &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Handler: accessLog.Middleware(web.AssetHandler(s.auth.Protect(s.proxy))),
|
Handler: meter.Middleware(accessLog.Middleware(web.AssetHandler(s.auth.Protect(s.proxy)))),
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user