Learning Platform
Глоссарий Troubleshooting
Урок 14.03 · 20 мин
Средний
LimitRangeadmissionnamespacedefaults

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. Он:

  1. Берёт все LimitRange в namespace, где создаётся Pod.
  2. Для каждого container проверяет: если request/limit не указан и в LimitRange есть defaultRequest/default для соответствующего type — проставляет.
  3. Проверяет min, max, maxLimitRequestRatio — если container выходит за рамки, отвергает Pod с ошибкой.
Поток admission через LimitRanger
kubectl apply PodКлиент отправляет манифест Pod. Может не содержать resources совсем, или содержать частично (только requests, только cpu и т.д.).
apiserver admission chainapiserver запускает enabled admission plugins. LimitRanger — мутирующий и валидирующий одновременно.
LimitRanger: mutateЕсли в LimitRange есть defaultRequest для memory, а в container нет requests.memory — LimitRanger дописывает в spec. Аналогично для default (это limits).
LimitRanger: validateЕсли итоговые requests/limits выходят за min/max или нарушают maxLimitRequestRatio — admission отвергает Pod с конкретной ошибкой.
Pod либо создан с defaults, либо rejectedkubectl видит либо успешное создание (где итоговый spec может отличаться от того, что отправлен — defaults подставлены), либо ошибку admission.

Структура манифеста

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.memorydefaultRequest.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 что-то уже было указано на тех же ключах.

NOTE

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
WARNING

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 такая:

  1. LimitRanger проставляет defaults в spec Pod.
  2. ResourceQuota видит, что requests/limits указаны (хоть и из defaults), проверяет лимит namespace.
  3. Если квота не превышена — Pod создаётся.

Без LimitRange ResourceQuota делает namespace непригодным для «забывчивых» пользователей. LimitRange делает дефолты дружелюбными. На CKAD комбинация LimitRange + ResourceQuota — самый частый сценарий.


Проверка знанийKnowledge check
В namespace создан LimitRange с defaultRequest cpu=100m memory=128Mi. Pod создаётся с containers без resources. Какие requests получит container?
ОтветAnswer
cpu=100m, memory=128Mi. LimitRanger (admission controller) применяет defaultRequest на этапе admission для container, у которого нет соответствующих ключей. Pod создаётся с уже подставленными значениями — это видно в kubectl get pod -o yaml после создания. Pod без LimitRange был бы BestEffort; с LimitRange — Burstable (или Guaranteed, если default тоже подставится и совпадёт).
Проверка знанийKnowledge check
В LimitRange указано max memory 1Gi для type: Container. Pod создаётся с container, у которого limits.memory=2Gi. Что произойдёт?
ОтветAnswer
Admission rejected. LimitRanger проверяет, что limit container не превышает max в LimitRange. Конкретное сообщение: 'pods is forbidden: maximum memory usage per Container is 1Gi, but limit is 2Gi'. Pod НЕ создаётся в etcd. min/max применяются и к requests, и к limits — это hard constraint.
Проверка знанийKnowledge check
LimitRange создан после того, как в namespace уже работают Pods. Они получат defaults?
ОтветAnswer
Нет. LimitRanger — это admission controller, он мутирует Pod spec в момент CREATE через kube-apiserver. Существующие Pods хранятся в etcd с тем spec, что был при создании. Чтобы дефолты применились — нужно пересоздать Pods (kubectl rollout restart deployment/...; для standalone Pods — delete+apply). LimitRange задним числом ничего не правит.
Проверка знанийKnowledge check
В LimitRange задан maxLimitRequestRatio для cpu = 5. Container указал requests.cpu=100m, limits.cpu=600m. Допустят ли admission?
ОтветAnswer
Нет. Ratio = 600 / 100 = 6, что больше 5. Admission rejected с сообщением 'cpu max limit to request ratio per Container is 5, but provided ratio is 6'. Чтобы пройти — нужно либо уменьшить limits до 500m (ratio=5), либо увеличить requests до 120m (ratio=5). maxLimitRequestRatio защищает от Pods с слишком большим разрывом между гарантией и максимумом.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. В namespace создан LimitRange с defaultRequest cpu=100m memory=128Mi и default cpu=500m memory=256Mi. Pod создан без resources блока. Что получит container?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5