Deploying Redis Cluster with StatefulSets
What will you learn
- Statefulsets
- initContainers
Creating a headless service
We will use Redis as Statefulsets for our instavote application stack.
It is similar to Deployment, but Statefulsets requires a Service Name
.
Lets being by cleaning up the existing redis installation
kubectl delete svc redis
kubectl delete deploy redis
So we will create a headless service for redis first. A headless service is the one which will be created with no ClusterIP of its own, but will return the actual IP address of its endpoints (e.g. pods), which we would create later in this case.
file: dev/redis-sts/redis-svc.yml
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
type: ClusterIP
ports:
- name: redis
port: 6379
targetPort: redis
clusterIP: None
selector:
app: redis
statefulset.kubernetes.io/pod-name: redis-0
Observe
- clusterIP value is set to None
- selector has been updated to send traffic only to the master
now apply this and validate
cd k8s-code/projects/instavote/dev/redis-sts
kubectl apply -f redis-svc.yml
kubectl get svc
kubectl describe svc redis
Adding Redis configurations with ConfigMap
Lets now add the redis configuration with configmap.
Redis ConfigMap has two keys * master.conf - to provide Redis master configs * slave.conf - to provide Redis slave configs
file: redis-cm.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: redis
data:
master.conf: |
bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize no
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
slave.conf: |
slaveof redis-0.redis 6379
apply and validate
kubectl apply -f redis-cm.yml
kubectl get cm
kubectl describe cm redis
Using initContainers to configure redis replication
We have to deploy redis master/slave set up from one statefulset cluster. This requires two different redis cofigurations , which needs to be described in one Pod template. This complexity can be resolved by using init containers. These init containers copy the appropriate redis configuration by analysing the hostname of the pod. If the Pod's (host)name has 0
as Ordinal number, then it is choosen as the master and master.conf is copied to /etc/ directory. Every other pod will be configured as a replica with slave.conf as configuration.
file: redis-sts.yml
[...]
initContainers:
- name: init-redis
image: redis:4.0.9
command:
- bash
- "-c"
- |
set -ex
# Generate mysql server-id from pod ordinal index.
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
# Copy appropriate conf.d files from config-map to emptyDir.
if [[ $ordinal -eq 0 ]]; then
cp /mnt/config-map/master.conf /etc/redis.conf
else
cp /mnt/config-map/slave.conf /etc/redis.conf
fi
volumeMounts:
- name: conf
mountPath: /etc
subPath: redis.conf
- name: config-map
mountPath: /mnt/config-map
Deploying Redis Master Slaves with Statefulsets
These redis containers are started after initContainers are successfully run and exit. One thing to note here, these containers mount the same volume, conf
, from the initContainers which has the proper Redis configuration.
file: redis-sts.yaml
[...]
containers:
- name: redis
image: redis:4.0.9
command: ["redis-server"]
args: ["/etc/redis.conf"]
env:
- name: ALLOW_EMPTY_PASSWORD
value: "yes"
ports:
- name: redis
containerPort: 6379
volumeMounts:
- name: redis-data
mountPath: /data
- name: conf
mountPath: /etc/
subPath: redis.conf
To apply
kubectl apply -f redis-sts.yml
Validate the MASTER-SLAVE configuration
kubectl exec redis-0 -- redis-cli ROLE
kubectl exec redis-1 -- redis-cli ROLE
redis-0 should have been configured as master, redis-1 as slave. You should also see that redis-1 is been configured as the slave of redis-0.redis as follows,
kubectl exec redis-1 redis-cli ROLE
slave
redis-0.redis
6379
connected
28
This validated the redis master slave configuration.
Nano Project: Configuring persistent volume claim per instance of redis
Similar to databases, each redis instance needs its own data store, which should also be persistent. Current code that you have applied uses emptyDir as the volume type. This is a problem as emptyDir gets deleted when the pod is deleted, and is not persistent.
Your task is to update the YAML file for the statefulset with the following changes,
- use volumeClaimTemplate instead of volumes for volume redis-data. This will ensure a persistentVolumeClaim per replica/pod. All other volumes remain unchanged.
- Provide the storageClass as NFS, a provisioner for which is already been configured
- Size of the volume could be 200Mi as its just a key value store used by the instavote app to store the votes.
- accessModes should be ReadWriteOnce as the volumes for redis should not be shared between multiple instances.
Update the statefulSet, apply and validate that persistentVolumeClaim and persistentVolume are created for each instance/pod of redis application.
Reading List
Search Keywords
- init containers
- kubernetes statefulsets
- redis replication