Kubernetes Log Collection at Scale: DaemonSet Architecture, Declarative Pipelines & Tuning
This article details replacing sidecar log collectors with node-level DaemonSets in Kubernetes, covering resource cost analysis, declarative LogPipeline CRD design, controller implementation, backpressure handling, capacity planning, and production verification steps for enterprise-scale log collection.
1. Scenario: The Problem Is Not Collection, But Replication Count
The order platform runs workloads like order-api and payment-worker. Applications emit JSON to stdout. Compliance requires retaining error, audit, and key transaction logs; ordinary debug logs may be dropped during sustained backend unavailability. The platform wants business teams to declare only "which logs from which Pods go where" without maintaining Fluent Bit configs directly.
Initial approach injected a logging sidecar into every business Pod. With P business Pods, each sidecar requesting c CPU and m memory, total collector requests become:
CPU = P × c
Memory = P × mA node-level DaemonSet costs N × c_node CPU and N × m_node memory, where N is node count. As long as P/N (Pods per node) significantly exceeds c_node/c, the node-level approach wins on resources. This formula is more reliable than a fixed percentage because collection rules, log rates, and image versions change actual usage.
Sidecars still fit when apps cannot write to shared node paths, need per-tenant credentials, or require synchronous per-request local processing. They are not a good default for all stdout logs because they amplify image upgrades, vulnerability patching, and quota management.
2. Four Running Models and Real Trade-offs
Sidecar
Data Entry: Shared volume or local port
Main Benefit: Per-Pod isolation, rules can differ
Main Cost: Cost grows with Pod count; upgrades drag business Pods
Applicable Condition: Strong isolation, few high-value workloads
DaemonSet
Data Entry: Node /var/log/containers/*.log Main Benefit: Cost grows with nodes; no business restarts
Main Cost: Single-node collection failure affects that node; needs metadata correlation
Applicable Condition: Most standard stdout logs
Deployment Gateway
Data Entry: SDK/OTLP/HTTP
Main Benefit: Independent horizontal scaling; good for centralized ingest
Main Cost: Business has integration cost; cannot natively read stdout
Applicable Condition: Traces, business events, custom metrics
Operator
Data Entry: Manages any of the above runtimes
Main Benefit: Declarative policies, auditable, rollbackable
Main Cost: Requires control-plane maintenance
Applicable Condition: Multi-team platforms with frequent rule changes
The Operator is not a second collector. It only compiles LogPipeline policies into config and publishes; the data plane remains one agent per node. Control-plane failure delays config changes but does not stop running collectors.
3. Runtime Principle: From CRI Logs to Backend
Kubelet/container runtime writes container stdout/stderr in CRI format to the node log directory; /var/log/containers is usually symlinks to actual Pod log files. The DaemonSet read-only mounts this directory, uses the tail input to maintain per-file offsets. The Kubernetes filter then calls the API to correlate Pod metadata, generating namespace, Pod, container, labels, etc. Finally, logs are routed to Kafka, Loki, or other backends per policy.
Do not treat /var/lib/docker/containers as the universal Kubernetes path; it is Docker's legacy layout and may not exist on containerd clusters. Verify kubelet's log directory on target nodes first; the article uses the common /var/log/containers as example.
The normal path guarantees at-least-once read, at-least-once delivery , not "no duplicates". Network retries or agent restarts create duplicate records; downstream should deduplicate using
cluster + namespace + pod_uid + container + timestamp + stream + offset(if backend supports). Log events are not part of cross-system transactions; distributed transactions cannot solve this.
4. Architecture and Module Boundaries
The control plane ( LogPipeline CR, Pipeline Controller, agent-config ConfigMap, DaemonSet pod-template annotation, CR Status: ObservedGeneration/ConfigRevision) is separate from the data plane (Fluent Bit, /var/log/containers read-only, filesystem buffer, Kafka/Loki, Prometheus metrics).
The controller must compute a stable digest for every successfully rendered config and write it into the DaemonSet's pod template annotation. Kubernetes then rolls out the agent; this is more controllable than assuming a collector version can reliably watch a ConfigMap. ConfigMap mount updates have kubelet sync latency and cannot serve as an urgent, all-nodes-consistent change protocol.
Recommended platform namespace observability-system hosts agent, controller, and aggregated config. LogPipeline is namespaced; the platform controller reads across business namespaces. Admission validation restricts each tenant to selecting labels only from its own namespace, forbids custom Kafka addresses, SASL secrets, and arbitrary Lua scripts. Destinations are referenced via controlled sinkRef pointing to platform-predefined targets, preventing data exfiltration via the collector.
5. Declarative Interface, Status, and Release Flow
Example order-service policy. Fields are deliberately small and verifiable: selector filters Pod labels only; minLevel filters structured JSON levels; sinkRef references platform-managed egress.
apiVersion: logging.example.io/v1alpha1
kind: LogPipeline
metadata:
name: order-audit
namespace: commerce
spec:
selector:
matchLabels:
app.kubernetes.io/name: order-api
format: json
minLevel: info
sinkRef: audit-kafka
status:
observedGeneration: 3
configRevision: "sha256:pending"Minimum interface validation must include: sinkRef required and in allowlist; label key/value conform to Kubernetes label constraints; log format limited to supported set; overlapping policies with same selector must have explicit priority. On render failure, controller keeps last valid ConfigMap, sets Ready=False with reason; must never publish half-baked config.
Release sequence: controller lists all policies → stable sort by namespace/name → validate, render, compute SHA-256 → server-side apply ConfigMap → update DaemonSet annotation → wait for rollout status → update each CR's status. Controller uses resource generation and ConfigMap resourceVersion for optimistic concurrency; repeated reconciles yielding same config do not recreate resources. This is control-plane idempotency.
5.1 What the CRD Should Express, and What Must Stay Closed
The CRD is a platform API, not a Fluent Bit config passthrough. Exposing all low-level plugin fields to tenants causes three problems: API gets bound to plugin implementation on collector upgrades; arbitrary regex, scripts, or output addresses lose resource and data control; platform cannot judge if a config is safe during incidents. Therefore CRD expresses only stable business intent; controller maps intent to specific collector version config.
selector – Define source Pods. Must validate: only match own namespace; limit expression complexity. Not suitable for CR: arbitrary file paths, arbitrary tags.
format – Choose JSON/text parsing strategy. Must validate: enum values; JSON parse failure retention policy. Not suitable for CR: arbitrary parser file contents.
minLevel – Log priority. Must validate: enum values; reject config if text logs don't support. Not suitable for CR: arbitrary grep/record accessor.
sinkRef – Select platform egress. Must validate: must be in approved catalog; authorized per namespace. Not suitable for CR: broker addresses, usernames, cert text.
retentionClass – Business retention tier. Must validate: only map to org's existing tiers. Not suitable for CR: backend index names, lifecycle DSL.
sampling – Low-value log sampling. Must validate: only controlled ratios and levels. Not suitable for CR: Lua, WASM, external HTTP filters.
If business truly needs custom field transforms, design as approved TransformProfile with platform-pre-registered limited rule sets, not open script execution. Rule changes must be audited, recording submitter, time, CR generation, render digest, and affected node count.
A more complete but still controlled object example includes fields.include, fields.redact, retentionClass, delivery.priority, delivery.onBackpressure, and status conditions with Ready, configRevision, and observed generation.
The status is not for show. Release system should write Ready=True only after the new revision's DaemonSet reaches expected available count. It means "config published", not "every log reached backend"; the latter must be proven by data-plane metrics.
5.2 Rule Conflicts, Routing Order, and Data Model
When a log matches multiple policies, system must have predictable behavior. Recommended: compute each record's target as unique primary route plus optional mirror routes . Primary route sorts by priority, selector specificity, namespace/name. Mirror routes only for short-term migration or audit replicas with quantity caps. Otherwise one log copied to multiple outputs silently inflates backend cost and duplication rate.
Unified post-collection event model must retain original timestamp, collection timestamp, and Kubernetes identity. Former for event ordering, latter for diagnosing transport latency; must not overwrite application-generated time with agent's current time.
{
"event_time": "2026-09-04T10:00:01.123Z",
"observed_time": "2026-09-04T10:00:01.280Z",
"severity": "INFO",
"message": "order accepted",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"k8s": {
"cluster": "prod-shanghai-1",
"namespace": "commerce",
"pod": "order-api-7c6d8d9db8-kx2jq",
"pod_uid": "<runtime-assigned-uid>",
"container": "app",
"node": "<node-name>"
},
"pipeline": {
"name": "order-audit",
"revision": "sha256:..."
}
}Cluster, UID, node name are filled by runtime; cannot hardcode in Deployment. For non-JSON lines, keep raw text in message, add parse_error=true rather than silently dropping; whether to send such events to low-cost isolated sink is decided by data retention policy.
5.3 End-to-End Timeline from Request to Queryable Log
Query Service → Kafka/Log Backend → Kubernetes API → Node Agent → Kubelet/CRI log → order-api
Alt: [Sink ACK success] [Timeout or unavailable]
stdout JSON (log contains trace_id)
tail new bytes and record offset
query Pod metadata on demand (namespace/labels/pod UID)
parse, redact, match unique primary route
batch send
ack advances confirmed buffer state
backoff retry and fall into file buffer
query by trace_id/order_idMetadata query is not per-log API call. Collector or sidecar metadata cache reuses results keyed by Pod identity, evicts on Pod deletion with TTL; otherwise log peaks turn observability traffic into API Server pressure. Cache hit rate, query errors, and expired entry counts should become control-plane capacity metrics.
6. Data Plane Manifest Templates
Below manifests are starting points. Image tags in production must be pinned to org-validated versions or digests; resource values must be set after load testing against node log rates. hostPath read-only, buffer uses separate emptyDir: node failure may still lose unsent buffer — this boundary must be accepted and monitored.
apiVersion: v1
kind: Namespace
metadata:
name: observability-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: node-log-agent
namespace: observability-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-log-agent
rules:
- apiGroups: [""]
resources: ["pods", "namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: node-log-agent
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: node-log-agent
subjects:
- kind: ServiceAccount
name: node-log-agent
namespace: observability-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: node-log-agent-config
namespace: observability-system
data:
fluent-bit.conf: |
[SERVICE]
Flush 1
Log_Level info
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
storage.path /buffers
storage.sync normal
storage.checksum on
storage.backlog.mem_limit 32M
[INPUT]
Name tail
Path /var/log/containers/*.log
Tag kube.*
DB /buffers/tail.db
DB.Sync Normal
Mem_Buf_Limit 16MB
Skip_Long_Lines On
Refresh_Interval 10
Rotate_Wait 30
storage.type filesystem
[FILTER]
Name kubernetes
Match kube.*
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Keep_Log On
Labels On
Annotations Off
# Actual output rendered by controller per approved sinkRef; do not let tenants supply output plugin params.
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-log-agent
namespace: observability-system
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 10%
selector:
matchLabels:
app.kubernetes.io/name: node-log-agent
template:
metadata:
labels:
app.kubernetes.io/name: node-log-agent
annotations:
logging.example.io/config-revision: bootstrap
spec:
serviceAccountName: node-log-agent
tolerations:
- operator: Exists
containers:
- name: fluent-bit
image: fluent/fluent-bit:<validated-version>
args: ["-c", "/fluent-bit/etc/fluent-bit.conf"]
ports:
- name: metrics
containerPort: 2020
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "1"
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: varlog
mountPath: /var/log/containers
readOnly: true
- name: config
mountPath: /fluent-bit/etc
readOnly: true
- name: buffer
mountPath: /buffers
livenessProbe:
httpGet:
path: /api/v1/health
port: metrics
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/v1/health
port: metrics
periodSeconds: 10
volumes:
- name: varlog
hostPath:
path: /var/log/containers
type: Directory
- name: config
configMap:
name: node-log-agent-config
- name: buffer
emptyDir:
sizeLimit: 2GiIf node log directory permissions disallow non-root reads, have security team confirm runtime file permissions and Pod Security policies; do not unconditionally add privileged: true just to "make collection work". Specific choices for webhooks, NetworkPolicy, image signing, and Secret mounting depend on existing platform capabilities; article does not fabricate them.
6.1 Output, Credentials, and Network: Config Must Be Independently Auditable
Most sensitive part of data-plane config is output destination. When rendering a predefined Kafka sink into Fluent Bit output, broker address, TLS CA, client cert, and auth Secret must come from platform objects; business CR only references sinkRef. Output config must be syntax-validated against real image version in pre-prod cluster. Below shows config organization only, not cross-version plugin params:
Pipeline policy (commerce/order-audit)
│ sinkRef=audit-kafka
▼
Sink catalog (platform-owned)
endpoint = kafka.logging.svc:9093
TLS secret = audit-kafka-client
delivery = at-least-once / bounded retry
▼
Rendered Agent configuration + Secret volumeSecret mounted as read-only volume or via controlled credential injection; agent ServiceAccount cannot list Secrets in Kubernetes API. If cert rotation requires restart, rotation flow: create new Secret version → update template reference → roll agent per maxUnavailable → observe two-cert window → revoke old cert. Never put credentials in CR spec, ConfigMap, command args, or collector debug logs.
NetworkPolicy must at least allow agent to Kubernetes API, DNS, and required sink egress; deny arbitrary public egress. Note: if log backend crosses AZs or clusters, egress cost and network latency affect batch size, flush interval, and buffer budget — cannot tune only from agent CPU perspective.
6.2 Node, Container Runtime, and Scheduling Boundaries
DaemonSet's "one per node" is not absolute: nodes with NoSchedule taints, Windows nodes, Fargate/serverless nodes, GPU-only nodes, and unschedulable nodes may need different policies. To avoid persistent Pending on unsupported nodes, explicitly constrain OS, arch, and verified runtime environments in agent's nodeSelector or node affinity, and decide separately for control-plane nodes whether to tolerate taints. hostPath path, mount propagation, and SELinux/AppArmor constraints may vary by distro. Standard rollout steps: on a test node, verify read-only mount; start minimal agent; confirm it discovers new container files, parses CRI lines, correlates metadata; then scale gradually. Any variant depending on /var/log/pods, journald, or specific containerd log paths must be tested and released as independent profiles.
On node disk layout, separate kubelet image disk, business ephemeral disk, and agent buffer. Placing buffer on same partition as kubelet logs that easily exhausts creates cascade: "backend slow → buffer grows → node DiskPressure → business Pods evicted". If org has dedicated local volumes, prefer putting agent buffer into quota-monitored dedicated directory; if not, strictly limit emptyDir.sizeLimit and include in node available-space alerts.
7. Controller Implementation: Rendering Is Not String Concatenation
Controller recommended to use Kubebuilder/controller-runtime scaffolding for types, RBAC, and manager. Core reconcile should be a pure function Render([]LogPipeline, SinkCatalog): input sorted, output byte string and digest; invalid policies return aggregated errors. This enables unit testing without API Server.
// Omitted Kubebuilder-generated API types; fields match Section 5 CR.
func configRevision(config []byte) string {
value := sha256.Sum256(config)
return "sha256:" + hex.EncodeToString(value[:])
}
func applyConfig(ctx context.Context, c client.Client, config string) error {
cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{
Name: "node-log-agent-config", Namespace: "observability-system",
}}
_, err := controllerutil.CreateOrUpdate(ctx, c, cm, func() error {
cm.Data = map[string]string{"fluent-bit.conf": config}
return nil
})
return err
}
func setRevision(ctx context.Context, c client.Client, revision string) error {
var ds appsv1.DaemonSet
key := client.ObjectKey{Namespace: "observability-system", Name: "node-log-agent"}
if err := c.Get(ctx, key, &ds); err != nil { return err }
base := ds.DeepCopy()
if ds.Spec.Template.Annotations == nil { ds.Spec.Template.Annotations = map[string]string{} }
ds.Spec.Template.Annotations["logging.example.io/config-revision"] = revision
return c.Patch(ctx, &ds, client.MergeFrom(base))
}Above is complete concurrency-safe update core, but not a standalone compilable Operator project: generating API, SetupWithManager, RBAC markers, and Render vary with org's sink types. Labeling it honestly as implementation skeleton is safer than calling missing-type fragments "runnable code". To deliver a runnable controller, generate full module in repo, run go test ./..., make manifests, then record actual versions and commands back into article.
7.1 Reconcile State Machine, Retry, and Rollback
Controller's key is not "update ConfigMap on event" but explicitly distinguishing desired state from observed state. An actionable state machine:
CR create/update → Validating → (schema/auth failed) → Rejected
Validating → (valid) → Rendering → (render/sink lookup failed) → Degraded
Rendering → (revision changed) → Publishing → (ConfigMap + DaemonSet annotation persisted) → RollingOut
RollingOut → (DaemonSet available) → Ready
RollingOut → (deadline exceeded) → Degraded
Degraded → (new generation) → Validating
Rejected → (user fixes spec) → Validating
Degraded → (retryable dependency recovered) → RenderingFor retryable errors (API temporarily unavailable, sink catalog read failure), use rate-limited queue with jitter; for non-retryable (invalid enum, unauthorized sink, conflicting policies), update status.conditions and stop hot loop, wait for user to change generation. Set context deadline on all reconciles, shorter sub-timeouts for external calls, to avoid downstream slow calls exhausting controller workers.
On publish failure, do not delete new ConfigMap. Controller keeps last known good revision, writes failed revision and reason to status; rollback only points DaemonSet annotation back to previous stable digest. ConfigMap may retain limited history, cleaned up by owner/label after retention limit to prevent long-run object leaks. If some nodes long fail to update, distinguish from "config render succeeded" and alert via DaemonSet unavailable metrics.
7.2 Concurrency, Rate Limiting, and Control-Plane Scaling
In large clusters, many CR changes in one namespace create reconcile storms. Controller should: merge changes within short time windows; use workqueue rate limiting; add debounce to global render; limit concurrent workers; cache render results by content digest. Cannot simply increase workers because all policies ultimately write one aggregated ConfigMap; excessive concurrency only increases resource version conflicts.
When policy count or config size grows beyond single ConfigMap capacity, shard publishing by node pool, tenant domain, or sink. Sharding prerequisite: each agent loads only config that may match its node; otherwise rules split but memory and matching overhead remain. Shard strategy must stay stable: same NodePool's agent config not fully restarted due to unrelated namespace changes.
Control-plane HA uses multi-replica deployment with leader election; only leader performs writes, others can take over. Leader election protects controller write ordering, does not replace API optimistic concurrency. Controller itself needs requests/limits, PDB, anti-affinity, and metrics; though not on log data hot path, it is critical for publishing and recovery.
8. Backpressure, Capacity, and HA: Define Drop Policy First
Most dangerous data-plane state is downstream slowdown: input rate Rin > output rate Rout, backlog slope = Rin - Rout. Given available disk buffer B, tolerable duration ≈ T = B / max(Rin - Rout, 0).
This determines buffer is not "bigger is better": it competes for node local disk and delays failure exposure. For order platform, define three log tiers: audit logs never silently dropped at agent layer, prioritize retention and trigger capacity alerts; error logs retry within disk buffer; debug logs sampled or dropped after sustained backpressure exceeds threshold, with drop counters. If business audit requires strict no-loss, simultaneously write critical state to business DB/outbox; do not rely solely on stdout.
Output should configure bounded retries, connection timeouts, exponential backoff with jitter, and limit per-node concurrent connections. Kafka partition count, ACK level, compression, batch size are part of end-to-end throughput design; cannot solve by just raising agent CPU. When log backend unavailable, agent disk and kubelet log rotation together determine recoverable window; permanent node loss or rotation before read still loses data.
Do not use HPA on DaemonSet to "replicate collector on same node": two instances compete for same files unless explicit sharding and offset coordination implemented. Hotspot mitigation should prioritize reducing low-value logs, adjusting business Pod anti-affinity/topology spread, raising single-agent quota, or isolating dedicated high-log nodes.
8.1 Resource Budget: Derive from Log Rate, Not Copy Requests
Resource requests should be based on per-node observable data. At minimum collect: active log file count F, avg/peak line size S, log lines per second L, post-parse expansion factor E, acceptable backend failure window W. Peak byte rate ≈ Rpeak = L × S × E; to reserve buffer for failure window, lower bound ≈ B = Rpeak × W, plus DB, metadata cache, and safety margin. This estimate excludes compression, batching, backend ACK latency; must be corrected by load testing.
CPU – Main Drivers: JSON parse, regex, TLS, compression, record transform. Observability Signals: process CPU, throttling, output latency. Preferred Optimization: Drop expensive transforms, batch, raise quota.
Memory – Main Drivers: Active chunks, metadata cache, batches, plugin queues. Observability Signals: RSS, paused input, buffer backlog. Preferred Optimization: Bounded memory + filesystem buffer, control labels.
Disk – Main Drivers: Backend failure, input/output rate diff, offset DB. Observability Signals: Buffer usage, inode, DiskPressure. Preferred Optimization: Explicit drop tiers, independent quota, shorten recovery.
Network – Main Drivers: Uncompressed log volume, cross-zone transfer, retry duplicates. Observability Signals: Egress, connection errors, RTT. Preferred Optimization: Same-zone sink, compression, connection reuse. limits.cpu too low causes CPU throttling, showing as input lag increase while process appears "not fully utilized"; limits.memory too low may OOMKill agent at peak backlog. Neither solved by unlimited limits: must first locate whether parsing, output, disk, or backend throughput is bottleneck. For clusters with large request peak/valley differences, can set different DaemonSet configs per node pool, but avoid running two instances reading same path on same node.
8.2 Failure Scenarios and Recovery Playbooks
Backend timeout, retries up – First Check: Sink availability, DNS, TLS cert, NetworkPolicy. Automatic Action: Bounded backoff, write to file buffer. Manual Handling & Recovery Criteria: Backlog continuously drops after reconnect; confirm no DiskPressure.
Buffer near limit – First Check: Input/output rate diff, low-priority volume, disk quota. Automatic Action: Sample/drop by level with counters. Manual Handling & Recovery Criteria: Scale up or throttle source; audit log near limit must escalate.
Agent OOMKill – First Check: RSS, chunks, single huge log line, metadata cache. Automatic Action: Restart and recover from offset DB. Manual Handling & Recovery Criteria: Limit log line length, raise load-tested memory, check dup rate.
Missing records after file rotation – First Check: Rotation speed, agent lag, offset DB, node I/O. Automatic Action: None. Manual Handling & Recovery Criteria: Adjust rotation and read capacity; data outside recovery window only salvageable per business tier.
CR updated but node not effective – First Check: Revision, DaemonSet rollout, node taints, image pull. Automatic Action: Controller timeout sets Degraded. Manual Handling & Recovery Criteria: Fix Pending/image/node issues, confirm available count recovers.
Missing metadata – First Check: Kubernetes API errors, RBAC, cache expiry. Automatic Action: Use limited cache or mark unknown. Manual Handling & Recovery Criteria: Fix permissions/connectivity; do not route unknown to wrong tenant.
Each playbook must define owner, alert severity, and max allowed duration. Especially "audit sink unavailable" and "debug logs sampled" cannot share alert level: former may affect compliance and liability, latter usually just cost and observability degradation.
8.3 Data Consistency, Idempotency, and Audit Boundaries
Log collection pipeline and business DB writes are two independent systems. Wrapping them in two-phase commit is neither feasible nor necessary; correct approach is split by data value. Order status, payment results — business facts — should go to transactional DB and propagate reliably via outbox/event stream; application logs serve diagnostics and audit assist. If regulation demands certain audit events never lost, business service must emit structured audit events via acknowledged, replayable channel; cannot rely on accidental survival of node log files.
Log backend duplicates usually from "send succeeded but ACK lost". Consumer should deduplicate by event ID or above composite identity; if log format lacks stable ID, only approximate dedup at query/index layer, cannot promise absolute accuracy. Also distinguish time ordering: within same Pod file order usually preserved, but cross-Pod, cross-node, post-retry global order not guaranteed. Troubleshooting queries should rely on trace ID, request ID, and event time range, not assume index write time equals occurrence time.
9. Performance Verification and Tuning Steps
Before production, complete following verification on same-class nodes as production, recording actual not preset results:
Baseline : With collector off, record node CPU, memory, disk IOPS, business P95/P99, and log bytes/sec.
Steady State : Push logs at real JSON size, file count, and backend latency; observe agent RSS, CPU throttling, tail lag, buffer usage, backend write latency.
Failure : Cut backend connection beyond half of T; confirm low-priority drop, alert, and catch-up after recovery; then simulate agent restart and log rotation, verify offset continuity and dup rate.
Release : Modify one policy; verify revision, DaemonSet updatedNumberScheduled and numberAvailable; confirm failed render does not replace existing config; finally execute rollback.
Useful metrics: per input/output bytes, retry count, dropped records, disk buffer usage, open file count, Kubernetes API metadata query errors, DaemonSet not-ready nodes, downstream topic/ingester backlog. When setting alerts, correlate duration with business tier to avoid alert storms from single brief rotation.
9.1 Three-Layer Observability Design: Metrics, Logs, Traces
Metrics answer "is system degrading"; logs answer "why was this record processed or dropped"; traces answer "which services did a request cross". All three label designs must be restrained: node, namespace, sink, pipeline usually suitable as metric dimensions, but pod UID, order ID, trace ID must not be Prometheus labels (high cardinality memory issues). Latter belong in log or trace attributes.
Recommended semantic metrics (not tied to specific collector version metric names):
Input/output bytes & record rate – Dimensions: nodepool, sink, priority. Purpose: Judge traffic & throughput gap. Alert Idea: Output persistently below input while buffer rises.
Retry/failed send count – Dimensions: sink, error category. Purpose: Distinguish network, auth, backend errors. Alert Idea: Exceed baseline and sustain for a window.
Dropped record count – Dimensions: pipeline, priority, reason. Purpose: Detect audit degradation. Alert Idea: Critical any drop; debug sustained anomaly.
Buffer available ratio – Dimensions: node, nodepool. Purpose: Predict disk exhaustion. Alert Idea: Two thresholds: warning and critical.
Config effective lag – Dimensions: revision, nodepool. Purpose: Release correctness. Alert Idea: Exceeds publish SLO without convergence.
Controller reconcile errors – Dimensions: controller, reason. Purpose: Control-plane availability. Alert Idea: Errors continuous or no successful publish.
Alert notifications must include config revision, affected node pool, sink, backlog start time, and runbook link so on-call doesn't guess "which config change". Collector verbose debug logs cannot stay on long; they create new log pressure at high throughput. Restore normal level after investigation.
9.2 Load Test Plan and Acceptance Table
To avoid false positives of "throughput great but loses logs on failure", load test must cover steady, burst, long backend failure, and rolling release. Test traffic must include real field counts, long-line ratios, malformed log ratios, and multi-file concurrency; fixed short text cannot reflect JSON parse, redaction, and label expansion costs.
Steady – Injection: Sustain baseline rate into multiple Pods. Verification: CPU, RSS, end-to-end latency no continuous drift. Pass Standard Defined By: Platform & business jointly.
Burst – Injection: Short spike to peak rate. Verification: No OOM, no uncontrolled throttling, buffer recedes. Pass Standard Defined By: Platform defines node safety line.
Sink failure – Injection: Block egress or force backend errors. Verification: Retry, tiered degradation, recovery catch-up. Pass Standard Defined By: Business defines data tiers, platform implements.
Log rotation – Injection: Lower test container rotation threshold. Verification: Offset continuity, dup & loss quantifiable. Pass Standard Defined By: Platform defines collection SLO.
Agent restart – Injection: Delete single-node agent Pod. Verification: Re-discover files, restore buffer, no abnormal API pressure. Pass Standard Defined By: Platform defines recovery time.
Config rollback – Injection: Publish valid rule then revert revision. Verification: Old rule recovers, no half-config produced. Pass Standard Defined By: Change owner confirms.
Article provides no "certain QPS passes" numbers because they depend on node, runtime, backend, and rules. Acceptance conclusion must come from test report, including at least env versions, node specs, log profile, config revision, observation window, raw metric links, and known deviations.
10. Observability, Security, and Change Governance
Collector logs must carry node name, config revision, sink name; controller logs carry reconcile request, generation, render digest. Agent metrics scraped by Prometheus; controller exposes reconcile errors and last successful config time. Trace IDs in app logs injected by app or OpenTelemetry SDK; collector preserves fields, must not guess or generate trace context.
Security focus beyond RBAC: logs often contain order IDs, phone numbers, tokens. Avoid emitting secrets at source; if needed, redact in controlled filters; transport uses TLS, credentials via Secret reference with least-privilege per sink; restrict network sources that can scrape agent metrics; audit every policy change. ConfigMap must not store passwords.
Config enters Git for GitOps publishing, but emergency rollback must have explicit path: restore last validated Git revision, controller produces new revision and rolls DaemonSet. Do not manually edit production ConfigMap; next reconcile would overwrite; controller should alert on such drift.
10.1 Canary Release and Multi-Environment Governance
Collector upgrades are underestimated more than business releases: a parser change may alter all log fields; an output plugin diff may affect entire node pool. Do not push new image or renderer straight to full prod. Manage environments and release batches separately: dev validates syntax and CRD; staging uses real runtime and sanitized logs for compatibility; prod starts with one isolated node pool or low-risk namespace canary, then expands gradually.
Every release must pin four dimensions: agent image digest, controller image digest, CRD API version, config revision. Recording only "upgraded to latest" cannot reproduce faults. Upgrade CRD with convertible version evolution: add fields and conversion first, deprecate old fields after all clients switch; never change semantics of same field. Deletion policy also explicit: on CR delete, controller re-renders remaining rules, confirms new revision published before removing that policy's status/audit records.
Canary auto-promotion conditions must be quantifiable signals: e.g., within observation window, agent restarts, buffer usage, output error rate, parse error rate non-degrading, and canary node revisions all converged. Any key condition fails → stop expansion and rollback to last stable revision. This mechanism needs change system or GitOps orchestration; if platform lacks it, do not assume it exists in article — instead put manual approval and rollback commands into runbook.
10.2 Cost Accounting: Look at TCO, Not Container Count
Sidecar-to-DaemonSet benefit not only in request subtraction. Also track: business Pod scheduling fragmentation from extra container; image pull and CVE fix counts; business releases blocked by collector upgrades; per-node disk and network cost; Kafka/log backend write cost from duplicate labels and duplicate routing; platform Operator dev cost.
Use ledger for continuous comparison:
Monthly total cost = compute & memory resources + network egress + storage/index writes
+ collector ops effort + incident loss riskDaemonSet often reduces first two items' container duplication overhead, but adds node-level permission governance and control-plane dev. For few-dozen Pods with highly heterogeneous rules, mature log platform or few sidecars may be more cost-effective; for multi-tenant, large-scale, standardizable clusters, declarative DaemonSet platform yields higher marginal benefit.
11. Common Anti-Patterns
• "One Pod one Agent is always more reliable" : Isolation ≠ system reliability. Must compare business impact, resource budget, upgrade frequency.
• "Filesystem buffer guarantees no loss" : Only covers agent restart and short backend failure; node disk corruption, eviction, space exhaustion, over-fast rotation still lose data.
• "Just edit ConfigMap for hot reload" : Mount propagation has latency; collector zero-downtime reload depends on validated version. Use versioned rollout as baseline.
• "Put all labels and annotations into logs" : High cardinality inflates storage and query cost, may leak data. Keep whitelist of fields actually needed for queries.
• "eBPF can directly replace log Agent" : eBPF strong for network, security, perf observability, but stdout collection, semantic parsing, persistence, multi-backend delivery still need product-specific validation and kernel compatibility.
• "DaemonSet naturally doesn't affect business" : Shares node CPU, memory, disk, network with business; unbounded buffer or excessive priority can still squeeze business.
• "Logs are all same, just route by namespace" : Audit, error, debug logs have different reliability, retention, sensitivity; mixing into one sink loses governance and cost control.
• "Status Ready means data complete" : Ready only means config version published; log completeness also depends on rotation, node survival, buffer capacity, downstream ACK, and business generation method.
12. Conclusion: Choose Default Model, Not Eliminate All Models
For standard container logs, DaemonSet turns collection cost from "one per Pod" to "one per node", then declarative policies solve multi-team config; for strong isolation or local protocol needs, keep few sidecars; for traces, business events, active reporting, use gateway collectors. Key is not betting on one component, but defining data reliability tiers, capacity windows, tenant boundaries, and rollback mechanisms.
Before production, run a drill with real runtime paths, real log rates, and real downstream failure windows. The resulting resource requests, buffer capacities, and alert thresholds are the only proven answers for this architecture in your cluster.
Appendix: Pre-Production Checklist
• Confirmed log paths, runtimes, permissions, and mount strategies on all target nodes.
• Defined drop, retain, alert, and business compensation boundaries per log level.
• LogPipeline passes schema, auth, and conflict validation; sinks contain no tenant-custom credentials.
• Controller retains last valid revision and does not overwrite it on publish failure.
• DaemonSet validated on canary node pool for image, resources, disk buffer, NetworkPolicy, Prometheus scrape.
• Completed steady, burst, backend failure, rotation, restart, and rollback drills; test reports saved.
• Critical alerts include revision, node pool, sink, impact scope, and runbook.
• Audit log authoritative source not pure stdout; sensitive fields and credentials covered in security audit.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Cloud Architecture
Focuses on cloud‑native and distributed architecture engineering, sharing practical solutions and lessons learned. Covers microservice governance, Kubernetes, observability, and stability engineering to help your systems run stable, fast, and cost‑effectively.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
