Integrate VictoriaLogs in milner #60
Labels
No labels
blocked
bug
duplicate
effort
high
effort
low
effort
medium
effort
minimal
enhancement
help wanted
invalid
pr
feedback
pr
reviewing
question
status
backlog
status
document
status
done
status
implement
status
pending
status
planning
status
reviewing
waiting
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
tepichord/milner#60
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
hostNetwork, both rejected by baseline.
helmrelease.yaml (chartRef + valuesFrom ConfigMap victoria-logs-values), values.yaml → configMapGenerator, kustomization.yaml with namespace: logging (copy
zot/kustomization.yaml shape).
CPU+memory resources (required by policy/k8s.rego), nodeSelector: node-role: worker, ClusterIP service. Pin the image tag — no :latest.
Talos docs example. Then point machine.logging.destinations at 127.0.0.1: on each node.
you do mint a token, it goes Vault-first via a VaultStaticSecret.
Goal of this step: walk every log source milner runs today, decide how its logs reach VictoriaLogs, and record the answer. Most apps need nothing per-app — the DaemonSet
collector already mirrors their stdout once it's running; the real decisions are exceptions, retention, and what stays out.
┌───────────────────────────────────────────────────┬─────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────┐
│ App (namespace) │ Log surface │ Strategy in VictoriaLogs │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Talos host: machined, apid, trustd, etcd, │ Not stdout; │ machine.logging.destinations (json_lines TCP/UDP) + KmsgLogConfig → collector host-network │
│ kubelet, containerd, syslogd + kernel (all 3 VMs) │ talosctl logs/dmesg │ receiver. Terraform change, user confirmation required. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ kube-system: Control-plane static pods │ Pod stdout │ Automatic — collector mirrors. Cross-check against the Talos service stream; they duplicate │
│ (apiserver, controller-manager, scheduler, etcd), │ │ deliberately. │
│ CoreDNS, metrics-server, CNI/kube-proxy │ │ │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ flux-system: source/kustomize/helm/notification │ Pod stdout │ Automatic. Flux errors are the highest-value stream for debugging drift. │
│ controllers │ │ │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ vault │ Pod stdout + audit │ Stdout automatic. Audit log is not stdout — needs a Vault audit device decision (file on the PVC │
│ │ log (separate │ + collector tail, or syslog device). Contains credentials: put it in a separate stream with │
│ │ device) │ tighter access, or leave it out of the agent-queryable store. Also note login failures stay │
│ │ │ invisible until verbose_oidc_logging=true (OPERATIONS.md §5f) — a config change, not a pipeline │
│ │ │ change. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ vault-secrets-operator │ Pod stdout │ Automatic. Sync/rotation failures surface here, not in the app. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ pocket-id │ Pod stdout │ Automatic. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ woodpecker (server + agent) │ Pod stdout │ Automatic, but lifecycle only — build output is not here (see below). │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ woodpecker-runtime (step pods) │ Step stdout, │ Best-effort mirror of {namespace="woodpecker-runtime"}. Not authoritative. │
│ │ deleted right after │ │
│ │ the step │ │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ zot │ Pod stdout (+ HTTP │ Automatic; enable access logging in Zot config if pull/push audit is wanted. │
│ │ access log if │ │
│ │ enabled in its │ │
│ │ config) │ │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ local-path-storage │ Pod stdout │ Automatic. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ tailscale: operator + ingress-proxies ProxyGroup │ Pod stdout │ Automatic. Ingress/proxy errors explain certificate and approval problems. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ cloudflare: cloudflared connectors │ Pod stdout │ Automatic. The only public-surface component — keep its stream. │
│ (cloudflare/cloudflared.tf) │ │ │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ dashboard: Headlamp │ Pod stdout │ Automatic. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ preview-system: router, oz console, reaper │ Pod stdout; reaper │ Automatic; CronJob pods vanish quickly — same tail-read race as Woodpecker steps, low volume. │
│ CronJob │ pods are ephemeral │ │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ preview- (ephemeral) │ Pod stdout │ Automatic via namespace label; useful while reviewing a PR. │
├───────────────────────────────────────────────────┼─────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
│ evolved-npcs (planned): orchestrator + │ Pod stdout │ Automatic, but the high-volume outlier. Decide retention/sampling and keep it off the worker-sim │
│ KEDA-scaled simulation pods │ │ disk. │
└───────────────────────────────────────────────────┴─────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────┘
Woodpecker findings (the one source that doesn't migrate by collection)
/api/repos//logs//; the server/agent pod logs contain no step output.
pipeline), ordering is lost, and a step that never scheduled leaves nothing.
OPERATIONS.md §9j) — that resolves pipeline → step id → log → text, and reads errors[] too.
at least makes them plain files on the existing PVC.
Verification + record
store.
Naina:
Overview
Add a centralised log store to milner: VictoriaLogs (server), vlagent (pod-stdout DaemonSet), a
loggingnamespace and a Tailscale ingress — all Flux-managed, copying the Zot layout. Two of the issue's steps are not part of this plan's definition of done: Talos host-log forwarding (step 4) and the 100 GiB → 1 TiB resize (step 7). Both are gated on your explicit go-ahead, and the resize turned out to be blocked twice (details below). Phase 1 needs no resize — 31 GiB ofpersistentclaims today + 20 GiB for logs = 51 GiB of 100 GiB.Technical Approach
Namespace —
infrastructure/namespaces/logging.yaml, privileged PSA (vlagent mountshostPath: /var/log/pods, which baseline forbids88b):Server chart source —
infrastructure/victoria-logs/ocirepository.yaml(tag verified against the registry1):Server values — the chart uses
server.persistentVolume.*22b, notpersistence.*/pvc.*as zot does;retentionDiskSpaceUsageis a real chart key3 andservice.clusterIPdefaults toNone4:Flux-owned PVC —
infrastructure/victoria-logs/persistentvolumeclaim.yaml, same shape aszot-data:Collector —
infrastructure/victoria-logs-collector/, its ownOCIRepository+HelmRelease+values.yaml(tag verified; the chart shipsvictoriametrics/vlagent, mountshostPathvolumes and declares nohostNetwork5; an un-pathedremoteWrite.urltargets/insert/native6):Ingress —
infrastructure/victoria-logs/ingress.yaml, copied from zot7; port9428,tls.hosts: [logs]→logs.faun-snapper.ts.net:Wiring:
Docs — three edits:
OPERATIONS.md§10: the resize procedure and its blockers; thelogsservice approval; the query recipes.ARCHITECTURE.md: storage budget row (+20 GiB) and thelogs.faun-snapper.ts.netendpoint.Implementation Details
Create
infrastructure/namespaces/logging.yamlenforce: privilegedinfrastructure/victoria-logs/{kustomization,ocirepository,helmrelease,values,persistentvolumeclaim,ingress}.yamlinfrastructure/victoria-logs-collector/{kustomization,ocirepository,helmrelease,values}.yamlModify
infrastructure/kustomization.yamltests/test_static.pyvalues.yamlpaths intest_yamllintOPERATIONS.md§10ARCHITECTURE.mdConstraints
policy/k8s.regorules 1–2 still bite: pinned tags and CPU+memory limits on every container of both workloads. Rule 3 only inspectscontainer.securityContext.privileged, so no policy exemption is needed for the hostPath pod — theprivilegedPSA label is what admits it9.server.image.tagand the vlagent tag explicitly; both charts default them toappVersion, which the L1 gate would accept but which makes the running version invisible in review.persistentisWaitForFirstConsumer10, so the PVC staysPendinguntil the server pod schedules. Flux reporting the Kustomization not-ready in that window is the class working as designed.vector.enabled: trueyet — a receiver with no sender is dead weight until step 4 is approved.Out-of-scope notes (deferred, need your go-ahead)
Talos host logs (step 4).
machine.logging.destinationsemits raw newline-delimited JSON over TCP/UDP; vlagent only speaks HTTP, so a Vector receiver is required. Thevictoria-logs-singleOCI artifact bundles the Vector subchart, so no extra Flux source is needed:Two caveats to settle before applying: destinations are appended under strategic-merge, so re-applying duplicates them (
talosctl edit mccleans up), andKmsgLogConfigis probably unnecessary because kernel messages already arrive in the service stream astalos-service=kernel.Disk resize (step 7). Leaving
data/main.tf'sdata_volume_sizeat 100 GiB. Changing it makesterraform plandirty and unapplyable:dmacvicar/libvirt0.9.9VolumeResource.Update()errors with "Storage volumes cannot be updated. All changes require replacement.", andprevent_destroy = trueforbids the replacement — so the plan shows an in-place update that apply refuses.disk-type user volumes when the backing disk expands (siderolabs/talos#12991; fixed by #13057, present only in ≥ v1.13.0), so even a hand-runqemu-img resizemay leave/var/mnt/dataat 100 GiB.When it is wanted:
qemu-img resize … +900G, then reconcile state (terraform apply -refresh-onlyor astate rm/importpair), then verifytalosctl get volumestatus -o yamlanddf -h /var/mnt/data.VictoriaLogs OSS has no auth. The ingress stays tailnet-only and nothing else publishes it; if a token is ever wanted it goes Vault-first via a
VaultStaticSecret.Adjacent, not requested:
victoria-logs-mcp(same registry) and the chart'sdashboards.enabled— both only if you want them.Acceptance Criteria
kustomize build gitops/clusters/milner/infrastructurerendersNamespace/loggingwithpod-security.kubernetes.io/enforce: privilegedplus both new HelmReleases and thevictoria-logs-dataPVC.victoria-logsisReady/Healthy;kubectl -n logging get pvc victoria-logs-dataisBoundon thepersistentclass at 20Gi and was created by Flux, not the chart (server.persistentVolume.existingClaimset).kubectl -n logging get ds victoria-logs-collectorreportsDESIRED == READY, and/select/logsql/queryreturns entries withkubernetes.pod_namespace.pytest tests/test_static.py -k "yamllint or conftest"andpytest tests/test_gitops.pypass, includingtest_all_namespaces_have_psa_enforceand the L1 Rego gate over the rendered logging workloads.pytest tests/test_chart_sources.pyresolves both new OCI sources (network-gated).OPERATIONS.md§10 documents thelogsservice approval, the query commands, the per-app capture table and the resize blockers;ARCHITECTURE.mdreflects the new claim and endpoint.Testing Plan
kustomize build gitops/clusters/milner/infrastructurepytest tests/test_gitops.py -k "kubeconform or namespaces or helmreleases"pytest tests/test_static.py -k conftestpytest tests/test_static.py -k "yamllint or terraform_fmt"pytest tests/test_chart_sources.py(needs network)kubectl -n logging port-forward svc/victoria-logs 9428:9428thencurl --data-urlencode 'query=kubernetes.pod_namespace:logging' http://127.0.0.1:9428/select/logsql/querycurl -N --data-urlencode 'query=*' http://127.0.0.1:9428/select/logsql/tailkubectl -n logging logs sts/victoria-logsshows no arg-parse error for-retentionPeriod=7d/-retention.maxDiskSpaceUsageBytes=18GiBtalosctl logs machinedand look fortalos-service: machinedin VictoriaLogsRelated Issues/PRs
oci://URL convention, Flux-owned PVC, Tailscale Ingress).Research Findings
victoria-logs-single0.13.9 andvictoria-logs-collector0.3.7 both exist as OCI artifacts underghcr.io/victoriametrics/helm-charts, and both server/collector images publishlinux/arm64.victoria-logs-singleOCI artifact bundles the Vector subchart (0.57.0), so a host-networkjson_linesreceiver needs no additional Flux source;timberio/vector:0.57.0-distroless-libcpublishes arm64.machine.logging.destinationsacceptstcp:///udp://endpoints withformat: json_linesand applies without a reboot; kernel logs already ride the service stream, makingKmsgLogConfigusually redundant./select/logsql/return(issue step 6) is not a real endpoint; the read paths are/select/logsql/queryand/select/logsql/tailon:9428.prevent_destroyforbids replacement, and Talos v1.11.1 will not grow the user volume even after the qcow2 grows.Pre-Mortem
fullnameOverrideunderserver:(only the collector chart puts it at top level) and depends onserver.persistentVolume.existingClaim,service.clusterIP: ""andretentionDiskSpaceUsage: 18GiBwithout ever runninghelm templateagainst the pinned0.13.9chart. Helm silently ignores unknown keys, so the release installs with defaults: the Service/StatefulSet keep a release-derived name, the Ingress backend and vlagent'shttp://victoria-logs:9428resolve to nothing, and every static test still passes while/select/logsql/queryreturns no entries.machine.logging.destinationsby appending arrays, so a re-applied Terraform patch duplicates destinations, andtcp://127.0.0.1:6051/underhostNetworkmakes every node report as127.0.0.1. When the resize is attempted, the libvirt provider's refusal to update volumes plusprevent_destroy = trueleavesterraform applyfailing anddata/state dirty, blocking unrelated changes to that root.