乐趣区

关于redis:网易有道-REDIS-云原生实战

REDIS 云原生实战

摘要
本次以 Redis 为范例,论述了有道基础架构团队在基础设施容器化路线上的实际,次要将从申明式治理,Operator 工作原理,容器编排,主从模式,集群模式,高可用策略,集群扩缩容等方面开展。

目录

  • 背景
  • 面临的挑战
  • 申明式治理
  • Operator 工作原理

    • 容器编排
    • 主从模式

    • 主从拓扑图
    • 和谐原理

  • 集群模式
    • 集群拓扑图
    • 和谐原理
  • 高可用策略
    • Kubernetes 保障的高可用
    • Redis 集群的高可用
  • 监控观测
  • 集群扩缩容
  • 总结与瞻望

背景

Redis 是业务零碎中较为罕用的缓存服务,罕用于流量顶峰、数据分析、积分排序等场景,并且通过中间件能够实现零碎之间的解耦,晋升零碎的可扩展性。

传统物理机部署中间件,须要运维人员手动搭建,启动工夫较长,也不利于前期保护,无奈满足业务疾速倒退的需要。

云原生相较于传统 IT,能够助力业务平滑迁徙、疾速开发、稳固运维,大幅升高技术老本,节约硬件资源。

云原生中间件是指依靠容器化、服务网格、微服务、Serverless 等技术,构建可扩大的基础设施,继续交付用于生产零碎的根底软件,在性能不变的前提下,进步了利用的可用性与稳定性。

在这种大趋势下,有道基础架构团队开始了云原生中间件的实际,除了本文介绍的 Redis,还包含 Elasticsearch、ZooKeeper 等。

面临的挑战

利用云原生技术能够解决以后 Redis 部署迟缓,资源利用率低等问题,同时容器化 Redis 集群也面临着一些挑战:

• Kubernetes 如何部署 Redis 有状态服务
• 容器 Crash 后如何不影响服务可用性;
• 容器重启后如何保障 Redis 内存中的数据不丢;
• 节点程度扩容时如何做到 slots 迁徙时不影响业务;
• pod ip 变动后集群的状态如何解决。

申明式治理

对于一个 Redis 集群,咱们的冀望是可能 7×24 小时无间断提供服务,遇故障可自行修复。这与 Kubernetes API 的申明式特点一模一样。

所谓“申明式”, 指的就是咱们只须要提交一个定义好的 API 对象来“申明”我所冀望的状态是什么样子,Kubernetes 中的资源对象可在无外界烦扰的状况下,实现以后状态到冀望状态的转换,这个过程就是 Reconcile 过程。例如,咱们通过 yaml 创立了一个 Deployment ,Kubernetes 将“主动的”依据 yaml 中的配置,为其创立好 Pod,并拉取指定存储卷进行挂载,以及其余一系列简单要求。

因而,咱们的 Redis 集群是否能够应用一个相似的服务去实现这个过程呢?即咱们须要定义这样的对象,定义服务 Reconcile 的过程。Kubernetes 的 Operator 刚好能够满足这个需要,能够简略的了解 Operator 由资源定义和资源控制器形成,在充沛解读集群和 Operator 的关系后,咱们将整体架构图设计如下

Operator 集群自身采纳 Deployment 部署,由 ETCD 实现选主,下层与 Kubernetes 的 Api Server、Controller Manager 等组件进行通信,上层继续和谐 Redis 集群状态。

哨兵模式中 Redis 服务用一套哨兵集群,应用 StatefulSet 部署,长久化配置文件。Redis server 也采纳 StatefulSet 部署, 哨兵模式的实例为一主多从。

集群模式中的每个分片应用 StatefulSet 部署,代理采纳 Deployment 部署。原生 Pod、StatefulSet、Service、调度策略等由 Kubernetes 自身负责。

Redis 的资源定义在 ETCD 中存储一份即可,咱们只须要事后提交自定义资源的 yaml 配置。如下所示为创立三个正本的 Redis 主从集群


apiVersion: Redis.io/v1beta1
kind: RedisCluster
metadata:
  name: my-release
spec:
  size: 3
  imagePullPolicy: IfNotPresent
  resources:
    limits:
      cpu: 1000m
      memory: 1Gi
    requests:
      cpu: 1000m
      memory: 1Gi
  config:
    maxclients: "10000"

其中,kind 定义应用的 CR 名称,size 为正本数,resources 定义资源配额,config 对应 Redis Server 的 config,该定义存储在 Kubernetes 的 ETCD 数据库中,后续的具体资源申请与应用由 Operator 的 Controller 实现。

Operator 工作原理

Operator 是 Kubernetes 的扩大模式,由 CRD、Controller 形成。它利用定制资源管理特定利用及其组件,Operator 遵循 Kubernetes 的理念。

Operator 无需任何批改,即可从 Kubernetes 外围中取得许多内置的自动化性能,如应用 Kubernetes 自动化部署和运行工作负载,甚至能够自动化 Kubernetes 本身。

Kubernetes 的 Operator 模式可在不批改 Kubernetes 本身的代码根底上,通过控制器关联到一个以上的定制资源,即能够扩大集群的行为。Operator 是 Kubernetes API 的客户端,外围性能是充当定制资源的控制器。

CRD: Custom Resource Definition, 在 Kubernetes 中所有皆是资源,资源就是 CRD,用户自定义的 Kubernetes 资源是一个类型,比方默认自带的由 Deployment,Pod,Service 等。

CR: Custom Resource 是实现 CRD 的具体实例。

用户创立一个 CRD 自定义资源,ApiServer 把 CRD 转发给 webhook,webhook 进行缺省值配置 验证配置和批改配置,webhook 解决实现后的的配置会存入 ETCD 中,返回给用户是否创立胜利信息。Controller 会监测到 CRD,依照事后写的业务逻辑,解决这个 CRD,比方创立 Pod、解决新节点与旧集群关系等,保障运行的状态与冀望的统一。

容器编排

Redis 集群在 Kubernetes 中的最小部署单位为 Pod,因而在架构设计之前,需事后思考 Redis 个性、资源限度、部署状态、数据存储、状态保护等内容,为不同类型的 Redis 集群配置适合的部署形式。

资源限度

Kubernetes 采纳 request 和 limit 两种限度类型来对资源进行调配。

• request(资源需要):即运行 Pod 的节点必须满足运行 Pod 的最根本需要能力启动。
\
• limit(资源限度):即运行 Pod 期间,可能内存使用量会减少,那最多能应用多少内存,这就是资源限额。

Redis 根本不会滥用 cpu,因而配置 1 - 2 个核即可。内存依据具体业务应用调配,思考到局部场景下会 fork 较多的内存,例如 aof 频繁刷写,aof 重写过程中,Redis 主程序称仍旧能够接管写操作,这时会采纳 copy on write (写时复制)的办法操作内存数据,若业务应用特点为“写多读少”,那么刷写期间将产生大量的内存拷贝,从而导致 OOM,服务重启。

一个无效的解决形式为缩小刷写次数,将刷写操作放在夜间低流量时段进行。缩小刷写次数的办法为适当减少 auto-aof-rewrite-min-size 的大小,可配置应用内存的 5 倍甚至更大的最小刷写量;其次能够被动触发刷写,判断内存应用达到的配额两倍时进行刷写,理论部署时个别也会预留 50% 的内存避免 OOM。

部署的根本状态

根据数据是否须要长久化或是否须要惟一标识辨别服务为无状态和有状态的服务,Redis 集群须要明确主从、分片标识,大部分场景也须要数据长久化,Kubernetes 应用 StatefulSet 来满足这一类需要。StatefulSet 的程序部署、逆序主动滚动更新更能进步 Redis 集群的可用性。

具体的:

• Redis Server 应用 StatefulSet 启动,为标识为{StatefulSetName}- 0 的 Pod 设置 Master 角色,给其余 Pod 设置为该 Master 的从节点。

• Proxy 无需存储任何数据,应用 Deployment 部署,便于动静扩大。

配置文件
Redis Server 启动时须要一些配置文件,外面波及到用户名和明码,咱们应用 Configmap 和 Secret 来存储的。Configmap 是 Kubernetes 的 Api 对象,罕用于存储小于 1MB 的非秘密键值对。而 Secret 能够用于存储蕴含敏感信息的明码、令牌、密钥等数据的对象。

两种资源均能够在 Pod 运行的时候通过 Volume 机制挂载到 Pod 外部。

存储
存储应用的是 PVC(PersistentVolumeClaim)加 PV(Persistent Volumes),PV 为 Kubernetes 集群中的资源,由存储类 StorageClass 来动静供给,PV 反对多种拜访模式:ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany,通过 PV 定义存储资源,PVC 申请应用该存储资源。另外通过依据存储的 StorageClass 字段 可形象不同的存储后端,如 Cephfs、Cephrbd、Openebs、LocalStorage 等。

主从模式

主从拓扑图

Redis 容器化后建设的每个 CR 示意一个残缺的 Redis 服务,具体的服务模式包含哨兵模式和集群模式两种,在进行容器化过程中,除笼罩裸服务器部署构造外,也对架构进行了肯定水平的优化。

原生哨兵模式
原生哨兵模式为每套实例配一组哨兵。

共用哨兵模式
所有实例共用一组哨兵将进一步提高实例启动速度,并在肯定水平上可进步硬件资源利用率,实测单组哨兵可轻松应答百规模的主从集群。

和谐原理

Reconcile 实现继续监测并对主从集群进行修复的性能。

  1. 查看是否依照预期启动了全副的 Pod,比方创立 3 个 Server,那么须要依照预期启动三个能力持续进行前面的操作。
  2. 查看 Master 的数量,确保该实例仅有一个主节点(数量为 0 被动选一个;数量大于 1 手动修复)。
  3. 查看哨兵:

    (1)所有的哨兵是否监控了正确的 Master;

    (2)所有的哨兵均晓得雷同的 Slave;

    (3)再次查看哨兵的数量,确保哨兵均可用。

  4. 查看 Service,使 Service 的 Endpoints 指向正确的 Master。
  5. 查看 Redis config 是否有做批改,有则对所有节点重写 config 参数。

集群模式

集群拓扑图

Redis Cluster + Proxy 模式

通过在传统 Redis Cluster 架构中引入代理性能,实现动静路由散发,并基于 Kubernetes 原生动静扩缩容个性,更易应答突发流量,正当调配应用资源。

代理根底转发规定如下

代理根底转发规定如下:

• 对于操作单个 Key 的命令,Proxy 会依据 Key 所属的 Slot(槽)将申请发送给所属的数据分片。

• 对于操作多个 Key 的命令,如果这些 Key 是贮存在不同的数据分片,Proxy 会将命令拆分成多个命令别离发送给对应的分片。

服务部署前,也对代理的局部性能进行了补充,例如移除不可用节点等。

和谐原理

reconcile 实现继续监测并对 Redis Cluster 进行修复性能。

确保集群衰弱的步骤

  1. 期待所有 Pod 状态变为 Ready 且每个节点互相辨认后,Operator 会在每个 StatefulSet 的 Pod 中筛选一个作为 Master 节点,其余节点为该 Master 的 Slave。
  2. 获取实例集群所有 Pod 的 ip、所有 Pod 的 cluster info(蕴含 nodeIP,主从关系等)。
  3. 进入复原流程

    (1)解决失败节点, 对局部节点重启后的有效 ip、状态为 noaddr 的僵尸节点进行 forget 操作;

    (2)解决不可信节点 (所有 handshake 状态的节点),产生于某一个节点被移除(由 forget node 触发),但试图退出集群时,即该 Pod 在 Operator 角度下存在,但理论集群节点并不需要该节点,解决形式为删掉这个 Pod,并再次做 forget 操作直到 Pod 被删除。

  4. 任选一个节点,应用 CLUSTER MEET 给该节点退出所有已知节点。
  5. 为 StatefulSet 中的 Pod 建设主从关系,同时给其调配 Slots。若以后 Master 数量同预期不统一,则对应扩缩容操作,具体见’集群扩缩容’的横向扩缩容大节。
  6. 查看 Redis config 是否有做批改,有则对所有节点重写 config 参数。

确保代理衰弱的步骤

  1. 获取所有 Running 状态代理的 Pod ip。
  2. 从代理获取 Redis Server 信息,将集群信息同步到所有的代理上,代理中不存在的 Server ip 做移除操作。
  3. 若代理中无可用 Redis Server, 示意被全副移除,则增加一个,代理可主动发现集群其余 Redis 节点。

高可用策略

Kubernetes 保障的高可用

(1) 容器部署保障高可用:

Redis 部署最小资源对象为 Pod,Pod 是 Kubernetes 创立或部署的最小 / 最简略的根本单位。

当启动出错,例如呈现“CrashLoopBackOff”时,Kubernetes 将主动在该节点上重启该 Pod, 当呈现物理节点故障时,Kubernetes 将主动在其余节点上从新拉起一个。
Pod 未出问题,但程序不可用时,依靠于健康检查策略,Kubernetes 也将重启该 Redis 节点。

(2) 滚动降级:

节点纵向扩容时,应用 StatefulSet 的滚动降级机制,Kubernetes 将逆序重启更新每个 Pod,进步了服务的可用性。

(3) 调度的高可用:

Kubernetes 自身不解决 Redis 多个 Pod 组建的集群之间的部署关系,但提供了部署策略,为保障特定场景下的高可用,如因物理节点导致所有 Redis 节点均宕机,CRD 在设计中退出了亲和与反亲和字段。
默认应用 podAntiAffinity 做节点打散,如下所示实例 instance1 的所有 Pod 将被尽可能调度到不同的节点上。

spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - podAffinityTerm:
              labelSelector:
                matchLabels:
                  Redis.io/name: instance1
              topologyKey: Kubernetes.io/hostname
            weight: 1

Redis 集群的高可用

Redis 服务运行期间不可避免的呈现各种非凡状况,如节点宕机、网络抖动等,如何继续监测这类故障并进行修复,实现 Redis 集群的高可用,也是 Operator 需解决的问题,上面以哨兵模式模式为例形容集群如何进行故障复原。

主节点宕机:因物理节点驱赶、节点重启、过程异样完结等导致的 Redis 主节点宕机状况,哨兵会进行切主操作,而后 Kubernetes 会在可用物理节点上从新拉起一个 Pod。

从节点宕机:哨兵模式的 Redis 集群未开启读写拆散,从节点宕机对服务无影响,后续 Kubernetes 会重启拉起一个 Pod,Operator 会将该 Pod 设置为新主节点的从节点。

集群全副节点宕机:产生概率极小,但基于长久化可将服务影响降至最低,集群复原后可持续提供服务。

节点网络故障:主从模式下配置了三个哨兵用于集群选主操作,哨兵集群的每一个节点会定时对 Redis 集群的所有节点发心跳包检测节点是否失常。如果一个节点在 down-after-milliseconds 工夫内没有回复 Sentinel 节点的心跳包,则该 Redis 节点被该 Sentinel 节点主观下线。

当节点被一个 Sentinel 节点记为主观下线时,并不意味着该节点必定故障了,还须要 Sentinel 集群的其余 Sentinel 节点独特判断为主观下线才行。

该 Sentinel 节点会询问其余 Sentinel 节点,如果 Sentinel 集群中超过 quorum 数量的 Sentinel 节点认为该 Redis 节点主观下线,则该 Redis 主观下线。

如果主观下线的 Redis 节点是从节点或者是 Sentinel 节点,则操作到此为止,没有后续的操作了;如果主观下线的 Redis 节点为主节点,则开始故障转移,从从节点中选举一个节点降级为主节点。

集群模式故障转移与上述相似,不过不须要哨兵干涉,而是由节点之间通过 PING/PONG 实现。

监控观测

Redis 的监控采纳经典的 Exporter+Promethus 的计划,Exporter 用于指标采集,数据存储在 Prometheus 或其余数据库中,最终 Grafana 前端将服务状态可视化。

集群扩缩容

(1)纵向扩缩容
纵向扩缩容次要指 Pod 的 CPU、内存资源的调整,基于 Kubernetes 的个性,只需批改实例对应的 spec 字段,Operator 的和谐机制将继续监测参数变动,并对实例做出调整。当批改 cpu、内存等参数时,Operator 同步更新 StatefulSet 的 limit、request 信息,Kubernetes 将逆序滚动更新 Pod,滚动更新时,若停掉的是主节点,主节点的 preStop 性能会先告诉哨兵或者集群进行数据保留,而后做主从切换操作,从而将服务的影响降至最低。更新后的主从关系建设以及哨兵 monitor 主节点性能也由 Operator 一并处理,全过程对客户端无感知。主从版、集群版在该场景下均反对秒级断闪。

(2)横向扩缩容
横向扩缩容次要指正本数或节点数的调整,得益于 Kubernetes 的申明式 API,能够通过更改申明的资源规模对集群进行无损弹性扩容和缩容。

Redis Server 扩容操作时,主从版本中 Operator 将获取新节点 ip, 新启动节点将在下一轮和谐时触发 slaveof 主节点操作,且同步过程中,哨兵不会将该节点选为主节点。集群版本中 Operator 将在同步节点信息后进行分片迁徙,保障所有节点上的 Slots 尽可能均匀分布。

Redis Server 缩容操作时,主从版本中 Operator 将逆序销毁 Pod,销毁时会先询问哨兵,本人是否为主节点,若为主节点则进行先 failover 操作再退出。集群版本中 Operator 中会先进行分片迁徙,再对该节点做删除操作。

代理的扩缩容,更易实现,依据流量波峰波谷法则,可手动定期在波峰到来时对 Proxy 进行扩容,波峰过后对 Proxy 进行缩容;也可依据 HPA 实现动静扩缩容,HPA 也是 Kubernetes 的一种资源,能够根据 Kubernetes 的 Metrics API 的数据,实现基于 CPU 使用率、内存使用率、流量的动静扩缩容。

总结与瞻望

本次以 Redis 为范例,论述了有道基础架构团队在基础设施容器化路线上的实际,Redis 上云后将大幅缩短集群部署工夫,反对秒级部署、分钟级启动、启动后的集群反对秒级自愈,集群依靠于哨兵和代理的个性,故障切换对用户无感知。

有道架构团队最终以云平台的模式提供中间件能力,用户无需关注基础设施的资源调度与运维,重点关注具体业务场景,助力业务增长。将来,将进一步围绕 Redis 实例动静扩缩容、故障剖析诊断、在线迁徙、混合部署等内容开展摸索。

Redis 容器化后有哪些劣势?

Kubernetes 是一个容器编排零碎,能够自动化容器利用的部署、扩大和治理。Kubernetes 提供了一些根底个性:

部署:部署更快,集群建设无需人工干预。容器部署后可保障每个的 Redis 节点服务失常,节点启动后将由 Operator 继续监测和谐 Redis 集群状态,包含主从关系、集群关系、哨兵监控、故障转移等。

资源隔离:如果所有服务都用同一个集群,批改了 Redis 集群配置的话,很可能会影响到其余的服务。但如果你是每个零碎独立用一个 Redis 群的话,彼此之间互不影响,也不会呈现某一个利用不小心把集群给打挂了,而后造成连锁反应的状况。

故障复原

(1) 实例的重启:容器化后的健康检查能够实现服务主动重启性能;
(2) 网络故障:因宿主机网络故障带来的实例提早高,哨兵可进行主从切换,而为了保障集群的衰弱,将由 Operator 负责同步集群信息。

扩缩容:容器部署可依据 limit 和 request 限度实例的 cpu 和内存,也能够进行扩缩容操作,扩容后的故障复原由 Operator 解决。

节点调整:基于 Operator 对 CRD 资源的继续和谐,可在 Operator 的 Controller 中为每个 Redis 实例进行状态保护,因而,节点调整后带来的主副关系建设、集群 Slots 迁徙等均可主动实现。

数据存储:容器化可挂载 Cephfs、LocalStorage 等多种存储卷。

监控与保护:实例隔离后搭配 Exporter、Prometheus 等监控工具更容易发现问题。

— END —

退出移动版