19.kubernetes上部署Redis主从并实现读写分离

本文介绍了如何在Kubernetes环境下使用Camellia-Redis-Proxy作为高性能代理,配合StatefulSet和HeadlessService为Redis主从节点分配固定DNS,实现实时读写分离和高可用性,同时展示了如何配置和验证Proxy的性能及功能特性。
摘要由CSDN通过智能技术生成

介绍

部署Redis主从首先需要确定Redis的master节点和slave节点,由于Kubernetes上的Pod默认是没有固定IP的,这里使用Headless Services结合StatefulSet,为每个Redis服务的Pod分配一个固定的DNS记录,就可以达到明确主从节点的目的

camellia-redis-proxy 是一款高性能的redis代理,使用netty4开发,主要特性如下:

  • 支持代理到redis-standalone、redis-sentinel、redis-cluster
  • 支持其他proxy作为后端(如双写迁移场景),如 twemproxycodis
  • 支持 kvrockspikatendis 等作为后端
  • 支持普通的GET/SET/EVAL,也支持MGET/MSET,也支持阻塞型的BLPOP,也支持PUBSUB和TRANSACTION,也支持STREAMS/JSON/SEARCH,也支持TAIR_HASH/TAIR_ZSET/TAIR_STRING
  • 支持的命令列表,具体见:supported_commands
  • 支持自定义分片
  • 支持读写分离
  • 支持双(多)写,可以proxy直连双写,也可以基于mq(如kafka)双写,也可以基于插件体系自定义双写规则
  • 支持双(多)读
  • 支持设置密码
  • 支持SELECT命令,当前仅当后端redis不包含redis-cluster(此时仅支持SELECT 0),可以是redis-standalone/redis-sentinel/redis-proxies或者其组合(分片/读写分离)
  • 支持阻塞式命令,如BLPOP/BRPOP/BRPOPLPUSH/BZPOPMIN/BZPOPMAX等
  • 支持PUBSUB系列命令,代理到redis-standalone/redis-sentinel/redis-cluster均支持
  • 支持事务命令(MULTI/EXEC/DISCARD/WATCH/UNWATCH),代理到redis-standalone/redis-sentinel/redis-cluster均支持
  • 支持redis5.0的STREAMS系列命令
  • 支持SCAN命令(代理到redis-standalone/redis-sentinel/redis-cluster均支持,自定义分片时也支持)
  • 支持阿里TairZSet、TairHash、TairString系列命令
  • 支持RedisJSON和RedisSearch系列命令
  • 支持读slave(redis-sentinel/redis-cluster均支持配置读从节点)
  • 支持SSL/TLS(proxy到client支持,proxy到redis也支持),具体见:ssl/tls
  • 支持unix-domain-socket(client到proxy支持,proxy到redis也支持),具体见:uds
  • 支持使用http协议访问proxy,类似于 webdis ,但是接口定义不一样,具体见:http
  • 支持多租户,即租户A路由到redis1,租户B路由到redis2(可以通过不同的clientname区分,也可以通过不同的password区分)
  • 支持多租户动态路由,支持自定义的动态路由数据源(内置:本地配置文件、nacos、etcd等,也可以自定义)
  • 支持自定义插件,并且内置了很多插件,可以按需使用(包括:大key监控、热key监控、热key缓存、key命名空间、ip黑白名单、速率控制等等)
  • 支持丰富的监控,可以监控客户端连接数、调用量、方法耗时、大key、热key、后端redis连接数和耗时等,并且支持以http接口形式获取监控数据
  • 支持使用prometheus/grafana来监控proxy集群,参考:prometheus-grafana
  • 支持info命令获取服务器相关信息(包括后端redis集群的信息)
  • 提供了一个spring-boot-starter,可以快速搭建proxy集群
  • 高可用,可以基于lb组成集群,也可以基于注册中心组成集群,也可以伪装成redis-cluster组成集群,也可以伪装成redis-sentinel组成集群
  • 提供了一个默认的注册发现实现组件(依赖zookeeper),如果端侧是java,则可以很简单的将JedisPool替换为RedisProxyJedisPool,即可接入redis proxy
  • 提供了一个spring-boot-starter用于SpringRedisTemplate以注册发现模式接入proxy
  • 支持整合hbase实现string/zset/hash等数据结构的冷热分离存储操作,具体见: redis-proxy-hbase

创建configmap

将redis和camellia-redis-proxy依赖的配置文件写入到configmap

[root@docker k8s]# ll config/
total 12
-rw-r--r-- 1 root root 719 Jan 30 15:55 application.yml
-rw-r--r-- 1 root root  32 Jan 30 16:04 redis.conf
-rw-r--r-- 1 root root 304 Jan 30 15:59 resource-table.json

[root@docker k8s]# kubectl create configmap redis-config --from-file=config/

[root@docker k8s]# kubectl get cm redis-config -oyaml
apiVersion: v1
data:
  application.yml: |
    server:
      port: 6380
    spring:
      application:
        name: camellia-redis-proxy-server

    camellia-redis-proxy:
      console-port: 16379
      password: changeme
      monitor-enable: true
      monitor-interval-seconds: 60
      plugins:
        - monitorPlugin
        - bigKeyPlugin
        - hotKeyPlugin
      transpond:
        type: local
        local:
          type: complex
          dynamic: true
          check-interval-millis: 3000
          json-file: /camellia-redis-proxy/config/resource-table.json
  redis.conf: |-
    bind 0.0.0.0
    port 6379
    dir /data
  resource-table.json: |
    {
        "type": "simple",
        "operation": {
            "read": {
                "resources": [
                    "redis://@redis-1.redis.default.svc.cluster.local:6379",
                    "redis://@redis-2.redis.default.svc.cluster.local:6379"
                ],
                "type": "random"
            },
            "type": "rw_separate",
            "write": "redis://@redis-0.redis.default.svc.cluster.local:6379"
        }
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2024-01-30T08:05:05Z"
  name: redis-config
  namespace: default
  resourceVersion: "667605"
  uid: 869c6b41-9ad8-4dff-b51e-95c3b3f8e553

部署 redis-statefulset

[root@docker k8s]# cat redis-statefulset.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: redis
  labels:
    app: redis
spec:
  ports:
    - port: 6379
      name: redis
  clusterIP: None
  selector:
    app: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  serviceName: "redis"
  replicas: 2
  minReadySeconds: 10
  template:
    metadata:
      labels:
        app: redis
    spec:
      initContainers:
        - name: redis-config-init
          image: redis:6.2
          imagePullPolicy: IfNotPresent
          command: ["/bin/bash", "-c", 'cp /tmp/redis.conf /etc/redis/redis.conf; if [[ "$(hostname)" != *-0 ]]; then echo "slaveof ${HOSTNAME%-*}-0.$(hostname -d) 6379" >> /etc/redis/redis.conf ;fi']
          volumeMounts:
            - name: etc-redis-dir
              mountPath: /etc/redis
            - name: redis-config
              mountPath: /tmp
      containers:
        - name: redis
          image: redis:6.2
          command: ["redis-server", "/etc/redis/redis.conf"]
          ports:
            - containerPort: 6379
              name: redis
          volumeMounts:
            - name: etc-redis-dir
              mountPath: /etc/redis
      volumes:
        - name: etc-redis-dir
          emptyDir:
            medium: Memory
            sizeLimit: 1Mi
        - name: redis-config
          configMap:
            name: redis-config
            items:
              - key: "redis.conf"
                path: "redis.conf"

[root@docker k8s]# kubectl apply -f redis-statefulset.yaml
service/redis created
statefulset.apps/redis created

[root@docker k8s]# kubectl get po
NAME                     READY   STATUS    RESTARTS      AGE
centos-888f48644-p7k2k   1/1     Running   3 (27m ago)   3d22h
redis-0                  1/1     Running   0             29s
redis-1                  1/1     Running   0             50s
redis-2                  1/1     Running   0             9s

# 查看 Redis Pod, redis-0为master节点, redis-1和redis-2为slave节点
[root@docker k8s]# kubectl exec -it redis-0 cat /etc/redis/redis.conf
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
bind 0.0.0.0
port 6379
dir /data

[root@docker k8s]# kubectl exec -it redis-1 cat /etc/redis/redis.conf
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
bind 0.0.0.0
port 6379
dir /data
slaveof redis-0.redis.default.svc.cluster.local 6379

[root@docker k8s]# kubectl exec -it redis-2 cat /etc/redis/redis.conf
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
bind 0.0.0.0
port 6379
dir /data
slaveof redis-0.redis.default.svc.cluster.local 6379

# 查看 Redis主从复制信息,已经有两个从节点
[root@docker k8s]# kubectl exec -it redis-0 bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
root@redis-0:/data# redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.244.209.1,port=6379,state=online,offset=98,lag=0
slave1:ip=10.244.138.75,port=6379,state=online,offset=98,lag=1
master_failover_state:no-failover
master_replid:180356a936c120511322bb516fb6e32135f6432a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:98
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:98

# 在 redis-0 上写入数据,在 redis-1 和 在 redis-2 上已经被复制
[root@docker k8s]# kubectl exec -it redis-0 redis-cli set k1 v1
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
OK
[root@docker k8s]# kubectl exec -it redis-1 redis-cli get k1
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
"v1"
[root@docker k8s]# kubectl exec -it redis-2 redis-cli get k1
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "redis" out of: redis, redis-config-init (init)
"v1"

部署camellia-redis-proxy

[root@docker k8s]# cat camellia-redis-proxy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: camellia-redis-proxy
  name: camellia-redis-proxy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: camellia-redis-proxy
  template:
    metadata:
      labels:
        app: camellia-redis-proxy
    spec:
      containers:
        - name: camellia-redis-proxy
          image: registry.cn-beijing.aliyuncs.com/kube-mirrors/camellia-redis-proxy:1.2.25
          command: ["java"]
          args: ["-jar", "camellia-redis-proxy.jar", "--spring.config.location=/camellia-redis-proxy/config/"]
          ports:
            - name: redis-proxy
              containerPort: 6380
          volumeMounts:
            - name: camellia-redis-proxy-config
              mountPath: /camellia-redis-proxy/config
      volumes:
        - name: camellia-redis-proxy-config
          configMap:
            name: redis-config
            items:
              - key: "application.yml"
                path: "application.yml"
              - key: "resource-table.json"
                path: "resource-table.json"
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: camellia-redis-proxy
  name: redis-proxy
spec:
  type: ClusterIP
  sessionAffinity: None
  selector:
    app: camellia-redis-proxy
  ports:
    - port: 6380
      protocol: TCP
      targetPort: redis-proxy

[root@docker k8s]# kubectl get svc
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)              AGE
kubernetes    ClusterIP   172.20.0.1     <none>        443/TCP              45d
redis         ClusterIP   None           <none>        6379/TCP             57m
redis-proxy   ClusterIP   172.20.99.82   <none>        6380/TCP,16379/TCP   7m43s

服务验证

# 访问 camellia-redis-proxy console info API 可以查看 camellia-redis-proxy 的基本信息
[root@docker k8s]# curl redis-proxy:16379/info
# Server
camellia_version:1.2.25
redis_version:7.0.11
...
# Route
route_nums:1
route_conf_default_default:{"type":"simple","operation":{"read":{"resources":["redis://@redis-1.redis.default.svc.cluster.local:6379","redis://@redis-2.redis.default.svc.cluster.local:6379"],"type":"random"},"type":"rw_separate","write":"redis://@redis-0.redis.default.svc.cluster.local:6379"}}

# Upstream
upstream_redis_nums:50
upstream_redis_nums[@redis-1.redis.default.svc.cluster.local:6379]:17
upstream_redis_nums[@redis-0.redis.default.svc.cluster.local:6379]:16
upstream_redis_nums[@redis-2.redis.default.svc.cluster.local:6379]:17
...

# 通过 redis-cli 访问 camellia-redis-proxy 进行读写测试,同时在redis-0,redis-1,redis-2上执行 redis-cli monitor,观察读写情况
[root@docker k8s]# redis-cli -h redis-proxy.default.svc.cluster.local -p 6380
redis-proxy.default.svc.cluster.local:6380> info
NOAUTH Authentication required
redis-proxy.default.svc.cluster.local:6380> auth changeme
OK
redis-proxy.default.svc.cluster.local:6380> info
# Server
camellia_version:1.2.25
redis_version:7.0.11
...
# Clients
connect_clients:1
connect_clients_default_default:1

# Route
route_nums:1
route_conf_default_default:{"type":"simple","operation":{"read":{"resources":["redis://@redis-1.redis.default.svc.cluster.local:6379","redis://@redis-2.redis.default.svc.cluster.local:6379"],"type":"random"},"type":"rw_separate","write":"redis://@redis-0.redis.default.svc.cluster.local:6379"}}

# Upstream
upstream_redis_nums:50
upstream_redis_nums[@redis-1.redis.default.svc.cluster.local:6379]:17
upstream_redis_nums[@redis-0.redis.default.svc.cluster.local:6379]:16
upstream_redis_nums[@redis-2.redis.default.svc.cluster.local:6379]:17

...

redis-proxy.default.svc.cluster.local:6380> get k2
"v2"
redis-proxy.default.svc.cluster.local:6380> set k3 v3
OK

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodingDemo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值