Run Memcached persistently on Kubernetes

When running Memcached on the Kubernetes cluster, the cache data will disappear whenever the pod restarts. I know that Redis has the built-in replication and it can be able to have data on persistence disks. However, I have some reasons to run the Memecached on our Kubernetes cluster. So, I was thinking about it and I came across my mind the way using postStart and preStop in lifecycle settings.

Before explaining, the disadvantages of this way are as follows.

  • Cannot cope with node failures
  • Need to add a persistent volume

First, create a script file that loads a dump file if it exists, and then output an empty file for the readiness probe. (post_start.sh)

 1#!/bin/ash
 2memc_host="127.0.0.1"
 3memc_port=11211
 4
 5# Load previous prestop data
 6dump_file="${DUMP_DIR}/${HOSTNAME}.dump.prestop"
 7if [ -f $dump_file ];then
 8    echo "Loading previous prestop data..."
 9    cat $dump_file | nc $memc_host $memc_port
10fi
11
12# For readiness probe
13touch /tmp/ready

And then, create a script that dump data before termination.

1#!/bin/ash
2
3# Load previous prestop data
4/memcached-tool ${HOSTNAME} dump > ${DUMP_DIR}/${HOSTNAME}.dump.prestop

After creating the above scripts, build the container image with the scripts by the following Dockerfile.

 1FROM memcached:1.6.6-alpine
 2
 3# Switch to root for installing packages
 4USER root
 5
 6RUN apk --update --no-cache add \
 7        bash \
 8        busybox-extras \
 9        perl
10
11COPY memcached-tool  /
12COPY post_start.sh   /
13COPY pre_stop.sh     /
14
15# Switch to memcache for running
16USER memcache

Finally, create the following YAML file and deploy it to the Kubernetes cluster.

 1apiVersion: apps/v1
 2kind: StatefulSet 
 3metadata:
 4  name: memcached
 5  namespace: app
 6  labels:
 7    app: memcached
 8spec:
 9  replicas: 2
10
11  selector:
12    matchLabels:
13      app: memcached
14
15  serviceName: memcached
16  template:
17    metadata:
18      labels:
19        app: memcached
20    spec:
21      affinity:
22        podAntiAffinity:
23          requiredDuringSchedulingIgnoredDuringExecution:
24          - labelSelector:
25              matchExpressions:
26              - key: app
27                operator: In
28                values:
29                - memcached
30            topologyKey: "kubernetes.io/hostname"
31
32      containers:
33        - name: memcached
34          image: registry/my-memcached:202010051112
35          imagePullPolicy: IfNotPresent
36          args: ["-U", "0", "-B", "ascii"]
37
38          ports:
39            - containerPort: 11211 
40
41          env:
42            - name: DUMP_DIR
43              value: "/var/tmp/memcache-dump"
44
45          resources:
46            requests:
47              cpu: 10m
48              memory: 16Mi
49
50          livenessProbe:
51            tcpSocket:
52              port: 11211
53            initialDelaySeconds: 2 
54            periodSeconds: 5
55
56          readinessProbe:
57            exec:
58              command:
59              - cat
60              - /tmp/ready
61            initialDelaySeconds: 5
62            periodSeconds: 5
63   
64          lifecycle:
65            postStart:
66              exec:
67                command: ["/post_start.sh"]
68            preStop:
69              exec:
70                command: ["/pre_stop.sh"]
71
72          volumeMounts:
73            - name: "memcache-dump"
74              mountPath: "/var/tmp/memcache-dump"
75
76      volumes:
77        - name: "memcache-dump"
78          persistentVolumeClaim:
79            claimName: memcache-efs

I know this is not a sophisticated way but it’s easy to create. So, I think it’s useful in some situations.