Skip to content

DevOps Day 55: Implementing the Kubernetes Sidecar Pattern

Today's task was a fantastic dive into an advanced and powerful Kubernetes design pattern: the Sidecar. My objective was to create a single Pod that ran two separate containers that worked together. The main container was an Nginx web server, and a "sidecar" container ran alongside it, with the sole purpose of reading and shipping the Nginx logs.

This was a critical lesson in the "separation of concerns" principle and demonstrated how tightly-coupled processes can cooperate within the Kubernetes ecosystem. I learned how to define a shared emptyDir volume at the Pod level and then mount it into each container, creating a shared filesystem that allowed them to communicate. This document is my very detailed, first-person guide to that entire process, written from the perspective of a beginner to this concept.

Table of Contents


The Task

My objective was to create a single Kubernetes Pod with two containers sharing a volume, implementing a log-shipping sidecar pattern. The specific requirements were: 1. The Pod must be named webserver. 2. It must have an emptyDir volume named shared-logs. 3. It must contain two containers: - nginx-container: using the nginx:latest image. - sidecar-container: using the ubuntu:latest image, running a continuous loop to cat the Nginx log files. 4. The shared-logs volume must be mounted at /var/log/nginx in both containers.


My Step-by-Step Solution

The professional way to create this complex Pod is with a single YAML manifest file.

Phase 1: Writing the Pod Manifest

  1. I connected to the jump host.
  2. I created a new file named webserver-pod.yaml using vi.
  3. Inside the editor, I wrote the following YAML code, which defines the shared volume at the Pod level and then mounts it into each of the two containers.
    apiVersion: v1
    kind: Pod
    metadata:
      name: webserver
    spec:
      volumes:
      - name: shared-logs
        emptyDir: {}
      containers:
      - name: nginx-container
        image: nginx:latest
        volumeMounts:
        - name: shared-logs
          mountPath: /var/log/nginx
      - name: sidecar-container
        image: ubuntu:latest
        command: ["/bin/sh", "-c"]
        args: ["while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]
        volumeMounts:
        - name: shared-logs
          mountPath: /var/log/nginx
    
  4. I saved and quit the file.

Phase 2: Applying the Manifest and Verifying

  1. I used kubectl to create the Pod from my manifest.
    kubectl apply -f webserver-pod.yaml
    
  2. Verification: The final part of the task was to prove that the sidecar was successfully reading the logs written by the main container.
    • First, I checked the Pod's status with kubectl get pods. The READY column showed 2/2, confirming both containers were running.
    • Next, I generated some log data. I did this by clicking the "Website" button in the lab UI, which sent a request to the Nginx server.
    • Finally, I checked the logs of the sidecar container.
      kubectl logs webserver -c sidecar-container
      
      The output showed the Nginx access log entry from my website visit. This was the definitive proof that the nginx-container wrote a log to the shared volume, and the sidecar-container successfully read it.

Why Did I Do This? (The "What & Why" for a K8s Beginner)

- The Sidecar Pattern: This is a powerful design pattern for extending or enhancing the functionality of an existing application container without changing it. The "sidecar" is a helper container that runs alongside the main app in the same Pod. - Separation of Concerns: This is the core principle behind the sidecar pattern. - The nginx container's only job is to be a web server. It does one thing, and it does it well. The Nginx developers don't have to build complex log-shipping logic into their application. - The sidecar-container's only job is to handle logs. It reads the logs and, in a real-world scenario, would forward them to a central logging service like Elasticsearch or Splunk. This separation makes both components simpler, more reusable, and easier to manage and update independently. - Shared EmptyDir Volume: This is the key that enables the sidecar pattern. An emptyDir is a temporary volume that is created when a Pod starts and is destroyed when the Pod is deleted. Its primary purpose is to provide a shared filesystem for containers running within the same Pod. In my task, Nginx writes its log files into this shared directory, and the sidecar container can immediately read those same files from that same directory.


Deep Dive: A Line-by-Line Explanation of My Pod YAML File

The YAML for a multi-container pod with a shared volume has two key parts: defining the volume and then mounting it into each container.

[Image of a Kubernetes Sidecar pattern diagram]

apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  # This 'volumes' block is at the Pod's 'spec' level. This is where I DECLARE
  # all the volumes that will be available to the containers in this Pod.
  volumes:
  # The '-' indicates an item in a list. This is my volume definition.
  - name: shared-logs  # I give the volume a name that I can refer to later.
    emptyDir: {}       # I specify the TYPE of volume as an empty directory.

  # 'containers' is a list of all the containers that will run in this Pod.
  containers:
  # This is the definition for the first container (the main app).
  - name: nginx-container
    image: nginx:latest

    # This 'volumeMounts' block is inside the container's definition.
    # It tells this specific container how to USE a volume declared above.
    volumeMounts:
    - name: shared-logs  # This name MUST match the name from the 'volumes' block.
      mountPath: /var/log/nginx # This is the path INSIDE this container to mount the volume.

  # This is the definition for the second container (the sidecar).
  - name: sidecar-container
    image: ubuntu:latest
    # This command runs a continuous loop that reads the log files every 30 seconds.
    command: ["/bin/sh", "-c"]
    args: ["while true; do cat /var/log/nginx/access.log /var/log/nginx/error.log; sleep 30; done"]

    # This container also gets a 'volumeMounts' block.
    volumeMounts:
    - name: shared-logs  # It refers to the SAME volume by name.
      mountPath: /var/log/nginx # It mounts the volume at the SAME path.

Common Pitfalls for Beginners

- Forgetting the -c flag: When a Pod has more than one container, you must use the -c <container-name> flag for commands like kubectl logs and kubectl exec to tell Kubernetes which container you want to interact with. - Confusing volumes and volumeMounts: volumes is defined once at the Pod level to create the volume. volumeMounts is defined inside each container that needs to access that volume. - Name Mismatch: The name in a container's volumeMounts section must exactly match the name of a volume defined in the Pod's volumes section. A typo will cause the Pod to fail to start. - Forgetting a command: The ubuntu image doesn't have a default command that keeps it running. Without the command and args to run the sleep loop, the sidecar container would start, do nothing, and immediately exit, causing the Pod to go into a CrashLoopBackOff state.


Exploring the Essential kubectl Commands

- kubectl apply -f [filename.yaml]: The standard way to create or update resources from a manifest file. - kubectl get pods: Gets a summary list of all Pods. I used this to check the READY status (should be 2/2). - kubectl describe pod [pod-name]: Describes a Pod in great detail. I used this to verify that the volumes and volume mounts were configured correctly for both containers. - kubectl logs [pod-name] -c [container-name]: Shows the logs from a specific container within a multi-container Pod. This was the essential command for my final verification step. The -c flag is mandatory for multi-container pods. - kubectl exec -it [pod-name] -c [container-name] -- [command]: Executes a command inside a specific container. I could have used this to ls /var/log/nginx inside both containers to verify the shared volume.