> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Replica Deployments Should Have A PodDisruptionBudget

### More Info:

Advisory: define a PodDisruptionBudget for each multi-replica Deployment so node drains and rollouts keep a minimum number of pods available.

### Risk Level

Informational

### Address

Security

### Compliance Standards

* Cloudanix Best Practice

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **List all multi-replica Deployments and their selectors**
           * Run on: any machine with kubectl access
           * Command:
             ```bash theme={null}
             kubectl get deploy -A -o jsonpath='{range .items[?(@.spec.replicas>1)]}{.metadata.namespace}{" "}{.metadata.name}{" "}{.spec.replicas}{" "}{@.spec.selector.matchLabels}{"\n"}{end}'
             ```
           * Review: Identify which multi-replica Deployments are critical (user-facing, stateful backends, control-plane add-ons, etc.).

        2. **List existing PodDisruptionBudgets and their selectors**
           * Run on: any machine with kubectl access
           * Command:
             ```bash theme={null}
             kubectl get pdb -A -o yaml
             ```
           * Review: For each PDB, note `.metadata.namespace`, `.metadata.name`, `.spec.selector.matchLabels`, and either `.spec.minAvailable` or `.spec.maxUnavailable`.

        3. **Map Deployments to PDBs and find gaps**
           * For each multi-replica Deployment from step 1, check if there is a PDB in the same namespace whose `spec.selector.matchLabels` matches the Deployment’s `spec.selector.matchLabels`.
           * If no matching PDB exists, or the PDB selector is broader/narrower than intended, mark that Deployment as needing a new or corrected PDB.

        4. **Design an appropriate availability policy per Deployment**
           * For each marked Deployment, decide:
             * Whether to use `minAvailable` (e.g., `"80%"` or `replicas-1`) to guarantee a minimum number of pods, **or**
             * `maxUnavailable` (e.g., `1`) to allow controlled disruptions.
           * Ensure the chosen value is compatible with the Deployment’s `spec.replicas` (e.g., do not set `minAvailable` equal to `replicas` if you still need to allow voluntary disruptions such as node drains).

        5. **Create or adjust PodDisruptionBudgets**
           * Run on: any machine with kubectl access
           * For a missing PDB, create a manifest like:
             ```yaml theme={null}
             apiVersion: policy/v1
             kind: PodDisruptionBudget
             metadata:
               name: <deployment-name>-pdb
               namespace: <namespace>
             spec:
               minAvailable: 1
               selector:
                 matchLabels:
                   app: <label-from-deployment-selector>
             ```
           * Apply it:
             ```bash theme={null}
             kubectl apply -f <pdb-manifest>.yaml
             ```
           * For an existing but misconfigured PDB, edit and correct it:
             ```bash theme={null}
             kubectl -n <namespace> edit pdb <pdb-name>
             ```
             Adjust `spec.selector.matchLabels` to match the Deployment labels and tune `minAvailable`/`maxUnavailable` per step 4.

        6. **Verify PDB coverage for all multi-replica Deployments**
           * Run on: any machine with kubectl access
           * Command to re-check multi-replica Deployments:
             ```bash theme={null}
             kubectl get deploy -A -o jsonpath='{range .items[?(@.spec.replicas>1)]}{.metadata.namespace}{" "}{.metadata.name}{" "}{.spec.selector.matchLabels}{"\n"}{end}'
             ```
           * For each listed Deployment, confirm there is at least one PDB in the same namespace whose `spec.selector.matchLabels` matches the Deployment’s selector:
             ```bash theme={null}
             kubectl get pdb -n <namespace> -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.spec.selector.matchLabels}{"\n"}{end}'
             ```
           * Optionally, simulate a drain to ensure PDBs are enforced (non-disruptively on a test node/pool):
             ```bash theme={null}
             kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data --dry-run=server
             ```
      </Accordion>

      <Accordion title="Using kubectl">
        ```bash theme={null}
        # 1. List all Deployments and their replica counts
        # Run on: any machine with kubectl access
        kubectl get deploy -A -o custom-columns=NS:metadata.namespace,NAME:metadata.name,REPLICAS:spec.replicas --sort-by=.metadata.namespace
        ```

        * Focus review on Deployments where `REPLICAS` is **2 or more** (single-replica workloads do not benefit from a PDB for disruption tolerance).

        ```bash theme={null}
        # 2. List all PodDisruptionBudgets and the selectors they use
        kubectl get pdb -A -o wide
        ```

        Key columns to review:

        * `NAMESPACE`, `NAME`: where the PDB lives.
        * `MIN AVAILABLE` / `MAX UNAVAILABLE`: ensures enough pods stay up.
        * `SELECTOR`: which pods the PDB matches.

        A problem is indicated when:

        * A namespace has multi-replica Deployments but **no PDBs at all**, or
        * There are PDBs, but their `SELECTOR` labels do **not correspond** to any multi-replica Deployment’s pod labels.

        ```bash theme={null}
        # 3. For each namespace with multi-replica Deployments, compare Deployment labels vs PDB selectors

        # Example: show Deployments with replicas >= 2 in a given namespace
        NAMESPACE=prod
        kubectl get deploy -n "$NAMESPACE" \
          -o jsonpath='{range .items[?(@.spec.replicas>=2)]}{.metadata.name}{" => "}{.spec.selector.matchLabels}{"\n"}{end}'

        # Show PDB selectors in the same namespace
        kubectl get pdb -n "$NAMESPACE" \
          -o jsonpath='{range .items}{.metadata.name}{" => "}{.spec.selector.matchLabels}{"\n"}{end}'
        ```

        A problem is indicated when, for a multi-replica Deployment:

        * There is **no PDB** in the same namespace whose selector `matchLabels` would match that Deployment’s pod labels, or
        * The PDB exists but its `MIN AVAILABLE` / `MAX UNAVAILABLE` values are clearly inconsistent with the desired availability for that workload (this judgment must be made by a human).

        ```bash theme={null}
        # 4. Spot-check a specific multi-replica Deployment against existing PDBs

        NAMESPACE=prod
        DEPLOYMENT=my-app

        # Show Deployment pod selector and replicas
        kubectl get deploy "$DEPLOYMENT" -n "$NAMESPACE" -o yaml | \
          egrep "name: |replicas:|matchLabels:|  app:|  component:"

        # Show PDBs that might match this Deployment (same namespace)
        kubectl get pdb -n "$NAMESPACE" -o yaml | \
          egrep "name: |minAvailable:|maxUnavailable:|matchLabels:|  app:|  component:"
        ```

        A problem is indicated when:

        * The Deployment’s `spec.selector.matchLabels` do not line up with any PDB `spec.selector.matchLabels`, meaning a node drain or rollout could evict all pods, or
        * No PDB exists at all for an application that you consider critical and that runs with multiple replicas.

        ```bash theme={null}
        # 5. Verification after you add or adjust PDBs (human judgment still required)

        # Re-list Deployments and PDBs
        kubectl get deploy -A -o custom-columns=NS:metadata.namespace,NAME:metadata.name,REPLICAS:spec.replicas --sort-by=.metadata.namespace
        kubectl get pdb -A -o wide

        # For each multi-replica Deployment you care about, confirm:
        # - There is at least one PDB in the same namespace
        # - The PDB selector matches the Deployment's pod labels
        # - The minAvailable / maxUnavailable values reflect your availability requirements
        ```
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        # Report multi-replica Deployments that lack an associated PodDisruptionBudget.
        # Run on: any machine with kubectl access and current context set.

        set -euo pipefail

        echo "Collecting multi-replica Deployments (replicas >= 2)..."
        # namespace/name  replicas  matchLabels(JSON)
        kubectl get deploy -A -o json \
          | jq -r '
            .items[]
            | select((.spec.replicas // 1) >= 2)
            | [.metadata.namespace,
               .metadata.name,
               (.spec.replicas // 1),
               (.spec.selector.matchLabels // {})]
            | @tsv' \
          | while IFS=$'\t' read -r ns name replicas matchlabels_json; do
              # Build a label selector from matchLabels
              if [[ "$matchlabels_json" == "{}" ]]; then
                # No matchLabels: skip, PDB matching is ambiguous and must be reviewed manually
                echo -e "WARN\t${ns}\t${name}\t${replicas}\tNO matchLabels in selector (review manually)"
                continue
              fi

              # Convert JSON object of labels to a key=value,key2=value2 selector string
              selector=$(
                jq -r 'to_entries | map("\(.key)=\(.value)") | join(",")' <<<"$matchlabels_json"
              )

              # Look for at least one PDB in the same namespace whose selector matches these labels
              pdb_count=$(kubectl get pdb -n "$ns" -o json \
                | jq --arg sel "$selector" '
                    .items[]
                    | select(.spec.selector != null)
                    | .spec.selector.matchLabels // {}
                    | to_entries
                    | map("\(.key)=\(.value)")
                    | join(",")
                    | select(. == $sel)
                    ' \
                | wc -l | tr -d '[:space:]')

              if [[ "$pdb_count" -eq 0 ]]; then
                echo -e "MISSING_PDB\t${ns}\t${name}\t${replicas}\tselector=${selector}"
              else
                echo -e "OK\t${ns}\t${name}\t${replicas}\tselector=${selector}\tPDBs=${pdb_count}"
              fi
            done
        ```

        Explanation of output (what indicates a problem):

        * Lines starting with `MISSING_PDB`:
          * Example: `MISSING_PDB    my-namespace    web-frontend    3    selector=app=web,role=frontend`
          * Interpretation: this Deployment has `replicas >= 2` and no PodDisruptionBudget with a matching label selector was found in its namespace. This is a candidate that should have a PodDisruptionBudget defined and reviewed.

        * Lines starting with `WARN`:
          * Example: `WARN    my-namespace    legacy-app    4    NO matchLabels in selector (review manually)`
          * Interpretation: the Deployment’s `.spec.selector.matchLabels` is empty; automatic matching to a PDB is not reliable. Manually inspect this Deployment and any PDBs in the same namespace.

        * Lines starting with `OK`:
          * Example: `OK    my-namespace    api-server    5    selector=app=api    PDBs=1`
          * Interpretation: at least one PodDisruptionBudget exists whose selector exactly matches the Deployment’s `matchLabels`. These look compliant but still deserve human review for correct `minAvailable`/`maxUnavailable` values.
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
