Containers Should Set CPU And Memory Requests
More Info:
Verifies every container sets resources.requests so the scheduler can place the pod correctly and QoS is not BestEffort.
Risk Level
Low
Address
Security
Compliance Standards
- Cloudanix Best Practice
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify non-compliant pods (run on any machine with kubectl access):
kubectl get pods --all-namespaces -o json | jq -r '[ .items[]| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (.spec.containers // [])[]| ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok| select($ok | not)| "ns=\($m.namespace) pod=\($m.name) container=\(.name)"][]' -
For a non-compliant pod owned by a higher-level controller (Deployment/StatefulSet/DaemonSet), edit the controller so all containers set CPU and memory requests (run on any machine with kubectl access). Example for a Deployment:
kubectl -n <namespace> edit deployment <deployment-name>In each container under
spec.template.spec.containers[], add or update:resources:requests:cpu: "100m"memory: "128Mi"Choose values appropriate for your workload.
-
For a non-compliant pod created directly (no controller owner), edit the Pod spec (run on any machine with kubectl access). Note this will delete and recreate the Pod:
kubectl -n <namespace> get pod <pod-name> -o yaml > /tmp/pod-<namespace>-<pod-name>.yaml# Edit the filevi /tmp/pod-<namespace>-<pod-name>.yamlUnder each
spec.containers[], add or update:resources:requests:cpu: "100m"memory: "128Mi"Then recreate:
kubectl -n <namespace> delete pod <pod-name>kubectl -n <namespace> apply -f /tmp/pod-<namespace>-<pod-name>.yaml -
For workloads managed via manifests or GitOps, update the source YAML so changes persist (run on your manifest/IaC repository, then apply from any machine with kubectl access):
# edit your deployment/statefulset/daemonset yaml in the repo# ensure each container has:resources:requests:cpu: "100m"memory: "128Mi"Then apply:
kubectl apply -f <path-to-updated-manifest>.yaml -
Wait for updated workloads to roll out and pods to be recreated (run on any machine with kubectl access):
kubectl -n <namespace> rollout status deployment/<deployment-name>kubectl -n <namespace> get pods -o wide -
Verify compliance (run on any machine with kubectl access):
kubectl get pods --all-namespaces -o json | jq -r '[ .items[]| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)| .metadata as $m| (.spec.nodeName // "") as $node| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own| (.spec.containers // [])[]| ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)+ (if $node == "" then "" else " node=\($node)" end)+ (if $labels == "" then "" else " labels=\($labels)" end)+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)+ " container=\(.name) image=\(.image)"+ " requestsCpu=\(.resources.requests.cpu // "unset") requestsMemory=\(.resources.requests.memory // "unset")"+ " is_compliant=\(if $ok then "true" else "false" end)"] as $rows| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'Ensure either the output is
is_compliant=trueor every listed container showsis_compliant=true.
Using kubectl
On any machine with kubectl access:
- Identify non‑compliant pods and their controllers
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| (.spec.containers // [])[]
| ((.resources.requests.cpu == null) or (.resources.requests.memory == null)) as $bad
| select($bad)
| "ns=\($m.namespace) pod=\($m.name) ownerKind=\($own.kind // "Pod") ownerName=\($own.name // $m.name)"
][]'
Focus on the ownerKind/ownerName (Deployment, StatefulSet, Job, etc.). Edit the controller, not the live pod.
- Edit a controller to add requests (example: Deployment)
kubectl -n <namespace> get deploy <deployment-name> -o yaml > /tmp/deploy-with-requests.yaml
Edit /tmp/deploy-with-requests.yaml and, for each container under spec.template.spec.containers, add a resources.requests block, for example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: <deployment-name>
namespace: <namespace>
spec:
template:
spec:
containers:
- name: <container-name>
image: <image>
resources:
requests:
cpu: "100m"
memory: "128Mi"
# optional but recommended
# limits:
# cpu: "500m"
# memory: "512Mi"
Apply the updated manifest:
kubectl apply -f /tmp/deploy-with-requests.yaml
This will roll pods for that Deployment as the template changes.
- Edit other owner types similarly (examples)
StatefulSet:
kubectl -n <namespace> get statefulset <sts-name> -o yaml > /tmp/sts-with-requests.yaml
# edit containers[].resources.requests as above
kubectl apply -f /tmp/sts-with-requests.yaml
DaemonSet:
kubectl -n <namespace> get daemonset <ds-name> -o yaml > /tmp/ds-with-requests.yaml
# edit containers[].resources.requests as above
kubectl apply -f /tmp/ds-with-requests.yaml
CronJob (template is nested):
kubectl -n <namespace> get cronjob <cronjob-name> -o yaml > /tmp/cronjob-with-requests.yaml
Edit:
spec:
jobTemplate:
spec:
template:
spec:
containers:
- name: <container-name>
resources:
requests:
cpu: "100m"
memory: "128Mi"
Apply:
kubectl apply -f /tmp/cronjob-with-requests.yaml
For standalone Pods created directly (ownerKind=Pod), either:
-
Edit in place (ephemeral; lost if pod is recreated by external system):
kubectl -n <namespace> get pod <pod-name> -o yaml > /tmp/pod-with-requests.yaml# edit containers[].resources.requestskubectl delete pod -n <namespace> <pod-name>kubectl apply -f /tmp/pod-with-requests.yaml -
Or better, manage them via a controller manifest going forward.
Operational impact: changing a controller’s pod template will trigger a rollout and recreate pods; ensure this is acceptable and coordinate if needed.
- Verification
Run the benchmark audit command again from any machine with kubectl access:
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (.spec.nodeName // "") as $node
| (($m.labels // {}) | to_entries | map("\(.key):\(.value)") | join(",")) as $labels
| ([ ($m.ownerReferences // [])[] | select(.controller) ] | first) as $own
| (.spec.containers // [])[]
| ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
| "kind=Pod ns=\($m.namespace) name=\($m.name) uid=\($m.uid) apiVersion=v1"
+ (if ($m.creationTimestamp // "") == "" then "" else " created=\($m.creationTimestamp)" end)
+ (if $node == "" then "" else " node=\($node)" end)
+ (if $labels == "" then "" else " labels=\($labels)" end)
+ (if $own == null then "" else " owner=\($own.kind)/\($m.namespace)/\($own.name)/\($own.uid)" end)
+ " container=\(.name) image=\(.image)"
+ " requestsCpu=\(.resources.requests.cpu // "unset") requestsMemory=\(.resources.requests.memory // "unset")"
+ " is_compliant=\(if $ok then "true" else "false" end)"
] as $rows
| if ($rows | length) == 0 then "is_compliant=true" else $rows[] end'
Automation
#!/usr/bin/env bash
#
# Ensure every non-system Pod container has CPU and memory requests set.
# Platform: Oracle OKE (or any standard Kubernetes cluster)
#
# Behavior:
# - Skips kube-system, kube-public, kube-node-lease
# - Patches only Pods/containers where requests.cpu or requests.memory is unset
# - Default requests: cpu=100m, memory=128Mi (adjust below if needed)
# - Idempotent: safe to re-run
#
# Requirements:
# - Run on any machine with kubectl and jq installed and access to the cluster
# - kubectl must be configured with appropriate permissions
set -euo pipefail
DEFAULT_CPU_REQUEST="100m"
DEFAULT_MEM_REQUEST="128Mi"
echo "Discovering non-compliant Pods/containers..."
NON_COMPLIANT_JSON=$(kubectl get pods --all-namespaces -o json | jq -c '
.items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| {
ns: .metadata.namespace,
name: .metadata.name,
containers: (
[ .spec.containers[]
| {
name,
hasCpu: (.resources.requests.cpu != null),
hasMem: (.resources.requests.memory != null)
}
| select(.hasCpu == false or .hasMem == false)
]
)
}
| select(.containers | length > 0)
')
if [ -z "$NON_COMPLIANT_JSON" ]; then
echo "No non-compliant Pods found."
else
echo "Patching non-compliant Pods to set CPU and memory requests..."
fi
# Iterate over each non-compliant Pod
echo "$NON_COMPLIANT_JSON" | jq -c '.' | while read -r POD; do
NS=$(echo "$POD" | jq -r '.ns')
NAME=$(echo "$POD" | jq -r '.name')
echo "Processing Pod ${NS}/${NAME}..."
# Build a strategic merge patch for this Pod
# We need to inspect the current Pod spec to avoid overwriting existing values
POD_JSON=$(kubectl get pod "$NAME" -n "$NS" -o json)
# Construct new containers array with ensured requests
NEW_CONTAINERS=$(echo "$POD_JSON" | jq --arg cpu "$DEFAULT_CPU_REQUEST" --arg mem "$DEFAULT_MEM_REQUEST" '
.spec.containers
| map(
.resources.requests = (
(.resources.requests // {})
| if (.cpu == null) then .cpu = $cpu else . end
| if (.memory == null) then .memory = $mem else . end
)
)
')
# Create patch JSON containing only the updated containers spec
PATCH=$(jq -n --argjson containers "$NEW_CONTAINERS" '{spec: {containers: $containers}}')
echo "$PATCH" | kubectl patch pod "$NAME" -n "$NS" --type merge -p "$(cat)"
done
echo "Verifying compliance..."
kubectl get pods --all-namespaces -o json | jq -r '
[ .items[]
| select(.metadata.namespace as $n | ["kube-system","kube-public","kube-node-lease"] | index($n) | not)
| .metadata as $m
| (.spec.containers // [])[]
| ((.resources.requests.cpu != null) and (.resources.requests.memory != null)) as $ok
| select($ok | not)
] as $rows
| if ($rows | length) == 0
then "All non-system Pods have cpu and memory requests set (is_compliant=true)."
else "Non-compliant containers remain (is_compliant=false). Re-run or inspect manually."
end'