state: add HA health prober
Ping HA subnet routers each probe cycle and mark unresponsive nodes unhealthy. Reconnecting a node clears its unhealthy state since the fresh Noise session proves basic connectivity. Updates #2129 Updates #2902
This commit is contained in:
parent
786ce2dce8
commit
90e65ccd63
3 changed files with 203 additions and 0 deletions
|
|
@ -60,6 +60,22 @@ type EphemeralConfig struct {
|
|||
InactivityTimeout time.Duration
|
||||
}
|
||||
|
||||
// HARouteConfig contains configuration for HA subnet router health probing.
|
||||
type HARouteConfig struct {
|
||||
// ProbeInterval is how often HA subnet routers are probed.
|
||||
// A zero or negative duration disables probing.
|
||||
ProbeInterval time.Duration
|
||||
|
||||
// ProbeTimeout is the maximum time to wait for a probe response
|
||||
// before declaring a node unhealthy. Must be less than ProbeInterval.
|
||||
ProbeTimeout time.Duration
|
||||
}
|
||||
|
||||
// RouteConfig contains configuration for route behaviour.
|
||||
type RouteConfig struct {
|
||||
HA HARouteConfig
|
||||
}
|
||||
|
||||
// NodeConfig contains configuration for node lifecycle and expiry.
|
||||
type NodeConfig struct {
|
||||
// Expiry is the default key expiry duration for non-tagged nodes.
|
||||
|
|
@ -70,6 +86,9 @@ type NodeConfig struct {
|
|||
|
||||
// Ephemeral contains configuration for ephemeral node lifecycle.
|
||||
Ephemeral EphemeralConfig
|
||||
|
||||
// Routes contains configuration for route behaviour.
|
||||
Routes RouteConfig
|
||||
}
|
||||
|
||||
// Config contains the initial Headscale configuration.
|
||||
|
|
@ -414,6 +433,8 @@ func LoadConfig(path string, isFile bool) error {
|
|||
|
||||
viper.SetDefault("node.expiry", "0")
|
||||
viper.SetDefault("node.ephemeral.inactivity_timeout", "120s")
|
||||
viper.SetDefault("node.routes.ha.probe_interval", "10s")
|
||||
viper.SetDefault("node.routes.ha.probe_timeout", "5s")
|
||||
|
||||
viper.SetDefault("tuning.notifier_send_timeout", "800ms")
|
||||
viper.SetDefault("tuning.batch_change_delay", "800ms")
|
||||
|
|
@ -576,6 +597,34 @@ func validateServerConfig() error {
|
|||
}
|
||||
}
|
||||
|
||||
// Validate HA health probing parameters
|
||||
if haInterval := viper.GetDuration(
|
||||
"node.routes.ha.probe_interval",
|
||||
); haInterval > 0 {
|
||||
if haInterval < 2*time.Second {
|
||||
errorText += fmt.Sprintf(
|
||||
"Fatal config error: node.routes.ha.probe_interval (%s) must be >= 2s\n",
|
||||
haInterval,
|
||||
)
|
||||
}
|
||||
|
||||
haTimeout := viper.GetDuration("node.routes.ha.probe_timeout")
|
||||
if haTimeout < 1*time.Second {
|
||||
errorText += fmt.Sprintf(
|
||||
"Fatal config error: node.routes.ha.probe_timeout (%s) must be >= 1s\n",
|
||||
haTimeout,
|
||||
)
|
||||
}
|
||||
|
||||
if haTimeout >= haInterval {
|
||||
errorText += fmt.Sprintf(
|
||||
"Fatal config error: node.routes.ha.probe_timeout (%s) must be less than node.routes.ha.probe_interval (%s)\n",
|
||||
haTimeout,
|
||||
haInterval,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate tuning parameters
|
||||
if size := viper.GetInt("tuning.node_store_batch_size"); size <= 0 {
|
||||
errorText += fmt.Sprintf(
|
||||
|
|
@ -1129,6 +1178,12 @@ func LoadServerConfig() (*Config, error) {
|
|||
Ephemeral: EphemeralConfig{
|
||||
InactivityTimeout: resolveEphemeralInactivityTimeout(),
|
||||
},
|
||||
Routes: RouteConfig{
|
||||
HA: HARouteConfig{
|
||||
ProbeInterval: viper.GetDuration("node.routes.ha.probe_interval"),
|
||||
ProbeTimeout: viper.GetDuration("node.routes.ha.probe_timeout"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Database: databaseConfig(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue