Bootstrap

第 12 篇 Helm 部署 Redis


🚀 本文内容:在 Helm 中部署 Redis。

Redis Chart

Helm 提供了两个 Redis Chart:

部署 Redis 单机版

Chart:redis 17.9.4 · bitnami/bitnami (artifacthub.io)

单机版非常简单,只跑一个 master 节点,指定密码,再指定 nodePort 暴露到集群外即可。

对应的 values.yaml 如下:

master:
  service:
    type: "NodePort"
    nodePorts:
      redis: "30567"
auth:
  # redis密码
  password: "Redis@pass123"

部署 Redis 哨兵版

https://artifacthub.io/packages/helm/bitnami/redis/17.13.2

当前环境:Helm 3.3.x、K8s 1.18

  • Chart 版本:17.13.2(Redis 为 7.10.x 版本)
  • 目标架构:1 主 2 从 3 哨兵

第 1 步:准备 values.yaml 配置文件

# 架构
architecture: replication

# 密码
auth:
  enabled: true
  sentinel: true
  password: Redis@pass123
  
# 主节点 1 个
master:
  count: 1
    
# 从节点 2 个
replica:
  # 这个应该是有误,实际上加上 master 才 3 个
  replicaCount: 3

# 哨兵
sentinel:
  enabled: true
  service:
    type: "NodePort"
    nodePorts:
      # 暴露 Redis 
      redis: 32700
      # 暴露 Sentinel
      sentinel: 32710

第 2 步:安装 bitnami/redis

helm install --values values.yaml my-redis bitnami/redis --version=17.13.2 
# NAME: my-redis
# LAST DEPLOYED: Wed May 22 18:53:52 2024
# NAMESPACE: default
# STATUS: deployed
# REVISION: 1
# TEST SUITE: None
# NOTES:
# CHART NAME: redis
# CHART VERSION: 17.13.2
# APP VERSION: 7.0.12
# 
# ** Please be patient while the chart is being deployed **
# 
# Redis® can be accessed via port 6379 on the following DNS name from within your cluster:
# 
#     my-redis.default.svc.cluster.local for read only operations
# 
# For read/write operations, first access the Redis® Sentinel cluster, which is available in port 26379 using the same domain name above.
# 
# 
# 
# To get your password run:
# 
#     export REDIS_PASSWORD=$(kubectl get secret --namespace default my-redis -o jsonpath="{.data.redis-password}" | base64 -d)
# 
# To connect to your Redis® server:
# 
# 1. Run a Redis® pod that you can use as a client:
# 
#    kubectl run --namespace default redis-client --restart='Never'  --env REDIS_PASSWORD=$REDIS_PASSWORD  --image docker.io/bitnami/redis:7.0.12-debian-11-r2 --command -- sleep infinity
# 
#    Use the following command to attach to the pod:
# 
#    kubectl exec --tty -i redis-client \
#    --namespace default -- bash
# 
# 2. Connect using the Redis® CLI:
#    REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h my-redis -p 6379 # Read only operations
#    REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h my-redis -p 26379 # Sentinel access
# 
# To connect to your database from outside the cluster execute the following commands:
# 
#     export NODE_IP=$(kubectl get nodes --namespace default -o jsonpath="{.items[0].status.addresses[0].address}")
#     export NODE_PORT=$(kubectl get --namespace default -o jsonpath="{.spec.ports[0].nodePort}" services my-redis)
#     REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h $NODE_IP -p $NODE_PORT
# No need to upgrade, ports and nodeports have been set from values

😂 注意:如果最后一行提示如下信息,则需要进行升级。

#!#!#!#!#!#!#!# IMPORTANT #!#!#!#!#!#!#!#
YOU NEED TO PERFORM AN UPGRADE FOR THE SERVICES AND WORKLOAD TO BE CREATED

则需要执行 helm upgrade 升级:

helm upgrade --values values.yaml my-redis bitnami/redis --version=17.13.2

第 3 步:解决 Pod Pending 问题

Helm 安装后,查看 pod 状态,发现都是 Pending 状态,本质上是因为 PVC 没有绑定到 PV。

查看 Pod 状态

查看 Pod 状态:提示为 Pending

kubectl get pods
# NAME              READY   STATUS    RESTARTS   AGE
# my-redis-node-0   0/2     Pending   0          68s

查看 Pod 详细信息:

kubectl describe pod my-redis-node-0
# Name:           my-redis-node-0
# Namespace:      default
# Priority:       0
# Node:           <none>
# Labels:         app.kubernetes.io/component=node
#                 app.kubernetes.io/instance=my-redis
#                 app.kubernetes.io/managed-by=Helm
#                 app.kubernetes.io/name=redis
#                 controller-revision-hash=my-redis-node-7945f545f7
#                 helm.sh/chart=redis-17.13.2
#                 statefulset.kubernetes.io/pod-name=my-redis-node-0
# Annotations:    checksum/configmap: b9d43febb642396f5ab9c8ab09bb5b4157fc3a46937ac846136d2858f0e4b023
#                 checksum/health: 134265b0a0a8aa6b15f7cda3672c0cf8f5c17c817a3061d22ccbfcc74a11454d
#                 checksum/scripts: 898e9e1f07fdbef56a8d034b9d85b1c169d7a4cced31918a92d5a6d606139bc2
#                 checksum/secret: fb9156fa8445c3bf10d1c751ac7641ab5d9a7e61929cd25238f6b69d22b44c79
# Status:         Pending
# IP:             
# IPs:            <none>
# Controlled By:  StatefulSet/my-redis-node
# Containers:
#   redis:
#     Image:      docker.io/bitnami/redis:7.0.12-debian-11-r2
#     Port:       6379/TCP
#     Host Port:  0/TCP
#     Command:
#       /bin/bash
#     Args:
#       -c
#       /opt/bitnami/scripts/start-scripts/start-node.sh
#     Liveness:   exec [sh -c /health/ping_liveness_local.sh 5] delay=20s timeout=5s period=5s #success=1 #failure=5
#     Readiness:  exec [sh -c /health/ping_readiness_local.sh 1] delay=20s timeout=1s period=5s #success=1 #failure=5
#     Startup:    exec [sh -c /health/ping_liveness_local.sh 5] delay=10s timeout=5s period=10s #success=1 #failure=22
#     Environment:
#       BITNAMI_DEBUG:               false
#       REDIS_MASTER_PORT_NUMBER:    6379
#       ALLOW_EMPTY_PASSWORD:        no
#       REDIS_PASSWORD:              <set to the key 'redis-password' in secret 'my-redis'>  Optional: false
#       REDIS_MASTER_PASSWORD:       <set to the key 'redis-password' in secret 'my-redis'>  Optional: false
#       REDIS_TLS_ENABLED:           no
#       REDIS_PORT:                  6379
#       REDIS_SENTINEL_TLS_ENABLED:  no
#       REDIS_SENTINEL_PORT:         26379
#       REDIS_DATA_DIR:              /data
#     Mounts:
#       /data from redis-data (rw)
#       /health from health (rw)
#       /opt/bitnami/redis/etc from redis-tmp-conf (rw)
#       /opt/bitnami/redis/mounted-etc from config (rw)
#       /opt/bitnami/scripts/start-scripts from start-scripts (rw)
#       /tmp from tmp (rw)
#       /var/run/secrets/kubernetes.io/serviceaccount from my-redis-token-6nqr8 (ro)
#   sentinel:
#     Image:      docker.io/bitnami/redis-sentinel:7.0.12-debian-11-r1
#     Port:       26379/TCP
#     Host Port:  0/TCP
#     Command:
#       /bin/bash
#     Args:
#       -c
#       /opt/bitnami/scripts/start-scripts/start-sentinel.sh
#     Liveness:   exec [sh -c /health/ping_sentinel.sh 5] delay=20s timeout=5s period=10s #success=1 #failure=6
#     Readiness:  exec [sh -c /health/ping_sentinel.sh 1] delay=20s timeout=1s period=5s #success=1 #failure=6
#     Startup:    exec [sh -c /health/ping_sentinel.sh 5] delay=10s timeout=5s period=10s #success=1 #failure=22
#     Environment:
#       BITNAMI_DEBUG:               false
#       REDIS_PASSWORD:              <set to the key 'redis-password' in secret 'my-redis'>  Optional: false
#       REDIS_SENTINEL_TLS_ENABLED:  no
#       REDIS_SENTINEL_PORT:         26379
#     Mounts:
#       /data from redis-data (rw)
#       /health from health (rw)
#       /opt/bitnami/redis-sentinel/etc from sentinel-data (rw)
#       /opt/bitnami/redis-sentinel/mounted-etc from config (rw)
#       /opt/bitnami/scripts/start-scripts from start-scripts (rw)
#       /var/run/secrets/kubernetes.io/serviceaccount from my-redis-token-6nqr8 (ro)
# Conditions:
#   Type           Status
#   PodScheduled   False 
# Volumes:
#   redis-data:
#     Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
#     ClaimName:  redis-data-my-redis-node-0
#     ReadOnly:   false
#   start-scripts:
#     Type:      ConfigMap (a volume populated by a ConfigMap)
#     Name:      my-redis-scripts
#     Optional:  false
#   health:
#     Type:      ConfigMap (a volume populated by a ConfigMap)
#     Name:      my-redis-health
#     Optional:  false
#   config:
#     Type:      ConfigMap (a volume populated by a ConfigMap)
#     Name:      my-redis-configuration
#     Optional:  false
#   sentinel-data:
#     Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
#     Medium:     
#     SizeLimit:  <unset>
#   redis-tmp-conf:
#     Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
#     Medium:     
#     SizeLimit:  <unset>
#   tmp:
#     Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
#     Medium:     
#     SizeLimit:  <unset>
#   my-redis-token-6nqr8:
#     Type:        Secret (a volume populated by a Secret)
#     SecretName:  my-redis-token-6nqr8
#     Optional:    false
# QoS Class:       BestEffort
# Node-Selectors:  <none>
# Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
#                  node.kubernetes.io/unreachable:NoExecute for 300s
# Events:
#   Type     Reason            Age   From               Message
#   ----     ------            ----  ----               -------
#   Warning  FailedScheduling  89s   default-scheduler  running "VolumeBinding" filter plugin for pod "my-redis-node-0": pod has unbound immediate PersistentVolumeClaims
#   Warning  FailedScheduling  89s   default-scheduler  running "VolumeBinding" filter plugin for pod "my-redis-node-0": pod has unbound immediate PersistentVolumeClaims

最后面,提示“running “VolumeBinding” filter plugin for pod “my-redis-node-0”: pod has unbound immediate PersistentVolumeClaims”,所以没法被调度。

查看 PVC:发现也是 Pending 状态

kubectl get pvc
# NAME                         STATUS    VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
# redis-data-my-redis-node-0   Pending                                                       4m1s

查看 PVC 详细信息:

kubectl describe pvc redis-data-my-redis-node-0
# Name:          redis-data-my-redis-node-0
# Namespace:     default
# StorageClass:  
# Status:        Pending
# Volume:        
# Labels:        app.kubernetes.io/component=node
#                app.kubernetes.io/instance=my-redis
#                app.kubernetes.io/name=redis
# Annotations:   <none>
# Finalizers:    [kubernetes.io/pvc-protection]
# Capacity:      
# Access Modes:  
# VolumeMode:    Filesystem
# Mounted By:    my-redis-node-0
# Events:
#   Type    Reason         Age                   From                         Message
#   ----    ------         ----                  ----                         -------
#   Normal  FailedBinding  13s (x19 over 4m31s)  persistentvolume-controller  no persistent volumes available for this claim and no storage class is set

😂 绑定失败原因:针对这个 Claim 没有可以的 PV,而且也没有设置 StorageClass,也就没法使用 StorageClass 去匹配 PV。

😊 在 values.yaml 中可以使用全局指定 storageClass,也可以针对 master、replica、sentinel 单独指定 storageClass 哦。

https://blog.csdn.net/qq_40887651/article/details/134916758

添加 PV 持久化卷

这里会用到三个 PV,创建步骤如下。

创建 pv1
mkdir -p /csp/local-pv/redis1
chmod 777 -R /csp/local-pv/redis1

kubectl apply -f pv1.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-redis1
spec:
  capacity:
    storage: 8Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/csp/local-pv/redis1"
创建 pv2
mkdir -p /csp/local-pv/redis2
chmod 777 -R /csp/local-pv/redis2

kubectl apply -f pv2.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-redis2
spec:
  capacity:
    storage: 8Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/csp/local-pv/redis2"
创建 pv3
mkdir -p /csp/local-pv/redis3
chmod 777 -R /csp/local-pv/redis3

kubectl apply -f pv3.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv-redis3
spec:
  capacity:
    storage: 8Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/csp/local-pv/redis3"

查看 Pod 状态

再次查看 Pod 状态:发现正常 Running 了,等待几分钟后,所有 Pod 创建完成

kubectl get pods -w
# NAME              READY   STATUS    RESTARTS   AGE
# my-redis-node-0   0/2     Running   0          20m
# my-redis-node-0   1/2     Running   0          22m
# my-redis-node-0   2/2     Running   0          22m
# my-redis-node-1   0/2     Pending   0          0s
# my-redis-node-1   0/2     ContainerCreating   0          0s
# my-redis-node-1   0/2     Running             0          7s
# my-redis-node-1   1/2     Running             0          28s
# my-redis-node-1   2/2     Running             0          29s
# my-redis-node-2   0/2     Pending             0          0s
# my-redis-node-2   0/2     ContainerCreating   0          2s
# my-redis-node-2   0/2     Running             0          10s
# my-redis-node-2   1/2     Running             0          29s
# my-redis-node-2   2/2     Running             0          31s

第 4 步:连接到 Redis

查看端口

Redis 端口:

  • 32700:访问下面三个之一【应该是轮询/随机】
  • 32701:master,my-redis-node-0.default
  • 32702:slave,my-redis-node-1.default
  • 32703:slave,my-redis-node-2.default

Sentinel 端口:

  • 32710:访问下面三个之一【应该是轮询/随机】
  • 32711:run_id:24eee4e4b30f448bd267fca21ef41956b1ad00a8
  • 32712:run_id:cf059f9de8325872c8c6bf71ea7ee7a8ecfb88c9
  • 32713:run_id:6aca574c82d5402b12e1b9b6692328f4b719ec26
netstat -tunlp | grep kube-proxy | grep 327
# tcp        0      0 0.0.0.0:32700           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32701           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32702           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32703           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32710           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32711           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32712           0.0.0.0:*               LISTEN      6243/kube-proxy     
# tcp        0      0 0.0.0.0:32713           0.0.0.0:*               LISTEN      6243/kube-proxy

使用 redis-cli 连接

# 1 进入容器内
kubectl exec -it my-redis-node-0 bash

# 2 使用 redis-cli 连接 sentinel
redis-cli -h 172.24.4.246 -p 32710 

# 2.1 密码认证
172.24.4.246:32710> auth Redis@pass123
# OK

# 2.2、查看信息
172.24.4.246:32710> info
# Server
# redis_version:7.0.12
# ...
# redis_mode:sentinel
# ...
# process_id:1
# run_id:cf059f9de8325872c8c6bf71ea7ee7a8ecfb88c9
# tcp_port:26379
# ...
# executable:/redis-server
# config_file:/opt/bitnami/redis-sentinel/etc/sentinel.conf
# ...
# # Sentinel
# sentinel_masters:1
# ...
# master0:name=mymaster,status=ok,address=my-redis-node-0.default:32701,slaves=2,sentinels=3

# 2.3 查看 master 信息
172.24.4.246:32710> sentinel get-master-addr-by-name mymaster
# 1) "my-redis-node-0.default"
# 2) "32701"

# 2.4 查看 master 的 replia 节点
172.24.4.246:32710> sentinel replicas mymaster
# my-redis-node-1.default:32702
# my-redis-node-2.default:32703

使用 Redis Desktop 连接

配置 /etc/hosts

172.24.4.246 为集群 IP 地址,需要将上述 k8s 中的主机名称,映射为 IP 地址访问。

在 /etc/hosts 添加如下配置:

172.24.4.246 my-redis-node-0.default my-redis-node-1.default my-redis-node-2.default
配置 Redis 连接信息

使用 32710 连接到任意一个 Sentinel,然后 Sentinel 会返回 master 地址(my-redis-node-0.default:32701),本地 hosts 解析此主机名后,即可访问成功!

存在的疑问及尝试

Redis Sentinel 返回的 master 地址为 my-redis-node-0.default:32701。

能不能让 Redis Sentinel 直接返回 172.24.4.246:32701 这样的 master 地址呢?

尝试 1:修改 replica、sentinel 配置文件,sentinel 配置中直接 announce-ip 为 172.24.4.246

# 从节点
replica:
  replicaCount: 3
  configuration: |-
    replica-announce-ip 172.24.4.246

# 哨兵
sentinel:
  enabled: true
  service:
    type: "NodePort"
    nodePorts:
      sentinel: 32710
      redis: 32700
  configuration: |-
    SENTINEL resolve-hostnames no
    SENTINEL announce-hostnames no
    SENTINEL announce-ip 172.24.4.246

配置添加后,redis-cli 登录 sentinel,执行 info 命令,发现配置文件是 config_file:/opt/bitnami/redis-sentinel/etc/sentinel.conf

但是,这个配置文件在 Pod 容器中找不到哇。所以依然是默认配置。

能找的 sentinel.conf 是 /opt/bitnami/redis/mounted-etc/sentinel.conf。里面的配置还真就是我们上面配置的,可惜了,不会生效。

尝试 2:使用 useHostnames 为 false

# Use hostnames internally when announcing replication	
useHostnames: false

修改后,发现只是不用主机名了,用的 IP 是内部 Pod IP。

127.0.0.1:26379> sentinel get-master-addr-by-name mymaster
1) "192.168.103.247"
2) "32701"

完。

相关博文

1.第 1 篇 Helm 简介及安装
2.第 2 篇 Helm 部署 MySQL【入门案例】
3.第 3 篇 Helm 命令、环境变量、相关目录
4.第 4 篇 Chart 仓库详解
5.第 5 篇 Chart 文件结构详解
6.第 6 篇 自定义 Helm Chart
7.第 7 篇 Helm 部署 Nacos【详细步骤】
8.第 8 篇 Chart 修改入门示例:Nacos
9.第 9 篇 Helm 部署 Seata Server
10.第 10 篇 Chart 修改完美示例:Seata Server
11.第 11篇 Helm 部署 RabbitMQ
12.第 12 篇 Helm 部署 Redis
13.第13 篇 Helm 部署 ElasticSearch

;