diff --git a/deploy/crds/k8s_v1alpha1_redis_crd.yaml b/deploy/crds/k8s_v1alpha1_redis_crd.yaml index 5fe143bc..0a32ab53 100644 --- a/deploy/crds/k8s_v1alpha1_redis_crd.yaml +++ b/deploy/crds/k8s_v1alpha1_redis_crd.yaml @@ -71,6 +71,11 @@ spec: image: description: Image is a standard path for a Container image type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer resources: description: Resources describes the compute resource requirements type: object @@ -86,6 +91,11 @@ spec: items: type: object type: array + initContainers: + description: Pod initContainers + items: + type: object + type: array password: properties: secretKeyRef: @@ -104,6 +114,11 @@ spec: image: description: Image is a standard path for a Container image type: string + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer resources: description: Resources describes the compute resource requirements type: object @@ -131,6 +146,11 @@ spec: items: type: object type: array + volumes: + description: Volumes for StatefulSet + items: + type: object + type: array required: - replicas - redis diff --git a/example/k8s_v1alpha1_redis_cr.yaml b/example/k8s_v1alpha1_redis_cr.yaml index 4dc7bf8e..fefb7da0 100644 --- a/example/k8s_v1alpha1_redis_cr.yaml +++ b/example/k8s_v1alpha1_redis_cr.yaml @@ -61,11 +61,6 @@ spec: annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: "true" seccomp.security.alpha.kubernetes.io/pod: runtime/default - securityContext: - runAsUser: 7777777 - runAsGroup: 7777777 - fsGroup: 7777777 - runAsNonRoot: true # dataVolumeClaimTemplate allows to define a persistent volume template for Redis. (optional) # If omitted, emptyDir will be used. @@ -86,6 +81,7 @@ spec: # More info: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.14/#container-v1-core redis: image: redis:5-alpine + initialDelaySeconds: 10 resources: limits: cpu: 100m @@ -96,6 +92,10 @@ spec: drop: - all readOnlyRootFilesystem: true + runAsUser: 7777777 + runAsGroup: 7777777 + fsGroup: 7777777 + runAsNonRoot: true # Redis exporter container definition (optional) exporter: @@ -110,3 +110,29 @@ spec: drop: - all readOnlyRootFilesystem: true + runAsUser: 7777777 + runAsGroup: 7777777 + fsGroup: 7777777 + runAsNonRoot: true + + # To disable THP + volumes: + - name: sys + hostPath: + path: /sys + initContainers: + - name: disable-thp + image: busybox + command: ["sh", "-c"] + args: + - |- + set -e + set -o pipefail + + echo never > /rootfs/sys/kernel/mm/transparent_hugepage/enabled + + grep -q -F [never] /sys/kernel/mm/transparent_hugepage/enabled + volumeMounts: + - name: sys + mountPath: /rootfs/sys + type: Directory diff --git a/go.mod b/go.mod index 9c54c041..062f31f8 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/amaizfinance/redis-operator go 1.13 require ( + github.com/NYTimes/gziphandler v1.0.1 // indirect github.com/cenkalti/backoff/v3 v3.0.0 github.com/go-openapi/spec v0.19.0 github.com/go-redis/redis v6.15.5+incompatible diff --git a/pkg/apis/k8s/v1alpha1/redis_types.go b/pkg/apis/k8s/v1alpha1/redis_types.go index 702e423b..a8b9c2ee 100644 --- a/pkg/apis/k8s/v1alpha1/redis_types.go +++ b/pkg/apis/k8s/v1alpha1/redis_types.go @@ -69,12 +69,17 @@ type RedisSpec struct { PriorityClassName string `json:"priorityClassName,omitempty"` // DataVolumeClaimTemplate for StatefulSet DataVolumeClaimTemplate corev1.PersistentVolumeClaim `json:"dataVolumeClaimTemplate,omitempty"` + // Volumes for StatefulSet + Volumes []corev1.Volume `json:"volumes,omitempty"` // Redis container specification Redis ContainerSpec `json:"redis"` // Exporter container specification Exporter ContainerSpec `json:"exporter,omitempty"` + + // Pod initContainers + InitContainers []corev1.Container `json:"initContainers,omitempty"` } // Password allows to refer to a Secret containing password for Redis @@ -104,6 +109,10 @@ type ContainerSpec struct { Resources corev1.ResourceRequirements `json:"resources,omitempty"` // SecurityContext holds security configuration that will be applied to a container SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` + // Number of seconds after the container has started before liveness probes are initiated. + // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + // +optional + InitialDelaySeconds int32 `json:"initialDelaySeconds,omitempty"` } // RedisStatus contains the observed state of Redis diff --git a/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go index d804c1a0..d3eecd32 100644 --- a/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/k8s/v1alpha1/zz_generated.deepcopy.go @@ -173,8 +173,22 @@ func (in *RedisSpec) DeepCopyInto(out *RedisSpec) { copy(*out, *in) } in.DataVolumeClaimTemplate.DeepCopyInto(&out.DataVolumeClaimTemplate) + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } in.Redis.DeepCopyInto(&out.Redis) in.Exporter.DeepCopyInto(&out.Exporter) + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } diff --git a/pkg/apis/k8s/v1alpha1/zz_generated.openapi.go b/pkg/apis/k8s/v1alpha1/zz_generated.openapi.go index 65844558..3119a55a 100644 --- a/pkg/apis/k8s/v1alpha1/zz_generated.openapi.go +++ b/pkg/apis/k8s/v1alpha1/zz_generated.openapi.go @@ -25,12 +25,12 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "./pkg/apis/k8s/v1alpha1.ContainerSpec": schema_pkg_apis_k8s_v1alpha1_ContainerSpec(ref), - "./pkg/apis/k8s/v1alpha1.Password": schema_pkg_apis_k8s_v1alpha1_Password(ref), - "./pkg/apis/k8s/v1alpha1.Redis": schema_pkg_apis_k8s_v1alpha1_Redis(ref), - "./pkg/apis/k8s/v1alpha1.RedisList": schema_pkg_apis_k8s_v1alpha1_RedisList(ref), - "./pkg/apis/k8s/v1alpha1.RedisSpec": schema_pkg_apis_k8s_v1alpha1_RedisSpec(ref), - "./pkg/apis/k8s/v1alpha1.RedisStatus": schema_pkg_apis_k8s_v1alpha1_RedisStatus(ref), + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.ContainerSpec": schema_pkg_apis_k8s_v1alpha1_ContainerSpec(ref), + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.Password": schema_pkg_apis_k8s_v1alpha1_Password(ref), + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.Redis": schema_pkg_apis_k8s_v1alpha1_Redis(ref), + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisList": schema_pkg_apis_k8s_v1alpha1_RedisList(ref), + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisSpec": schema_pkg_apis_k8s_v1alpha1_RedisSpec(ref), + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisStatus": schema_pkg_apis_k8s_v1alpha1_RedisStatus(ref), } } @@ -59,6 +59,13 @@ func schema_pkg_apis_k8s_v1alpha1_ContainerSpec(ref common.ReferenceCallback) co Ref: ref("k8s.io/api/core/v1.SecurityContext"), }, }, + "initialDelaySeconds": { + SchemaProps: spec.SchemaProps{ + Description: "Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes", + Type: []string{"integer"}, + Format: "int32", + }, + }, }, Required: []string{"image"}, }, @@ -117,12 +124,12 @@ func schema_pkg_apis_k8s_v1alpha1_Redis(ref common.ReferenceCallback) common.Ope }, "spec": { SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/k8s/v1alpha1.RedisSpec"), + Ref: ref("github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisSpec"), }, }, "status": { SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/k8s/v1alpha1.RedisStatus"), + Ref: ref("github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisStatus"), }, }, }, @@ -130,7 +137,7 @@ func schema_pkg_apis_k8s_v1alpha1_Redis(ref common.ReferenceCallback) common.Ope }, }, Dependencies: []string{ - "./pkg/apis/k8s/v1alpha1.RedisSpec", "./pkg/apis/k8s/v1alpha1.RedisStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisSpec", "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.RedisStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } @@ -161,7 +168,7 @@ func schema_pkg_apis_k8s_v1alpha1_RedisList(ref common.ReferenceCallback) common Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/k8s/v1alpha1.Redis"), + Ref: ref("github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.Redis"), }, }, }, @@ -172,7 +179,7 @@ func schema_pkg_apis_k8s_v1alpha1_RedisList(ref common.ReferenceCallback) common }, }, Dependencies: []string{ - "./pkg/apis/k8s/v1alpha1.Redis"}, + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.Redis"}, } } @@ -205,7 +212,7 @@ func schema_pkg_apis_k8s_v1alpha1_RedisSpec(ref common.ReferenceCallback) common }, "password": { SchemaProps: spec.SchemaProps{ - Ref: ref("./pkg/apis/k8s/v1alpha1.Password"), + Ref: ref("github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.Password"), }, }, "annotations": { @@ -280,16 +287,42 @@ func schema_pkg_apis_k8s_v1alpha1_RedisSpec(ref common.ReferenceCallback) common Ref: ref("k8s.io/api/core/v1.PersistentVolumeClaim"), }, }, + "volumes": { + SchemaProps: spec.SchemaProps{ + Description: "Volumes for StatefulSet", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, "redis": { SchemaProps: spec.SchemaProps{ Description: "Redis container specification", - Ref: ref("./pkg/apis/k8s/v1alpha1.ContainerSpec"), + Ref: ref("github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.ContainerSpec"), }, }, "exporter": { SchemaProps: spec.SchemaProps{ Description: "Exporter container specification", - Ref: ref("./pkg/apis/k8s/v1alpha1.ContainerSpec"), + Ref: ref("github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.ContainerSpec"), + }, + }, + "initContainers": { + SchemaProps: spec.SchemaProps{ + Description: "Pod initContainers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Container"), + }, + }, + }, }, }, }, @@ -297,7 +330,7 @@ func schema_pkg_apis_k8s_v1alpha1_RedisSpec(ref common.ReferenceCallback) common }, }, Dependencies: []string{ - "./pkg/apis/k8s/v1alpha1.ContainerSpec", "./pkg/apis/k8s/v1alpha1.Password", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PersistentVolumeClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration"}, + "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.ContainerSpec", "github.com/amaizfinance/redis-operator/pkg/apis/k8s/v1alpha1.Password", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Container", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PersistentVolumeClaim", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume"}, } } diff --git a/pkg/controller/redis/object_generator.go b/pkg/controller/redis/object_generator.go index 8b55506c..93b15108 100644 --- a/pkg/controller/redis/object_generator.go +++ b/pkg/controller/redis/object_generator.go @@ -263,6 +263,11 @@ func generateStatefulSet(r *k8sv1alpha1.Redis, password string) *appsv1.Stateful }, }} + // append external volumes + if r.Spec.Volumes != nil { + volumes = append(volumes, r.Spec.Volumes...) + } + // redis container goes first containers := []corev1.Container{{ Name: redisName, @@ -276,8 +281,14 @@ func generateStatefulSet(r *k8sv1alpha1.Redis, password string) *appsv1.Stateful MountPath: configMapMountPath, SubPath: configFileName, }}, - LivenessProbe: &corev1.Probe{Handler: corev1.Handler{Exec: &corev1.ExecAction{Command: []string{"redis-cli", "ping"}}}}, - ReadinessProbe: &corev1.Probe{Handler: corev1.Handler{Exec: &corev1.ExecAction{Command: []string{"redis-cli", "ping"}}}}, + LivenessProbe: &corev1.Probe{ + Handler: corev1.Handler{Exec: &corev1.ExecAction{Command: []string{"redis-cli", "ping"}}}, + InitialDelaySeconds: r.Spec.Redis.InitialDelaySeconds, + }, + ReadinessProbe: &corev1.Probe{ + Handler: corev1.Handler{Exec: &corev1.ExecAction{Command: []string{"redis-cli", "ping"}}}, + InitialDelaySeconds: r.Spec.Redis.InitialDelaySeconds, + }, ImagePullPolicy: corev1.PullAlways, SecurityContext: r.Spec.Redis.SecurityContext, }} @@ -382,6 +393,7 @@ func generateStatefulSet(r *k8sv1alpha1.Redis, password string) *appsv1.Stateful Spec: corev1.PodSpec{ Volumes: volumes, Containers: containers, + InitContainers: r.Spec.InitContainers, ServiceAccountName: r.Spec.ServiceAccountName, SecurityContext: r.Spec.SecurityContext, ImagePullSecrets: r.Spec.ImagePullSecrets,