【基于 K8S+NFS 动态存储实战部署 Redis-Cluster 集群(含三主三从配置与访问配置)】

提示:本文原创作品,良心制作,干货为主,简洁清晰,一看就会

文章目录

  • 前言
  • 一、整体流程概述
  • 二、NFS+K8S部署redis-cluster集群实战
    • [2.1 安装NFS](#2.1 安装NFS)
    • [2.2 创建SC](#2.2 创建SC)
    • [2.3 创建SA并授权](#2.3 创建SA并授权)
    • [2.4 部署自动创建PV的pod服务](#2.4 部署自动创建PV的pod服务)
    • [2.5 创建Redis的Sts](#2.5 创建Redis的Sts)
    • [2.6 测试集群内Redis域名解析连通性](#2.6 测试集群内Redis域名解析连通性)
    • [2.7 安装redis-trib](#2.7 安装redis-trib)
    • [2.8 配置Redis三主三从](#2.8 配置Redis三主三从)
    • [2.9 创建用于访问Redis的SVC](#2.9 创建用于访问Redis的SVC)

前言

在Kubernetes中搭建Redis Cluster,是实现高可用、可扩展缓存服务的关键实践。本文从底层存储配置出发,完整演示基于NFS动态供给的Redis集群部署流程,帮助你快速构建一套生产可用的分布式缓存系统

本文实验基于 K8s PV/PVC 动态存储前置知识,对此尚不熟悉的同学可先参阅此篇文章https://blog.csdn.net/m0_63756214/article/details/160648532?spm=1001.2014.3001.5502

一、整体流程概述

1,准备一台 NFS 服务器,为 K8s 提供统一后端存储,供 PV/PVC 挂载使用

2,创建 StorageClassServiceAccount 并授权部署自动供给 PV/PVC 的 NFS Provisioner Pod

3,创建 ConfigMap 存储自定义 redis.conf 配置文件,创建 Headless Service,为 Redis 有状态应用提供固定域名解析

4,部署 StatefulSet,引用 ConfigMap 与 Service,配置 6 副本,通过 StorageClass 自动申请 NFS 持久化存储

5,如果是centos系统,则创建一个ubuntu的pod,在 Ubuntu 容器内安装集群管理工具redis-trib,将 6 个 Redis 节点配置为三主三从高可用集群;如果是ubuntu系统直接安装集群管理工具redis-trib,执行相关命令

二、NFS+K8S部署redis-cluster集群实战

2.1 安装NFS

yaml 复制代码
## 准备一台机器安装NFS,后续的PV/PVC会到NFS中拿空间
## 我这台nfs的ip是192.168.13.137
root@NFS:~# apt -y install nfs-kernel-server
root@NFS:~# systemctl start nfs-kernel-server
root@NFS:~# mkdir /data
root@NFS:~# vim /etc/exports
/data *(rw,sync,no_root_squash,no_subtree_check)
root@NFS:~# chmod 777 /data
root@NFS:~# systemctl restart nfs-kernel-server
root@NFS:~# systemctl enable nfs-kernel-server

## 在K8S的每个node节点都安装nfs客户端,不然会挂载失败
root@k8s-node1:~# apt -y install nfs-common
root@k8s-node2:~# apt -y install nfs-common

2.2 创建SC

yaml 复制代码
root@k8s-master1:~# vim storageclass-nfs.yaml 
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
        name: managed-nfs-storage
## 指定外部NFS供给器的名字
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
root@k8s-master1:~# kubectl apply -f storageclass-nfs.yaml 

2.3 创建SA并授权

yaml 复制代码
## 创建RBAC
root@k8s-master1:~# vim rbac.yaml 
## ServiceAccount:NFS provisioner pod 使用的身份
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # 需要指定命名空间
  namespace: default
---
## ClusterRole:定义集群维度的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-client-provisioner-ClusterRole
rules:
  - apiGroups: [""]
    resources: ["nodes"]       # 需要获取节点信息,用于拓扑感知
    verbs: ["get","list","watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]   # 需要全量操作PV
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]      # 需要监控PVC,并更新其状态
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]   # 需要获取storageclasses信息
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]  # 需要记录事件
    verbs: ["create", "update", "patch"]
---
## ClusterRoleBinding:将ServiceAccount绑定到ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: nfs-clinet-provisioner-ClusterRoleBinding
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # 必须添加:ServiceAccount所在命名空间
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-clinet-provisioner-ClusterRole
  apiGroup: rbac.authorization.k8s.io
---
## Role: 命名空间维度的权限,可以和clusterrole合并
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: nfs-clinet-provisioner-Role
  namespace: default 
rules:
  - apiGroups: [""]
    resources: ["endpoints"]  # 某些provisioner实现会使用endpoints做leader election
    verbs: ["get","list","watch","create","update","patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: nfs-clinet-provisioner-RoleBinding
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # 必须添加:ServiceAccount所在命名空间
    namespace: default
roleRef:
  kind: Role
  name: nfs-clinet-provisioner-Role
  apiGroup: rbac.authorization.k8s.io
yaml 复制代码
root@k8s-master1:~# kubectl apply -f rbac.yaml 

2.4 部署自动创建PV的pod服务

yaml 复制代码
root@k8s-master1:~# vim deploy-nfs.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:  # 升级策略
    type: Recreate  # 当需要更新Pod时,K8S会先删除现有的Pod,再创建新的Pod;这适用于不能同时运行多个副本的有状态服务
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      # 指定pod运行时使用的sa名称,其绑定的RBAC权限决定了provisioner能够访问哪些K8S API资源
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          # 这里使用阿里云镜像仓库中的nfs-subdir-external-provisioner镜像,版本 v4.0.0;该镜像实现了nfs动态供给器
          image: registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0
          volumeMounts:
            - name: nfs-root
            # 将nfs共享挂载到容器内的 /persistentvolumes 路径,不建议改;provisioner 会在该目录下为每个pvc创建子目录
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              # 必须与StorageClass中的 provisioner 字段完全一致,这样StorageClass才能调用该供给器
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.13.137 # nfs服务器的ip地址
            - name: NFS_PATH
              value: /data  # nfs服务器上导出的共享目录路径
      volumes:
        - name: nfs-root
          nfs:
            server: 192.168.13.137
            path: /data
yaml 复制代码
root@k8s-master1:~# kubectl apply -f deploy-nfs.yaml 

2.5 创建Redis的Sts

yaml 复制代码
root@k8s-master1:~# cat redis-cm-svc-pod.yaml 
# 1. Redis 配置文件 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-conf
data:
  redis.conf: |
    port 6379
    cluster-enabled yes
    cluster-config-file /var/lib/redis/nodes.conf
    cluster-node-timeout 5000
    appendonly yes
    dir /var/lib/redis  # 数据存放目录
    protected-mode no
    daemonize no
    pidfile /var/run/redis.pid
    logfile ""

---
# 2. Redis Headless Service(集群必须)
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  labels:
    app: redis
spec:
  ports:
    - name: redis-port
      port: 6379
  clusterIP: None   # 不分配clusterIP
  selector:
    app: redis
    appCluster: redis-cluster

---
# 3. Redis StatefulSet 有状态部署
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  # 指定关联的Headless Service名称(必须与Service同名)
  serviceName: redis-service
  # 创建6个Redis实例(3主3从)
  replicas: 6
  selector:
    matchLabels:
      app: redis
      appCluster: redis-cluster
  template:
    metadata:
      labels:
        app: redis
        appCluster: redis-cluster
    spec:
      containers:
      - name: redis
        image: redis:latest
        # 启动命令:执行redis-server
        command:
          - "redis-server"
        # 启动参数:指定配置文件 + 关闭保护模式
        args:
                - "/etc/redis/redis.conf"  # 加载自定义配置
                - "--protected-mode"    # 关闭保护模式(允许集群通信)
                - "no"
        # 资源限制:CPU与内存申请
        resources:
                requests:
                        cpu: "100m"
                        memory: "200Mi"
        # 声明容器内部监听了什么端口,不提供流量转发与访问暴露
        ports:
        - containerPort: 6379  # redis客户端连接端口
          name: redis
          protocol: "TCP"
        - containerPort: 16379  # redis 集群节点间通信的端口
          name: cluster
          protocol: "TCP"
        volumeMounts:
        - name: redis-config   # 挂载配置文件
          mountPath: /etc/redis/redis.conf  
          subPath: redis.conf
        - name: redis-data   # 挂载数据文件
          mountPath: /var/lib/redis
      # 挂载ConfigMap配置
      volumes:
      - name: redis-config
        configMap:
          name: redis-conf
  # 动态存储申请(配合之前的 NFS StorageClass)
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: [ "ReadWriteOnce" ]  # 访问模式:单节点读写
      resources:
        requests:
          storage: 1Gi  # 申请大小
      # 这里使用上面创建的 StorageClass
      storageClassName: "managed-nfs-storage"
yaml 复制代码
root@k8s-master1:~# kubectl apply -f redis-cm-svc-pod.yaml
root@k8s-master1:~# kubectl get svc
NAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes      ClusterIP   10.96.0.1    <none>        443/TCP    37d
redis-service   ClusterIP   None         <none>        6379/TCP   17s

## 通过创建时间可以看出,sts在创建pod的时候不是同时启动的,而是一个一个的顺序启动
root@k8s-master1:~# kubectl get pod -o wide | grep redis
redis-0                                   1/1     Running   0          3m53s   10.244.36.108    k8s-node1   <none>           <none>
redis-1                                   1/1     Running   0          3m31s   10.244.169.183   k8s-node2   <none>           <none>
redis-2                                   1/1     Running   0          2m51s   10.244.36.109    k8s-node1   <none>           <none>
redis-3                                   1/1     Running   0          2m26s   10.244.169.184   k8s-node2   <none>           <none>
redis-4                                   1/1     Running   0          2m1s    10.244.36.110    k8s-node1   <none>           <none>
redis-5                                   1/1     Running   0          98s     10.244.169.185   k8s-node2   <none>           <none>

## 查看nfs的/data目录,会产生数据
root@NFS:~# ls /data/
default-redis-data-redis-0-pvc-992e88f2-1460-4b48-8096-491dc31fa274  default-redis-data-redis-3-pvc-57b4f9bf-50b6-4057-a844-fed705b1ec8f
default-redis-data-redis-1-pvc-d9414620-ec81-4464-906a-473e54882199  default-redis-data-redis-4-pvc-eab88346-8fc9-4467-ad71-9f044ec412c2
default-redis-data-redis-2-pvc-299b0bc2-c6c8-4a3f-b068-f65b365d9e2d  default-redis-data-redis-5-pvc-644ffafe-9d26-4852-81ea-e46e9082f481
root@NFS:~# ls /data/default-redis-data-redis-0-pvc-992e88f2-1460-4b48-8096-491dc31fa274/
appendonlydir  nodes.conf
root@NFS:~# 

2.6 测试集群内Redis域名解析连通性

StatefulSet 中的每个 Pod 会获得形如 <pod-name>.<service-name>.<namespace>.svc.cluster.local 的稳定 DNS 名称

例如,redis-0.redis.default.svc.cluster.local,即使 Pod 重新调度,该域名也不会改变

我们可以进入 busybox 测试容器,对 Redis 各节点域名进行连通性验证,确保 Kubernetes 集群内部 DNS 解析正常、Pod 网络通信正,为后续 Redis 集群组建提供稳定的网络环境

yaml 复制代码
root@k8s-master1:~# kubectl run busybox --image=busybox -it -- /bin/sh
If you don't see a command prompt, try pressing enter.
/ # ping redis-0.redis.default.svc.cluster.local
ping: bad address 'redis-0.redis.default.svc.cluster.local'
/ # ping redis-0.redis-service.default.svc.cluster.local  # ping该域名解析出来的就是redis-0对应的ip 10.244.36.108
PING redis-0.redis-service.default.svc.cluster.local (10.244.36.108): 56 data bytes
64 bytes from 10.244.36.108: seq=0 ttl=63 time=0.648 ms
64 bytes from 10.244.36.108: seq=1 ttl=63 time=0.084 ms
64 bytes from 10.244.36.108: seq=2 ttl=63 time=0.086 ms
^C
--- redis-0.redis-service.default.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.084/0.272/0.648 ms
/ # ping redis-4.redis-service.default.svc.cluster.local # ping该域名解析出来的就是redis-4对应的ip 10.244.36.110
PING redis-4.redis-service.default.svc.cluster.local (10.244.36.110): 56 data bytes
64 bytes from 10.244.36.110: seq=0 ttl=63 time=0.099 ms
64 bytes from 10.244.36.110: seq=1 ttl=63 time=0.107 ms
64 bytes from 10.244.36.110: seq=2 ttl=63 time=0.169 ms
^C
--- redis-4.redis-service.default.svc.cluster.local ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.099/0.125/0.169 ms
/ # 

2.7 安装redis-trib

如果是centos系统,需要先运行一个ubuntu的pod,然后在ubuntu中安装redis-trib命令

如果是ubuntu系统,直接安装下列命令即可,不需要运行ubuntu的pod

我的是ubuntu系统,为了方便演示,我这里还是创建了一个ubuntu的pod

yaml 复制代码
root@k8s-master1:~# kubectl run -it ubuntu --image=ubuntu:18.04 --restart=Never /bin/bash
If you don't see a command prompt, try pressing enter.
## 把软件下载源替换成阿里云镜像源
root@ubuntu:/# cat > /etc/apt/sources.list << EOF
> deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
> deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
> 
> deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
> deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
> 
> deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
> deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
> 
> deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
> deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
> 
> deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
> deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
> EOF

root@ubuntu:/# apt update

## 安装相关命令
root@ubuntu:/# apt install -y python2.7 python-pip redis-tools dnsutils
root@ubuntu:/# pip install redis-trib==0.5.1

## 查这个域名在 K8s 里的ip地址,可以测试一下是否正确
root@ubuntu:/# dig +short redis-0.redis-service.default.svc.cluster.local
10.244.36.108
root@ubuntu:/# dig +short redis-5.redis-service.default.svc.cluster.local
10.244.169.185
root@ubuntu:/# 

2.8 配置Redis三主三从

在Ubuntu pod中设置redis三主三从

yaml 复制代码
## 设置redis-0  redis-1 redis-2 为master
root@ubuntu:/# redis-trib.py create \
> `dig +short redis-0.redis-service.default.svc.cluster.local`:6379 \
> `dig +short redis-1.redis-service.default.svc.cluster.local`:6379 \
> `dig +short redis-2.redis-service.default.svc.cluster.local`:6379

## 设置redis-3为redis-0的slave
root@ubuntu:/# redis-trib.py replicate   --master-addr `dig +short redis-0.redis-service.default.svc.cluster.local`:6379   --slave-addr `dig +short redis-3.redis-service.default.svc.cluster.local`:6379
## 设置redis-4为redis-1的slave
root@ubuntu:/# redis-trib.py replicate   --master-addr `dig +short redis-1.redis-service.default.svc.cluster.local`:6379   --slave-addr `dig +short redis-4.redis-service.default.svc.cluster.local`:6379
## 设置redis-5为redis-2的slave
root@ubuntu:/# redis-trib.py replicate   --master-addr `dig +short redis-2.redis-service.default.svc.cluster.local`:6379   --slave-addr `dig +short redis-5.redis-service.default.svc.cluster.local`:6379
yaml 复制代码
## 退出ubuntu pod,然后随意进入一个redis pod,查看集群信息
root@k8s-master1:~# kubectl exec -it redis-0 /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@redis-0:/data# redis-cli -c
127.0.0.1:6379> cluster nodes
de0431085ec24f3445be041d33911d1ada4d7bda 10.244.169.184:6379@16379 slave c7f02678e65c8a5100dd3a96f82840efff27a3fc 0 1778838395000 3 connected
e193aed56f924e576e6788050b7d285e9756e480 10.244.36.110:6379@16379 slave aac66b559d8f6b738861eff8e5bcdf7ee49789e7 0 1778838396871 1 connected
104beb94505d8a6ef14995f1759dd1db7204f2f0 10.244.169.185:6379@16379 slave 6232737d86561370f931c3b18a3f1170f7173b08 0 1778838396368 2 connected
c7f02678e65c8a5100dd3a96f82840efff27a3fc 10.244.36.108:6379@16379 myself,master - 0 0 3 connected 0-5461
aac66b559d8f6b738861eff8e5bcdf7ee49789e7 10.244.169.183:6379@16379 master - 0 1778838395000 1 connected 10923-16383
6232737d86561370f931c3b18a3f1170f7173b08 10.244.36.109:6379@16379 master - 0 1778838396000 2 connected 5462-10922
127.0.0.1:6379> 

2.9 创建用于访问Redis的SVC

在上面我们创建了用于实现sts的headless service,但该svc没有cluster ip,因此不能用于外接访问,所以我们还需要创建一个svc来为redis集群提供访问和负载均衡

yaml 复制代码
root@k8s-master1:~# vim redis-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-access-service
  labels:
    app: redis
spec:
  type: NodePort  
  ports:
  - name: redis-port
    protocol: "TCP"
    port: 6379        # 集群内访问端口
    targetPort: 6379  # 映射Pod的6379端口
    nodePort: 30079   # 外部固定访问端口(范围30000-32767)
  selector:
    # 多标签为AND关系,精准匹配当前Redis集群Pod
    app: redis
    appCluster: redis-cluster
yaml 复制代码
root@k8s-master1:~# kubectl apply -f redis-svc.yaml
root@k8s-master1:~# kubectl get svc | grep redis
redis-access-service   NodePort    10.106.169.154   <none>        6379:30079/TCP   3m7s
redis-service          ClusterIP   None             <none>        6379/TCP         50m
root@k8s-master1:~# kubectl describe endpoints redis-access-service
Name:         redis-access-service
Namespace:    default
Labels:       app=redis
Annotations:  endpoints.kubernetes.io/last-change-trigger-time: 2026-05-18T00:16:02Z
Subsets:
  Addresses:          10.244.169.129,10.244.169.132,10.244.169.133,10.244.36.116,10.244.36.117,10.244.36.120
  NotReadyAddresses:  <none>
  Ports:
    Name        Port  Protocol
    ----        ----  --------
    redis-port  6379  TCP

Events:  <none>
yaml 复制代码
## pod内部登录redis,K8s Service 域名只在集群内部 Pod 中生效,宿主机无法直接解析
## 进入业务 Pod 使用服务名 redis-access-service:6379
root@k8s-master1:~# kubectl exec -it redis-0 /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@redis-0:/data# redis-cli -h redis-access-service -p 6379  # 或者redis-cli -h redis-access-service.default.svc.cluster.local -p 6379
redis-access-service:6379> ping
PONG
redis-access-service:6379> quit
root@redis-0:/data# exit
exit
root@k8s-master1:~# 

## 宿主机访问redis,使用 NodePort 端口 30079
root@k8s-master1:~# redis-cli -h 127.0.0.1 -p 30079
127.0.0.1:30079> ping
PONG
127.0.0.1:30079> quit

## K8S集群外部访问redis,使用 NodePort 端口 30079
root@NFS:~# redis-cli -h 192.168.13.139 -p 30079
192.168.13.139:30079> ping
PONG
192.168.13.139:30079> quit
root@NFS:~# 

至此,K8S部署redis-cluster集群完成!


注:

文中若有疏漏,欢迎大家指正赐教。

本文为100%原创,转载请务必标注原创作者,尊重劳动成果。

求赞、求关注、求评论!你的支持是我更新的最大动力,评论区等你~

相关推荐
phltxy9 小时前
Redis Sentinel:主从架构的自动保镖详解
redis·架构·sentinel
gQ85v10Db9 小时前
Redis分布式锁进阶第三十八篇
数据库·redis·分布式
Cat_Rocky9 小时前
Kubernetes etcd备份恢复
容器·kubernetes·etcd
会编程的吕洞宾9 小时前
跳表_Skip_List_的_凌云九阶阵__从概率平衡到_Redis
数据结构·redis·list
June`9 小时前
多线程redis项目之rdb
数据库·redis·缓存
青柠代码录9 小时前
【Redis】数据类型:Set
redis
少司府10 小时前
C++基础入门:深挖list的那些事
开发语言·数据结构·c++·容器·list·类型转换·类和对象
东北甜妹10 小时前
K8s etdc备份恢复 和 集群升级 证书更新
云原生·容器·kubernetes
念恒1230610 小时前
Docker基础--CGroups资源控制实战(包含部分指令)
运维·docker·容器