LimitRange: namespace-level defaults
LimitRange — это namespaced объект (v1/LimitRange), который через admission controller LimitRanger штампует defaults и проверяет границы для requests/limits каждого нового Pod в namespace. Без LimitRange команда может ничего не указать в Pod spec — и получить BestEffort, который умрёт первым под pressure. С LimitRange нельзя забыть: admission controller либо проставит дефолт, либо отвергнет создание Pod. Это самый дешёвый guard-rail в кластере и очень популярная CKAD-задача.
Memory hierarchy: registers, cache, RAM, disk и реальная latency
Что делает LimitRange
При создании Pod kube-apiserver прогоняет манифест через цепочку admission plugins. Один из стандартных, включённых из коробки, — LimitRanger. Он:
- Берёт все LimitRange в namespace, где создаётся Pod.
- Для каждого container проверяет: если request/limit не указан и в LimitRange есть
defaultRequest/defaultдля соответствующего type — проставляет. - Проверяет
min,max,maxLimitRequestRatio— если container выходит за рамки, отвергает Pod с ошибкой.
Структура манифеста
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: team-a
spec:
limits:
- type: Container
default: # = limits
memory: 256Mi
cpu: 500m
defaultRequest: # = requests
memory: 128Mi
cpu: 100m
min: # минимальные допустимые значения
memory: 64Mi
cpu: 50m
max: # максимальные допустимые значения
memory: 1Gi
cpu: 2
maxLimitRequestRatio: # limit/request <= ratio
memory: 4
cpu: 10
Что означают поля:
default— limits для container, у которого limits НЕ указаны для данного ресурса. Это мутация.defaultRequest— requests для container без requests. Тоже мутация.min— минимально допустимое значение request и limit. Если container выходит ниже — admission rejected.max— максимальное. Симметрично.maxLimitRequestRatio— limit / request не должно превышать число. Например, ratio=4 для memory: при request=128Mi limit может быть до 512Mi, не больше.type— на каком уровне применять. Возможные значения:Container,Pod,PersistentVolumeClaim.
# type: Pod — проверка применяется к СУММЕ всех container в Pod
spec:
limits:
- type: Pod
max:
cpu: 4
memory: 8Gi
# type: PersistentVolumeClaim — границы для storage в PVC
spec:
limits:
- type: PersistentVolumeClaim
min:
storage: 1Gi
max:
storage: 100Gi
Порядок применения defaults
Важная деталь поведения LimitRanger: default подставляется только для тех ключей, которые отсутствуют. Если container явно указал requests.cpu: 100m, но не указал requests.memory — defaultRequest.memory подставится, а requests.cpu останется как было.
# LimitRange
defaultRequest: { cpu: 100m, memory: 128Mi }
default: { cpu: 500m, memory: 256Mi }
# Container в Pod указал только:
requests: { memory: 200Mi }
# После admission spec будет:
requests:
cpu: 100m # из defaultRequest
memory: 200Mi # как указано
limits:
cpu: 500m # из default
memory: 256Mi # из default
Это объясняет частую путаницу на CKAD: «я добавил LimitRange, но в Pod не вижу дефолтов» — потому что в Pod что-то уже было указано на тех же ключах.
LimitRanger мутирует Pod на момент admission, то есть один раз при создании. Pod существует — изменение LimitRange его не затронет. Чтобы дефолты подтянулись, нужен новый Pod (например, через rollout restart Deployment).
min/max и валидация
Когда container выходит за min или max — admission отвергает Pod. Пример:
# LimitRange
min: { memory: 64Mi }
max: { memory: 1Gi }
# Pod с container, превышающим max
resources:
limits:
memory: 2Gi # > max 1Gi
Результат:
Error from server (Forbidden): error when creating "pod.yaml":
pods "name" is forbidden: maximum memory usage per Container is 1Gi, but limit is 2Gi
min и max применяются и к requests, и к limits в одинаковых ключах. Поэтому для widely-applicable LimitRange min обычно ставят низким (отдельно для request), и максимум — высоким для limit.
maxLimitRequestRatio
Это ограничение на расхождение между request и limit. Используется чтобы не дать командам объявить «request 10m, limit 10000m» — Pod с маленьким request scheduler разместит хоть где, а под burst он съест всё.
maxLimitRequestRatio:
cpu: 4
memory: 2
Это значит:
- CPU:
limit <= request * 4. С request 250m максимум limit 1000m. - Memory:
limit <= request * 2. С request 256Mi максимум limit 512Mi.
При нарушении — admission rejected:
Error: cpu max limit to request ratio per Container is 4, but provided ratio is 8
Полный CKAD-style пример
Типичная задача на экзамене: «В namespace team-a создайте LimitRange, чтобы по умолчанию containers получали requests cpu=100m memory=128Mi и limits cpu=500m memory=256Mi. Pods не должны иметь memory limit больше 1Gi.»
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: team-a
spec:
limits:
- type: Container
defaultRequest:
cpu: 100m
memory: 128Mi
default:
cpu: 500m
memory: 256Mi
max:
memory: 1Gi
Применить:
kubectl apply -f limitrange.yaml -n team-a
Проверить, что defaults подтянулись на новом Pod:
kubectl run test --image=nginx -n team-a
kubectl get pod test -n team-a -o jsonpath='{.spec.containers[0].resources}'
# {"limits":{"cpu":"500m","memory":"256Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}
И что валидация работает:
kubectl run big --image=nginx --limits=memory=2Gi -n team-a
# Error: pods "big" is forbidden: maximum memory usage per Container is 1Gi, but limit is 2Gi
LimitRange действует только на новые Pods. Уже работающие в namespace Pods не получат defaults и не будут провалидированы. После создания LimitRange — kubectl rollout restart deployment чтобы пересоздать.
Killer момент: LimitRange + ResourceQuota — обязательная связка
Если в namespace есть ResourceQuota, который ограничивает requests.cpu/requests.memory/limits.cpu/limits.memory, то каждый Pod в этом namespace обязан явно указать соответствующие requests/limits — иначе admission ResourceQuota отвергнет создание. Pod без resources не пройдёт.
Решение: добавить LimitRange, который проставит defaults. Тогда последовательность admission такая:
- LimitRanger проставляет defaults в spec Pod.
- ResourceQuota видит, что requests/limits указаны (хоть и из defaults), проверяет лимит namespace.
- Если квота не превышена — Pod создаётся.
Без LimitRange ResourceQuota делает namespace непригодным для «забывчивых» пользователей. LimitRange делает дефолты дружелюбными. На CKAD комбинация LimitRange + ResourceQuota — самый частый сценарий.