Skip to content

ops: budget alerts (S4) + SLI/SLO (X5) — re-homed from retired observability-stack - #29

Draft
braghettos wants to merge 2 commits into
mainfrom
feat/s4x5-rehome
Draft

ops: budget alerts (S4) + SLI/SLO (X5) — re-homed from retired observability-stack#29
braghettos wants to merge 2 commits into
mainfrom
feat/s4x5-rehome

Conversation

@braghettos

Copy link
Copy Markdown
Collaborator

Re-homes the S4 (budget threshold alerts) + X5 (SLI/SLO) observability work into the current observability home. observability-stack is a retired shell (its ClickStack/OTel/HyperDX pipeline decomposed here into krateo-clickstack-chart), so the budget DDL/evaluator/alerts and SLI/SLO belong in ops/, next to clickhouse-config/ and the pod-restart-alert/ bootstrap they mirror.

  • ops/budget-alert/ — budget DDL (001_budgets.sql, 002_budget_status.sql), budget-evaluator-cronjob.yaml, bootstrap-budget-alerts.sh (HyperDX), smoke test, .env.example.
  • ops/slo-alert/sli-queries.sql, bootstrap-slo-alerts.sh, .env.example.
  • docs/SLO.md.

Supersedes braghettos/observability-stack#7 (landed in the wrong/retired repo). Content carried over 1:1; internal path refs updated alerts/budgetops/budget-alert, dashboards/sloops/slo-alert.

Note: the review-pass fixes for this work (align budget DDL with the showback DDL, template CLICKHOUSE_DATABASE, make HyperDX bootstrap fail loud, D19a-align the evaluator's stdout) still apply and should be folded in here.

braghettos and others added 2 commits July 21, 2026 18:38
…lity-stack

The observability data plane split out of observability-stack (now a legacy
kagent/docs shell). Budget threshold alerts + budget DDL/evaluator and the
SLI/SLO queries/alerts belong here in krateo-clickstack-chart/ops, alongside
clickhouse-config and the pod-restart-alert bootstrap they mirror.

- ops/budget-alert/  (was alerts/budget/): DDL, budget-evaluator CronJob,
  HyperDX budget-alert bootstrap, smoke test.
- ops/slo-alert/     (was dashboards/slo/): SLI queries + SLO alert bootstrap.
- docs/SLO.md.

Supersedes braghettos/observability-stack#7 (wrong/retired repo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… glob)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment on lines +61 to +141
- name: evaluator
image: clickhouse/clickhouse-server:24.8-alpine
# ClickHouse builds the complete OTel-shaped JSON line per row
# (concat + toJSONString: all user-supplied values are
# JSON-escaped server-side; no shell parsing of row data).
# The scalar subquery is evaluated once, so every line of a
# run shares one trace_id; rand64() gives a per-line span_id.
# FORMAT TSVRaw = one unescaped line per row (toJSONString
# never emits raw control characters, lines stay atomic).
command:
- sh
- -c
- |
set -eu
clickhouse-client \
--host "${CLICKHOUSE_HOST}" \
--port "${CLICKHOUSE_PORT}" \
--user "${CLICKHOUSE_USER}" \
--password "${CLICKHOUSE_PASSWORD}" <<SQL
WITH (SELECT lower(hex(randomString(16)))) AS run_trace_id
SELECT concat(
'{"timestamp":"', formatDateTime(now(), '%Y-%m-%dT%H:%i:%SZ', 'UTC'), '",',
'"trace_id":"', run_trace_id, '",',
'"span_id":"', lower(hex(rand64())), '",',
'"severity_text":"', if(status = 'breached', 'ERROR', 'WARN'), '",',
'"severity_number":', if(status = 'breached', '17', '13'), ',',
'"body":"budget threshold crossed",',
'"attributes":', toJSONString(map(
'event.name', 'krateo.budget.threshold',
'service.name', 'krateo-budget-evaluator',
'krateo.budget.id', budget_id,
'krateo.budget.org', org,
'krateo.budget.tenant', tenant,
'krateo.budget.service', service,
'krateo.budget.tag_key', tag_key,
'krateo.budget.tag_value', tag_value,
'krateo.budget.period', period,
'krateo.budget.amount', toString(amount),
'krateo.budget.currency', currency,
'krateo.budget.spend', toString(spend),
'krateo.budget.spend_ratio', toString(round(spend_ratio, 4)),
'krateo.budget.status', status)),
'}')
FROM ${CLICKHOUSE_DATABASE}.budget_status
WHERE status != 'ok'
FORMAT TSVRaw
SQL
env:
- name: CLICKHOUSE_HOST
value: clickhouse.clickhouse-system.svc.cluster.local
- name: CLICKHOUSE_PORT
value: "9000"
# Must match the {{database}} the ddl/ files were rendered
# with (= the showback engine database; its default is
# `showback`), otherwise the budget_status view is not found.
- name: CLICKHOUSE_DATABASE
value: showback
- name: CLICKHOUSE_USER
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: username
- name: CLICKHOUSE_PASSWORD
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: password
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 20m
memory: 64Mi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
Comment on lines +61 to +141
- name: evaluator
image: clickhouse/clickhouse-server:24.8-alpine
# ClickHouse builds the complete OTel-shaped JSON line per row
# (concat + toJSONString: all user-supplied values are
# JSON-escaped server-side; no shell parsing of row data).
# The scalar subquery is evaluated once, so every line of a
# run shares one trace_id; rand64() gives a per-line span_id.
# FORMAT TSVRaw = one unescaped line per row (toJSONString
# never emits raw control characters, lines stay atomic).
command:
- sh
- -c
- |
set -eu
clickhouse-client \
--host "${CLICKHOUSE_HOST}" \
--port "${CLICKHOUSE_PORT}" \
--user "${CLICKHOUSE_USER}" \
--password "${CLICKHOUSE_PASSWORD}" <<SQL
WITH (SELECT lower(hex(randomString(16)))) AS run_trace_id
SELECT concat(
'{"timestamp":"', formatDateTime(now(), '%Y-%m-%dT%H:%i:%SZ', 'UTC'), '",',
'"trace_id":"', run_trace_id, '",',
'"span_id":"', lower(hex(rand64())), '",',
'"severity_text":"', if(status = 'breached', 'ERROR', 'WARN'), '",',
'"severity_number":', if(status = 'breached', '17', '13'), ',',
'"body":"budget threshold crossed",',
'"attributes":', toJSONString(map(
'event.name', 'krateo.budget.threshold',
'service.name', 'krateo-budget-evaluator',
'krateo.budget.id', budget_id,
'krateo.budget.org', org,
'krateo.budget.tenant', tenant,
'krateo.budget.service', service,
'krateo.budget.tag_key', tag_key,
'krateo.budget.tag_value', tag_value,
'krateo.budget.period', period,
'krateo.budget.amount', toString(amount),
'krateo.budget.currency', currency,
'krateo.budget.spend', toString(spend),
'krateo.budget.spend_ratio', toString(round(spend_ratio, 4)),
'krateo.budget.status', status)),
'}')
FROM ${CLICKHOUSE_DATABASE}.budget_status
WHERE status != 'ok'
FORMAT TSVRaw
SQL
env:
- name: CLICKHOUSE_HOST
value: clickhouse.clickhouse-system.svc.cluster.local
- name: CLICKHOUSE_PORT
value: "9000"
# Must match the {{database}} the ddl/ files were rendered
# with (= the showback engine database; its default is
# `showback`), otherwise the budget_status view is not found.
- name: CLICKHOUSE_DATABASE
value: showback
- name: CLICKHOUSE_USER
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: username
- name: CLICKHOUSE_PASSWORD
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: password
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 20m
memory: 64Mi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
Comment on lines +61 to +141
- name: evaluator
image: clickhouse/clickhouse-server:24.8-alpine
# ClickHouse builds the complete OTel-shaped JSON line per row
# (concat + toJSONString: all user-supplied values are
# JSON-escaped server-side; no shell parsing of row data).
# The scalar subquery is evaluated once, so every line of a
# run shares one trace_id; rand64() gives a per-line span_id.
# FORMAT TSVRaw = one unescaped line per row (toJSONString
# never emits raw control characters, lines stay atomic).
command:
- sh
- -c
- |
set -eu
clickhouse-client \
--host "${CLICKHOUSE_HOST}" \
--port "${CLICKHOUSE_PORT}" \
--user "${CLICKHOUSE_USER}" \
--password "${CLICKHOUSE_PASSWORD}" <<SQL
WITH (SELECT lower(hex(randomString(16)))) AS run_trace_id
SELECT concat(
'{"timestamp":"', formatDateTime(now(), '%Y-%m-%dT%H:%i:%SZ', 'UTC'), '",',
'"trace_id":"', run_trace_id, '",',
'"span_id":"', lower(hex(rand64())), '",',
'"severity_text":"', if(status = 'breached', 'ERROR', 'WARN'), '",',
'"severity_number":', if(status = 'breached', '17', '13'), ',',
'"body":"budget threshold crossed",',
'"attributes":', toJSONString(map(
'event.name', 'krateo.budget.threshold',
'service.name', 'krateo-budget-evaluator',
'krateo.budget.id', budget_id,
'krateo.budget.org', org,
'krateo.budget.tenant', tenant,
'krateo.budget.service', service,
'krateo.budget.tag_key', tag_key,
'krateo.budget.tag_value', tag_value,
'krateo.budget.period', period,
'krateo.budget.amount', toString(amount),
'krateo.budget.currency', currency,
'krateo.budget.spend', toString(spend),
'krateo.budget.spend_ratio', toString(round(spend_ratio, 4)),
'krateo.budget.status', status)),
'}')
FROM ${CLICKHOUSE_DATABASE}.budget_status
WHERE status != 'ok'
FORMAT TSVRaw
SQL
env:
- name: CLICKHOUSE_HOST
value: clickhouse.clickhouse-system.svc.cluster.local
- name: CLICKHOUSE_PORT
value: "9000"
# Must match the {{database}} the ddl/ files were rendered
# with (= the showback engine database; its default is
# `showback`), otherwise the budget_status view is not found.
- name: CLICKHOUSE_DATABASE
value: showback
- name: CLICKHOUSE_USER
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: username
- name: CLICKHOUSE_PASSWORD
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: password
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 20m
memory: 64Mi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
Comment on lines +61 to +141
- name: evaluator
image: clickhouse/clickhouse-server:24.8-alpine
# ClickHouse builds the complete OTel-shaped JSON line per row
# (concat + toJSONString: all user-supplied values are
# JSON-escaped server-side; no shell parsing of row data).
# The scalar subquery is evaluated once, so every line of a
# run shares one trace_id; rand64() gives a per-line span_id.
# FORMAT TSVRaw = one unescaped line per row (toJSONString
# never emits raw control characters, lines stay atomic).
command:
- sh
- -c
- |
set -eu
clickhouse-client \
--host "${CLICKHOUSE_HOST}" \
--port "${CLICKHOUSE_PORT}" \
--user "${CLICKHOUSE_USER}" \
--password "${CLICKHOUSE_PASSWORD}" <<SQL
WITH (SELECT lower(hex(randomString(16)))) AS run_trace_id
SELECT concat(
'{"timestamp":"', formatDateTime(now(), '%Y-%m-%dT%H:%i:%SZ', 'UTC'), '",',
'"trace_id":"', run_trace_id, '",',
'"span_id":"', lower(hex(rand64())), '",',
'"severity_text":"', if(status = 'breached', 'ERROR', 'WARN'), '",',
'"severity_number":', if(status = 'breached', '17', '13'), ',',
'"body":"budget threshold crossed",',
'"attributes":', toJSONString(map(
'event.name', 'krateo.budget.threshold',
'service.name', 'krateo-budget-evaluator',
'krateo.budget.id', budget_id,
'krateo.budget.org', org,
'krateo.budget.tenant', tenant,
'krateo.budget.service', service,
'krateo.budget.tag_key', tag_key,
'krateo.budget.tag_value', tag_value,
'krateo.budget.period', period,
'krateo.budget.amount', toString(amount),
'krateo.budget.currency', currency,
'krateo.budget.spend', toString(spend),
'krateo.budget.spend_ratio', toString(round(spend_ratio, 4)),
'krateo.budget.status', status)),
'}')
FROM ${CLICKHOUSE_DATABASE}.budget_status
WHERE status != 'ok'
FORMAT TSVRaw
SQL
env:
- name: CLICKHOUSE_HOST
value: clickhouse.clickhouse-system.svc.cluster.local
- name: CLICKHOUSE_PORT
value: "9000"
# Must match the {{database}} the ddl/ files were rendered
# with (= the showback engine database; its default is
# `showback`), otherwise the budget_status view is not found.
- name: CLICKHOUSE_DATABASE
value: showback
- name: CLICKHOUSE_USER
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: username
- name: CLICKHOUSE_PASSWORD
valueFrom:
secretKeyRef:
name: otel-clickhouse-credentials
key: password
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 20m
memory: 64Mi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants