Panfactum LogoPanfactum
Infrastructure ModulesSubmodulesKuberneteskube_stateful_set
kube_stateful_set
Stable
Submodule
Source Code Link

Kubernetes StatefulSet

Provides a production-hardened instance of a Kubernetes StatefulSet with the following enhancements:

Usage

Basics

This module follows the basic workload deployment patterns describe in this guide.

Horizontal Autoscaling

By default, this module does not have horizontal autoscaling built-in. If you wish to add horizontal autoscaling via the HPA (or similar controller), you should set ignore_replica_count to true to prevent this module from overriding the replica count set via horizontal autoscaling.

Persistence

One of the core use cases for a StatefulSet is the ability to persistent data across pod restarts through the use of Persistent Volume Claims (PVCs).

You can configure the StatefulSet's PVCs via the volume_mounts input. This input is a map of names (arbitrary) to configuration values for each volume that should mounted to every pod in the StatefulSet.

The configuration values are as follows:

  • storage_class: The Storage Class to use for the volume. To learn more about the available storage class options, see our guide.
  • initial_size_gb: The size of the volume when it is first created.
  • increase_gb: How much the volume will grow every time it is autoscaled by the PVC autoresizer.
  • increase_threshold_percent: When free storage drops below this percent on the volume, the volume will be autoscaled.
  • size_limit_gb: The maximum size the volume is allowed to grow to.
  • mount_path: Absolute path inside each container that the volume is mounted to.
  • backups_enabled: Whether the PVC snapshots will be created when Velero backups are created (by default hourly).

You can configure the retention policy of the volumes through the volume_retention_policy input.

Providers

The following providers are needed by this module:

  • kubectl (2.0.4)

  • kubernetes (2.27.0)

  • pf (0.0.3)

  • random (3.6.0)

  • time (0.10.0)

Required Inputs

The following input variables are required:

containers

Description: A list of container configurations for the pod

Type:

list(object({
    name                    = string                           # A unique name for the container within the pod
    init                    = optional(bool, false)            # Iff true, the container will be an init container
    image_registry          = string                           # The URL for a container image registry (e.g., docker.io)
    image_repository        = string                           # The path to the image repository within the registry (e.g., library/nginx)
    image_tag               = string                           # The tag for a specific image within the repository (e.g., 1.27.1)
    command                 = list(string)                     # The command to be run as the root process inside the container
    working_dir             = optional(string, null)           # The directory the command will be run in. If left null, will default to the working directory set by the image
    image_pull_policy       = optional(string, "IfNotPresent") # Sets the container's ImagePullPolicy
    minimum_memory          = optional(number, 100)            #The minimum amount of memory in megabytes
    maximum_memory          = optional(number, null)           #The maximum amount of memory in megabytes
    memory_limit_multiplier = optional(number, 1.3)            # memory limits = memory request x this value
    minimum_cpu             = optional(number, 10)             # The minimum amount of cpu millicores
    maximum_cpu             = optional(number, null)           # The maximum amount of cpu to allow (in millicores)
    privileged              = optional(bool, false)            # Whether to allow the container to run in privileged mode
    run_as_root             = optional(bool, false)            # Whether to run the container as root
    uid                     = optional(number, 1000)           # user to use when running the container if not root
    linux_capabilities      = optional(list(string), [])       # Default is drop ALL
    read_only               = optional(bool, true)             # Whether to use a readonly file system
    env                     = optional(map(string), {})        # Environment variables specific to the container
    liveness_probe_command  = optional(list(string), null)     # Will run the specified command as the liveness probe if type is exec
    liveness_probe_port     = optional(number, null)           # The number of the port for the liveness_probe
    liveness_probe_type     = optional(string, null)           # Either exec, HTTP, or TCP
    liveness_probe_route    = optional(string, null)           # The route if using HTTP liveness_probes
    liveness_probe_scheme   = optional(string, "HTTP")         # HTTP or HTTPS
    readiness_probe_command = optional(list(string), null)     # Will run the specified command as the ready check probe if type is exec (default to liveness_probe_command)
    readiness_probe_port    = optional(number, null)           # The number of the port for the ready check (default to liveness_probe_port)
    readiness_probe_type    = optional(string, null)           # Either exec, HTTP, or TCP (default to liveness_probe_type)
    readiness_probe_route   = optional(string, null)           # The route if using HTTP ready checks (default to liveness_probe_route)
    readiness_probe_scheme  = optional(string, null)           # Whether to use HTTP or HTTPS (default to liveness_probe_scheme)
    ports = optional(map(object({                              # Keys are the port names, and the values are the port configuration.
      port              = number                               # Port on the backing pods that traffic should be routed to
      service_port      = optional(number, null)               # Port to expose on the service. defaults to port
      protocol          = optional(string, "TCP")              # One of TCP, UDP, or SCTP
      expose_on_service = optional(bool, true)                 # Whether this port should be listed on the StatefulSet's service
    })), {})
  }))

name

Description: The name of this workload

Type: string

namespace

Description: The namespace the cluster is in

Type: string

volume_mounts

Description: A mapping of names to configuration for PersistentVolumeClaims used by the StatefulSet

Type:

map(object({
    storage_class              = optional(string, "ebs-standard-retained")
    access_modes               = optional(list(string), ["ReadWriteOnce"])
    initial_size_gb            = optional(number, 1)    # The initial size of the volume when first created
    size_limit_gb              = optional(number, null) # The maximum number of GB that this volume will scale to
    increase_threshold_percent = optional(number, 20)   # Dropping below this percent of free storage will trigger an automatic increase in storage size
    increase_gb                = optional(number, 1)    # The number of GB to increase the volume by when it needs to scale up
    mount_path                 = string                 # Where in the containers to mount the volume
    backups_enabled            = optional(bool, true)   # True iff velero should make snapshot backups of the volumes
  }))

Optional Inputs

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

arm_nodes_enabled

Description: Whether to allow pods to schedule on arm64 nodes

Type: bool

Default: true

az_anti_affinity_required

Description: Whether to prevent pods from being scheduled on the same availability zone

Type: bool

Default: false

az_spread_preferred

Description: Whether to enable topology spread constraints to spread pods across availability zones (with ScheduleAnyways)

Type: bool

Default: false

az_spread_required

Description: Whether to enable topology spread constraints to spread pods across availability zones (with DoNotSchedule)

Type: bool

Default: true

burstable_nodes_enabled

Description: Whether to allow pods to schedule on burstable nodes

Type: bool

Default: false

common_env

Description: Key pair values of the environment variables for each container

Type: map(string)

Default: {}

common_env_from_config_maps

Description: Environment variables that are sourced from existing Kubernetes ConfigMaps. The keys are the environment variables names and the values are the ConfigMap references.

Type:

map(object({
    config_map_name = string
    key             = string
  }))

Default: {}

common_env_from_secrets

Description: Environment variables that are sourced from existing Kubernetes Secrets. The keys are the environment variables names and the values are the Secret references.

Type:

map(object({
    secret_name = string
    key         = string
  }))

Default: {}

common_secrets

Description: Key pair values of secrets to add to the containers as environment variables

Type: map(string)

Default: {}

config_map_mounts

Description: A mapping of ConfigMap names to their mount configuration in the containers of the Pod

Type:

map(object({
    mount_path = string                # Where in the containers to mount the ConfigMap
    optional   = optional(bool, false) # Whether the pod can launch if this ConfigMap does not exist
  }))

Default: {}

controller_nodes_enabled

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

Type: bool

Default: false

controller_nodes_required

Description: Whether the pods must be scheduled on a controller node

Type: bool

Default: false

dns_policy

Description: The DNS policy for the pods

Type: string

Default: "ClusterFirst"

extra_pod_annotations

Description: Annotations to add to the pods in the Pod

Type: map(string)

Default: {}

extra_pod_labels

Description: Extra pod labels to use

Type: map(string)

Default: {}

extra_tolerations

Description: Extra tolerations to add to the pods

Type:

list(object({
    key      = optional(string)
    operator = string
    value    = optional(string)
    effect   = optional(string)
  }))

Default: []

host_anti_affinity_required

Description: Whether to prefer preventing pods from being scheduled on the same host

Type: bool

Default: true

ignore_replica_count

Description: Whether to ignore changes to the replica count. Useful when implementing horizontal autoscaling.

Type: bool

Default: false

instance_type_anti_affinity_required

Description: Whether to enable anti-affinity to prevent pods from being scheduled on the same instance type

Type: bool

Default: true

max_unavailable

Description: Controls how many pods are allowed to be unavailable in the StatefulSet under the Pod Disruption Budget

Type: number

Default: 1

mount_owner

Description: The ID of the group that owns the mounted volumes

Type: number

Default: 1000

node_image_cached_enabled

Description: Whether to add the container images to the node image cache for faster startup times

Type: bool

Default: false

node_preferences

Description: Node label preferences for the pods

Type: map(object({ weight = number, operator = string, values = list(string) }))

Default: {}

node_requirements

Description: Node label requirements for the pods

Type: map(list(string))

Default: {}

panfactum_scheduler_enabled

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

Type: bool

Default: true

pod_management_policy

Description: The StatefulSets pod management policy

Type: string

Default: "OrderedReady"

pod_version_labels_enabled

Description: Whether to add version labels to the Pod. Useful for ensuring pods do not get recreated on frequent updates.

Type: bool

Default: true

priority_class_name

Description: The priority class to use for pods in the StatefulSet

Type: string

Default: null

pull_through_cache_enabled

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

Type: bool

Default: true

replicas

Description: The desired number of pods in the StatefulSet

Type: number

Default: 1

restart_policy

Description: The pod restart policy

Type: string

Default: "Always"

secret_mounts

Description: A mapping of Secret names to their mount configuration in the containers of the Pod

Type:

map(object({
    mount_path = string                # Where in the containers to mount the Secret
    optional   = optional(bool, false) # Whether the pod can launch if this Secret does not exist
  }))

Default: {}

service_ip

Description: If provided, the StatefulSet's service will be statically bound to this IP address. Must be within the Service IP CIDR range for the cluster.

Type: string

Default: null

service_load_balancer_class

Description: Iff service_type == LoadBalancer, the loadBalancerClass to use.

Type: string

Default: "service.k8s.aws/nlb"

service_name

Description: If provided, the StatefulSet's service will have this name. If not provided, will default to name.

Type: string

Default: null

service_public_domain_names

Description: Iff service_type == LoadBalancer, the public domains names that this service will be accessible from.

Type: list(string)

Default: []

service_type

Description: The type of the StatefulSet's Service.

Type: string

Default: "ClusterIP"

spot_nodes_enabled

Description: Whether to allow pods to schedule on spot nodes

Type: bool

Default: true

termination_grace_period_seconds

Description: The number of seconds to wait for graceful termination before forcing termination

Type: number

Default: 30

tmp_directories

Description: A mapping of temporary directory names (arbitrary) to their configuration

Type:

map(object({
    mount_path = string                # Where in the containers to mount the temporary directories
    size_mb    = optional(number, 100) # The number of MB to allocate for the directory
    node_local = optional(bool, false) # If true, the temporary storage will come from the node rather than a PVC
  }))

Default: {}

unhealthy_pod_eviction_policy

Description: Whether to allow unhealthy pods to be evicted. See https://kubernetes.io/docs/tasks/run-application/configure-pdb/#unhealthy-pod-eviction-policy.

Type: string

Default: "AlwaysAllow"

update_type

Description: The type of update that the StatefulSEt should use

Type: string

Default: "RollingUpdate"

volume_retention_policy

Description: The persistentVolumeClaimRetentionPolicy to use of the StatefulSet

Type:

object({
    when_deleted = optional(string, "Retain")
    when_scaled  = optional(string, "Retain")
  })

Default:

{
  "when_deleted": "Retain",
  "when_scaled": "Retain"
}

vpa_enabled

Description: Whether to enable the vertical pod autoscaler

Type: bool

Default: true

Outputs

The following outputs are exported:

headless_service_name

Description: The name of the headless service where StatefulSet pods are registered

labels

Description: The labels assigned to all resources in this deployment

match_labels

Description: The labels unique to this deployment that can be used to select the pods in this deployment

service_account_name

Description: The service account used for the pods

Usage

No notes