126 lines
5.2 KiB
Text
126 lines
5.2 KiB
Text
# Service classification — the prior that derives a component's compute envelope,
|
|
# QoS intent, stateful-ness, and preferred node placement. A component declares
|
|
# `class` and optionally overrides `resources`/`placement`; consumers (render
|
|
# pipeline, placement simulator) resolve effective values via `effective`.
|
|
|
|
{
|
|
# Coarse service taxonomy. Drives the default resource envelope and node affinity.
|
|
# 'gateway is the non-compute case: a declarative resource (e.g. a Cilium Gateway
|
|
# API object) with no pods — no resource envelope, no placement.
|
|
ServiceClass = [| 'controller, 'web, 'app_server, 'cache, 'datastore, 'storage_engine, 'edge, 'gateway |],
|
|
|
|
# Node role taxonomy. A component's class maps to the node classes it may run on.
|
|
NodeClass = [| 'control_plane, 'general, 'storage, 'edge |],
|
|
|
|
# Effective compute envelope — the complete output shape of `effective`.
|
|
ResourceSpec = {
|
|
cpu | { request | String, limit | String },
|
|
memory | { request | String, limit | String },
|
|
},
|
|
|
|
# Partial override a component may declare under `resources`. Any omitted field
|
|
# falls back to the class default during resolution.
|
|
ResourceOverride = {
|
|
cpu | { request | String, limit | String } | optional,
|
|
memory | { request | String, limit | String } | optional,
|
|
},
|
|
|
|
# Runtime health probe. `type` selects the k8s probe handler; `port` defaults to
|
|
# the component's first declared port when omitted by the renderer.
|
|
ProbeSpec = {
|
|
type | [| 'http, 'tcp, 'exec |] | doc "k8s probe handler" | default = 'http,
|
|
path | String | doc "httpGet path (http type)" | default = "/",
|
|
port | Number | optional,
|
|
command | Array String | doc "exec command (exec type)" | default = [],
|
|
initial_delay | Number | doc "initialDelaySeconds" | default = 30,
|
|
period | Number | doc "periodSeconds" | default = 15,
|
|
timeout | Number | doc "timeoutSeconds" | default = 5,
|
|
failure_threshold | Number | doc "failureThreshold" | default = 3,
|
|
},
|
|
|
|
# Placement policy. `mode` 'prefer = soft affinity (respects cost co-location);
|
|
# 'require = hard nodeSelector. `spread` = anti-affinity across nodes.
|
|
Placement = {
|
|
node_class | Array NodeClass | optional,
|
|
mode | [| 'prefer, 'require |] | doc "soft (prefer) vs hard (require) placement" | default = 'prefer,
|
|
spread | Bool | doc "anti-affinity across nodes" | default = true,
|
|
},
|
|
|
|
# Per-class default envelope. cpu/memory are k8s quantity strings; `qos` is the
|
|
# intended QoS class (k8s derives actual QoS from request==limit); `stateful`
|
|
# marks PVC-backed services; `node_class` is the preferred placement.
|
|
class_default = fun class =>
|
|
class
|
|
|> match {
|
|
'controller => {
|
|
cpu = { request = "50m", limit = "200m" },
|
|
memory = { request = "64Mi", limit = "128Mi" },
|
|
qos = 'burstable,
|
|
stateful = false,
|
|
node_class = ['control_plane, 'general],
|
|
},
|
|
'web => {
|
|
cpu = { request = "50m", limit = "500m" },
|
|
memory = { request = "128Mi", limit = "256Mi" },
|
|
qos = 'burstable,
|
|
stateful = false,
|
|
node_class = ['general],
|
|
},
|
|
'app_server => {
|
|
cpu = { request = "100m", limit = "1000m" },
|
|
memory = { request = "384Mi", limit = "768Mi" },
|
|
qos = 'burstable,
|
|
stateful = false,
|
|
node_class = ['general],
|
|
},
|
|
'cache => {
|
|
cpu = { request = "50m", limit = "500m" },
|
|
memory = { request = "64Mi", limit = "256Mi" },
|
|
qos = 'burstable,
|
|
stateful = false,
|
|
node_class = ['general, 'storage],
|
|
},
|
|
'datastore => {
|
|
cpu = { request = "200m", limit = "1000m" },
|
|
memory = { request = "512Mi", limit = "1Gi" },
|
|
qos = 'burstable,
|
|
stateful = true,
|
|
node_class = ['storage],
|
|
},
|
|
'storage_engine => {
|
|
cpu = { request = "100m", limit = "500m" },
|
|
memory = { request = "256Mi", limit = "512Mi" },
|
|
qos = 'guaranteed,
|
|
stateful = true,
|
|
node_class = ['storage],
|
|
},
|
|
'edge => {
|
|
cpu = { request = "50m", limit = "500m" },
|
|
memory = { request = "64Mi", limit = "256Mi" },
|
|
qos = 'burstable,
|
|
stateful = false,
|
|
node_class = ['edge, 'general],
|
|
},
|
|
'gateway => {
|
|
# Non-compute declarative resource (Gateway CR) — no pods, no footprint.
|
|
cpu = { request = "0", limit = "0" },
|
|
memory = { request = "0", limit = "0" },
|
|
qos = 'besteffort,
|
|
stateful = false,
|
|
node_class = [],
|
|
},
|
|
},
|
|
|
|
# Resolve effective resources + placement for a class with optional partial
|
|
# overrides ({cpu?, memory?, node_class?}). Override fields win; the rest fall
|
|
# back to the class default. Output conforms to ResourceSpec plus placement hints.
|
|
effective = fun class override =>
|
|
let d = class_default class in
|
|
{
|
|
cpu = if std.record.has_field "cpu" override then override.cpu else d.cpu,
|
|
memory = if std.record.has_field "memory" override then override.memory else d.memory,
|
|
qos = d.qos,
|
|
stateful = d.stateful,
|
|
node_class = if std.record.has_field "node_class" override then override.node_class else d.node_class,
|
|
},
|
|
}
|