blob: 270ec8b6e651de5b916e89bb51f5cf3ca61ba48f [file] [log] [blame]
Serge Bazanski54e212a2023-06-14 13:45:11 +02001package metrics
2
3import (
4 "fmt"
5 "io"
Serge Bazanski54e212a2023-06-14 13:45:11 +02006 "net/http"
Tim Windelschmidtfd49f222023-07-20 14:27:50 +02007
Serge Bazanski54e212a2023-06-14 13:45:11 +02008 "source.monogon.dev/metropolis/node"
Tim Windelschmidtf64f1972023-07-28 00:00:50 +00009 "source.monogon.dev/metropolis/pkg/supervisor"
Serge Bazanski54e212a2023-06-14 13:45:11 +020010)
11
12// An Exporter is a Prometheus binary running under the Metrics service which
13// collects some metrics and exposes them on a locally bound TCP port.
14//
15// The Metrics Service will forward requests from /metrics/<name> to the
16// exporter.
17type Exporter struct {
18 // Name of the exporter, which becomes part of the metrics URL for this exporter.
19 Name string
20 // Port on which this exporter will be running.
21 Port node.Port
22 // Executable to run to start the exporter.
23 Executable string
24 // Arguments to start the exporter. The exporter should listen at 127.0.0.1 and
25 // the port specified by Port, and serve its metrics on /metrics.
26 Arguments []string
27}
28
29// DefaultExporters are the exporters which we run by default in Metropolis.
Tim Windelschmidtf64f1972023-07-28 00:00:50 +000030var DefaultExporters = []*Exporter{
Serge Bazanski54e212a2023-06-14 13:45:11 +020031 {
32 Name: "node",
33 Port: node.MetricsNodeListenerPort,
34 Executable: "/metrics/bin/node_exporter",
35 Arguments: []string{
36 "--web.listen-address=127.0.0.1:" + node.MetricsNodeListenerPort.PortString(),
Tim Windelschmidt3df66eb2023-07-17 15:58:07 +020037 "--collector.buddyinfo",
38 "--collector.zoneinfo",
39 "--collector.tcpstat",
Tim Windelschmidt79d0b0d2023-11-18 23:12:13 +010040 "--collector.cpu.info",
Tim Windelschmidt9907d192023-11-18 23:11:56 +010041 "--collector.netclass.ignored-devices=^(veth.*)$",
42 "--collector.netdev.device-exclude=^(veth.*)$",
Tim Windelschmidt3df66eb2023-07-17 15:58:07 +020043 "--collector.filesystem.mount-points-exclude=^/(dev|proc|sys|data/kubernetes/kubelet/pods/.+|tmp/.+|ephermal/containerd/.+)($|/)",
Serge Bazanski54e212a2023-06-14 13:45:11 +020044 },
45 },
Tim Windelschmidtc37a8862023-07-19 16:33:21 +020046 {
47 Name: "etcd",
48 Port: node.MetricsEtcdListenerPort,
49 },
Tim Windelschmidtfd49f222023-07-20 14:27:50 +020050 {
Tim Windelschmidtf64f1972023-07-28 00:00:50 +000051 Name: "kubernetes-scheduler",
52 Port: node.MetricsKubeSchedulerListenerPort,
Tim Windelschmidtfd49f222023-07-20 14:27:50 +020053 },
54 {
Tim Windelschmidtf64f1972023-07-28 00:00:50 +000055 Name: "kubernetes-controller-manager",
56 Port: node.MetricsKubeControllerManagerListenerPort,
Tim Windelschmidtfd49f222023-07-20 14:27:50 +020057 },
Lorenz Brun4b42c8a2023-11-19 07:02:51 +010058 {
59 Name: "kubernetes-apiserver",
60 Port: node.MetricsKubeAPIServerListenerPort,
61 },
Tim Windelschmidtfd49f222023-07-20 14:27:50 +020062}
63
Tim Windelschmidtf64f1972023-07-28 00:00:50 +000064func (e *Exporter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
65 if r.Method != http.MethodGet {
66 http.Error(w, fmt.Sprintf("method %q not allowed", r.Method), http.StatusMethodNotAllowed)
Serge Bazanski54e212a2023-06-14 13:45:11 +020067 return
68 }
69
Tim Windelschmidtf64f1972023-07-28 00:00:50 +000070 ctx := r.Context()
71
72 // We are supplying the http.Server with a BaseContext that contains the
73 // context from our runnable which contains the logger.
74 logger := supervisor.Logger(ctx)
75
76 url := "http://127.0.0.1:" + e.Port.PortString() + "/metrics"
77 outReq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
78 if err != nil {
79 logger.Errorf("%s: forwarding to %q failed: %v", r.RemoteAddr, e.Name, err)
80 http.Error(w, "internal server error", http.StatusInternalServerError)
81 return
82 }
83
84 res, err := http.DefaultTransport.RoundTrip(outReq)
85 if err != nil {
86 logger.Errorf("%s: forwarding to %q failed: %v", r.RemoteAddr, e.Name, err)
87 http.Error(w, "could not reach exporter", http.StatusBadGateway)
88 return
89 }
90 defer res.Body.Close()
91
Serge Bazanski54e212a2023-06-14 13:45:11 +020092 copyHeader(w.Header(), res.Header)
93 w.WriteHeader(res.StatusCode)
94
95 if _, err := io.Copy(w, res.Body); err != nil {
96 logger.Errorf("%s: copying response from %q failed: %v", r.RemoteAddr, e.Name, err)
97 return
98 }
Serge Bazanski54e212a2023-06-14 13:45:11 +020099}
100
101func copyHeader(dst, src http.Header) {
102 for k, vv := range src {
103 for _, v := range vv {
104 dst.Add(k, v)
105 }
106 }
107}
108
109func (e *Exporter) externalPath() string {
110 return "/metrics/" + e.Name
111}