Skip to content

Running Stateless Applications

The most common type of application run on Kubernetes is a stateless application. These applications, such as web servers, APIs, and many microservices, do not store persistent data or session state within the container. They can be stopped, started, and scaled horizontally without any loss of data.

This guide covers the best practices for running stateless applications on the platform using the standard Kubernetes Deployment resource, and how to ensure they remain highly available during planned maintenance.

Using a Deployment for Stateless Workloads

The primary tool for managing a stateless application in Kubernetes is the Deployment. A Deployment provides declarative updates for your application's Pods. You describe the desired state in a Deployment manifest, and the Deployment Controller works to change the actual state to the desired state at a controlled rate.

Deployments are ideal for stateless applications because they manage ReplicaSets, which ensure that a specified number of replica Pods are running at any given time.

Example Deployment Manifest

Here is an example of a Deployment for a stateless application, incorporating platform-specific best practices.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-stateless
  labels:
    app.kubernetes.io/name: hello-stateless
    netic.dk/network-rules-egress: "hello-stateless"
    netic.dk/network-rules-ingress: "hello-stateless"
spec:
  # Run at least 2 replicas to ensure high availability.
  replicas: 2
  revisionHistoryLimit: 5
  selector:
    matchLabels:
      app.kubernetes.io/name: hello-stateless
  strategy:
    # Use rolling updates to perform zero-downtime deployments.
    type: RollingUpdate
    rollingUpdate:
      maxSurge: "25%"
      maxUnavailable: "25%"
  template:
    metadata:
      labels:
        app.kubernetes.io/name: hello-stateless
        # Allows ingress traffic from the shared ingress controller.
        netic.dk/network-ingress: "contour"
    spec:
      containers:
        - name: app
          image: ghcr.io/neticdk/kubernetes-debug-image:v0.0.8 # Replace with your application image
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
          # Set requests and limits to ensure predictable performance.
          resources:
            limits:
              cpu: 100m
              memory: 128Mi
            requests:
              cpu: 100m
              memory: 128Mi
          # Mandatory security settings to reduce attack surface.
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 30001
      # Determines the importance of the pod for scheduling and preemption.
      priorityClassName: "secure-cloud-stack-tenant-namespace-application-non-critical"

Key Configuration Best Practices

  • Replicas: Always run at least replicas: 2 to ensure your application remains available if one pod fails or is rescheduled.
  • Rolling Updates: The RollingUpdate strategy ensures zero-downtime deployments. maxUnavailable controls how many old pods can be removed, while maxSurge controls how many new pods can be added during an update.
  • Resource Requests and Limits:
  • Security Context: Setting a securityContext may be enforced by platform policies. You must configure your pods to run as non-root users (runAsNonRoot: true) and drop all Linux capabilities (capabilities: drop: ALL). This significantly improves the security of your application.
  • Priority Class: The priorityClassName influences the Kubernetes scheduler's decisions. For most applications, the default value of secure-cloud-stack-tenant-namespace-application-non-critical is appropriate.

Ensuring High Availability with PodDisruptionBudget

A Deployment with multiple replicas protects against unexpected failures. However, you also need to protect against planned, voluntary disruptions, such as when a cluster node is drained for maintenance.

A PodDisruptionBudget (PDB) is a Kubernetes resource that limits the number of pods of a given application that can be down simultaneously from voluntary disruptions.

Example PodDisruptionBudget Manifest

This PDB should be deployed alongside your Deployment. It uses a label selector to target the pods managed by the hello-stateless deployment.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: hello-stateless-pdb
spec:
  # Ensures that at least 75% of your desired replicas are available during a node drain.
  # This value should be coordinated with your deployment strategy.
  minAvailable: 75%
  selector:
    matchLabels:
      app.kubernetes.io/name: hello-stateless

Replicas vs. Disruption Budget

It is crucial that your PDB's availability requirements can be met. For example, if you set minAvailable: 2 but your Deployment only has replicas: 2, a node drain will stall because evicting even one pod would violate the budget. Always ensure your replica count is high enough to satisfy the PDB during maintenance.