mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
runsc create: Record metric information when instrumentation is requested.
This changes the `runsc` sandbox creation process to gather metric
registration data before any container is started, and saves this in the state
file.
This change has no tests, but coverage is provided in a later change that
provides an end-to-end container tests that the metric server works and
exports data faithfully.
This change is part of a series of changes to support Prometheus-style metrics
in `runsc`. Doing so requires making several seemingly-odd design decisions,
due to the following architectural constraints:
- Prometheus requires an HTTP server serving the `/metrics` endpoint.
- For performance reasons, the `runsc boot` process cannot run the `netpoller`
goroutine.
- Since we don't want to write our own HTTP server implementation, this
means the HTTP endpoint has to be served by a separate process that
remains running during the lifetime of the container.
- The `runsc boot` process is untrusted.
- This means we cannot trust metrics data that comes out of the Sentry.
Therefore, there needs to be an elaborate dance where we pre-register
metric metadata before starting any untrusted workload. Then, the server
relaying the metric data must verify the validity of metric values against
this metric metadata. This avoids leaking metrics, cardinality blow-ups,
and other such DoS vectors.
- This feature needs to be easy-to-use in a typical Docker setting.
- This means having the ability to just say
`--metrics-server=localhost:1337` in the `runsc` runtime entry in
`/etc/docker/daemon.json` and have that Just Work(TM), even when multiple
containers are running.
- Since only one process may listen on a port at a given time, this means
the metric server needs to be able to multiplex requests out to multiple
running sandboxes, and remain alive for the entire duration of either of
these sandboxes.
- For this reason, the metrics server runs *outside* of the usual
per-container cgroups.
- This also saves system resources by not running one server per sandbox.
- The metrics server must be exposed to the outside world, and cannot assume
that its clients are trustworthy.
- For this reason, a metrics server is bound to a runtime root directory,
and double-checks all that the sandboxes it is asked to follow actually
exist in this root directory.
PiperOrigin-RevId: 503539630
This commit is contained in:
committed by
gVisor bot
parent
b331253f04
commit
8a63501720
+1
-1
@@ -15,6 +15,7 @@ go_library(
|
||||
],
|
||||
visibility = [
|
||||
"//runsc:__subpackages__",
|
||||
"//test:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/atomicbitops",
|
||||
@@ -45,7 +46,6 @@ go_library(
|
||||
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
|
||||
"@com_github_syndtr_gocapability//capability:go_default_library",
|
||||
"@com_github_vishvananda_netlink//:go_default_library",
|
||||
"@org_golang_google_protobuf//encoding/prototext:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
+15
-10
@@ -33,7 +33,6 @@ import (
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
"golang.org/x/sys/unix"
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
"gvisor.dev/gvisor/pkg/atomicbitops"
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
"gvisor.dev/gvisor/pkg/control/client"
|
||||
@@ -137,8 +136,7 @@ type Sandbox struct {
|
||||
// Used for verifying metric data integrity after containers are started.
|
||||
// Only populated if exporting metrics was requested when the sandbox was
|
||||
// created.
|
||||
// This is a textproto string of metricpb.MetricRegistration.
|
||||
RegisteredMetrics string `json:"registeredMetrics"`
|
||||
RegisteredMetrics *metricpb.MetricRegistration `json:"registeredMetrics"`
|
||||
|
||||
// MetricServerAddress is the address of the metric server that this sandbox
|
||||
// intends to export metrics for.
|
||||
@@ -281,6 +279,17 @@ func New(conf *config.Config, args *Args) (*Sandbox, error) {
|
||||
return nil, fmt.Errorf("cannot read client sync file: %w", err)
|
||||
}
|
||||
|
||||
if conf.MetricServer != "" {
|
||||
// The control server is up and the sandbox was configured to export metrics.
|
||||
// We must gather data about registered metrics prior to any process starting in the sandbox.
|
||||
log.Debugf("Getting metric registration information from sandbox %q", s.ID)
|
||||
var registeredMetrics control.MetricsRegistrationResponse
|
||||
if err := s.call(boot.MetricsGetRegistered, nil, ®isteredMetrics); err != nil {
|
||||
return nil, fmt.Errorf("cannot get registered metrics: %v", err)
|
||||
}
|
||||
s.RegisteredMetrics = registeredMetrics.RegisteredMetrics
|
||||
}
|
||||
|
||||
c.Release()
|
||||
return s, nil
|
||||
}
|
||||
@@ -990,7 +999,7 @@ func (s *Sandbox) IsRootContainer(cid string) bool {
|
||||
// Destroy frees all resources associated with the sandbox. It fails fast and
|
||||
// is idempotent.
|
||||
func (s *Sandbox) destroy() error {
|
||||
log.Debugf("Destroy sandbox %q", s.ID)
|
||||
log.Debugf("Destroying sandbox %q", s.ID)
|
||||
pid := s.Pid.load()
|
||||
if pid != 0 {
|
||||
log.Debugf("Killing sandbox %q", s.ID)
|
||||
@@ -1116,14 +1125,10 @@ func (s *Sandbox) UsageFD() (*control.MemoryUsageRecord, error) {
|
||||
// bogus metrics.
|
||||
// This returns an error if the sandbox has not requested instrumentation during creation time.
|
||||
func (s *Sandbox) GetRegisteredMetrics() (*metricpb.MetricRegistration, error) {
|
||||
if s.RegisteredMetrics == "" {
|
||||
if s.RegisteredMetrics == nil {
|
||||
return nil, errors.New("sandbox did not request instrumentation when it was created")
|
||||
}
|
||||
registeredMetrics := &metricpb.MetricRegistration{}
|
||||
if err := prototext.Unmarshal([]byte(s.RegisteredMetrics), registeredMetrics); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registeredMetrics, nil
|
||||
return s.RegisteredMetrics, nil
|
||||
}
|
||||
|
||||
// ExportMetrics returns a snapshot of metric values from the sandbox in Prometheus format.
|
||||
|
||||
Reference in New Issue
Block a user