Shared Volume in Kubernetes Pod: A fun experiment [ linux fortune, nginx ]
We all know that each container in a Kubernetes Pod gets its own filesystem. Also Pods are ephemeral i.e, they get restarted frequently. This will make you realise that any data that was written by the pod into its own filesystem will be lost once the container restarts, and the new container (even though in the same pod) will not see the data written by the previous container.
The code for this project is available in my Github repo:
Backdrop
We have a frequent usecase where a single container running in a pod will need supplementary help from another container in the same pod, usually called a sidecar container. These two (might be many more) containers will need to share data between them right ?
How do we achieve this ? In comes Kubernetes Volumes.
Volumes !
Volumes are a component of a K8s Pod and bound to the lifecycle of the Pod itself. They aren’t a standalone k8s object and cannot be created independently. They are created along with a Pod and individual containers inside it can access it by mounting it at their desired location.
- We will use emptyDir volume (just a jargon) because it is the simplest volume that lets us share data between containers. The storage is empty by default and containers are free to dump data, as required.
Architecture !!
The architecture we want to achieve is shown below.
Everything you see above is happening inside a single Pod.
- nginx webserver — this container is running a nginx webserver and the html it is serving is fetched from the shared emptyDir volume. This container has mounted the volume at
/usr/share/nginx/html
. FYI, this is the directory from which nginx (by default) serves html pages. - ubuntu fortune container — this container is running a bash script that periodically (time is configurable) uses the linux
fortune
command to fetch random quotations and dumps them into the shared volume (for the nginx webserver to read).
As a result, if you are viewing the nginx root webpage, it should be serving new quotes every 5s (if time period = 5s).
3. htmldump volume — it is a Kubernetes storage volume that is being used in tandem with the two containers.
Creating the ubuntu fortune image
Nginx image is publicly available. However, we need to create the fortune
image according to our particular usecase.
# fortune-fetcher.sh
#!/bin/bash
trap "exiting the fortune loop" SIGINT
mkdir /var/html
while :
do
echo $(date) Writing fortune to /var/html/index.html
/usr/games/fortune > /var/html/index.html
sleep 5
done
Script Logic
This script basically runs a while loop
- that sleeps for 5s
- Redirects the output from the
fortune
command to the/var/html/index.html
file. This is the file which we plan on exposing to nginx for webpage rendering.
Dockerfile
Let us build a public image for this script.
# Dockerfile
FROM ubuntu:latest
RUN apt-get update; apt-get -y install fortune
ADD fortune-fetcher.sh /bin/fortune-fetcher.sh
RUN chmod +x /bin/fortune-fetcher.sh
ENTRYPOINT [ "/bin/fortune-fetcher.sh" ]
Dockerfile Logic
This Dockerfile builds on top of the ubuntu
image
- We apt install the
fortune
binary, which by default gets installed in/usr/bin/fortune
path. Ref: https://linux.die.net/man/6/fortune - We copy our script to the path
/bin/fortune-fetcher.sh
in the container. - We add execution permission to the script binary.
- The entrypoint is executing the script binary itself.
~ Building the image ~
> docker build -t sabujjana/fortune
Now that we have the image built, let us push it to our public DockerHub -https://hub.docker.com/u/sabujjana.
> docker push sabujjana/fortune
We can view the image here: https://hub.docker.com/r/sabujjana/fortune
Creating the Pod Manifest
Now that we have the container images in place, let us go ahead and write the Pod manifest. Naming it fortune-pod.yaml
under manifests
directory.
> mkdir manifests
> touch manifests/fortune-pod.yaml
# fortune-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
containers:
- image: sabujjana/fortune
name: fortunegenerator
volumeMounts:
- name: htmldump
mountPath: /var/html
- image: nginx:latest
name: webserver
volumeMounts:
- name: htmldump
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: htmldump
emptyDir: {}
Pod YAML Logic
- We have named the pod
fortune
- Container 1 — fortunegenerator — runs our image
sabujjana/fortune
and mounts the emptyDir volume at/var/html
path - Container 2 — webserver — runs the public image
nginx:latest
; mounts the emptyDir volume at/usr/share/nginx/html
path and exposes the web server at Port 80 - Volume
htmldump
is the emptyDir volume
Deploying the Pod Manifest !
- Let us create a namespace
fortune-nginx
where we will run our Pod.
- Now if we list the pods in the ns, we can see
Our fortune pod is up and running successfully. If you look at the READY
column, it says 2/2
which means that both the containers are up and running successfully.
Exposing the Pod to access the webpage
Let us use port-forwarding to expose the nginx server to localhost.
Now if we hit localhost:8080
, we should see the page that nginx renders.
- After
5s
, if you refresh the page, you should see a new quote.
You can continue playing with this — who knows, maybe a quote will encourage you to change your life perspective !
Checking the Pod Logs
A good practice is to look at the Pod logs to ensure that our service is running perfectly.
You can see that the logs are the echo
messages we had used in our fortune-generator.sh
script.
Wrapping up
- Find the github code here: https://github.com/JanaSabuj/fortune-nginx-volume
- Find the fortune image here: https://hub.docker.com/r/sabujjana/fortune
- Archive of all my blogposts can be found on my personal website: https://janasabuj.github.io/posts/
- For more such reads, consider following my Medium Space !