DevOps Day 54: Shared Volumes in Multi-Container Pods¶
Today's task was a fantastic dive into a more advanced Kubernetes pattern: the multi-container Pod. My objective was to create a single Pod that ran two separate containers and, most importantly, to set up a shared volume that both containers could read from and write to.
This was a critical lesson in how tightly-coupled processes can work together within the Kubernetes ecosystem. I learned how to define a shared emptyDir volume at the Pod level and then mount it into each container at different paths. This document is my very detailed, first-person guide to that entire process, written from the perspective of a complete beginner to this concept.
Table of Contents¶
The Task¶
My objective was to create a single Kubernetes Pod with two containers sharing a volume. The specific requirements were:
1. The Pod must be named volume-share-nautilus.
2. It must contain two containers, volume-container-nautilus-1 and volume-container-nautilus-2, both using the debian:latest image.
3. A shared emptyDir volume named volume-share must be created.
4. This volume must be mounted at /tmp/news in the first container and at /tmp/cluster in the second container.
5. I had to verify the setup by creating a file in one container's mount path and confirming its existence in the other's.
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¶
- I connected to the jump host.
- I created a new file named
shared-volume-pod.yamlusingvi. - Inside the editor, I wrote the following YAML code, which defines the shared volume at the Pod level and then mounts it into each container.
apiVersion: v1 kind: Pod metadata: name: volume-share-nautilus spec: volumes: - name: volume-share emptyDir: {} containers: - name: volume-container-nautilus-1 image: debian:latest command: ["/bin/sh", "-c", "sleep 3600"] volumeMounts: - name: volume-share mountPath: /tmp/news - name: volume-container-nautilus-2 image: debian:latest command: ["/bin/sh", "-c", "sleep 3600"] volumeMounts: - name: volume-share mountPath: /tmp/cluster - I saved and quit the file.
Phase 2: Applying the Manifest and Verifying¶
- I used
kubectlto create the Pod from my manifest.kubectl apply -f shared-volume-pod.yaml - Verification: The final part of the task was to prove that the volume was truly shared.
- First, I checked the Pod's status with
kubectl get pods. TheREADYcolumn showed2/2, confirming both containers were running. - Next, I
exec'd into the first container to create the test file.kubectl exec -it volume-share-nautilus -c volume-container-nautilus-1 -- /bin/bash # Inside the container shell: echo "Shared volume test" > /tmp/news/news.txt exit - Finally, I
exec'd into the second container to look for the file.The output listed thekubectl exec -it volume-share-nautilus -c volume-container-nautilus-2 -- /bin/bash # Inside the container shell: ls -l /tmp/cluster/news.txtfile. This was the definitive proof that both containers were writing to and reading from the exact same directory, successfully completing the task.
- First, I checked the Pod's status with
Why Did I Do This? (The "What & Why" for a K8s Beginner)¶
- Multi-Container Pods: This is a powerful Kubernetes pattern. While most Pods have a single container, you can run multiple containers together in one Pod when they are very tightly coupled and need to share resources. The classic example is a "sidecar" container that helps a main application container (e.g., a log shipper that reads the main app's logs and sends them to a central location). Because containers in a Pod share a network, they can communicate via localhost.
- Shared Volumes: This is the key concept of the task. By defining a volume at the Pod level (spec.volumes), I create a storage resource that can be accessed by any container within that Pod.
- emptyDir Volume: This is the simplest type of volume in Kubernetes.
- What it is: An emptyDir is exactly what it sounds like: a new, empty directory that is created when the Pod is scheduled on a Node.
- Lifecycle: It is ephemeral. It exists only as long as the Pod exists. When the Pod is deleted, the emptyDir and all its data are permanently erased.
- Use Case: It's perfect for temporary scratch space or, as in my task, for providing a shared filesystem for multiple containers in the same Pod. One container can write data, and the other can immediately read it.
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.
apiVersion: v1
kind: Pod
metadata:
name: volume-share-nautilus
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 first volume definition.
- name: volume-share # I give the volume a name that I can refer to later.
emptyDir: {} # I specify the TYPE of volume. '{}' means use the defaults.
# 'containers' is a list of all the containers that will run in this Pod.
containers:
# This is the definition for the first container.
- name: volume-container-nautilus-1
image: debian:latest
command: ["/bin/sh", "-c", "sleep 3600"] # A command to keep it running.
# This 'volumeMounts' block is inside the container's definition.
# It tells this specific container how to USE a volume declared above.
volumeMounts:
- name: volume-share # This name MUST match the name from the 'volumes' block.
mountPath: /tmp/news # This is the path INSIDE this container to mount the volume.
# This is the definition for the second container.
- name: volume-container-nautilus-2
image: debian:latest
command: ["/bin/sh", "-c", "sleep 3600"]
# This container also gets a 'volumeMounts' block.
volumeMounts:
- name: volume-share # It refers to the SAME volume by name.
mountPath: /tmp/cluster # But it mounts it at a DIFFERENT path inside this container.
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 exec and kubectl logs to tell Kubernetes which specific container you want to interact with. Forgetting it will result in an error.
- 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 debian image doesn't have a default command that keeps it running. Without the command: ["/bin/sh", "-c", "sleep 3600"], the containers 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 (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 exec -it [pod-name] -c [container-name] -- [command]: Executes a command inside a specific container within a multi-container Pod. This was the essential command for my verification step.
- -i: Interactive.
- -t: Allocate a TTY.
- -c: Specifies the container name.
- --: Separates the kubectl command from the command to be run inside the container.