1. 准备工作
1-1. 安装NFC作为持久化存储(所有节点都需要安装 nfc-utils)
yum -y install nfs-utils rpcbind
# 创建NFS共享文件夹
mkdir -p /data/nfs/redis/pv{1..6}
#配置共享文件夹
vim /etc/exports
/data/nfs/redis/pv1 *(rw,sync,no_root_squash)
/data/nfs/redis/pv2 *(rw,sync,no_root_squash)
/data/nfs/redis/pv3 *(rw,sync,no_root_squash)
/data/nfs/redis/pv4 *(rw,sync,no_root_squash)
/data/nfs/redis/pv5 *(rw,sync,no_root_squash)
/data/nfs/redis/pv5 *(rw,sync,no_root_squash)
# 生效
exportfs -r
# 启动NFS
systemctl start nfs-server
systemctl enabled nfs-server
systemctl start rpcbind
systemctl enabled rpcbind
1-2. 其他节点安装nfc-utils
yum -y install nfs-utils
1-3. 创建ns(namespace:命名空间) nfc 客户端操作 以下没有特殊说明 都是在客户端节点操作
kubectl create ns redis-cluster
1-4. 设置对应的账号信息和角色
-
ServiceAccount服务账户- 创建名为
redis-nfs-client的账户,作为NFS存储操作的身份凭证 - 限定在
redis-cluster命名空间内使用
- 创建名为
-
ClusterRole集群角色- 定义
nfs-client-runner角色,授予以下权限:- 对PV/PVC的全生命周期管理(创建/删除/查看)
- 查看StorageClass信息(用于动态供给)
- 操作Events和Endpoints资源(用于状态监控和服务发现)
- 定义
-
**
ClusterRoleBinding集群角色绑定**- 将
redis-nfs-client服务账户与nfs-client-runner角色关联 - 权限作用范围:整个集群(ClusterRole特性)
- 将
-
设计意图
- 安全隔离
- 为Redis集群单独配置服务账户,避免使用default账户的高风险操作
- 最小权限原则
- 精确控制NFS客户端所需的权限(如无需Secret访问权限)
- 支持动态存储供给
- 通过PVC自动创建PV时需具备相关API操作权限
- 安全隔离
cat > redis-nfs-client-sa.yaml<<EOF
# ========== ServiceAccount 配置 ==========
apiVersion: v1
kind: ServiceAccount
metadata:
name: redis-nfs-client # 服务账户名称,将被Provisioner Pod使用
namespace: redis-cluster # 必须与后续资源同一命名空间
---
# ========== ClusterRole 集群角色配置 ==========
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-runner # 集群角色名称(全局有效)
namespace: redis-cluster
rules:
- apiGroups: [""] # 核心API组
resources: ["persistentvolumes"]
verbs: ["get","list","watch","create","delete"] # PV全生命周期管理
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get","list","watch","create","delete"] # PVC联动操作权限
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get","list","watch"] # 需读取SC配置
- apiGroups: [""]
resources: ["events"]
verbs: ["get","list","watch","create","update","patch"] # 事件记录
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["create","delete","get","list","watch","patch","update"] # NFS服务依赖
---
# ========== ClusterRoleBinding 配置 ==========
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner # 绑定名称
namespace: redis-cluster
subjects:
- kind: ServiceAccount
name: redis-nfs-client
namespace: redis-cluster # 指定服务账户来源命名空间
roleRef:
kind: ClusterRole
name: nfs-client-runner # 必须与ClusterRole名称一致
apiGroup: rbac.authorization.k8s.io
EOF
1-5. 创建NFC客户端
cat > redis-nfs-client.yaml<<EOF
# ========== 基础部署配置 ==========
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-nfs-client # 部署名称
labels:
app: redis-nfs-client # 用于Service/Selector匹配
namespace: redis-cluster # 必须与ServiceAccount同命名空间
# ========== 部署策略配置 ==========
spec:
replicas: 1 # 单副本部署(NFS Provisioner建议单实例)
strategy:
type: Recreate # 更新策略:先删除旧Pod再创建新Pod(避免PV冲突)
selector:
matchLabels:
app: redis-nfs-client # 必须与template.metadata.labels一致
# ========== Pod模板配置 ==========
template:
metadata:
labels:
app: redis-nfs-client # 必须与selector.matchLabels一致
spec:
serviceAccountName: redis-nfs-client # 关联之前创建的SA
containers:
- name: redis-nfs-client
# 使用阿里云镜像仓库的NFS Provisioner
image: registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0
# ========== 存储卷挂载 ==========
volumeMounts:
- name: redis-nfs-client-root
mountPath: /persistentvolumes # Provisioner工作目录
# ========== 关键环境变量 ==========
env:
- name: PROVISIONER_NAME # 必须与StorageClass的provisioner字段完全一致
value: my-redis-nfs
- name: ENABLE_LEADER_ELECTION # 即使单副本也建议开启(兼容后续扩展)
value: "True"
- name: NFS_SERVER # NFS服务器地址(需替换为实际IP)
value: 192.168.133.133
- name: NFS_PATH # NFS共享路径(需确保目录已存在且有权限)
value: /data/nfs/redis
# ========== NFS卷声明 ==========
volumes:
- name: redis-nfs-client-root
nfs:
server: 192.168.133.133 # 必须与env.NFS_SERVER一致
path: /data/nfs/redis # 必须与env.NFS_PATH一致
EOF
1-6. 创建SC(storeclass) 存储类
cat > redis-storeclass.yaml<<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: redis-nfs-storage
namespace: redis-cluster
provisioner: my-redis-nfs
EOF
1-7. 执行命令使上面1-4至1-6的内容生效
执行命令使其生效
kubectl apply -f redis-nfs-client-sa.yaml
kubectl apply -f redis-nfs-client.yaml
kubectl apply -f redis-storeclass.yaml
1-8. 创建6个PV卷
rm -rf redis-pv.yaml
cat > redis-pv.yaml<<EOF
# ========== PV基础定义 ==========
apiVersion: v1
kind: PersistentVolume
metadata:
# PV资源名称,集群内唯一标识
name: redis-nfs-pv1
# ========== 核心规格配置 ==========
spec:
# 存储类名称,需与StorageClass的metadata.name匹配
storageClassName: redis-nfs-storage
# 存储容量配置(注意单位需使用Mi/Gi等标准格式)
capacity:
# 实际容量需小于NFS服务器可用空间
storage: 500M
# 访问模式:支持多节点同时读写
accessModes:
# 适用于NFS/Ceph等共享存储
- ReadWriteMany
# ========== NFS专属配置 ==========
nfs:
# NFS服务器IP地址
server: 192.168.133.133
path: "/data/nfs/redis/pv1"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-nfs-pv2
spec:
storageClassName: redis-nfs-storage
capacity:
storage: 500M
accessModes:
- ReadWriteMany
nfs:
server: 192.168.133.133
path: "/data/nfs/redis/pv2"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-nfs-pv3
spec:
storageClassName: redis-nfs-storage
capacity:
storage: 500M
accessModes:
- ReadWriteMany
nfs:
server: 192.168.133.133
path: "/data/nfs/redis/pv3"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-nfs-pv4
spec:
storageClassName: redis-nfs-storage
capacity:
storage: 500M
accessModes:
- ReadWriteMany
nfs:
server: 192.168.133.133
path: "/data/nfs/redis/pv4"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-nfs-pv5
spec:
storageClassName: redis-nfs-storage
capacity:
storage: 500M
accessModes:
- ReadWriteMany
nfs:
server: 192.168.133.133
path: "/data/nfs/redis/pv5"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: redis-nfs-pv6
spec:
storageClassName: redis-nfs-storage
capacity:
storage: 500M
accessModes:
- ReadWriteMany
nfs:
server: 192.168.133.133
path: "/data/nfs/redis/pv6"
EOF
# 使这些pv卷 创建出来
kubectl apply -f redis-pv.yaml
2. 创建redis-configmap.yaml
rm -rf redis-configmap.yaml
cat > redis-configmap.yaml<<EOF
apiVersion: v1
kind: ConfigMap
# 配置元数据
metadata:
# 配置字典名称
name: redis-conf
# 必须与部署命名空间一致
namespace: redis-cluster
# 配置数据
data:
# ===== Pod IP自动修复脚本 =====
#fix-pod-ip.sh: |
##!/bin/sh
#CLUSTER_CONFIG="/var/lib/redis/nodes.conf" # 集群配置文件路径(需与redis.conf中cluster-config-file一致)
#if [ -f ${CLUSTER_CONFIG} ]; then # 检查集群配置文件是否存在
# if [ -z "${POD_IP}" ]; then # 验证环境变量POD_IP是否设置
# echo "Unable to determine Pod IP address! 自主报错"
# exit 1
# fi
# # 使用sed替换nodes.conf中的IP地址(匹配myself行)
# echo "Updating pod IP to ${POD_IP} in ${CLUSTER_CONFIG}"
# sed -i -e '/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/'${POD_IP}'/' ${CLUSTER_CONFIG}
# count=`grep -c ${POD_IP} ${CLUSTER_CONFIG}` # 验证IP更新结果
# if [[ $count > 0 ]];then
# echo "Successful updated pod Ip to ${POD_IP} in ${CLUSTER_CONFIG}"
# fi
#fi
#exec "$@" # 执行容器原始启动命令
# Redis核心配置文件
redis.conf: |
# 集群模式开关(必须开启)
cluster-enabled yes
# 网络配置
bind 0.0.0.0
port 6379
# 安全配置
protected-mode no
# 超时设置
timeout 300
cluster-node-timeout 5000
# 集群配置
cluster-config-file /var/lib/redis/nodes.conf
# 存储配置
dir /var/lib/redis
maxmemory 1000mb
maxmemory-policy volatile-lru
# 持久化配置
appendonly yes
appendfsync everysec
appendfilename "appendonly.aof"
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-rewrite-incremental-fsync yes
# 性能优化
maxclients 10000
loglevel notice
hash-max-ziplist-entries 512
hash-max-ziplist-value 64kb
hz 10
EOF
# 执行
kubectl apply -f redis-configmap.yaml -n redis-cluster
3. 创建redis-headless-svc.yaml
rm -rf redis-headless-svc.yaml
cat > redis-headless-svc.yaml<<EOF
# ========== 基础服务定义 ==========
apiVersion: v1
kind: Service
# 服务元数据配置
metadata:
# 服务名称(需与StatefulSet中serviceName一致)
name: redis-service
# 必须与Pod部署命名空间一致
namespace: redis-cluster
# 服务标签(用于资源分类)
labels:
app: redis
# ========== 服务规格配置 ==========
spec:
# 端口配置
ports:
# 客户端访问端口名称
- name: redis-port
# 标准Redis服务端口
port: 6379
# 集群总线端口名称
- name: redis-cluster-port
# Redis集群节点通信端口
port: 16379
# 无头服务配置(禁用自动分配ClusterIP)
clusterIP: None
# Pod选择器配置
selector:
# 必须匹配Pod的app标签
app: redis
# 必须匹配Pod的集群标识标签
appCluster: redis-cluster
EOF
kubectl apply -f redis-headless-svc.yaml
4. 创建redis-nodeport-svc.yaml (注意:外网访问的端口在这个地方设置)
rm -rf redis-nodeport-svc.yaml
cat >redis-nodeport-svc.yaml<<EOF
# ========== 基础服务定义 ==========
# ========== 基础服务定义 ==========
apiVersion: v1
kind: Service
# 服务元数据配置
metadata:
# 服务资源名称(用于外部访问)
name: redis-cluster-access-service
# 必须与Pod部署命名空间一致
namespace: redis-cluster
# 服务标签(用于资源分类)
labels:
app: redis
# ========== 服务规格配置 ==========
spec:
# 端口配置
ports:
# 端口名称标识
- name: redis-cluster-port
# 使用TCP协议
protocol: TCP
# 服务暴露的集群内部端口
port: 6379
# 容器实际监听端口(需与Pod定义一致)
targetPort: 6379
# 访问端口(30000-32767范围)
nodePort: 30476
# Pod选择器配置
selector:
# 匹配Pod的app标签
app: redis
# 匹配Pod的集群专用标签
appCluster: redis-cluster
# 服务类型(NodePort允许通过节点IP访问)
type: NodePort
EOF
kubectl apply -f redis-nodeport-svc.yaml
5. 创建statefulset(状态集)
rm -rf redis-StatefulSet-svc.yaml
cat > redis-StatefulSet-svc.yaml<<EOF
# ========== 基础定义 ==========
# Kubernetes API版本
apiVersion: apps/v1
# 资源类型(适用于有状态应用)
kind: StatefulSet
metadata:
# StatefulSet名称
name: redis-app
# 命名空间(需与Service/ConfigMap一致)
namespace: redis-cluster
# ========== 核心配置 ==========
spec:
# 关联的无头服务名称
serviceName: "redis-service"
# 6节点集群(3主3从架构)
replicas: 6
# ===== 选择器 =====
selector:
matchLabels:
app: redis
appCluster: redis-cluster
# ===== Pod模板 =====
template:
metadata:
# 必须与Service选择器匹配
labels:
app: redis
appCluster: redis-cluster
spec:
# 优雅终止等待时间
terminationGracePeriodSeconds: 20
# 反亲和性配置(避免Pod部署到同一节点)
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values: ["redis"]
topologyKey: kubernetes.io/hostname
# ===== 容器配置 =====
containers:
- name: redis
# 官方Redis镜像
image: redis:6.2.12
# 启动时执行IP修复脚本并加载配置
command:
# - "/etc/redis/fix-pod-ip.sh"
- "redis-server"
- "/etc/redis/redis.conf"
# 资源限制(根据业务需求调整)
resources:
requests:
cpu: "100m"
memory: "100Mi"
# 环境变量(获取Pod IP用于集群通信)
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# 端口定义
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配置
configMap:
name: "redis-conf"
# 文件权限设置 原0755
defaultMode: 0755
# ===== 持久化存储声明 =====
volumeClaimTemplates:
- metadata:
name: redis-data
namespace: redis-cluster
spec:
# 多节点读写模式
accessModes: ["ReadWriteMany"]
resources:
requests:
# 存储空间大小
storage: 500M
# 存储类名称
storageClassName: redis-nfs-storage
EOF
kubectl apply -f redis-StatefulSet-svc.yaml
6. 获取集群node ip
#### 手动获取集群node ip
kubectl get pod -n redis-cluster -o wide
#### 命令获取集群node ip
kubectl get pods -n redis-cluster -l app=redis -o jsonpath='{range.items[*]}{.status.podIP}:6379 ' | sed 's/ :6379//g'
7. 访问
ip:30476 这里笔者没有设置密码 所以可以直接链接
一些其他的方法
1. 重启pod
# 删除并重新创建 Pod,或触发 Pod 的滚动更新,以确保新的 ConfigMap 内容被加载。
kubectl delete pod -n redis-cluster redis-app-0
# 或者使用 StatefulSet 的滚动更新机制
kubectl rollout restart statefulset -n redis-cluster redis-app
一些问题:
1. 链接之后 切换数据库报错 ERR SELECT is not allowed in cluster mode
在Redis集群模式下,尝试使用SELECT命令切换数据库时遇到错误“ERR SELECT is not allowed in cluster mode”是因为Redis集群不支持多数据库的概念。以下是解决此问题的方法及步骤:
1. 理解Redis集群的数据库限制
- 单一数据库:Redis集群在逻辑上被视为一个单一的数据库,不支持传统的多数据库索引(如0-15)。\
- Redis Cluster设计上只支持db0,禁用SELECT命令是为了保证数据分片一致性
- 所有键都分布在16384个槽位中,无法按数据库隔离数据
- 数据分片:集群将数据自动分片到多个节点上,但所有节点共享相同的键值空间。
2. 修改应用逻辑
- 移除SELECT命令:由于集群模式不支持
SELECT命令,需要从应用程序中移除所有尝试切换数据库的代码。 - 使用正确的键名:确保应用程序使用唯一的键名来避免数据冲突,因为所有节点共享相同的键值空间。
3. 重新配置Redis客户端
- 配置为集群模式:确保Redis客户端配置为集群模式,而不是单节点模式。这通常涉及指定集群中多个节点的地址以及启用集群支持。
- 更新连接字符串:如果客户端使用连接字符串,请更新它以包含集群中所有节点的地址,并使用集群特定的配置选项。
4. 测试和验证
- 连接到集群:使用Redis CLI或其他客户端工具以集群模式连接到Redis集群。
- 执行命令:尝试执行一些基本的读写命令,确保它们正常工作而不涉及
SELECT命令。 - 监控和日志:监控Redis集群的性能和日志,确保没有错误或异常行为。
5. 文档和培训
- 更新文档:更新应用程序和Redis集群的文档,以反映集群模式的限制和配置要求。
- 培训开发人员:确保所有相关人员都了解Redis集群的工作方式以及如何在应用程序中使用它。

1645

被折叠的 条评论
为什么被折叠?



