Build a self-hosted Redis server K8S RHEL

Building a self-hosted Redis server on a Kubernetes cluster, especially on Red Hat servers, involves several key steps. We’ll cover deploying a single-instance Redis with persistence, and then discuss high-availability options and Red Hat/OpenShift specific considerations.

This guide assumes you already have a Kubernetes cluster up and running on your Red Hat servers (e.g., using kubeadm, OpenShift Container Platform, or other methods).

Part 1: Prerequisites

  1. Kubernetes Cluster: A functional Kubernetes cluster (v1.18+ recommended).
  2. kubectl: Configured to connect to your cluster.
  3. StorageClass: A default StorageClass configured in your cluster for dynamic PersistentVolume provisioning. If not, you’ll need to manually create PersistentVolume objects. Common storage solutions for Red Hat environments include:
    • Ceph/Rook: A robust, open-source distributed storage system often integrated with OpenShift.
    • NFS: Network File System, simple to set up but less robust for production K8s.
    • Local Persistent Volumes: Using disk space directly on the node, but requires careful management for HA.
    • Cloud Provider Storage: If your RHEL VMs are on a cloud (AWS EBS, Azure Disk, GCP Persistent Disk).
  4. Red Hat Specifics (for OpenShift): If you’re using OpenShift, you’ll need to be aware of Security Context Constraints (SCCs). The default restricted SCC often prevents pods from running as root or using certain host paths, which can impact deployments if not configured correctly. We’ll address this.

Part 2: Deploying a Single-Instance Redis with Persistence

For a single, stateful application like Redis, a StatefulSet is generally preferred over a Deployment because it provides stable network identifiers, ordered graceful scaling, and persistent storage via volumeClaimTemplates.

We’ll use:

  • Namespace: To isolate our Redis resources.
  • ConfigMap: To store the Redis configuration file (redis.conf).
  • Secret: To store sensitive information like the Redis password.
  • StatefulSet: To manage the Redis pod(s) and their persistent storage.
  • Service: To provide a stable network endpoint for other applications to connect to Redis.

Step 1: Create a Namespace

It’s good practice to isolate your applications in their own namespaces.

# redis-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: redis-production

Apply it:

kubectl apply -f redis-namespace.yaml

Step 2: Create a ConfigMap for Redis Configuration

This allows you to customize redis.conf. For a basic setup, appendonly yes is crucial for data persistence.

# redis-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
  namespace: redis-production
data:
  redis.conf: |
    # Basic Redis configuration
    port 6379
    bind 0.0.0.0
    
    # Enable AOF persistence for data durability
    appendonly yes
    appendfsync everysec
    
    # Set a maximum memory usage (adjust as needed)
    # This prevents Redis from consuming all available memory.
    # When maxmemory is reached, Redis will start evicting keys based on the policy.
    maxmemory 2gb # Example: 2GB. Adjust based on your data and server capacity.
    maxmemory-policy allkeys-lru # Least Recently Used eviction policy
    
    # RDB persistence (snapshotting)
    save 900 1
    save 300 10
    save 60 10000
    
    # Set log level to verbose
    loglevel verbose
    
    # Disable protected mode (only if bind 0.0.0.0 is used and firewalls are in place)
    # For production, ensure network policies are strict.
    protected-mode no

Apply it:

kubectl apply -f redis-configmap.yaml

Step 3: Create a Secret for the Redis Password

Important: Never hardcode passwords in your YAML files directly. Use Kubernetes Secrets.

# redis-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: redis-password
  namespace: redis-production
type: Opaque
stringData:
  password: your_secure_redis_password_here # <<-- CHANGE THIS TO A STRONG PASSWORD

Apply it:

kubectl apply -f redis-secret.yaml

Step 4: Create the Redis StatefulSet

This defines the Redis pod, its persistent storage, and how it uses the ConfigMap and Secret.

# redis-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: redis-production
spec:
  serviceName: "redis-service" # This must match the Service's name below
  replicas: 1 # Start with 1 replica for a single instance
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:6.2.7-alpine # Use a specific, stable version. Consider Red Hat UBI images for OpenShift.
        imagePullPolicy: IfNotPresent
        command: ["redis-server"]
        args:
          - "/etc/redis/redis.conf" # Mount our config file
          - "--requirepass"
          - "$(REDIS_PASSWORD)" # Use the password from the Secret
        env:
        - name: REDIS_PASSWORD
          valueFrom:
            secretKeyRef:
              name: redis-password
              key: password
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - name: redis-config # Mount the ConfigMap as a volume
          mountPath: /etc/redis
        - name: redis-data # Mount the persistent volume for data
          mountPath: /data
        livenessProbe:
          exec:
            command:
            - redis-cli
            - -a
            - "$(REDIS_PASSWORD)"
            - ping
          initialDelaySeconds: 15
          timeoutSeconds: 5
          periodSeconds: 10
          successThreshold: 1
          failureThreshold: 3
        readinessProbe:
          exec:
            command:
            - redis-cli
            - -a
            - "$(REDIS_PASSWORD)"
            - ping
          initialDelaySeconds: 5
          timeoutSeconds: 3
          periodSeconds: 5
          successThreshold: 1
          failureThreshold: 3
      volumes:
      - name: redis-config
        configMap:
          name: redis-config
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: standard # <<-- CHANGE THIS to your cluster's default or desired StorageClass
      resources:
        requests:
          storage: 5Gi # <<-- CHANGE THIS to your desired storage size

Important Notes for redis-statefulset.yaml:

  • image: For production, use a specific, stable tag (e.g., redis:6.2.7-alpine). On OpenShift, you might prefer a Red Hat Universal Base Image (UBI) based image if available for Redis, or ensure the base image is compatible with OpenShift’s SCCs.
  • storageClassName: This must match a StorageClass defined in your Kubernetes cluster. Replace standard with the name of your desired StorageClass. If you don’t have one, you’ll need to provision PersistentVolume objects manually and remove storageClassName from volumeClaimTemplates.
  • resources.requests.storage: Adjust the storage size (5Gi) according to your expected data volume.
  • livenessProbe / readinessProbe: These are crucial for Kubernetes to manage your Redis instance effectively. They check if Redis is running and ready to accept connections. Make sure they use the password from the environment variable.

Apply it:

kubectl apply -f redis-statefulset.yaml

Step 5: Create a Service for Redis

This Service provides a stable internal DNS name (redis-service.redis-production.svc.cluster.local) and IP address for other applications within the cluster to connect to Redis.

# redis-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-service
  namespace: redis-production
spec:
  selector:
    app: redis # Matches the label on our StatefulSet's pods
  ports:
  - protocol: TCP
    port: 6379
    targetPort: 6379
  clusterIP: None # headless service for statefulset. If you need a stable ClusterIP for a single instance, remove this line.

Note on clusterIP: None: For a StatefulSet with a single replica, clusterIP: None (a headless service) allows direct access to the pod via its hostname (redis-0.redis-service.redis-production.svc.cluster.local). If you prefer a single stable IP for a single Redis instance, remove the clusterIP: None line, and Kubernetes will assign a ClusterIP. For HA setups, headless services are often preferred for direct pod access.

Apply it:

kubectl apply -f redis-service.yaml

Step 6: Verify the Deployment

Check the status of your resources:

kubectl get all -n redis-production
kubectl get pvc -n redis-production
kubectl describe pod redis-0 -n redis-production
kubectl logs redis-0 -n redis-production

You should see a pod named redis-0 running, a PersistentVolumeClaim named redis-data-redis-0 bound, and the Redis logs indicating it started successfully.


Step 7: Access Redis

From within the cluster (e.g., another pod):

Your application can connect to Redis using the service name: redis-service.redis-production.svc.cluster.local:6379 (or redis-0.redis-service.redis-production.svc.cluster.local:6379 if using the headless service and accessing the specific pod). Remember to use the password defined in your secret.

Example redis-cli access from a temporary pod:

# Get the password
REDIS_PASSWORD=$(kubectl get secret redis-password -n redis-production -o jsonpath='{.data.password}' | base64 --decode)

# Create a temporary busybox pod to test connection
kubectl run -it --rm busybox --image=busybox --namespace redis-production -- /bin/sh
/ # wget -qO- https://download.redis.io/redis-stable/src/redis-cli.c | gcc -xc - -o /usr/bin/redis-cli # compile redis-cli (might need more libs)
# A simpler way is to use a redis-cli image or connect to the Redis pod directly
# Exit busybox, then connect using kubectl exec:
exit

# Connect to redis-cli in the running Redis pod
kubectl exec -it redis-0 -n redis-production -- redis-cli -a "$REDIS_PASSWORD"

# Inside redis-cli:
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set mykey "hello redis"
OK
127.0.0.1:6379> get mykey
"hello redis"
127.0.0.1:6379> quit

From outside the cluster (for testing/debugging, not recommended for production):

kubectl port-forward svc/redis-service 6379:6379 -n redis-production

Then, from your local machine, use redis-cli:

redis-cli -h 127.0.0.1 -p 6379 -a "your_secure_redis_password_here"

Warning: Exposing Redis directly outside the cluster should generally be avoided for production environments due to security risks. If absolutely necessary, consider a NodePort or LoadBalancer service type with strict network policies and firewall rules.

Part 3: High Availability (HA) Options

For production, a single Redis instance is a single point of failure. Kubernetes helps with process restarts, but not data replication or automatic failover. Here are common HA strategies for Redis:

  1. Redis Sentinel:

    • A system that monitors Redis master and replica instances.
    • Automatically promotes a replica to master if the current master fails.
    • Requires at least 3 Redis instances (1 master, 2 replicas) and 3 Sentinel instances (on different nodes).
    • More complex to set up manually with YAML, often deployed using Helm charts.
  2. Redis Cluster:

    • Provides sharding (data is partitioned across multiple master nodes) and replication.
    • No need for Sentinels as failover is handled by the cluster itself.
    • Requires a minimum of 3 master nodes (and typically 1 replica per master for HA, so 6 nodes total).
    • Even more complex to set up manually; Helm charts are almost essential here.

Recommendation for HA: Use a Helm Chart

For Redis HA (Sentinel or Cluster), manually writing and maintaining all the Kubernetes YAML can be extremely challenging and error-prone. It’s highly recommended to use a well-maintained Helm chart.

The official Bitnami Redis chart is a popular and robust choice:

Example Helm Installation (for Sentinel HA):

# Add the Bitnami Helm repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# Install Redis with Sentinel (adjust values as needed)
helm install my-redis-ha bitnami/redis -n redis-production \
  --set architecture="replication" \
  --set master.persistence.enabled=true \
  --set master.persistence.storageClass="standard" \
  --set master.persistence.size="10Gi" \
  --set replica.replicaCount=2 \
  --set replica.persistence.enabled=true \
  --set replica.persistence.storageClass="standard" \
  --set replica.persistence.size="10Gi" \
  --set sentinel.enabled=true \
  --set sentinel.replicaCount=3 \
  --set auth.password="your_secure_redis_password_here" \
  --set metrics.enabled=true # Enable Prometheus metrics if you have monitoring

This example creates a master, two replicas, and three Sentinels, providing robust HA.

Part 4: Red Hat / OpenShift Specific Considerations

If your “Red Hat server” implies using OpenShift Container Platform, there are a few additional points:

  1. Security Context Constraints (SCCs):

    • OpenShift’s default restricted SCC is stricter than vanilla Kubernetes. Pods often run with an arbitrarily assigned user ID, and cannot run as root (UID 0).
    • The Redis image typically runs as root (UID 0) by default to manage file permissions in /data. This might cause permission issues with volumeMounts on OpenShift.
    • Solution 1 (Recommended): Use a Redis image that explicitly supports running as a non-root user and specify a securityContext in your StatefulSet’s spec.template.spec:
      # ... inside spec.template.spec
      securityContext:
        fsGroup: 1000 # Example: Assign group 1000 to the volume.
        runAsUser: 1000 # Example: Run the container as user 1000.
      containers:
      - name: redis
        image: redis:6.2.7-alpine # Some images support non-root, some don't. Test thoroughly.
        # ... other container settings
        securityContext:
          allowPrivilegeEscalation: false # Good practice
          capabilities:
            drop: ["ALL"]
      
      You’ll need to ensure the Redis image’s entrypoint can handle this, and that /data directory has appropriate permissions for user 1000.
    • Solution 2 (Less Ideal, but common): If a non-root image isn’t readily available or causes issues, you might need to relax the SCC for your Redis namespace/service account.
      # Grant the 'anyuid' SCC to the default service account in your namespace
      oc adm policy add-scc-to-user anyuid -z default -n redis-production
      
      Warning: Granting anyuid is less secure as it allows pods to run as any user ID, including root. Only do this if strictly necessary and understand the implications.
  2. Image Streams and Red Hat UBI:

    • OpenShift has ImageStreams for managing container images. You can import external images or build your own based on Red Hat’s Universal Base Image (UBI) for better compatibility and support.
    • Using a redis image from a trusted source, possibly built on UBI, is a good practice on OpenShift.
  3. oc vs kubectl:

    • OpenShift uses the oc command-line tool, which is a superset of kubectl. Most kubectl commands work, but oc provides additional OpenShift-specific features.
  4. Storage Providers:

    • OpenShift Container Storage (OCS) / Rook-Ceph: This is the native, highly recommended persistent storage solution for OpenShift. If you have OCS deployed, you’d use its StorageClass (e.g., ocs-storagecluster-cephfs or ocs-storagecluster-ceph-rbd).

Part 5: Monitoring and Backups

  • Monitoring: Integrate with Prometheus and Grafana. The Redis Helm chart (Bitnami) can enable Prometheus metrics scraping.
  • Backups: For production, regularly back up your Redis data.
    • Utilize Redis’s built-in RDB snapshots (SAVE command, configured in redis.conf).
    • Periodically copy the /data directory (containing dump.rdb and appendonly.aof) from your PersistentVolume to an external object storage (S3, Azure Blob, etc.). This might involve a sidecar container or a separate cron job pod that mounts the PVC.
    • Consider a tool like Velero for Kubernetes native backups, which can back up PVs.

By following these steps, you can successfully deploy a self-hosted, persistent Redis server on your Kubernetes cluster running on Red Hat servers, with considerations for security and future scalability. Remember to adjust resource requests, storage sizes, and passwords to fit your specific environment and needs.

Leave a Comment

Your email address will not be published. Required fields are marked *


The reCAPTCHA verification period has expired. Please reload the page.

Scroll to Top