istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/bootstrap/monitoring.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package bootstrap
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"net/http"
    21  	"time"
    22  
    23  	commonFeatures "istio.io/istio/pkg/features"
    24  	"istio.io/istio/pkg/log"
    25  	"istio.io/istio/pkg/monitoring"
    26  	istioNetUtil "istio.io/istio/pkg/util/net"
    27  	"istio.io/istio/pkg/version"
    28  )
    29  
    30  type monitor struct {
    31  	monitoringServer *http.Server
    32  }
    33  
    34  const (
    35  	metricsPath = "/metrics"
    36  	versionPath = "/version"
    37  )
    38  
    39  var (
    40  	serverStart = time.Now()
    41  
    42  	_ = monitoring.NewDerivedGauge(
    43  		"istiod_uptime_seconds",
    44  		"Current istiod server uptime in seconds",
    45  	).ValueFrom(func() float64 {
    46  		return time.Since(serverStart).Seconds()
    47  	})
    48  
    49  	versionTag   = monitoring.CreateLabel("version")
    50  	pilotVersion = monitoring.NewGauge(
    51  		"pilot_info",
    52  		"Pilot version and build information.",
    53  	)
    54  )
    55  
    56  func init() {
    57  	pilotVersion.With(versionTag.Value(version.Info.String())).Record(1)
    58  }
    59  
    60  func addMonitor(mux *http.ServeMux) error {
    61  	exporter, err := monitoring.RegisterPrometheusExporter(nil, nil)
    62  	if err != nil {
    63  		return fmt.Errorf("could not set up prometheus exporter: %v", err)
    64  	}
    65  	mux.Handle(metricsPath, metricsMiddleware(exporter))
    66  
    67  	mux.HandleFunc(versionPath, func(out http.ResponseWriter, req *http.Request) {
    68  		if _, err := out.Write([]byte(version.Info.String())); err != nil {
    69  			log.Errorf("Unable to write version string: %v", err)
    70  		}
    71  	})
    72  
    73  	return nil
    74  }
    75  
    76  func metricsMiddleware(handler http.Handler) http.Handler {
    77  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    78  		if commonFeatures.MetricsLocalhostAccessOnly && !istioNetUtil.IsRequestFromLocalhost(r) {
    79  			http.Error(w, "Only requests from localhost are allowed", http.StatusForbidden)
    80  			return
    81  		}
    82  		// Pass control back to the handler
    83  		handler.ServeHTTP(w, r)
    84  	})
    85  }
    86  
    87  // Deprecated: we shouldn't have 2 http ports. Will be removed after code using
    88  // this port is removed.
    89  func startMonitor(addr string, mux *http.ServeMux) (*monitor, error) {
    90  	m := &monitor{}
    91  
    92  	// get the network stuff setup
    93  	var listener net.Listener
    94  	if addr != "" {
    95  		var err error
    96  		if listener, err = net.Listen("tcp", addr); err != nil {
    97  			return nil, fmt.Errorf("unable to listen on socket: %v", err)
    98  		}
    99  	}
   100  
   101  	// NOTE: this is a temporary solution to provide bare-bones debug functionality
   102  	// for pilot. a full design / implementation of self-monitoring and reporting
   103  	// is coming. that design will include proper coverage of statusz/healthz type
   104  	// functionality, in addition to how pilot reports its own metrics.
   105  	if err := addMonitor(mux); err != nil {
   106  		return nil, fmt.Errorf("could not establish self-monitoring: %v", err)
   107  	}
   108  	if addr != "" {
   109  		m.monitoringServer = &http.Server{
   110  			Addr:        listener.Addr().String(),
   111  			Handler:     mux,
   112  			IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
   113  			ReadTimeout: 30 * time.Second,
   114  		}
   115  	}
   116  
   117  	version.Info.RecordComponentBuildTag("pilot")
   118  
   119  	if addr != "" {
   120  		go func() {
   121  			_ = m.monitoringServer.Serve(listener)
   122  		}()
   123  	}
   124  
   125  	return m, nil
   126  }
   127  
   128  func (m *monitor) Close() error {
   129  	if m.monitoringServer != nil {
   130  		return m.monitoringServer.Close()
   131  	}
   132  	return nil
   133  }
   134  
   135  // initMonitor initializes the configuration for the pilot monitoring server.
   136  func (s *Server) initMonitor(addr string) error { // nolint: unparam
   137  	s.addStartFunc("monitoring", func(stop <-chan struct{}) error {
   138  		monitor, err := startMonitor(addr, s.monitoringMux)
   139  		if err != nil {
   140  			return err
   141  		}
   142  		go func() {
   143  			<-stop
   144  			err := monitor.Close()
   145  			log.Debugf("Monitoring server terminated: %v", err)
   146  		}()
   147  		return nil
   148  	})
   149  	return nil
   150  }