placement

package
v0.8.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package placement is the multi-node scheduler: label selectors, a fit check against node CPU, memory and disk headroom, then a spread that prefers the most headroom. No bin-packing and no affinity rules, by design.

Index

Constants

View Source
const (
	RiskReplicasOneZone     = "replicas_one_zone"
	RiskReplicasOneProvider = "replicas_one_provider"
)

Domain risk kinds.

View Source
const (
	LevelWarn = "warn"
	LevelInfo = "info"
)

Risk levels.

View Source
const (
	DimCPU    = "cpu"
	DimMemory = "memory"
	DimDisk   = "disk"
)

Dimension names in Evaluation.

View Source
const (
	LabelRegion   = "region"
	LabelZone     = "zone"
	LabelProvider = "provider"
)

Well-known node label keys that name a failure domain.

View Source
const EnvProviderSpreadWeight = "APP_PLACEMENT_PROVIDER_SPREAD_WEIGHT"

EnvProviderSpreadWeight names the env var weighting provider spread in replica scoring. 0 turns provider preference off.

View Source
const EnvRegionSpreadWeight = "APP_PLACEMENT_REGION_SPREAD_WEIGHT"

EnvRegionSpreadWeight names the env var weighting region spread in replica scoring. 0 turns region preference off.

Variables

View Source
var ErrNoEligibleNode = errors.New("placement: no eligible node")

ErrNoEligibleNode is returned when no candidate passes every filter.

Functions

func Check

func Check(c Candidate, req Request) error

Check validates an explicit target against req, the manual-assignment path.

func Filter

func Filter(c Candidate, req Request) string

Filter returns why c may not take req, or "" when it may.

func FormatBytes

func FormatBytes(b int64) string

FormatBytes renders bytes with a binary unit.

func FormatCPU

func FormatCPU(milli int64) string

FormatCPU renders millicores as cores.

func ParseLabels

func ParseLabels(pairs []string) (map[string]string, error)

ParseLabels turns ["k=v", ...] into a validated map.

func ValidateLabels

func ValidateLabels(labels map[string]string) error

ValidateLabels checks keys and values are short, plain identifiers.

func ZoneOf

func ZoneOf(labels map[string]string) string

ZoneOf returns the node's failure domain: its zone label, or "" when unlabeled.

Types

type Amount

type Amount struct {
	Value int64 `json:"value"`
	Known bool  `json:"known"`
}

Amount is a measured quantity. Known is false when the node never reported it: unknown is not zero (nothing fits) and not infinite (everything fits), it is "cannot be verified".

func Measured

func Measured(v int64) Amount

Measured returns a known Amount.

type Candidate

type Candidate struct {
	ID          string
	Name        string
	Labels      map[string]string
	Schedulable bool
	Online      bool
	// Load is how many apps and databases already run there.
	Load int
	// Capacity is what the node reported; Requested is what workloads
	// already placed on it asked for.
	Capacity  Capacity
	Requested Resources
}

Candidate is one node as the scheduler sees it.

type Capacity

type Capacity struct {
	CPUMilli       Amount `json:"cpu_milli"`
	MemoryBytes    Amount `json:"memory_bytes"`
	DiskTotalBytes Amount `json:"disk_total_bytes"`
	DiskFreeBytes  Amount `json:"disk_free_bytes"`
}

Capacity is what a node reports about itself.

type Decision

type Decision struct {
	NodeID     string       `json:"node_id"`
	Why        string       `json:"why"`
	Needs      Resources    `json:"needs"`
	Policy     Policy       `json:"policy"`
	Considered []Evaluation `json:"considered"`
	Rejected   []Rejection  `json:"rejected"`
	Warnings   []string     `json:"warnings,omitempty"`
}

Decision is the chosen node and the full account of how it was chosen.

func Pinned

func Pinned(cands []Candidate, req Request, nodeID string) Decision

Pinned records an operator's explicit choice. It is honored even when the node lacks room, and the shortfall is carried as a warning.

func Spread

func Spread(cands []Candidate, req Request) (Decision, error)

Spread filters out nodes that cannot take req, then picks the one that keeps the most headroom. Nodes whose capacity is unknown rank after every verified node; ties fall to load, then node id, so the choice is stable.

func (Decision) CapacityBlocked

func (d Decision) CapacityBlocked() bool

CapacityBlocked reports whether any node was refused for lack of room.

type Dimension

type Dimension struct {
	Name        string `json:"name"`
	Allocatable *int64 `json:"allocatable"`
	Requested   int64  `json:"requested"`
	Need        int64  `json:"need"`
	Free        *int64 `json:"free"`
	Shortfall   int64  `json:"shortfall"`
}

Dimension is one resource on one node, in the unit the operator reads. Allocatable is nil when the node never reported it. For disk, Requested is measured use and Allocatable already excludes the free-space floor.

func (Dimension) Describe

func (d Dimension) Describe() string

Describe renders d's shortfall or state for an operator.

type DomainGroup

type DomainGroup struct {
	Name          string   `json:"name"`
	Region        string   `json:"region,omitempty"`
	Providers     []string `json:"providers,omitempty"`
	NodeIDs       []string `json:"node_ids"`
	Nodes         int      `json:"nodes"`
	Online        int      `json:"online_nodes"`
	Schedulable   int      `json:"schedulable_nodes"`
	Workloads     int      `json:"workloads"`
	CPUMilli      int64    `json:"cpu_milli"`
	MemoryBytes   int64    `json:"memory_bytes"`
	DiskFreeBytes int64    `json:"disk_free_bytes"`
	Unverified    int      `json:"unverified_nodes"`
}

DomainGroup is the capacity of every node sharing one provider, region or zone. Capacity sums only what nodes reported; Unverified counts the nodes that reported nothing, so a total is never mistaken for the whole fleet.

type DomainOverview

type DomainOverview struct {
	Providers []DomainGroup `json:"providers"`
	Regions   []DomainGroup `json:"regions"`
	Zones     []DomainGroup `json:"zones"`
	Unlabeled int           `json:"unlabeled_nodes"`
	Total     int           `json:"total_nodes"`
}

DomainOverview is the fleet grouped three ways. Unlabeled nodes are counted separately instead of being hidden in an empty-named group.

func Domains

func Domains(cands []Candidate) DomainOverview

Domains groups candidates by provider, region and zone. Zones are keyed by region and zone together because zone names repeat across regions.

type DomainRisk

type DomainRisk struct {
	Kind         string   `json:"kind"`
	Level        string   `json:"level"`
	Name         string   `json:"name"`
	NodeID       string   `json:"node_id"`
	Replicas     int      `json:"replicas"`
	Zone         string   `json:"zone,omitempty"`
	Region       string   `json:"region,omitempty"`
	Provider     string   `json:"provider,omitempty"`
	Alternatives []string `json:"alternatives"`
	Message      string   `json:"message"`
}

DomainRisk says a workload's replicas share one failure domain although another domain could host a copy. All replicas of a workload run on one node today, so a workload with two or more replicas always shares fate.

func DomainRisks

func DomainRisks(cands []Candidate, in []RiskInput) []DomainRisk

DomainRisks lists workloads with two or more replicas whose node sits in a labeled zone or provider while an eligible node exists in a different one. The result is sorted by name so the same fleet always gives the same list.

type Evaluation

type Evaluation struct {
	NodeID     string      `json:"node_id"`
	Name       string      `json:"name"`
	Zone       string      `json:"zone,omitempty"`
	Region     string      `json:"region,omitempty"`
	Provider   string      `json:"provider,omitempty"`
	Eligible   bool        `json:"eligible"`
	Reason     string      `json:"reason,omitempty"`
	Capacity   bool        `json:"capacity_blocked,omitempty"`
	Unverified bool        `json:"unverified,omitempty"`
	Headroom   *int        `json:"headroom_permille,omitempty"`
	Load       int         `json:"load"`
	Dimensions []Dimension `json:"dimensions"`
}

Evaluation is how one node fared for one placement request.

func Evaluate

func Evaluate(c Candidate, req Request) Evaluation

Evaluate checks c against req: the structural filter first, then each resource. An unknown dimension never fails the node by itself, it marks the node Unverified (or fails it when the policy rejects unknowns).

type Placed

type Placed struct {
	Name     string `json:"name"`
	From     string `json:"from_node_id,omitempty"`
	NodeID   string `json:"node_id,omitempty"`
	NodeName string `json:"node_name,omitempty"`
	Zone     string `json:"zone,omitempty"`
	Why      string `json:"why,omitempty"`
	// Unplaced carries the refusal when no node could take the workload.
	Unplaced string `json:"unplaced,omitempty"`
}

Placed is one simulated workload and where the scheduler would put it.

type Policy

type Policy struct {
	CPUOvercommitPercent    int  `json:"cpu_overcommit_percent"`
	MemoryOvercommitPercent int  `json:"memory_overcommit_percent"`
	MinFreeDiskPercent      int  `json:"min_free_disk_percent"`
	RejectUnknown           bool `json:"reject_unknown"`
}

Policy is the operator's overcommit and headroom setting. Percent 100 means requests may add up to the node's measured total and no further; above 100 allows overcommit. MinFreeDiskPercent keeps that share of the disk unplaceable. RejectUnknown filters nodes whose capacity cannot be verified instead of ranking them last.

func DefaultPolicy

func DefaultPolicy() Policy

DefaultPolicy never overcommits, keeps 10 percent of disk free and ranks unverifiable nodes last rather than refusing them.

func (Policy) Validate

func (p Policy) Validate() error

Validate rejects percentages outside their useful range.

type Rejection

type Rejection struct {
	NodeID string `json:"node_id"`
	Reason string `json:"reason"`
}

Rejection says why one candidate was filtered out.

type Request

type Request struct {
	Selector Selector
	// Exclude is skipped outright, typically the node being drained.
	Exclude string
	// Needs are the workload's total requests; zero asks for nothing.
	Needs  Resources
	Policy Policy
	// Prefer wins when it is eligible, e.g. the node that serves ingress.
	Prefer string
	// Peers are node IDs already running this workload's other replicas.
	Peers []string
}

Request is what one placement decision needs.

type Resources

type Resources struct {
	CPUMilli    int64 `json:"cpu_milli"`
	MemoryBytes int64 `json:"memory_bytes"`
	DiskBytes   int64 `json:"disk_bytes"`
}

Resources is what a workload asks for, or what placed workloads add up to.

func (Resources) Add

func (r Resources) Add(o Resources) Resources

Add returns r plus o.

func (Resources) Times

func (r Resources) Times(n int64) Resources

Times returns r multiplied by n, the replica count.

type RiskInput

type RiskInput struct {
	Name     string
	NodeID   string
	Replicas int
	Selector Selector
}

RiskInput is one workload for DomainRisks.

type Selector

type Selector map[string]string

Selector is a set of key=value pairs a node's labels must all carry.

func (Selector) Matches

func (sel Selector) Matches(labels map[string]string) bool

Matches reports whether labels satisfy every pair in sel.

func (Selector) String

func (sel Selector) String() string

String renders sel as sorted "k=v,k=v".

type Simulation

type Simulation struct {
	Placed   []Placed      `json:"placed"`
	Unplaced int           `json:"unplaced"`
	Zones    []ZoneSummary `json:"zones"`
}

Simulation is a dry run: nothing is written, the candidates are copied.

func FailNodes

func FailNodes(cands []Candidate, workloads []Workload, dead func(Candidate) bool) Simulation

FailNodes simulates losing the nodes for which dead returns true: every workload on them is rescheduled onto survivors in a stable order (largest memory first, then name) so the same fleet always gives the same answer.

func Simulate

func Simulate(cands []Candidate, req Request, count int) Simulation

Simulate places count identical workloads one after another, each seeing the load the previous ones added, exactly as real creations would.

type Workload

type Workload struct {
	Name   string
	NodeID string
	Needs  Resources
	// Selector is the label constraint the workload was placed under.
	Selector Selector
}

Workload is something already running that a failure would displace.

type ZoneSummary

type ZoneSummary struct {
	Zone      string   `json:"zone"`
	Region    string   `json:"region,omitempty"`
	Providers []string `json:"providers,omitempty"`
	NodeIDs   []string `json:"node_ids"`
	Online    int      `json:"online_nodes"`
	Workloads int      `json:"workloads"`
}

ZoneSummary is one failure domain with the workloads it carries.

func Zones

func Zones(cands []Candidate) []ZoneSummary

Zones groups candidates by zone (unlabeled nodes share the "" zone).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL