Documentation
¶
Overview ¶
Package hotrestart implements a supervisor that manages the aether-proxy Envoy process and performs Envoy hot restarts across restart epochs, replicating the behavior of Envoy's hot-restarter.py in Go (see docs/proposals/001_proxy-hot-restart.md).
Model: the supervisor is the proxy container's entrypoint (PID 1). It forks an Envoy child with --restart-epoch 0 and a fixed --base-id. On a hot-restart trigger (a watched bootstrap-config change or SIGHUP) it forks a new Envoy with the next epoch; Envoy's own shared-memory + abstract-domain-socket IPC transfers the listen-socket FDs and stats to the new process, the old process drains, and after --parent-shutdown-time-s the supervisor terminates it.
The handoff also works ACROSS the pod boundary during a surge upgrade: the overlapping old and new aether-proxy pods share the node's network namespace, /dev/shm (a hostPath) and the same --base-id, so the new pod's Envoy hot-restarts from the old pod's. The supervisors coordinate the per-node restart epoch through a heartbeat file on the shared StateDir and gate pod readiness (ReadyMarkerPath) so the DaemonSet keeps the predecessor until the successor has taken over.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// EnvoyPath is the path to the Envoy binary.
EnvoyPath string
// ConfigPath is the Envoy bootstrap config (-c). A change to this file (when
// WatchConfig is set) triggers a hot restart.
ConfigPath string
// BaseID is Envoy's --base-id, pinned so successive epochs find the same
// shared-memory segment. Must be stable for the life of the container.
BaseID uint32
// DrainTime maps to Envoy --drain-time-s: how long the draining (old) epoch
// takes to gracefully close connections.
DrainTime time.Duration
// ParentShutdownTime maps to Envoy --parent-shutdown-time-s and gates when the
// supervisor SIGTERMs the previous epoch. Must exceed DrainTime.
ParentShutdownTime time.Duration
// ExtraArgs are appended to every Envoy invocation (e.g. -l, --service-cluster,
// --service-node, --service-zone, --concurrency). Concurrency must stay constant
// across epochs to avoid dropping accept-queue connections.
ExtraArgs []string
// WatchConfig enables an fsnotify watch on ConfigPath's directory that
// self-triggers a hot restart when the bootstrap config changes (e.g. a
// ConfigMap update propagated by the kubelet).
WatchConfig bool
// StateDir is the shared-hostPath dir holding the per-node epoch heartbeat
// file. A surging successor pod reads it to start at (live predecessor epoch +
// 1) and hot-restart across the pod boundary. Required.
StateDir string
// ReadyMarkerPath is the pod-local readiness marker the supervisor keeps
// present only while the node's Envoy admin reports LIVE at this supervisor's
// newest epoch. An exec readiness probe checks the marker so the DaemonSet
// keeps the old pod until the new one has taken over. Required.
ReadyMarkerPath string
// AdminAddress is the Envoy admin host:port used for the readiness check.
AdminAddress string
// HandoffDeadline overrides defaultHandoffDeadline (0 = default). Must be
// comfortably larger than ParentShutdownTime plus worst-case xDS-gated init.
HandoffDeadline time.Duration
// AdminUnresponsiveDeadline overrides defaultAdminUnresponsiveDeadline
// (0 = default).
AdminUnresponsiveDeadline time.Duration
}
Config configures the Envoy hot-restart supervisor.
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor owns the Envoy process lifecycle and performs hot restarts.
func New ¶
func New(cfg Config, log *slog.Logger, metrics *SupervisorMetrics) *Supervisor
New creates a Supervisor. metrics may be nil to disable instrumentation.
func (*Supervisor) Run ¶
func (s *Supervisor) Run(ctx context.Context) error
Run starts Envoy at epoch 0 and supervises it until ctx is canceled (SIGTERM/ SIGINT, which controller-runtime's signal handler maps to ctx.Done) or the newest epoch exits unexpectedly. A watched-config change or SIGHUP triggers a hot restart; SIGUSR1 is forwarded to the current child for log reopen.
type SupervisorMetrics ¶
type SupervisorMetrics struct {
// contains filtered or unexported fields
}
SupervisorMetrics holds the hot-restart lifecycle instruments. All methods are nil-receiver-safe so the supervisor runs unchanged without telemetry.
These exist for post-mortems of wedged or crashed proxy pods: the epoch gauge and handoff histogram show how far a handoff got and how long it took, the wedge counter says why the watchdog killed the pod, and the child-exit counter distinguishes expected drains from crashes.
func NewSupervisorMetrics ¶
func NewSupervisorMetrics(meter metric.Meter) (*SupervisorMetrics, error)
NewSupervisorMetrics registers the supervisor instruments on the given meter.
type Telemetry ¶
type Telemetry struct {
// contains filtered or unexported fields
}
Telemetry is the supervisor's metrics pipeline: a plain OTel SDK MeterProvider pushing to the collector over OTLP. No Prometheus registry, no scrape endpoint.
func NewTelemetry ¶
func NewTelemetry(ctx context.Context, cfg TelemetryConfig) (*Telemetry, error)
NewTelemetry builds the supervisor metrics pipeline.
type TelemetryConfig ¶
type TelemetryConfig struct {
// OTLPEndpoint is the OTLP gRPC collector metrics are pushed to
// (host:port, insecure). Required: the supervisor is push-only — it runs
// outside the controller-runtime manager (no shared Prometheus registry to
// bridge into) and in the host netns, where a scrape port would collide
// between surge predecessor and successor pods.
OTLPEndpoint string
// ServiceVersion is the build version recorded on the OTel resource.
ServiceVersion string
}
TelemetryConfig configures the supervisor's metrics pipeline.