✦ For everyone, free.

Practical knowledge for real and everyday life

Home

Kubernetes Container Log Handling

Kubernetes Container Log Handling ensures centralized, efficient, and secure logging across containerized applications using integrated tools and best practices.

Kubernetes Container Log Handling covers how the container runtime itself — containerd, CRI-O, or another CRI-compliant runtime — captures a container's stdout and stderr streams and makes them available to the kubelet, a layer beneath the kubelet's own log file management and distinct from it, since the runtime is what actually intercepts the container process's output at the moment it is written.


The Container Runtime's Role in Logging

Capturing Streams at the Process Level

When a container process writes to stdout or stderr, the container runtime (via its shim process managing the container) is what actually captures those file descriptors, since Kubernetes itself has no direct visibility into a container's I/O — it depends entirely on the runtime correctly redirecting and persisting these streams.

# containerd's shim process holds the container's log pipe
ps aux | grep containerd-shim

CRI Logging Requirements

The Container Runtime Interface specifies that a compliant runtime must write logs to a file at a path the kubelet provides, in the standardized CRI log format (timestamp, stream tag, log content), ensuring the kubelet can consistently parse output regardless of which specific runtime is in use underneath.

2024-01-15T10:32:14.123456789Z stdout F {"level":"info","msg":"request handled"}

Streaming Logs on Demand

The CRI Streaming API

Beyond the persisted log file, the CRI streaming API supports live log streaming, exec, attach, and port-forward operations, each requiring the runtime to expose a streaming endpoint that the kubelet proxies requests to — this is the underlying mechanism kubectl logs --follow and kubectl exec rely on.

kubectl logs api-service-7d4f9 --follow

How kubectl Requests Reach the Container Runtime

A kubectl logs request travels from the client to the API server, which proxies it to the appropriate node's kubelet, which in turn either reads the persisted log file directly or, for follow requests, establishes a streaming connection through the container runtime's streaming server — a multi-hop path where a failure at any layer can prevent logs from being retrieved.


Runtime-Specific Logging Behavior

containerd's Logging Implementation

containerd uses its shim architecture to manage each container's I/O independently of the main containerd daemon process, meaning a containerd daemon restart does not necessarily interrupt already-running containers' logging, since the shim continues operating independently.

CRI-O's Logging Implementation

CRI-O similarly delegates container process supervision to a per-container conmon process, which handles log capture and forwarding to the configured log path, following the same general CRI logging contract but with its own specific process architecture underneath.

Consistency Across Runtimes From the Kubelet's Perspective

Because both runtimes conform to the same CRI logging contract, the kubelet's own log-serving logic (and by extension, kubectl logs behavior) remains consistent regardless of which specific runtime a node uses, which is precisely the abstraction the CRI is designed to provide.


Log Driver Considerations Beyond CRI Defaults

Alternative Logging Configurations

Some container runtime configurations support alternative log handling beyond the default CRI json-file-equivalent behavior, such as forwarding directly to a logging daemon rather than writing to a local file first, which can reduce disk I/O on the node at the cost of tighter coupling to a specific logging backend's availability.

Implications for Log Availability During Runtime Issues

Because the container runtime itself is what captures and persists container output, a malfunctioning or overloaded runtime component can itself become a source of log loss or delay, independent of anything happening in the application or the kubelet's own log-serving logic — worth considering during runtime upgrades or when diagnosing logging gaps that do not correlate with application-level issues.


Diagnosing Logging Problems at the Runtime Layer

Distinguishing Runtime-Level From Kubelet-Level Failures

When kubectl logs fails to return expected output, checking the container runtime's own health and logs (via crictl logs directly against the runtime, bypassing the kubelet) helps isolate whether the problem originates in the runtime's log capture itself versus in the kubelet's proxying or the API server's connection to it.

crictl logs <container-id>