Redis master
There was nothing wrong with the previous deployment. In practical use cases, it would be rare that you would launch an application without some configuration settings. In this case, we are going to set the following configuration settings for redis-master (from the example in https://Kubernetes.io/docs/tutorials/configuration/configure-redis-using-configmap/ ).
Copy and paste the following two lines into the Azure Cloud Shell editor and save it as redis-config:
maxmemory 2mb
maxmemory-policy allkeys-lru
Now we can create the ConfigMap using the following code:
kubectl create configmap example-redis-config --from-file=redis-config
You should get this as the output:
configmap/example-redis-config created
We will use the same trick to get the scoop on this ConfigMap:
kubectl describe configmap/example-redis-config
The output should look like this:
Name: example-redis-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
redis-config:
----
maxmemory 2mb
maxmemory-policy allkeys-lru
Events: <none>
Creating from the command line is not very portable. It would be better if we could describe the preceding code in a yaml file. If only there was a command that would get the same information in yaml format. Not to worry, kubectl has the get command:
kubectl get -o yaml configmap/example-redis-config
The preceding command gets close to what we want as shown:
apiVersion: v1
data:
redis-config: |-
maxmemory 2mb
maxmemory-policy allkeys-lru
kind: ConfigMap
metadata:
creationTimestamp: 2018-12-26T06:35:57Z
name: example-redis-config
namespace: default
resourceVersion: "23430"
selfLink: /api/v1/namespaces/default/configmaps/example-redis-config
uid: 80624489-08d8-11e9-9914-82000ff4ac53
Let's create our yaml version of this. But first, let's delete the already-created ConfigMap:
kubectl delete configmap/example-redis-config
Copy and paste the following lines into a file named example-redis-config.yaml, and then save the file:
apiVersion: v1
data:
redis-config: |-
maxmemory 2mb
maxmemory-policy allkeys-lru
kind: ConfigMap
metadata:
name: example-redis-config
namespace: default
kubectl get -o yaml is a useful trick to get a deployable yaml file from a running system. It takes care of tricky yaml indentation and saves you from spending hours trying to get the format right.
ConfigMap is a portable way of configuring containers without having specialized images for each configuration. ConfigMap has a key-value pair for data that needs to be set on a container. In this case, redis-config is the key and
maxmemory 2mbmaxmemory-policy allkeys-lru is the value.
Run the following command:
kubectl create -f example-redis-config
The output should be as follows:
configmap/example-redis-config created
Next, run the following command:
kubectl describe configmap/example-redis-config
The preceding command returns the same output as the previous one:
Name: example-redis-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
redis-config:
----
maxmemory 2mb
maxmemory-policy allkeys-lru
Events: <none>
Now that we have ConfigMap defined, let's use it. Modify redis-master-deployment.yaml to use ConfigMap, as follows:
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: redis-master
labels:
app: redis
spec:
selector:
matchLabels:
app: redis
role: master
tier: backend
replicas: 1
template:
metadata:
labels:
app: redis
role: master
tier: backend
spec:
containers:
- name: master
image: Kubernetes /redis:v1
env:
- name: MASTER
value: "true"
volumeMounts:
- mountPath: /redis-master
name: config
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 6379
volumes:
- name: config
configMap:
name: example-redis-config
items:
- key: redis-config
path: redis.conf
We are using a different image Kubernetes/redis:v1 that reads redis-config from /redis-master/redis.conf (instead of the usual /etc/redis/redis.conf in the other image). As usual, we are going to explain the new sections with line numbers:
1 apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
2 kind: Deployment
3 metadata:
4 name: redis-master
5 labels:
6 app: redis
7 spec:
8 selector:
9 matchLabels:
10 app: redis
11 role: master
12 tier: backend
13 replicas: 1
14 template:
15 metadata:
16 labels:
17 app: redis
18 role: master
19 tier: backend
20 spec:
21 containers:
22 - name: master
23 image: Kubernetes /redis:v1
24 env:
25 - name: MASTER
26 value: "true"
27 volumeMounts:
28 - mountPath: /redis-master
29 name: config
30 resources:
31 requests:
32 cpu: 100m
33 memory: 100Mi
34 ports:
35 - containerPort: 6379
36 volumes:
37 - name: config
38 configMap:
39 name: example-redis-config
40 items:
41 - key: redis-config
42 path: redis.conf
The following lines will give a detailed explanation of the preceding code:
- Lines 24-26: Show another way of configuring your running container. This method uses environment variables. In Docker form, this would be equivalent to docker run -e "MASTER=TRUE". --name master -p 6379:6379 -m 100M -c 100m -d Kubernetes /redis:v1 # set the environment variable MASTER=TRUE. Your application can read the environment variable settings for its configuration. Check out https://12factor.net/config to see why this is a very powerful idea.
- Lines 27-28: Mounts the named volume (config that is defined in lines 36-42) at the /redis-master path on the running container. Since this is bind mount, it will hide whatever is exists on /redis-master on the original container.
In Docker terms, it would be equivalent to
docker run -v config:/redis-master. -e "MASTER=TRUE" --name master -p 6379:6379 -m 100M -c 100m -d Kubernetes /redis:v1 # mount the config folder at redis-master on the container.
- Line 36-42: Here is where Kubernetes takes the config as ENV vars to the next level.
- Line 37: Gives the volume the config.
- Line 38-39: Declares this volume should be loaded from the ConfigMap example-redis-config (which has to be already defined [unless declared optional]). We have defined this already, so we are good.
- Line 40-42: Here is where the Kubernetes magic comes in. Configure a Pod with ConfigMap (https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#use-configmap-defined-environment-variables-in-pod-commands). This shows different ways you can load the config on to a pod. Here we are loading the value of the redis-config key (the two-line maxmemory settings) as a file called redis.conf.
We can check whether the settings were applied by running the following commands:
kc exec -it redis-master-<pod-id> redis-cli
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "2097152"
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "allkeys-lru"
127.0.0.1:6379>exit
kc exec -it redis-master-<pod-id> bash
root@redis-master-585bd9d8fb-p9qml:/data# ps
bash: ps: command not found
root@redis-master-585bd9d8fb-p9qml:/data# cat /proc/1/cmdline
s h-c/run.sh
root@redis-master-585bd9d8fb-p9qml:/data# cat /run.sh |grep redis.conf
redis-server /redis-master/redis.conf
perl -pi -e "s/%master-ip%/${master}/" /redis-slave/redis.conf
perl -pi -e "s/%master-port%/6379/" /redis-slave/redis.conf
redis-server /redis-slave/redis.conf
root@redis-master-585bd9d8fb-p9qml:/data# cat /run.sh |grep MASTER
if [[ "${MASTER}" == "true" ]]; then
root@redis-master-585bd9d8fb-p9qml:/data# cat /redis-master/redis.conf
maxmemory 2mb
maxmemory-policy allkeys-lru
Somehow, in this image, ps is not installed. Not to worry, we can get the info by examining the contents of the cmdline file under pid 1. Now we know that run.sh is the file that is run, so somewhere in that redis.conf file from ConfigMap mounted at /redis-master would be used. So we grep redis.conf. For sure, we can see redis-server when it is started, when MASTER=TRUE uses the config from /redis-master/redis.conf . To make sure that Kubernetes did its magic, we examine the contents of redis.conf by running cat /redis-master/redis.conf and, lo and behold, it is exactly the values we specified in the ConfigMap example-redis-conf.
To repeat, you have just performed the most important and tricky part of configuring cloud-native applications. You have also noticed that the apps have to be modified to be cloud-friendly to read config dynamically (the old image didn't support dynamic configuration easily).