Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ certain backends. Some of them can be disabled at compile-time using a build tag
|----------|:-----------|:--------------------|
| Kubernetes exporter | Kubernetes exporter reports node problems to Kubernetes API server: temporary problems get reported as Events, and permanent problems get reported as Node Conditions. |
| Prometheus exporter | Prometheus exporter reports node problems and metrics locally as Prometheus metrics |
| HTTP exporter | HTTP exporter serves the local `/healthz`, `/conditions` and `/debug/pprof` endpoints. It keeps node conditions in memory and does not require a Kubernetes API server, so it also works when `--enable-k8s-exporter` is `false`. |
| [Stackdriver exporter](https://github.com/kubernetes/node-problem-detector/blob/master/config/exporter/stackdriver-exporter.json) | Stackdriver exporter reports node problems and metrics to Stackdriver Monitoring API. | disable_stackdriver_exporter

# Usage
Expand Down Expand Up @@ -122,8 +123,14 @@ For example, to run without auth, use the following config:
http://APISERVER_IP:APISERVER_PORT?inClusterConfig=false
```
Refer to [heapster docs](https://github.com/kubernetes/heapster/blob/master/docs/source-configuration.md#kubernetes) for a complete list of available options.
* `--address`: The address to bind the node problem detector server.
* `--port`: The port to bind the node problem detector server. Use 0 to disable.

#### For HTTP exporter

The HTTP exporter serves `/healthz`, `/conditions` and `/debug/pprof`. It does not talk to the
Kubernetes API server, so these endpoints are available even when `--enable-k8s-exporter` is `false`.

* `--address`: The address to bind the node problem detector server, default to `127.0.0.1`.
* `--port`: The port to bind the node problem detector server, default to 20256. Use 0 to disable.

#### For Prometheus exporter

Expand Down
5 changes: 5 additions & 0 deletions cmd/nodeproblemdetector/node_problem_detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
_ "k8s.io/node-problem-detector/cmd/nodeproblemdetector/problemdaemonplugins"
"k8s.io/node-problem-detector/cmd/options"
"k8s.io/node-problem-detector/pkg/exporters"
"k8s.io/node-problem-detector/pkg/exporters/httpexporter"
"k8s.io/node-problem-detector/pkg/exporters/k8sexporter"
"k8s.io/node-problem-detector/pkg/exporters/prometheusexporter"
"k8s.io/node-problem-detector/pkg/problemdaemon"
Expand All @@ -51,6 +52,10 @@ func npdMain(ctx context.Context, npdo *options.NodeProblemDetectorOptions) erro

// Initialize exporters.
defaultExporters := []types.Exporter{}
if he := httpexporter.NewExporterOrDie(npdo); he != nil {
defaultExporters = append(defaultExporters, he)
klog.Info("HTTP exporter started.")
}
if ke := k8sexporter.NewExporterOrDie(ctx, npdo); ke != nil {
defaultExporters = append(defaultExporters, ke)
klog.Info("K8s exporter started.")
Expand Down
104 changes: 104 additions & 0 deletions pkg/exporters/httpexporter/http_exporter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
Copyright 2026 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package httpexporter

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add tests for this package?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have unit-tests for the other exporters. I can start adding unit-tests for sure, maybe it makes sense to cover unit-tests for the other exporters in a followup as well.


import (
"net"
"net/http"
"net/http/pprof"
"strconv"
"sync"

"k8s.io/klog/v2"

"k8s.io/node-problem-detector/cmd/options"
"k8s.io/node-problem-detector/pkg/types"
"k8s.io/node-problem-detector/pkg/util"
)

type httpExporter struct {
mu sync.RWMutex
conditions map[string]types.Condition
}

// NewExporterOrDie creates the standalone HTTP exporter and starts the server.
// Returns nil if --port is 0 (disabled). Panics on bind errors.
func NewExporterOrDie(npdo *options.NodeProblemDetectorOptions) types.Exporter {
if npdo.ServerPort <= 0 {
return nil
}

he := &httpExporter{
conditions: make(map[string]types.Condition),
}

addr := net.JoinHostPort(npdo.ServerAddress, strconv.Itoa(npdo.ServerPort))
mux := he.buildMux()
go func() {
if err := http.ListenAndServe(addr, mux); err != nil {
klog.Fatalf("Failed to start HTTP server: %v", err)
}
}()

klog.Infof("HTTP exporter started on %s", addr)
return he
}

func (he *httpExporter) buildMux() *http.ServeMux {
mux := http.NewServeMux()

// Add healthz http request handler. Always return ok now, add more health check
// logic in the future.
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte("ok")); err != nil {
klog.Errorf("Failed to write response: %v", err)
}
})

// Add the handler to serve condition http request.
mux.HandleFunc("/conditions", func(w http.ResponseWriter, r *http.Request) {
util.ReturnHTTPJson(w, he.getConditions())
})

// register pprof
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)

return mux
}

func (he *httpExporter) ExportProblems(status *types.Status) {
he.mu.Lock()
defer he.mu.Unlock()
for _, cdt := range status.Conditions {
he.conditions[cdt.Type] = cdt
}
}

func (he *httpExporter) getConditions() []types.Condition {
he.mu.RLock()
defer he.mu.RUnlock()
conditions := make([]types.Condition, 0, len(he.conditions))
for _, c := range he.conditions {
conditions = append(conditions, c)
}
return conditions
}
Loading