在Kubernetes上部署Redis集群
Redis的分布式模式有主从模式、哨兵模式和Cluster模式,相对来说Cluster模式的机制更完善,内存利用率更高。因为项目在使用Redis进行存储时碰到了性能瓶颈,所以准备用Cluster模式尝试部署分布式Redis。
ps:Redis分布式的介绍可以参考https://segmentfault.com/a/1190000022808576
一、配置文件
先配置一个ConfigMap,后面会挂载到Redis的实例上,里面有一个脚本和一个redis的配置文件,redis配置文件可以根据需求修改,脚本的作用后面会讲。
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cluster
data:
fix-ip.sh: |
#!/bin/sh
CLUSTER_CONFIG="/data/nodes.conf"
echo "creating nodes"
if [ -f ${CLUSTER_CONFIG} ]; then
echo "[ INFO ]File:${CLUSTER_CONFIG} is Found"
else
touch $CLUSTER_CONFIG
fi
if [ -z "${POD_IP}" ]; then
echo "Unable to determine Pod IP address!"
exit 1
fi
echo "Updating my IP to ${POD_IP} in ${CLUSTER_CONFIG}"
sed -i.bak -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${CLUSTER_CONFIG}
echo "done"
exec "$@"
redis.conf: |+
cluster-enabled yes
cluster-require-full-coverage no
cluster-node-timeout 15000
cluster-config-file /data/nodes.conf
cluster-migration-barrier 1
appendonly yes
protected-mode no
二、启动节点
这里使用StatefulSet作为工作负载,因为redis集群的节点是有状态的,这个状态会记录在之前配置指定的/data/nodes.conf文件里,节点重启后会根据这个文件的内容恢复节点在集群里的状态,所以需要StatefulSet提供持久化。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
labels:
run: redis-cluster
spec:
serviceName: redis-cluster
replicas: 6
selector:
matchLabels:
run: redis-cluster
template:
metadata:
labels:
run: redis-cluster
spec:
containers:
- name: redis
image: redis:alpine
ports:
- containerPort: 6379
name: redis
- containerPort: 16379
name: gossip
command: ["/conf/fix-ip.sh", "redis-server", "/conf/redis.conf"]
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: conf
mountPath: /conf
readOnly: false
- name: data
mountPath: /data
readOnly: false
volumes:
- name: conf
configMap:
name: redis-cluster
defaultMode: 0755
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
storageClassName: default
三、创建集群
加载上面两个配置文件后执行:
kubectl exec -it redis-cluster-0 -- redis-cli --cluster create --cluster-replicas 1 $(kubectl get pods -l run=redis-cluster -o jsonpath='{range.items[*]}{.status.podIP}:6379 {end}')
这样就创建好了redis集群,redis-server会写入/data/nodes.conf文件,记录集群状态。因为在创建集群的时候是使用固定ip创建的,当节点重启后ip会发生变化,这个文件的信息就不准确了,所以需要在启动时更新/data/nodes.conf。这个就是fix-ip.sh这个脚本的工作,如果文件存在,就更新ip,如果不存在就创建文件。所以k8s的容器配置里有两个点和单机不一样:一个是command里使用"/conf/fix-ip.sh"作为启动项,先更新ip再启动实例;一个是加载pod ip到环境变量POD_IP,方便脚本加载。
四、创建service
创建好redis集群后还需要访问接口
apiVersion: v1
kind: Service
metadata:
name: redis-cluster
spec:
selector:
run: redis-cluster
ports:
- port: 6379
targetPort: 6379
name: server
- port: 16379
targetPort: 16379
name: gossip
type: ClusterIP
五、坑
一开始是想用service的域名创建redis集群的,这样节点重启后就不需要更新ip,但是redis不支持使用域名,所以只能绕了一圈又回到固定ip的方法,和容器环境很不协调。