Sidecar Containers: The Agent's Best Friend
You've deployed your agent to Kubernetes. Requests arrive. Your agent handles them. But where do the logs go? Where's the monitoring data? How do you trace which request took how long?
Traditional single-container deployments mix your application logic with logging, monitoring, and debugging concerns. Your agent code becomes cluttered. Operational concerns become tangled with business logic. Testing gets harder.
Sidecars solve this. A sidecar is a helper container that runs alongside your main application container—in the same Pod, sharing the same network and storage. Your agent focuses on its job. The sidecar handles logging, metrics, proxying, or security concerns independently. They coordinate through shared volumes and localhost networking.
This lesson teaches you to design and deploy sidecars using Kubernetes' native sidecar support (available since Kubernetes 1.28). By the end, you'll deploy an agent with a logging sidecar and a metrics sidecar, keeping operational concerns separated from application logic.
The Sidecar Pattern: Separation of Concerns
The Problem: Tangled Concerns
Imagine your FastAPI agent needs to:
- Handle incoming requests
- Write structured logs to a file
- Expose Prometheus metrics
- Trace request latency
If all this logic lives in one container, your code becomes messy:
@app.post("/inference")
async def inference(request: Request):
# Business logic
result = model.predict(request.text)
# Logging concern
with open("/var/log/requests.log", "a") as f:
f.write(json.dumps({"request": request.text, "result": result}))
# Metrics concern
inference_counter.inc()
inference_latency.observe(time.time() - start)
# Return result
return {"prediction": result}
Your application code is entangled with operational concerns. Testing requires mocking file I/O and metrics. Scaling requires coordinating all concerns together.
The Solution: Sidecars
Instead, separate concerns into independent containers:
┌──────────────────────────────────────────────────┐
│ Pod (Shared Network) │
├──────────────────────┬──────────────────────────┤
│ │ │
│ Main Container │ Logging Sidecar │
│ (FastAPI Agent) │ │
│ │ - Reads /var/log/agent │
│ @app.post("/") │ - Streams to stdout │
│ Handle requests │ - Or sends to central │
│ │ logging system │
│ │ │
├──────────────────────┼──────────────────────────┤
│ Metrics Sidecar │ Proxy Sidecar │
│ │ (Optional) │
│ - Scrapes metrics │ │
│ - Exposes on :9090 │ - TLS termination │
│ │ - Service mesh inject │
│ │ - Request routing │
└──────────────────────┴──────────────────────────┘
Shared Volume (/var/log)
Shared Network (localhost)
Your agent writes logs to /var/log/agent/requests.log. The logging sidecar watches that file and streams it to a central logging service. Your agent exposes metrics on localhost:8000/metrics. A metrics sidecar scrapes and forwards those metrics.
Each container has one job. Testing is simpler. Scaling is predictable.
Native Sidecars: Kubernetes 1.28+
Before Kubernetes 1.28, sidecars were regular containers in the containers field, but Kubernetes couldn't distinguish them from the main application container. This created ambiguity about startup ordering and lifecycle.
Kubernetes 1.28 introduced the initContainers field with a new restartPolicy: Always option specifically for sidecars:
How It Works
- Init containers with
restartPolicy: Alwaysstart before main containers - They run continuously (unlike traditional init containers that exit after setup)
- If a sidecar crashes, Kubernetes restarts it independently
- Main container startup is NOT blocked by sidecar readiness
This guarantees:
- Sidecars are ready before main application starts processing requests
- Sidecars stay alive throughout the Pod's lifetime
- Main container failures don't restart sidecars unnecessarily
Concept 1: Writing Logs to Shared Volumes
Your agent and sidecars must communicate. The most reliable method: shared volumes.
Create a Pod with:
- Main container: Writes logs to
/var/log/agent/ - Logging sidecar: Reads from
/var/log/agent/, streams to stdout or external service
Step 1: Create Agent with Log Volume
Create agent-with-logging.yaml:
apiVersion: v1
kind: Pod
metadata:
name: agent-with-logging
spec:
containers:
- name: agent
image: myregistry.azurecr.io/agent:v1
ports:
- containerPort: 8000
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
env:
- name: LOG_FILE
value: /var/log/agent/requests.log
initContainers:
- name: logging-sidecar
image: fluent/fluent-bit:latest
restartPolicy: Always # <-- Native sidecar: Always restarts
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
args:
- -c
- /fluent-bit/etc/fluent-bit.conf
volumes:
- name: log-volume
emptyDir: {}
Key points:
- emptyDir: Temporary storage, shared across containers in the Pod
- volumeMounts: Both containers mount the same volume at
/var/log/agent - initContainers with restartPolicy: Always: Logging sidecar starts before agent and stays running
Deploy it:
kubectl apply -f agent-with-logging.yaml
Output:
pod/agent-with-logging created
Verify the Pod is running:
kubectl get pods
Output:
NAME READY STATUS RESTARTS AGE
agent-with-logging 2/2 Running 0 5s
The 2/2 shows both containers are ready. Without the sidecar, you'd see 1/1.
Step 2: Verify Log Volume Sharing
Exec into the agent container and create a test log:
kubectl exec -it agent-with-logging -c agent -- sh
Inside the container:
echo '{"request": "test", "result": "success"}' >> /var/log/agent/requests.log
cat /var/log/agent/requests.log
Output:
{"request": "test", "result": "success"}
Exit:
exit
Now exec into the logging sidecar and read the same file:
kubectl exec -it agent-with-logging -c logging-sidecar -- sh
Inside the sidecar:
cat /var/log/agent/requests.log
Output:
{"request": "test", "result": "success"}
Both containers see the same logs. The sidecar can now stream this data to a centralized logging service (e.g., Elasticsearch, Splunk, or Stackdriver).
Concept 2: Multi-Sidecar Pod with Metrics
Most production Pods run multiple sidecars. Let's add a metrics sidecar alongside logging.
Create agent-with-logging-and-metrics.yaml:
apiVersion: v1
kind: Pod
metadata:
name: agent-with-sidecars
spec:
containers:
- name: agent
image: myregistry.azurecr.io/agent:v1
ports:
- containerPort: 8000
name: http
- containerPort: 8001
name: metrics # <-- Agent exposes metrics here
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
env:
- name: LOG_FILE
value: /var/log/agent/requests.log
initContainers:
- name: logging-sidecar
image: fluent/fluent-bit:latest
restartPolicy: Always
volumeMounts:
- name: log-volume
mountPath: /var/log/agent
- name: metrics-sidecar
image: prom/prometheus:latest
restartPolicy: Always
ports:
- containerPort: 9090
name: prometheus
volumeMounts:
- name: prometheus-config
mountPath: /etc/prometheus
args:
- --config.file=/etc/prometheus/prometheus.yml
volumes:
- name: log-volume
emptyDir: {}
- name: prometheus-config
configMap:
name: prometheus-config
Deploy:
kubectl apply -f agent-with-logging-and-metrics.yaml
Output:
pod/agent-with-sidecars created
Check Pod status:
kubectl get pods
Output:
NAME READY STATUS RESTARTS AGE
agent-with-sidecars 3/3 Running 0 8s
Three containers running in one Pod: agent, logging sidecar, metrics sidecar. All share the Pod's network namespace, so the metrics sidecar can reach the agent's metrics endpoint at localhost:8001/metrics without any network configuration.
Concept 3: Sidecar Lifecycle and Ordering
Init Containers with restartPolicy: Always
When you use initContainers with restartPolicy: Always, Kubernetes guarantees:
- Startup order: Init containers start BEFORE main containers
- Independence: Each init container runs to completion before the next starts
- Restart behavior: If an init container crashes, it restarts (not the entire Pod)
- Main container waits: Main containers don't start until all init containers are healthy
This is different from:
- Traditional init containers (
restartPolicy: OnFailureor default): Run once, exit, never restart - Regular containers: Can be restarted independently, but don't block main container startup
Example: Startup Sequence
Time 0: Pod created
└─ Init container 1 (logging-sidecar) starts
Time 1: Logging sidecar ready
└─ Init container 2 (metrics-sidecar) starts
Time 2: Metrics sidecar ready
└─ Main container (agent) starts
Time 3: Agent running, processing requests
└─ All sidecars handle logging/metrics independently
Time 10: Metrics sidecar crashes
└─ Kubernetes immediately restarts it
└─ Agent continues running (unaffected)
Time 15: Metrics sidecar recovered
└─ Continues scraping metrics from agent
This ordering prevents race conditions where the agent starts before sidecars are ready.
Shutdown Sequence
When a Pod terminates:
Time 0: Pod receives termination signal
└─ All containers receive SIGTERM
Time 0-30: Containers have grace period to shut down
└─ Agent stops accepting requests
└─ Sidecars flush any buffered data
└─ Connections close gracefully
Time 30: Grace period expires
└─ Kubernetes sends SIGKILL
└─ Pod and all containers terminated
All containers terminate together, which is why sidecars (not separate sidecar Pods) are essential—they share the Pod's lifecycle.
Practical Configuration: Logging Sidecar for Agent Inference
Let's build a realistic logging sidecar for your agent. Your agent logs inference requests and latencies. The sidecar streams these to stdout for collection.
Create agent-inference-logging.yaml:
apiVersion: v1
kind: Pod
metadata:
name: agent-inference-logger
spec:
containers:
- name: agent
image: myregistry.azurecr.io/agent:v1
ports:
- containerPort: 8000
volumeMounts:
- name: logs
mountPath: /var/log/agent
env:
- name: LOG_FILE
value: /var/log/agent/inference.log
initContainers:
- name: log-collector
image: fluent/fluent-bit:latest
restartPolicy: Always
volumeMounts:
- name: logs
mountPath: /var/log/agent
- name: fluent-config
mountPath: /fluent-bit/etc
args:
- -c
- /fluent-bit/etc/fluent-bit.conf
volumes:
- name: logs
emptyDir: {}
- name: fluent-config
configMap:
name: fluent-bit-config
Deploy:
kubectl apply -f agent-inference-logging.yaml
Output:
pod/agent-inference-logger created
View logs from the sidecar:
kubectl logs agent-inference-logger -c log-collector
Output:
[2025-12-22 14:35:12.123] [info] [fluent-bit] version 2.1.0, commit hash: abc1234
[2025-12-22 14:35:12.234] [info] starting log collection from /var/log/agent/inference.log
[2025-12-22 14:35:23.456] [info] collected: {"request_id": "abc123", "latency_ms": 245, "status": "success"}
[2025-12-22 14:35:45.789] [info] collected: {"request_id": "def456", "latency_ms": 198, "status": "success"}
The sidecar reads and streams logs, which Kubernetes collects and makes available via kubectl logs.
Summary
Sidecars are helper containers that live in the same Pod as your main application. They handle operational concerns (logging, metrics, proxying, security) independently, keeping your application code clean.
Key takeaways:
- Native sidecars (Kubernetes 1.28+) use
initContainerswithrestartPolicy: Always - Shared volumes (
emptyDir) let containers communicate reliably - Shared network (
localhost) means sidecar and app can reach each other without service discovery - Lifecycle guarantees: Sidecars start first, restart independently, and shutdown with the Pod
- Separation of concerns: Application logic stays separate from operational concerns
Your FastAPI agent writes logs to /var/log/agent/. The logging sidecar reads them. Your agent exposes metrics on localhost:8001/metrics. The metrics sidecar scrapes them. Clean. Scalable. Testable.
Try With AI
Open a terminal and work through these scenarios with an AI assistant's help:
Scenario 1: Design a Logging Sidecar
Your task: Design a Pod manifest where:
- Main container: A Python FastAPI agent running on port 8000
- Sidecar: Fluent Bit collecting logs from
/var/log/agent/requests.logand forwarding to an external logging service (e.g., Datadog or CloudWatch)
Ask AI: "Create a Pod manifest with a FastAPI agent and a Fluent Bit sidecar that collects logs and forwards them to [your logging service]."
Review AI's response:
- Is the sidecar configured as an init container with
restartPolicy: Always? - Do both containers mount the same log volume?
- Is the Fluent Bit configuration correct for your logging service?
- Are environment variables set correctly for API keys or endpoints?
Tell AI: "The logging service requires authentication. I need to pass credentials securely."
Ask: "Update the manifest to inject credentials using a Kubernetes Secret."
Reflection:
- How does the sidecar access the Secret?
- What would break if the logging service becomes unavailable?
- Could you test this locally with Minikube?
Scenario 2: Multi-Sidecar Deployment
Your task: Your agent now needs:
- Logging sidecar (Fluent Bit)
- Metrics sidecar (Prometheus)
- Security sidecar (mTLS termination with Linkerd or Istio)
Ask AI: "Create a Pod manifest with three sidecars: logging, metrics, and security. Explain the startup and shutdown ordering."
AI might suggest:
- All sidecars as init containers with
restartPolicy: Always - Separate ConfigMaps for each sidecar's configuration
- Shared volumes for log files, shared network for metrics scraping
Ask: "What happens if the metrics sidecar crashes? Will my agent stop handling requests?"
Reflection:
- Why are sidecars better than separate Pods?
- How would you test sidecar failures locally?
- What's the memory and CPU cost of adding a third sidecar?
Scenario 3: Troubleshoot Sidecar Failures
Your task:
You deployed a Pod with an agent and logging sidecar. The Pod shows 2/2 READY, but logs aren't being collected. The Fluent Bit sidecar is healthy, but no data is appearing in your logging service.
Ask AI: "My logging sidecar is running but not collecting logs. The agent container is writing to /var/log/agent/requests.log. What could be wrong?"
AI might suggest:
- Verify the volume mount path is correct
- Check Fluent Bit configuration syntax
- Confirm the logging service credentials are valid
- Inspect Fluent Bit logs with
kubectl logs -c logging-sidecar
Ask: "Show me the exact kubectl commands to debug this."
Reflection:
- What would you look for in the sidecar logs?
- How would you verify the agent is actually writing to the shared volume?
- Could a permissions issue prevent the sidecar from reading the log file?