Panfactum LogoPanfactum
Infrastructure ModulesSubmodulesKuberneteskube_redis_sentinel
kube_redis_sentinel
Stable
Submodule
Source Code Link

Redis with Sentinel

This module deploys a highly-available set of Redis nodes.

This is deployed in a single master, many replica configuration. Failover is handled by Redis Sentinel which is also deployed by this module.

Usage

Credentials

1 For in-cluster applications, credentials can be sourced from the following Kubernetes Secrets named in the module's outputs:

  • superuser_creds_secret: Complete access to the database
  • admin_creds_secret: Read and write access to the database (does not include the ability to preform sensitive operations like schema or permission manipulation)
  • reader_creds_secret: Read-only access to the database

Each of the above named Secrets contains the following values:

  • username: The username to use for authentication
  • password: The password to use for authentication

The credentials in each Secret are managed by Vault and rotated automatically before they expire. In the Panfactum Stack, credential rotation will automatically trigger a pod restart for pods that reference the credentials.

The credential lifetime is configured by the vault_credential_lifetime_hours input (defaults to 16 hours). Credentials are rotated 50% of the way through their lifetime. Thus, in the worst-case, credentials that a pod receives are valid for vault_credential_lifetime_hours / 2.

Connecting

The below example show how to connect to the Redis master using dynamically rotated admin credentials by setting various environment variables in our kube_deployment module.

module "redis" {
  source = "github.com/Panfactum/stack.git//packages/infrastructure/kube_redis_sentinel?ref=edge.24-09-12" # pf-update
  ...
}

module "deployment" {
  "github.com/Panfactum/stack.git//packages/infrastructure/kube_pg_deployment?ref=edge.24-09-12" # pf-update
  ...
  common_env_from_secrets = {
    REDIS_USERNAME = {
      secret_name = module.redis.admin_creds_secret
      key = "username"
    }
    REDIS_PASSWORD = {
      secret_name = module.redis.admin_creds_secret
      key = "password"
    }
  }
  common_env = {
    REDIS_HOST = module.redis.redis_master_host
    REDIS_PORT = module.redis.redis_port
  }
}

Persistence

Redis provides two mechanisms for persistence: AOF and RDB. This module uses RDB by default (tuned via redis_save).

Using AOF (whether independently or concurrently with RDB) negates the ability to do partial resynchronizations after restarts and failovers. Instead, a copy of the database must be transferred from the current master to restarted or new replicas. This greatly increases the time-to-recover as well as incurs a high network cost. In fact, there is arguably no benefit to AOF-based persistence at all with our replicated architecture as new Redis nodes will always pull their data from the running master, not from their local AOF. The only benefit would be if all Redis nodes simultaneously failed with a non-graceful shutdown (an incredibly unlikely scenario).

Persistence is always enabled in this module for similar reasons. Without persistence, an entire copy of the database would have to be transferred from the master to each replica on every Redis node restart. The cost of storing data on disk is far less than the network costs associated with this transfer. Moreover, persistence should never impact performance as writes are completed asynchronously.

Disruptions

By default, failovers of Redis pods in this module can be initiated at any time. This enables the cluster to automatically perform maintenance operations such as instance resizing, AZ re-balancing, version upgrades, etc. However, every time a Redis pod is disrupted, a short period of downtime might occur if the disrupted pod is the master instance.

While this can generally be mitigated when using a Sentinel-aware client, you may want to provide more control over when these failovers can occur, so we provide the following options:

Disruption Windows

Disruption windows provide the ability to confine disruptions to specific time intervals (e.g., periods of low load) if this is needed to meet your stability goals. You can enable this feature by setting voluntary_disruption_window_enabled to true.

The disruption windows are scheduled via voluntary_disruption_window_cron_schedule and the length of time of each window via voluntary_disruption_window_seconds.

If you use this feature, we strongly recommend that you allow disruptions at least once per day, and ideally more frequently.

For more information on how this works, see the kube_disruption_window_controller submodule.

Custom PDBs

Rather than time-based disruption windows, you may want more granular control of when disruptions are allowed and disallowed.

You can do this by managing your own PodDisruptionBudgets. This module provides outputs that will allow you to match certain subsets of Redis pods.

For example:

module "redis" {
  source = "github.com/Panfactum/stack.git//packages/infrastructure/kube_redis_sentinel?ref=edge.24-09-12" # pf-update
  ...
}

resource "kubectl_manifest" "pdb" {
  yaml_body = yamlencode({
    apiVersion = "policy/v1"
    kind       = "PodDisruptionBudget"
    metadata = {
      name      = "custom-pdb"
      namespace = module.redis.namespace
    }
    spec = {
      unhealthyPodEvictionPolicy = "AlwaysAllow"
      selector = {
        matchLabels = module.redis.match_labels_master # Selects only the Redis master (writable) pod
      }
      maxUnavailable = 0 # Prevents any disruptions
    }
  })
  force_conflicts   = true
  server_side_apply = true
}

While this example is constructed via IaC, you can also create / destroy these PDBs directly in your application logic via YAML manifests and the Kubernetes API. This would allow you to create a PDB prior to initiating a long-running operation that you do not want disrupted and then delete it upon completion.

Completely Disabling Voluntary Disruptions

Allowing the cluster to periodically initiate failovers of Redis is critical to maintaining system health. However, there are rare cases where you want to override the safe behavior and disable voluntary disruptions altogether. Setting the voluntary_disruptions_enabled to false will set up PDBs that disallow any voluntary disruption of any Redis pod in this module.

This is strongly discouraged. If limiting any and all potential disruptions is of primary importance you should instead:

  • Create a one-hour weekly disruption window to allow some opportunity for automatic maintenance operations
  • Ensure that spot_instances_enabled and burstable_instances_enabled are both set to false
  • Connect with a Sentinel-aware client
  • Set enhanced_ha_enabled to true

Note that the above configuration will significantly increase the costs of running the Redis cluster (2.5-5x) versus more flexible settings. In the vast majority of cases, this is entirely unnecessary, so this should only be used as a last resort.

Extra Redis Configuration

You can add extra Redis configuration flags via the redis_flags module variable.

These flags are passed as commandline arguments to the redis servers. This ensures they will be of the highest precedence.

For more information about passing flags through the commandline and available options, see this documentation.

Providers

The following providers are needed by this module:

  • aws (5.39.1)

  • kubectl (2.0.4)

  • kubernetes (2.27.0)

  • random (3.6.0)

  • vault (3.25.0)

Required Inputs

The following input variables are required:

namespace

Description: The namespace to deploy to the redis instances into

Type: string

Optional Inputs

The following input variables are optional (have default values):

arm_nodes_enabled

Description: Whether the database pods can be scheduled on arm64 nodes

Type: bool

Default: true

burstable_nodes_enabled

Description: Whether the database pods can be scheduled on burstable nodes

Type: bool

Default: false

controller_nodes_enabled

Description: Whether to allow pods to schedule on EKS Node Group nodes (controller nodes)

Type: bool

Default: false

helm_version

Description: The version of the bitnami/redis helm chart to use

Type: string

Default: "19.0.2"

instance_type_spread_required

Description: Whether to enable topology spread constraints to spread pods across instance types (with DoNotSchedule)

Type: bool

Default: true

lfu_cache_enabled

Description: Whether redis will be deployed as an LFU cache

Type: bool

Default: false

minimum_memory_mb

Description: The minimum memory in Mb to use for the redis nodes

Type: number

Default: 25

monitoring_enabled

Description: Whether to allow monitoring CRs to be deployed in the namespace

Type: bool

Default: false

panfactum_scheduler_enabled

Description: Whether to use the Panfactum pod scheduler with enhanced bin-packing

Type: bool

Default: true

persistence_backups_enabled

Description: Whether to enable backups of the Redis durable storage.

Type: bool

Default: true

persistence_size_gb

Description: How many GB to initially allocate for persistent storage (will grow automatically as needed)

Type: number

Default: 1

persistence_storage_increase_gb

Description: The amount of GB to increase storage by if free space drops below the threshold

Type: number

Default: 1

persistence_storage_increase_threshold_percent

Description: Dropping below this percent of free storage will trigger an automatic increase in storage size

Type: number

Default: 20

persistence_storage_limit_gb

Description: The maximum number of gigabytes of storage to provision for each redis node

Type: number

Default: null

pull_through_cache_enabled

Description: Whether to use the ECR pull through cache for the deployed images

Type: bool

Default: false

redis_flags

Description: Extra configuration flags to pass to each redis node

Type: list(string)

Default: []

redis_save

Description: Sets the save option for periodic snapshotting

Type: string

Default: "300 100"

replica_count

Description: The number of redis replicas to deploy

Type: number

Default: 3

spot_nodes_enabled

Description: Whether the database pods can be scheduled on spot nodes

Type: bool

Default: true

vault_credential_lifetime_hours

Description: The lifetime of database credentials generated by Vault

Type: number

Default: 16

voluntary_disruption_window_cron_schedule

Description: The times when disruption windows should start

Type: string

Default: "0 4 * * *"

voluntary_disruption_window_enabled

Description: Whether to confine voluntary disruptions of pods in this module to specific time windows

Type: bool

Default: false

voluntary_disruption_window_seconds

Description: The length of the disruption window in seconds

Type: number

Default: 3600

voluntary_disruptions_enabled

Description: Whether to enable voluntary disruptions of pods in this module.

Type: bool

Default: true

vpa_enabled

Description: Whether the VPA resources should be enabled

Type: bool

Default: true

Outputs

The following outputs are exported:

admin_creds_secret

Description: The name of the Kubernetes Secret holding credentials for the admin role in the Redis database

admin_role

Description: The Vault role used to get admin credentials for the created Redis cluster

master_set

Description: The value for the master set to use when configuring Sentinel-aware Redis clients

match_labels

Description: A label selector that matches all Redis pods in the cluster

match_labels_master

Description: A label selector that matches only the Redis master pod in the cluster

namespace

Description: Kubernetes namespace where module resources are created

reader_creds_secret

Description: The name of the Kubernetes Secret holding credentials for the reader role in the Redis database

reader_role

Description: The Vault role used to get read-only credentials for the created Redis cluster

redis_host

Description: A service address that points to all Redis nodes

redis_host_list

Description: A list of domain names for every Redis pod in the cluster

redis_master_host

Description: A service address that points to only the writable redis master

redis_port

Description: The port that the Redis servers listen on

redis_sentinel_host

Description: A service address that points to the Redis Sentinels

redis_sentinel_port

Description: The port that the Sentinel servers listen on

root_name

Description: The name of the superuser of the database

root_password

Description: The password for root user of the database

superuser_creds_secret

Description: The name of the Kubernetes Secret holding credentials for the superuser role in the Redis database

superuser_role

Description: The Vault role used to get superuser credentials for the created Redis cluster

Usage

No notes