logging: add trace analytics ingestion

This commit is contained in:
Brad Stein 2026-01-10 00:12:55 -03:00
parent 67415e665c
commit af9ab30849
11 changed files with 304 additions and 44 deletions

View File

@ -8,6 +8,7 @@ resources:
- jetstack.yaml - jetstack.yaml
- jenkins.yaml - jenkins.yaml
- mailu.yaml - mailu.yaml
- opentelemetry.yaml
- opensearch.yaml - opensearch.yaml
- harbor.yaml - harbor.yaml
- prometheus.yaml - prometheus.yaml

View File

@ -0,0 +1,9 @@
# infrastructure/sources/helm/opentelemetry.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: opentelemetry
namespace: flux-system
spec:
interval: 1h
url: https://open-telemetry.github.io/opentelemetry-helm-charts

View File

@ -180,7 +180,7 @@ def build_objects() -> tuple[list[dict], list[dict], list[dict]]:
"description": app.description, "description": app.description,
"baseQuery": app.base_query, "baseQuery": app.base_query,
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [], "traceGroups": [app.name],
} }
for app in apps for app in apps
] ]

View File

@ -0,0 +1,75 @@
# services/logging/data-prepper-helmrelease.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: data-prepper
namespace: logging
spec:
interval: 15m
timeout: 10m
chart:
spec:
chart: data-prepper
version: "~0.3.1"
sourceRef:
kind: HelmRepository
name: opensearch
namespace: flux-system
values:
fullnameOverride: data-prepper
replicaCount: 1
config:
data-prepper-config.yaml: |
ssl: false
pipelineConfig:
enabled: true
config:
entry-pipeline:
delay: "100"
source:
otel_trace_source:
ssl: false
sink:
- pipeline:
name: "raw-pipeline"
- pipeline:
name: "service-map-pipeline"
raw-pipeline:
source:
pipeline:
name: "entry-pipeline"
processor:
- otel_traces:
sink:
- opensearch:
hosts: ["http://opensearch-master.logging.svc.cluster.local:9200"]
index_type: trace-analytics-raw
service-map-pipeline:
delay: "100"
source:
pipeline:
name: "entry-pipeline"
processor:
- service_map:
sink:
- opensearch:
hosts: ["http://opensearch-master.logging.svc.cluster.local:9200"]
index_type: trace-analytics-service-map
resources:
requests:
cpu: "200m"
memory: "512Mi"
limits:
memory: "1Gi"
nodeSelector:
node-role.kubernetes.io/worker: "true"
hardware: rpi5
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values:
- rpi5

View File

@ -3,11 +3,13 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- namespace.yaml - namespace.yaml
- opensearch-dashboards-objects.yaml
- opensearch-observability-objects.yaml
- opensearch-pvc.yaml - opensearch-pvc.yaml
- opensearch-helmrelease.yaml - opensearch-helmrelease.yaml
- opensearch-dashboards-helmrelease.yaml - opensearch-dashboards-helmrelease.yaml
- opensearch-dashboards-objects.yaml - data-prepper-helmrelease.yaml
- opensearch-observability-objects.yaml - otel-collector-helmrelease.yaml
- opensearch-ism-job.yaml - opensearch-ism-job.yaml
- opensearch-dashboards-setup-job.yaml - opensearch-dashboards-setup-job.yaml
- opensearch-observability-setup-job.yaml - opensearch-observability-setup-job.yaml

View File

@ -4,132 +4,170 @@
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'bstein-dev-home'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'bstein-dev-home'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"bstein-dev-home"
]
}, },
{ {
"name": "pegasus", "name": "pegasus",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'pegasus'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'pegasus'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"pegasus"
]
}, },
{ {
"name": "jellyfin", "name": "jellyfin",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'jellyfin'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'jellyfin'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"jellyfin"
]
}, },
{ {
"name": "vaultwarden", "name": "vaultwarden",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vaultwarden'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vaultwarden'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"vaultwarden"
]
}, },
{ {
"name": "mailu", "name": "mailu",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'mailu-mailserver'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'mailu-mailserver'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"mailu"
]
}, },
{ {
"name": "nextcloud", "name": "nextcloud",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'nextcloud'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'nextcloud'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"nextcloud"
]
}, },
{ {
"name": "gitea", "name": "gitea",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'gitea'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'gitea'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"gitea"
]
}, },
{ {
"name": "jenkins", "name": "jenkins",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jenkins'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jenkins'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"jenkins"
]
}, },
{ {
"name": "harbor", "name": "harbor",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'harbor'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'harbor'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"harbor"
]
}, },
{ {
"name": "vault", "name": "vault",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vault'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vault'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"vault"
]
}, },
{ {
"name": "keycloak", "name": "keycloak",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'sso'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'sso'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"keycloak"
]
}, },
{ {
"name": "flux-system", "name": "flux-system",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'flux-system'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'flux-system'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"flux-system"
]
}, },
{ {
"name": "comms", "name": "comms",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"comms"
]
}, },
{ {
"name": "element-web", "name": "element-web",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'element-web'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'element-web'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"element-web"
]
}, },
{ {
"name": "element-call", "name": "element-call",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'element-call'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'element-call'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"element-call"
]
}, },
{ {
"name": "matrix-synapse", "name": "matrix-synapse",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'synapse'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'synapse'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"matrix-synapse"
]
}, },
{ {
"name": "livekit", "name": "livekit",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'livekit'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'livekit'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"livekit"
]
}, },
{ {
"name": "coturn", "name": "coturn",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'coturn'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'coturn'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"coturn"
]
}, },
{ {
"name": "lesavka", "name": "lesavka",
"description": "", "description": "",
"baseQuery": "source = journald-* | where _HOSTNAME = 'titan-jh'", "baseQuery": "source = journald-* | where _HOSTNAME = 'titan-jh'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"lesavka"
]
} }
] ]

View File

@ -2,7 +2,7 @@
apiVersion: batch/v1 apiVersion: batch/v1
kind: Job kind: Job
metadata: metadata:
name: opensearch-ism-setup-4 name: opensearch-ism-setup-5
namespace: logging namespace: logging
spec: spec:
backoffLimit: 3 backoffLimit: 3
@ -48,6 +48,11 @@ spec:
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d "${policy}" >/dev/null -d "${policy}" >/dev/null
trace_policy='{"policy":{"description":"Delete trace analytics after 30 days","schema_version":1,"default_state":"hot","states":[{"name":"hot","actions":[],"transitions":[{"state_name":"delete","conditions":{"min_index_age":"30d"}}]},{"name":"delete","actions":[{"delete":{}}],"transitions":[]}]}}'
curl -sS -X PUT "${OS_URL}/_plugins/_ism/policies/trace-analytics-30d" \
-H 'Content-Type: application/json' \
-d "${trace_policy}" >/dev/null
kube_template='{"index_patterns":["kube-*"],"priority":200,"template":{"settings":{"index.number_of_shards":1,"index.number_of_replicas":0,"index.refresh_interval":"30s","plugins.index_state_management.policy_id":"logging-180d"},"mappings":{"properties":{"@timestamp":{"type":"date"}}}}}' kube_template='{"index_patterns":["kube-*"],"priority":200,"template":{"settings":{"index.number_of_shards":1,"index.number_of_replicas":0,"index.refresh_interval":"30s","plugins.index_state_management.policy_id":"logging-180d"},"mappings":{"properties":{"@timestamp":{"type":"date"}}}}}'
curl -sS -X PUT "${OS_URL}/_index_template/kube-logs" \ curl -sS -X PUT "${OS_URL}/_index_template/kube-logs" \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
@ -58,6 +63,11 @@ spec:
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d "${journal_template}" >/dev/null -d "${journal_template}" >/dev/null
trace_template='{"index_patterns":["trace-analytics-*"],"priority":200,"template":{"settings":{"index.number_of_shards":1,"index.number_of_replicas":0,"index.refresh_interval":"30s","plugins.index_state_management.policy_id":"trace-analytics-30d"}}}'
curl -sS -X PUT "${OS_URL}/_index_template/trace-analytics" \
-H 'Content-Type: application/json' \
-d "${trace_template}" >/dev/null
curl -sS -X PUT "${OS_URL}/_all/_settings" \ curl -sS -X PUT "${OS_URL}/_all/_settings" \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{"index":{"number_of_replicas":0}}' >/dev/null -d '{"index":{"number_of_replicas":0}}' >/dev/null

View File

@ -13,133 +13,171 @@ data:
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'bstein-dev-home'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'bstein-dev-home'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"bstein-dev-home"
]
}, },
{ {
"name": "pegasus", "name": "pegasus",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'pegasus'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'pegasus'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"pegasus"
]
}, },
{ {
"name": "jellyfin", "name": "jellyfin",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'jellyfin'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jellyfin' and kubernetes.labels.app = 'jellyfin'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"jellyfin"
]
}, },
{ {
"name": "vaultwarden", "name": "vaultwarden",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vaultwarden'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vaultwarden'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"vaultwarden"
]
}, },
{ {
"name": "mailu", "name": "mailu",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'mailu-mailserver'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'mailu-mailserver'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"mailu"
]
}, },
{ {
"name": "nextcloud", "name": "nextcloud",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'nextcloud'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'nextcloud'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"nextcloud"
]
}, },
{ {
"name": "gitea", "name": "gitea",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'gitea'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'gitea'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"gitea"
]
}, },
{ {
"name": "jenkins", "name": "jenkins",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jenkins'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'jenkins'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"jenkins"
]
}, },
{ {
"name": "harbor", "name": "harbor",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'harbor'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'harbor'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"harbor"
]
}, },
{ {
"name": "vault", "name": "vault",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vault'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'vault'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"vault"
]
}, },
{ {
"name": "keycloak", "name": "keycloak",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'sso'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'sso'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"keycloak"
]
}, },
{ {
"name": "flux-system", "name": "flux-system",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'flux-system'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'flux-system'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"flux-system"
]
}, },
{ {
"name": "comms", "name": "comms",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"comms"
]
}, },
{ {
"name": "element-web", "name": "element-web",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'element-web'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'element-web'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"element-web"
]
}, },
{ {
"name": "element-call", "name": "element-call",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'element-call'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'element-call'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"element-call"
]
}, },
{ {
"name": "matrix-synapse", "name": "matrix-synapse",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'synapse'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.container_name = 'synapse'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"matrix-synapse"
]
}, },
{ {
"name": "livekit", "name": "livekit",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'livekit'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'livekit'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"livekit"
]
}, },
{ {
"name": "coturn", "name": "coturn",
"description": "", "description": "",
"baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'coturn'", "baseQuery": "source = kube-* | where kubernetes.namespace_name = 'comms' and kubernetes.labels.app = 'coturn'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"coturn"
]
}, },
{ {
"name": "lesavka", "name": "lesavka",
"description": "", "description": "",
"baseQuery": "source = journald-* | where _HOSTNAME = 'titan-jh'", "baseQuery": "source = journald-* | where _HOSTNAME = 'titan-jh'",
"servicesEntities": [], "servicesEntities": [],
"traceGroups": [] "traceGroups": [
"lesavka"
]
} }
] ]
saved_queries.json: | saved_queries.json: |

View File

@ -150,7 +150,7 @@ data:
apiVersion: batch/v1 apiVersion: batch/v1
kind: Job kind: Job
metadata: metadata:
name: opensearch-observability-setup-1 name: opensearch-observability-setup-2
namespace: logging namespace: logging
spec: spec:
backoffLimit: 3 backoffLimit: 3

View File

@ -122,7 +122,7 @@ spec:
- name: LOG_LIMIT_BYTES - name: LOG_LIMIT_BYTES
value: "1099511627776" value: "1099511627776"
- name: LOG_INDEX_PATTERNS - name: LOG_INDEX_PATTERNS
value: "kube-*,journald-*" value: "kube-*,journald-*,trace-analytics-*"
volumeMounts: volumeMounts:
- name: scripts - name: scripts
mountPath: /scripts mountPath: /scripts

View File

@ -0,0 +1,87 @@
# services/logging/otel-collector-helmrelease.yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: otel-collector
namespace: logging
spec:
interval: 15m
timeout: 10m
chart:
spec:
chart: opentelemetry-collector
version: "~0.143.0"
sourceRef:
kind: HelmRepository
name: opentelemetry
namespace: flux-system
values:
fullnameOverride: otel-collector
mode: deployment
replicaCount: 1
ports:
otlp:
enabled: true
containerPort: 4317
servicePort: 4317
protocol: TCP
otlp-http:
enabled: true
containerPort: 4318
servicePort: 4318
protocol: TCP
jaeger-compact:
enabled: false
jaeger-thrift:
enabled: false
jaeger-grpc:
enabled: false
zipkin:
enabled: false
metrics:
enabled: false
config:
receivers:
otlp:
protocols:
grpc:
endpoint: ${env:MY_POD_IP}:4317
http:
endpoint: ${env:MY_POD_IP}:4318
processors:
memory_limiter:
check_interval: 5s
limit_percentage: 80
spike_limit_percentage: 25
batch: {}
exporters:
otlp/data-prepper:
endpoint: data-prepper.logging.svc.cluster.local:21890
tls:
insecure: true
service:
extensions:
- health_check
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/data-prepper]
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
memory: "512Mi"
nodeSelector:
node-role.kubernetes.io/worker: "true"
hardware: rpi5
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values:
- rpi5