π Production Readiness Continued
We're not done improving things yet! This section is a continuation of the previous one, where we will further enhance our deployment by adding a few more important features. Using ConfigMaps and volumes, we'll continue stepping towards a more production-ready deployment.
Remember, back in the section 4 where we set up our Postgres database, we used a pre-built container image that automatically initialized the database & schema needed to run our application. This really was little more than a hacky workaround to get us up and running quickly, and in a real-world scenario, we should use the official Postgres image and inject the initialization script(s) at runtime. This is what we'll do in this section. This is a pattern you should adopt in your own applications, as baking in configuration and initialization data into your container images is not a good practice.
πΊοΈ ConfigMaps
A ConfigMap in Kubernetes is an API object used to store non-confidential configuration data. ConfigMaps allow you to decouple configuration artifacts from image content and your application code. They can be used to store settings, scripts and entire configuration files. They are a little like Secrets, but are intended for non-sensitive data, and can be created from files.
π Kubernetes Docs: ConfigMaps
In our case, we'll use a ConfigMap to store the database initialization SQL script, which will then be mounted into the Postgres container.
The SQL script can be found & downloaded here. You can grab it straight into a file using curl
like so:
curl -O https://kube-workshop.benco.io/08-more-improvements/nanomon_init.sql
We can then create a ConfigMap from this file using kubectl
kubectl create configmap nanomon-sql-init --from-file=nanomon_init.sql
Like every object in Kubernetes, ConfigMaps can also be created with a YAML manifest, but when working with external files/scripts etc, kubectl is your only real option.
There are three mains ways to use a ConfigMap in with a Pod: as container command and args, as environment variables, or as files in a volume. In this section we'll use the volume method.
πΎ Volumes & Volume Mounts
A Volume in Kubernetes is a directory that is accessible to containers in a pod. Volumes are used to persist data, share data between containers, and manage configuration. When it comes to persisting data and storage in Kubernetes, it's a stageringly complex & deep topic. However volumes can also be used to easily provide a container with access to configuration files, via a ConfigMap.
There's always two parts to using a volume:
- Define the volume in the Pod spec, and specify the source of the volume.
- Define a volume mount in the container spec, which references the volume, and specifies the filesystem path inside the container where the volume should be mounted.
Update the Postgres deployment manifest to include the volume and volume mount, as follows:
# Add this volume section to the pod spec, at the same level as containers
volumes:
- name: initdb-vol
configMap:
name: nanomon-sql-init
Β
# Add this volumeMounts section to the postgres container spec
volumeMounts:
- name: initdb-vol
mountPath: /docker-entrypoint-initdb.d
readOnly: true
Hey, what's this /docker-entrypoint-initdb.d
path? Is this some Kubernetes thing? No, this is a special directory in
the official Postgres image. Any *.sql
or *.sh
files found in this directory when the container starts will be
automatically executed by the Postgres entrypoint script. This is a really useful feature of the official Postgres
image, and is why we don't need to create our own custom Postgres image.
The last thing to do is to update the Postgres container spec to use the official Postgres image, hosted publically on Dockerhub, rather than our custom one. Change the image line to:
image: postgres:17
You'll need to delete the existing Postgres deployment and recreate it for these changes to take effect:
kubectl delete deployment postgres
kubectl apply -f postgres-deployment.yaml
After it has started if you check the logs of the Postgres pod, you should see that the initialization script
/docker-entrypoint-initdb.d/nanomon_init.sql
has been executed:
kubectl logs -l app=postgres
If you want to check or debug mounted volumes, you can run commands in a running container using kubectl exec
, this is
a SUPER useful command to know about as it allows you to get shell access inside any container in your cluster, assuming
you have the right permissions. You can then inspect the filesystem, environment variables, running processes etc.
kubectl get pod -l app=postgres
kubectl exec -it <postgres-pod-name> -- /bin/bash
# Inside the container now, spooky!
ls /docker-entrypoint-initdb.d
cat /docker-entrypoint-initdb.d/nanomon_init.sql
πββοΈ Running The Runner
This part is optional, but if you want to run the runner part of NanoMon, you can create a Deployment for it. The runner is what executes and runs the monitoring tasks, and is a separate component to the API and frontend.
- The runner image is ACR_NAME.azurecr.io/nanomon-runner:latest
- The runner needs to connect to the Postgres database just like the API, so it needs the exact same environment variables set.
- Run a single replica of the runner Pod.
- It doesn't need any ports exposed, as it doesn't serve any traffic, it also doesn't need any Service.
Once the runner is running, you can check the logs of the runner pod to see it in action, you can visit the NanoMon frontend to create some monitors, and see their status update and change as they are executed by the runner. Have fun!
The runner can make network connections out to the internet, this is the default in AKS. Controlling network egress is outside the scope of this workshop. If you are interested in learning more about this, you can look into Network Policies.
πΌοΈ Cluster & Architecture Diagram
Here's an updated architecture diagram showing the components we've built so far, some of the details from previous sections have been omitted for clarity.
π Reference Manifests
If you get stuck and are looking for working manifests you can refer to, they are available here: