DevOps Day 65: Deploying a Configured Redis Cache on Kubernetes¶
Today's task was a fantastic, real-world example of deploying a stateful service—a Redis cache—on Kubernetes. This went far beyond just running a container; I had to manage its configuration externally using a ConfigMap, provide it with storage volumes, and define its resource requirements.
This was a brilliant exercise that taught me how to take an off-the-shelf Docker image (redis:alpine) and customize its runtime behavior without modifying the image itself. I learned how to create a ConfigMap, mount it as a file into a Pod, and override the container's startup command to use that new configuration file. This document is my very detailed, first-person guide to that entire process, written for a Kubernetes beginner.
Table of Contents¶
- DevOps Day 65: Deploying a Configured Redis Cache on Kubernetes
- Table of Contents
- The Task
- My Step-by-Step Solution
- Phase 1: Creating the ConfigMap
- Phase 2: Writing the Deployment Manifest
- Phase 3: Applying the Manifest and Verifying
- Why Did I Do This? (The "What \& Why" for a K8s Beginner)
- Deep Dive: A Line-by-Line Explanation of My Deployment YAML
- Common Pitfalls for Beginners
- Exploring the Essential
kubectlCommands
The Task¶
My objective was to deploy a customized Redis instance using a Kubernetes Deployment. The specific requirements were:
1. Create a ConfigMap named my-redis-config containing a key redis-config with the value maxmemory 2mb.
2. Create a Deployment named redis-deployment with 1 replica and the label app: redis.
3. The container had to be named redis-container, use the redis:alpine image, and request 1 CPU.
4. It required two volumes:
- An emptyDir volume named data mounted at /redis-master-data.
- A ConfigMap volume named redis-config (using the ConfigMap I created) mounted at /redis-master.
My Step-by-Step Solution¶
The solution involved an imperative command to create the ConfigMap and a declarative manifest to create the Deployment that uses it.
Phase 1: Creating the ConfigMap¶
First, I needed to create the configuration object that my Redis container would later consume.
1. I connected to the jump host.
2. I used a single, imperative kubectl create configmap command to generate the ConfigMap with the required key-value data.
kubectl create configmap my-redis-config --from-literal=redis-config="maxmemory 2mb"
configmap/my-redis-config created.
Phase 2: Writing the Deployment Manifest¶
- I created a new file named
redis-deployment.yamlusingvi. - Inside the editor, I wrote the following YAML code. This defines the Deployment, its container, the volumes, and critically, overrides the container's command to use my custom config.
apiVersion: apps/v1 kind: Deployment metadata: name: redis-deployment spec: replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis-container image: redis:alpine command: ["redis-server"] args: ["/redis-master/redis-config"] ports: - containerPort: 6379 resources: requests: cpu: "1" volumeMounts: - name: data mountPath: /redis-master-data - name: redis-config mountPath: /redis-master volumes: - name: data emptyDir: {} - name: redis-config configMap: name: my-redis-config - I saved and quit the file.
Phase 3: Applying the Manifest and Verifying¶
- I used
kubectlto create the Deployment from my manifest.kubectl apply -f redis-deployment.yaml - Verification: The final part of the task was to prove that the Redis server was actually using my custom configuration.
- First, I checked that the Pod was
Runningwithkubectl get pods. - This was the definitive proof: I
exec'd into the running container and used the Redis command-line interface (redis-cli) to inspect its live configuration.# Get the full name of the pod POD_NAME=$(kubectl get pods -l app=redis -o jsonpath='{.items[0].metadata.name}') # Exec into the pod and run redis-cli kubectl exec -it $POD_NAME -- redis-cli - Inside the Redis prompt, I ran the command:
The output was
127.0.0.1:6379> CONFIG GET maxmemory2097152(2 megabytes), which proved that mymaxmemory 2mbsetting from the ConfigMap was successfully loaded and applied by the Redis server.
- First, I checked that the Pod was
Why Did I Do This? (The "What & Why" for a K8s Beginner)¶
- Redis: Redis is an extremely fast, in-memory key-value data store. It's most commonly used as a cache to speed up applications by reducing the load on slower, disk-based databases. It's also used for real-time analytics, session management, and as a message broker.
- ConfigMap Volume: This is a core Kubernetes pattern for decoupling configuration from your application image.
- The Problem: I could have built a custom Docker image with a redis.conf file inside it, but what if I need to change the maxmemory setting later? I would have to rebuild the entire image.
- The Solution: I stored my configuration (maxmemory 2mb) in a Kubernetes ConfigMap. Then, I told my Pod to mount this ConfigMap as a volume. Kubernetes creates a file named redis-config (the key from the ConfigMap) inside the /redis-master directory in my container. This way, I can update the ConfigMap in the cluster, and the new configuration will be available to the Pod without needing an image rebuild.
- Overriding Container command and args: This was the crucial step to make the ConfigMap useful. The default redis:alpine image just runs the redis-server command. By setting the command and args in my Pod spec, I overrode that default. I told the container to run redis-server /redis-master/redis-config, which explicitly instructs the Redis server to start up using my custom configuration file.
Deep Dive: A Line-by-Line Explanation of My Deployment YAML¶
This YAML manifest is a great example of how to configure a custom application.
[Image of a Kubernetes Deployment with ConfigMap and emptyDir volumes]
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-deployment
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis-container
image: redis:alpine
# This 'command' and 'args' block overrides the image's default startup command.
# It tells the container to run the 'redis-server' executable...
command: ["redis-server"]
# ...and to pass '/redis-master/redis-config' as an argument to it.
# This instructs Redis to load our custom config file.
args: ["/redis-master/redis-config"]
ports:
- containerPort: 6379 # The default Redis port.
# This block defines the resource requests for the container.
resources:
requests:
cpu: "1" # Requesting 1 full CPU core.
# This block defines where to mount the volumes inside the container.
volumeMounts:
- name: data # This name must match a volume below.
mountPath: /redis-master-data
- name: redis-config # This name must match a volume below.
mountPath: /redis-master
# This block defines the volumes that are available to this Pod.
volumes:
# The first volume is a temporary scratch space.
- name: data
emptyDir: {}
# The second volume's source is our ConfigMap.
- name: redis-config
configMap:
# This must be the name of the ConfigMap I created earlier.
name: my-redis-config
Common Pitfalls for Beginners¶
- Forgetting to Override the Command: If I had forgotten the command and args block, the Redis container would have started, but it would have used its default, built-in configuration, ignoring my maxmemory setting completely.
- ConfigMap Name Mismatch: A typo in the configMap.name in the Deployment's volumes section would cause the Pod to fail to start with a CreateContainerConfigError because it couldn't find the specified ConfigMap.
- Key/File Mismatch: The args path (/redis-master/redis-config) works because the volume is mounted at /redis-master and the key in the ConfigMap is redis-config. If the key in the ConfigMap was different, the filename inside the container would be different, and the redis-server command would fail.
- exec into the wrong tool: For my verification, I had to use redis-cli, the specific client for Redis. Just getting a shell (/bin/bash) would not have been enough to query the live configuration.
Exploring the Essential kubectl Commands¶
- kubectl create configmap [name] --from-literal=[key]=[value]: An imperative command to create a ConfigMap directly from a key-value pair on the command line. This is very fast for simple configurations.
- kubectl apply -f [filename.yaml]: The standard way to create or update resources from a manifest file.
- kubectl get pods / kubectl get deployment: My high-level tools to check the overall status.
- kubectl describe pod [pod-name]: My primary tool for troubleshooting startup issues. It would have shown me errors if the ConfigMap volume failed to mount.
- kubectl exec -it [pod-name] -- redis-cli: The most important verification command. It gave me an interactive terminal with the redis-cli tool inside my running container, allowing me to directly query the live application's state.