Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ redis-cluster-operator 1/1 1 1 1d

#### Deploy a sample Redis Cluster

NOTE: **Only the redis cluster that use persistent storage(pvc) can recover after accidental deletion or rolling update.Even if you do not use persistence(like rdb or aof), you need to set pvc for redis.**

```
$ kubectl apply -f deploy/example/redis.kun_v1alpha1_distributedrediscluster_cr.yaml
```
Expand Down Expand Up @@ -168,7 +170,7 @@ spec:

#### Backup and Restore

**Only Ceph object storage is supported now**
NOTE: **Only Ceph S3 object storage and PVC is supported now**

Backup
```
Expand Down
28 changes: 28 additions & 0 deletions deploy/example/backup-restore/redisclusterbackup_topvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-backup
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
storageClassName: {storageClassName}
volumeMode: Filesystem
---

apiVersion: redis.kun/v1alpha1
kind: RedisClusterBackup
metadata:
name: example-redisclusterbackup
annotations:
redis.kun/scope: cluster-scoped
spec:
image: hub.ucloudadmin.com/uaek/redis-tools:5.0.4
# on same namespace
redisClusterName: test
local:
mountPath: /back
persistentVolumeClaim:
claimName: test-backup
2 changes: 1 addition & 1 deletion deploy/example/backup-restore/restore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ spec:
init:
backupSource:
name: example-redisclusterbackup
namespace: default
namespace: default
38 changes: 38 additions & 0 deletions deploy/example/backup-restore/restore_frompvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
apiVersion: redis.kun/v1alpha1
kind: DistributedRedisCluster
metadata:
name: restore
spec:
clusterReplicas: 1
config:
appendfsync: everysec
appendonly: "yes"
auto-aof-rewrite-min-size: 64mb
auto-aof-rewrite-percentage: "100"
cluster-node-timeout: "5000"
loglevel: verbose
maxclients: "1000"
maxmemory: "0"
notify-keyspace-events: ""
rdbcompression: "yes"
save: 900 1 300 10
stop-writes-on-bgsave-error: "yes"
tcp-keepalive: "0"
timeout: "0"
image: redis:5.0.4-alpine
masterSize: 3
resources:
limits:
cpu: 400m
memory: 300Mi
requests:
cpu: 400m
memory: 300Mi
storage:
class: {storageClassName}
size: 10Gi
type: persistent-claim
init:
backupSource:
name: example-redisclusterbackup
namespace: default
34 changes: 34 additions & 0 deletions doc/design/zh/cluster_backup_and_restore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 备份和恢复

目前只支持备份到 ceph S3对象存储及本地 pvc 中。

备份开始时使用 redis-cli 同步 Master 的 RDB到本地后再使用 [Rclone](https://rclone.org/) 将
RDB 文件传输到对象存储或者 pvc 中,恢复时先使用 Rclone 从之前备份的位置同步备份到本地后,再启动 Redis
服务。备份恢复的工具类镜像中预置了 redis-cli 和 Rclone,参见 [Dockerfile](hack/docker/redis-tools/Dockerfile)。

## 备份

Operator 会 Watch 集群所有的 RedisClusterBackup 实例变化,当用户提交一个备份的 CR 之后,Operator 会:

1. 创建一个 Kubernetes batch job,根据 Redis 集群分布数,在 job 中注入相同数量的 container,每个 container 向一个 Master 发起备份请求,设置开始时间及备份状态。
2. 同步完成 RDB 文件后,将 Redis 集群每个分片的 RDB 文件和 cluster-config-file(记录节点slots信息) 上传到对象存储,同时将 CR 的状态置为 Succeeded,设置完成时间。redis集群备份的快照和节点元数据信息,上传到对象存储后,有统一的路径,当前的规则是:redis/{Namespace}/{RedisClusterName}/{StartTime}/{BackupName}-x
比如一个备份一个在 default 命名空间的名为 redis-cluster-test 的 Redis 集群(集群含有三个 master 节点),备份名为 backup , 备份开始时间为 20191101083020,最后会有如下对象存储路径:

```
redis/default/redis-cluster-test/20191101083020/backup-0
redis/default/redis-cluster-test/20191101083020/backup-1
redis/default/redis-cluster-test/20191101083020/backup-2
```

每个master节点备份的快照和节点元数据信息会存储在上述路径,用户可以到相应的 bucket 中查看。

## 从备份恢复

从备份恢复和创建步骤不同,分为两阶段,第一阶段同步数据,从快照启动 Master 节点;第二阶段启动 Slave 节点。

1. 设置`DistributedRedisCluster.Status.Restore.Phase=Running`,根据备份信息,创建与备份集群切片数相同的 Statefulset,
设置 Replicas 为 1,只启动 master 节点,注入 init container,init container 的作用是拉取对象存储上的快照数据。
2. 等待第1步同步数据完成,master 启动完成后,设置`DistributedRedisCluster.Status.Restore.Phase=Restart`,移除
init container 后等待节点重启。
3. 第2步完成之后,增加每个分片的副本数调大 Statefulset 的 Replicas,拉起 Slave 节点,设置`DistributedRedisCluster.Status.Restore.Phase=Succeeded`,
等待所有 Pod 节点状态变为 Runing 之后,设置每个 Statefulset 的 Slave 节点 replicate Master 节点,加入集群。
39 changes: 39 additions & 0 deletions doc/design/zh/cluster_create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 创建集群

**需要注意的是,只有配置了持久化存储(PVC)的 CR 实例可以故障自愈**

如用户希望创建 3 分片的集群,每个分片一主一从:

```
apiVersion: redis.kun/v1alpha1
kind: DistributedRedisCluster
metadata:
annotations:
# if your operator run as cluster-scoped, add this annotations
redis.kun/scope: cluster-scoped
name: example-distributedrediscluster
spec:
masterSize: 3 # 三分片
clusterReplicas: 1 # 每个主节点一个从节点
image: redis:5.0.4-alpine
storage:
type: persistent-claim
size: 1Gi
class: {StorageClassName}
deleteClaim: true # 删除 Redis Cluster 时,自动清理 pvc
```

Operator Watch 到新的 Redis Cluster CR 实例被创建时,Operator 会执行以下操作:

1. 为每个分片创建一个 Statefulset,每个 Statefulset Name 后缀以 0,1,2... 递增,设置 Statefulset Replicas 为副本数+1(Master),每个 Statefulset 代表着一个分片及其所有副本,所以将创建 3 个 Statefulset,每个 Statefulset 的 Replicas 为 3,每个 Pod 代表一个 Redis 实例。
2. 等待所有 Pod 状态变为 Ready 且每个节点相互识别后,Operator 会在每个 Statefulset 的 Pod 中挑选一个作为 Master 节点,其余节点为该 Master 的 Slave,并尽可能保证所有 Master 节点不在同一个 k8s node。
3. 为 Master 分配 Slots,将 Slave 加入集群,从而组建集群。
4. 为每一个 Statefulset 创建一个 Headless Service,为整个集群创建一个 Service 指向所有的 pod。

## 亲和性和反亲和性

为保证 Redis CLuster 的高可用性,CRD 中设计了 `affinity` 及 `requiredAntiAffinity` (bool) 字段来做 Redis 节点间的打散:

* 当 affinity 和 requiredAntiAffinity 都未设置时,Operator 默认设置 Statefulset 管理的一组 pod 及 所有 pod 尽量反亲和;
* 当用户只设置 requiredAntiAffinity 字段的时,Operator 会设置 Statefulset 管理的一组 pod 强制反亲和,所有 pod 尽量反亲和;
* 当用户设置了 affinity 时,Statefulset 直接继承 affinity,Operator 不做额外设置。
43 changes: 43 additions & 0 deletions doc/design/zh/cluster_scaling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Redis Cluster 横向扩容和缩容

## 扩容

当 Operator Watch 到一个 Redis Cluster 需要扩容时,Operator 会:

1. 新建 Statefulset。
2. 等待新的 Pod 状态变为 Ready 且每个节点相互识别后后从新的 Statefulset 选出 Master 节点,其余为 Slave。
3. Operator 调整 Slot,将其他 Statefulset 的 Master 节点的 Slots 调度到新的 Master,尽可能使其平均分布到所有的 Redis 节点上。

以扩容一个节点为例。
numSlots(迁移节点数): 表示扩容时分配到新节点的 slot 数量
numSlots=16384/集群节点。
集群现有的每个 Master 节点待迁移slot数计算公式为:
待迁移slot数量 * (该源节点负责的slot数量 / slot总数)

当前 Master Slots 分布:
```
Master[0] -> Slots 0 - 5460 slots=5461
Master[1] -> Slots 5461 - 10922 slots=5462
Master[2] -> Slots 10923 - 16383 slots=5461
```
加入节点 Master[3],numSlots=16384/4=4096
那么分配到集群现有每个 Master 节点的待迁移 migratingSlots 数为:
```
Master[0] migratingSlots = 4096 * (5461 / 16384) = 1365.25=1365
Master[1] migratingSlots = 4096 * (5462 / 16384) = 1365.5=1366
Master[2] migratingSlots = 4096 * (5461 / 16384) = 1365.25=1365
```
最终:
```
Master[0] -> Slots 1365-5460 slots=4096
Master[1] -> Slots 6827-10922 slots=4096
Master[2] -> Slots 12288-16383 slots=4096
Master[3] -> Slots 0-1364 5461-6826 10923-12287 slots=4096
```
## 缩容

当 Operator Watch 到一个 Redis Cluster 需要缩容时,Operator 会:

1. 将待删除 Statefulset 的 Master 节点所有的 Slots 迁移到其他 Master。
2. 从后缀名最大的 Statefulset 的开始,将其所有的节点踢出集群。
3. 删除 Statefulset 及相关资源。