本方案采用StatefulSet进行redis的部署。它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序。在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
在k8s上搭建Redis sentinel完全没有意义,经过测试,当master节点宕机后,sentinel选择新的节点当主节点,当原master恢复后,此时无法再次成为集群节点。因为在物理机上部署时,sentinel探测以及更改配置文件都是以IP的形式,集群复制也是以IP的形式,但是在容器中,虽然采用的StatefulSet的Headless Service来建立的主从,但是主从建立后,master、slave、sentinel记录还是解析后的IP,但是pod的IP每次重启都会改变,所有sentinel无法识别宕机后又重新启动的master节点,所以一直无法加入集群,虽然可以通过固定pod IP或者使用NodePort的方式来固定,或者通过sentinel获取当前master的IP来修改配置文件。 sentinel实现的是高可用Redis主从,检测Redis Master的状态,进行主从切换等操作,但是在k8s中,无论是dc或者ss,都会保证pod以期望的值进行运行,再加上k8s自带的活性检测,当端口不可用或者服务不可用时会自动重启pod或者pod的中的服务,所以当在k8s中建立了Redis主从同步后,相当于已经成为了高可用状态,并且sentinel进行主从切换的时间不一定有k8s重建pod的时间快,所以个人认为在k8s上搭建sentinel没有意义。
K8s集群环境
节点
IP
master
192.168.1.100
node1
192.168.1.101
node2
192.168.1.102
node3
192.168.1.103
下载镜像经导入集群中redis:3.2.8、inem0o/redis-trib:latest
docker pull redis:3.2.8; docker save –o redis.tar redis:3.2.8
docker pull inem0o/redis-trib:latest; docker save –o redis-trib.tar inem0o/redis-trib:latest
redis.tar上传至k8s的各个node节点;redis-trib.tar上传至k8s的master节点
创建NFS存储主要是为了给Redis提供稳定的后端存储,当Redis的Pod重启或迁移后,依然能获得原先的数据。
安装nfs软件包
]# yum –y install nfs-utils rpcbind #任选一台节点(这里选k8s-master)
创建共享存储
]# mkdir -p /usr/local/k8s/redis/pv{1…6} #创建共享目录
]# cat /etc/exports #配置共享路径
/redis/pv1 192.168.1.0/24(rw,sync,no_root_squash)
/redis/pv2 192.168.1.0/24(rw,sync,no_root_squash)
/redis/pv3 192.168.1.0/24(rw,sync,no_root_squash)
/redis/pv4 192.168.1.0/24(rw,sync,no_root_squash)
/redis/pv5 192.168.1.0/24(rw,sync,no_root_squash)
/redis/pv6 192.168.1.0/24(rw,sync,no_root_squash)
]# systemctl restart rpcbind;systemctl restart nfs;systemctl enable nfs
查看共享路径
exportfs –v #k8s-master上操作
其他节点安装nfs客户端,并验证
]# yum -y install nfs-utils
]# showmount -e 192.168.1.100
Export list for 192.168.1.10:
/redis/pv1 192.168.1.0/24
/redis/pv2 192.168.1.0/24
/redis/pv3 192.168.1.0/24
/redis/pv4 192.168.1.0/24
/redis/pv5 192.168.1.0/24
/redis/pv6 192.168.1.0/24
以下操作在k8s-master上执行即可
1.每一个Redis Pod都需要一个独立的PV来存储自己的数据,因此可以创建一个pv.yaml文件,包含6个PV:
]# vim pv.yaml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv1
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis"
nfs:
server: 192.168.1.10
path: "/usr/local/k8s/redis/pv1"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-vp2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis"
nfs:
server: 192.168.1.10
path: "/usr/local/k8s/redis/pv2"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv3
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis"
nfs:
server: 192.168.1.10
path: "/usr/local/k8s/redis/pv3"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-vp4
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis"
nfs:
server: 192.168.1.10
path: "/usr/local/k8s/redis/pv4"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv5
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis"
nfs:
server: 192.168.1.10
path: "/usr/local/k8s/redis/pv5"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-vp6
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
volumeMode: Filesystem
persistentVolumeReclaimPolicy: Recycle
storageClassName: "redis"
nfs:
server: 192.168.1.10
path: "/usr/local/k8s/redis/pv6"
]# kubectl create -f pv.yaml #创建pv存储卷
persistentvolume "nfs-pv1" created
persistentvolume "nfs-pv2" created
persistentvolume "nfs-pv3" created
persistentvolume "nfs-pv4" created
persistentvolume "nfs-pv5" created
persistentvolume "nfs-pv6" created
将Redis的配置文件转化为Configmap,这是一种更方便的配置读取方式。配置文件redis.conf如下
]# vim redis.conf
appendonly yes
cluster-enabled yes
cluster-config-file /var/lib/redis/nodes.conf
cluster-node-timeout 5000
dir /var/lib/redis
port 6379
]# kubectl create configmap redis-conf --from-file=redis.conf
]# kubectl describe cm redis-conf #查看创建的configmap:
Name: redis-conf
Namespace: default
Labels:
Annotations:
Data
====
redis.conf:
----
appendonly yes
cluster-enabled yes
cluster-config-file /var/lib/redis/nodes.conf
cluster-node-timeout 5000
dir /var/lib/redis
port 6379
Events:
Headless service是StatefulSet实现稳定网络标识的基础,我们需要提前创建。准备文件headless-service.yml如下:
]# vim headless-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-service
labels:
app: redis
spec:
ports:
- name: redis-port
port: 6379
clusterIP: None
selector:
app: redis
]# kubectl create -f headless-service.yml
]# kubectl get svc redis-service
通过StatefulSet创建6个redis的pod ,实现3主3从的redis集群
]# vim redis.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-app
spec:
serviceName: “redis-service”
replicas: 6
selector:
matchLabels:
app: redis
appCluster: redis-cluster
template:
metadata:
labels:
app: redis
appCluster: redis-cluster
spec:
containers:
- name: redis
image: “redis:3.2.8”
command:
- “redis-server”
args:
- “/etc/redis/redis.conf”
- “–protected-mode”
- “no”
resources:
requests:
cpu: “100m”
memory: “100Mi”
ports:
- name: redis
containerPort: 6379
protocol: “TCP”
- name: cluster
containerPort: 16379
protocol: “TCP”
volumeMounts:
- name: “redis-conf”
mountPath: “/etc/redis”
- name: “redis-data”
mountPath: “/var/lib/redis”
volumes:
- name: “redis-conf”
configMap:
name: “redis-conf”
items:
- key: “redis.conf”
path: “redis.conf”
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ “ReadWriteMany” ]
storageClassName: “redis”
resources:
requests:
storage: 2Gi
]# kubectl create -f redis.yaml
]# kubectl get pods -o wide
初始化集群采用redis-trib这个工具,直接yum安装redis-trib这个软件,执行初始化
yum -y install redis-trib
]# redis-trib create --replicas 1 10.244.3.20:6379 10.244.2.42:6379 10.244.1.44:6379 10.244.2.43:6379 10.244.3.21:6379 10.244.1.45:6379
输入 yes 确定
或者采用部署redis-trib容器初始化:参考链接https://github.com/iNem0o/docker-redis-trib
]# docker run --rm -ti inem0o/redis-trib create --replicas 1 10.244.3.20:6379 10.244.2.42:6379 10.244.1.44:6379 10.244.2.43:6379 10.244.3.21:6379 10.244.1.45:6379 # IP为6个redis pod的IP以及端口
在提示中输入Yes继续初始化
验证集群状态,集群节点状态为OK,共有6个节点
#kubectl exec -ti redis-app-0 -- redis-cli -c
> cluster iinfo # 查看集群状态
> role #查看角色
之前创建了用于实现StatefulSet的Headless Service,但该Service没有Cluster IP,因此不能用于外界访问。所以,我们还需要创建一个Service,专用于为Redis集群提供访问和负载均衡;也可以部署为Ingress供集群外部访问。这里只创建用于内部访问的service
]# vim redis-access-service.yaml
apiVersion: v1
kind: Service
metadata:
name: redis-access-service
labels:
app: redis
spec:
ports:
- name: redis-port
protocol: "TCP"
port: 6379
targetPort: 6379
selector:
app: redis
appCluster: redis-cluster
]# kubectl create -f redis-access-service.yaml
该Service名称为 redis-access-service,在K8S集群中暴露6379端口。如果需要提供集群外部访问,可以创建NodePort或者ingress类型
删除一个pod为redis的master
]# kubectl delete pods redis-app-1
pod “redis-app-0” deleted
删除pod后,k8s会自动重建一个名称为“redis-app-1”并加入到集群中,但IP不一定是原来的IP;再次查看集群角色,redis-app-1由master变为salve,集群整体依然是6个