Skip to content

Commit

Permalink
Merge pull request #68 from spotahome/devops-785-add-persistance
Browse files Browse the repository at this point in the history
Add Persistence to Redis pods
  • Loading branch information
jchanam authored Jul 18, 2018
2 parents 2c2212c + 5f32d56 commit 40666ba
Show file tree
Hide file tree
Showing 14 changed files with 532 additions and 21 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION := 0.3.0
VERSION := 0.4.0

# Name of this service/application
SERVICE_NAME := redis-operator
Expand Down Expand Up @@ -144,6 +144,6 @@ endif

# Generate kubernetes code for types..
.PHONY: update-codegen
update-codegen: build
update-codegen: docker-build
@echo ">> Generating code for Kubernetes CRD types..."
docker run --rm -v $(PWD):/go/src/github.com/spotahome/redis-operator/ $(REPOSITORY)-dev /bin/bash -c '$(UPDATE_CODEGEN_CMD)'
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ helm install --name redisfailover charts/redisoperator
## Usage
Once the operator is deployed inside a Kubernetes cluster, a new API will be accesible, so you'll be able to create, update and delete redisfailovers.

In order to deploy a new redis-failover a [specification](example/redisfailover.yaml) has to be created:
In order to deploy a new redis-failover a [specification](example/redisfailover/all-options.yaml) has to be created:
```
kubectl create -f https://raw.githubusercontent.com/spotahome/redis-operator/master/example/redisfailover.yaml
kubectl create -f https://raw.githubusercontent.com/spotahome/redis-operator/master/redisfailover/all-options.yaml
```

This redis-failover will be managed by the operator, resulting in the following elements created inside Kubernetes:
Expand All @@ -51,6 +51,13 @@ This redis-failover will be managed by the operator, resulting in the following

**NOTE**: `NAME` is the named provided when creating the RedisFailover.

### Persistance
The operator has the ability of add persistance to Redis data. By default an `emptyDir` will be used, so the data is not saved.

In order to have persistance, a PersistentVolumeClaim usage is allowed. The full [PVC definition has to be added](example/redisfailover/persistant-storage.yaml) to the Redis Failover Spec under the `Storage` section.

**IMPORTANT**: By default, the persistent volume claims will be deleted when the Redis Failover is. If this is not the expected usage, a `keepAfterDeletion` flag can be added under the `storage` section of Redis. [An example is given](example/redisfailover/persistant-storage-no-pvc-deletion.yaml).

### Connection
In order to connect to the redis-failover and use it, a [Sentinel-ready](https://redis.io/topics/sentinel-clients) library has to be used. This will connect through the Sentinel service to the Redis node working as a master.
The connection parameters are the following:
Expand Down
9 changes: 8 additions & 1 deletion api/redisfailover/v1alpha2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type RedisSettings struct {
Image string `json:"image,omitempty"`
Version string `json:"version,omitempty"`
ConfigMap string `json:"configMap,omitempty"`
DataVolume corev1.Volume `json:"dataVolume,omitempty"`
Storage RedisStorage `json:"storage,omitempty"`
}

// SentinelSettings defines the specification of the sentinel cluster
Expand All @@ -65,6 +65,13 @@ type CPUAndMem struct {
Memory string `json:"memory"`
}

// RedisStorage defines the structure used to store the Redis Data
type RedisStorage struct {
KeepAfterDeletion bool `json:"keepAfterDeletion,omitempty"`
EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
PersistentVolumeClaim *corev1.PersistentVolumeClaim `json:"persistentVolumeClaim,omitempty"`
}

// RedisFailoverStatus has the status of the cluster
type RedisFailoverStatus struct {
Phase Phase `json:"phase"`
Expand Down
36 changes: 35 additions & 1 deletion api/redisfailover/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ spec:
exporter: false # Optional. False by default. Adds a redis-exporter container to export metrics.
exporterImage: oliver006/redis_exporter # Optional. oliver006/redis_exporter by default.
exporterVersion: v0.11.3 # Optional. v0.11.3 by default.
dataVolume:
name: redis-data
emptyDir: {}
storage:
emptyDir: {} # Optional. emptyDir by default.

File renamed without changes.
21 changes: 21 additions & 0 deletions example/redisfailover/persistant-storage-no-pvc-deletion.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: storage.spotahome.com/v1alpha2
kind: RedisFailover
metadata:
name: redisfailover-persistant-keep
spec:
sentinel:
replicas: 3
redis:
replicas: 3
storage:
keepAfterDeletion: true
persistentVolumeClaim:
metadata:
name: redisfailover-persistant-keep-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

20 changes: 20 additions & 0 deletions example/redisfailover/persistant-storage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: storage.spotahome.com/v1alpha2
kind: RedisFailover
metadata:
name: redisfailover-persistant
spec:
sentinel:
replicas: 3
redis:
replicas: 3
storage:
persistentVolumeClaim:
metadata:
name: redisfailover-persistant-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

69 changes: 57 additions & 12 deletions operator/redisfailover/service/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import (
"github.com/spotahome/redis-operator/operator/redisfailover/util"
)

const (
redisConfigurationVolumeName = "redis-config"
redisStorageVolumeName = "redis-data"
)

func generateSentinelService(rf *redisfailoverv1alpha2.RedisFailover, labels map[string]string, ownerRefs []metav1.OwnerReference) *corev1.Service {
name := GetSentinelName(rf)
namespace := rf.Namespace
Expand Down Expand Up @@ -205,6 +210,16 @@ func generateRedisStatefulSet(rf *redisfailoverv1alpha2.RedisFailover, labels ma
},
}

if rf.Spec.Redis.Storage.PersistentVolumeClaim != nil {
if !rf.Spec.Redis.Storage.KeepAfterDeletion {
// Set an owner reference so the persistent volumes are deleted when the RF is
rf.Spec.Redis.Storage.PersistentVolumeClaim.OwnerReferences = ownerRefs
}
ss.Spec.VolumeClaimTemplates = []corev1.PersistentVolumeClaim{
*rf.Spec.Redis.Storage.PersistentVolumeClaim,
}
}

if rf.Spec.Redis.Exporter {
exporter := createRedisExporterContainer(rf)
ss.Spec.Template.Spec.Containers = append(ss.Spec.Template.Spec.Containers, exporter)
Expand Down Expand Up @@ -523,17 +538,13 @@ func getRedisExporterImage(rf *redisfailoverv1alpha2.RedisFailover) string {
func getRedisVolumeMounts(rf *redisfailoverv1alpha2.RedisFailover) []corev1.VolumeMount {
volumeMounts := []corev1.VolumeMount{
{
Name: "redis-config",
Name: redisConfigurationVolumeName,
MountPath: "/redis",
},
}

// check if data volume is set, if set, mount to /data
if rf.Spec.Redis.DataVolume.Name != "" {
volumeMounts = append(volumeMounts, corev1.VolumeMount{
Name: rf.Spec.Redis.DataVolume.Name,
{
Name: getRedisDataVolumeName(rf),
MountPath: "/data",
})
},
}

return volumeMounts
Expand All @@ -544,7 +555,7 @@ func getRedisVolumes(rf *redisfailoverv1alpha2.RedisFailover) []corev1.Volume {

volumes := []corev1.Volume{
{
Name: "redis-config",
Name: redisConfigurationVolumeName,
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Expand All @@ -555,10 +566,44 @@ func getRedisVolumes(rf *redisfailoverv1alpha2.RedisFailover) []corev1.Volume {
},
}

// check if data volume is set, if not set skip it
if rf.Spec.Redis.DataVolume.Name != "" {
volumes = append(volumes, rf.Spec.Redis.DataVolume)
dataVolume := getRedisDataVolume(rf)
if dataVolume != nil {
volumes = append(volumes, *dataVolume)
}

return volumes
}

func getRedisDataVolume(rf *redisfailoverv1alpha2.RedisFailover) *corev1.Volume {
// This will find the volumed desired by the user. If no volume defined
// an EmptyDir will be used by default
switch {
case rf.Spec.Redis.Storage.PersistentVolumeClaim != nil:
return nil
case rf.Spec.Redis.Storage.EmptyDir != nil:
return &corev1.Volume{
Name: redisStorageVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: rf.Spec.Redis.Storage.EmptyDir,
},
}
default:
return &corev1.Volume{
Name: redisStorageVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
}
}
}

func getRedisDataVolumeName(rf *redisfailoverv1alpha2.RedisFailover) string {
switch {
case rf.Spec.Redis.Storage.PersistentVolumeClaim != nil:
return rf.Spec.Redis.Storage.PersistentVolumeClaim.Name
case rf.Spec.Redis.Storage.EmptyDir != nil:
return redisStorageVolumeName
default:
return redisStorageVolumeName
}
}
Loading

0 comments on commit 40666ba

Please sign in to comment.