Информация этой страницы может быть устаревшей

Оригинальная (английская) версия этого документа обновлялась с момента последнего перевода, поэтому информация может быть устаревшей. Если вы читаете на английском, посмотрите на оригинальную версию с наиболее актуальной информацией: Assign Memory Resources to Containers and Pods

Задание ресурсов памяти для контейнеров и Pod'ов

На этой странице рассказывается, как настраивать запрос памяти и её лимит для контейнеров. Контейнеру гарантируется столько памяти, сколько он запросит, но не больше установленных ограничений.

Подготовка к работе

Вам нужен Kubernetes кластер и инструмент командной строки kubectl должен быть настроен на связь с вашим кластером. Если у вас ещё нет кластера, вы можете создать, его используя Minikube, или вы можете использовать одну из песочниц Kubernetes:

Чтобы проверить версию, введите kubectl version.

Каждая нода вашего кластера должна располагать хотя бы 300 Мб памяти.

Некоторые операции на этой странице предполагают работу сервера метрик на вашем кластере. Если сервер метрик у вас уже запущен, следующие действия можно пропустить.

Если вы используете Minikube, выполните следующую команду, чтобы запустить сервер метрик:

minikube addons enable metrics-server

Чтобы проверить работу сервера меток или другого провайдера API ресурсов метрик (metrics.k8s.io), запустите команду:

kubectl get apiservices

Если API ресурсов метрики доступно, в выводе команды будет содержаться ссылка на metrics.k8s.io.

NAME      
v1beta1.metrics.k8s.io

Создание пространства имён

Создадим пространство имён, чтобы ресурсы, которыми будем пользоваться в данном упражнении, были изолированы от остального кластера:

kubectl create namespace mem-example

Установка запроса памяти и лимита памяти

Для установки запроса памяти контейнеру подключите поле resources:requests в манифест ресурсов контейнера. Для ограничений по памяти - добавьте resources:limits.

В этом упражнении создаётся Pod, содержащий один контейнер. Зададим контейнеру запрос памяти в 100 Мб и её ограничение в 200 Мб. Конфигурационный файл для Pod'а:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

Раздел args конфигурационного файла содержит аргументы для контейнера в момент старта. Аргументы "--vm-bytes", "150M" указывают контейнеру попытаться занять 150 Мб памяти.

Создадим Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit.yaml --namespace=mem-example

Убедимся, что контейнер Pod'a запущен:

kubectl get pod memory-demo --namespace=mem-example

Посмотрим подробную информацию о Pod'е:

kubectl get pod memory-demo --output=yaml --namespace=mem-example

В выводе мы видим, что для контейнера в Pod'е зарезервировано 100 Мб памяти и выставлено 200 Мб ограничения.

...
resources:
  limits:
    memory: 200Mi
  requests:
    memory: 100Mi
...

Запустим kubectl top, чтобы получить метрики Pod'a:

kubectl top pod memory-demo --namespace=mem-example

Вывод команды показывает, что Pod использовал примерно 162900000 байт памяти - и это около 150 Мб. Данная величина больше установленного запроса в 100 Мб, но укладывается в имеющееся ограничение на 200 Мб.

NAME                        CPU(cores)   MEMORY(bytes)
memory-demo                 <something>  162856960

Удалим Pod:

kubectl delete pod memory-demo --namespace=mem-example

Превышение контейнером лимита памяти

Контейнер может превысить величину запроса памяти, если нода имеет достаточно ресурсов памяти. Но превышение заданного ограничения памяти не допускается. Если контейнер запрашивает больше памяти, чем ему разрешено использовать, то он становится кандидатом на удаление. Если превышение лимита памяти продолжится, контейнер удаляется. Если удалённый контейнер может быть перезапущен, то kubelet перезапускает его, как и в случае любой другой неполадки в работе.

В этом упражнении создадим Pod, который попытается занять больше памяти, чем для него ограничено. Ниже представлен конфигурационный файл для Pod'a с одним контейнером, имеющим 50 Мб на запрос памяти и 100 Мб лимита памяти:

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-2
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-2-ctr
    image: polinux/stress
    resources:
      requests:
        memory: "50Mi"
      limits:
        memory: "100Mi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "250M", "--vm-hang", "1"]

В разделе args можно увидеть, что контейнер будет пытаться занять 250 Мб - и это значительно превышает лимит в 100 Мб.

Создадим Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-2.yaml --namespace=mem-example

Посмотрим подробную информацию о Pod'е:

kubectl get pod memory-demo-2 --namespace=mem-example

В этот момент контейнер уже либо запущен, либо убит. Будем повторять предыдущую команду, пока контейнер не окажется убитым:

NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          24s

Посмотрим ещё более подробный вид статуса контейнера:

kubectl get pod memory-demo-2 --output=yaml --namespace=mem-example

В выводе показано, что контейнер был убит по причине недостатка памяти (OOM):

lastState:
   terminated:
     containerID: docker://65183c1877aaec2e8427bc95609cc52677a454b56fcb24340dbd22917c23b10f
     exitCode: 137
     finishedAt: 2017-06-20T20:52:19Z
     reason: OOMKilled
     startedAt: null

В данном упражнении контейнер может быть перезапущен, поэтому kubelet стартует его. Выполните следующую команду несколько раз, чтобы увидеть, как контейнер раз за разом убивается и запускается снова:

kubectl get pod memory-demo-2 --namespace=mem-example

Вывод показывает, что контейнер убит, перезапущен, снова убит, перезапущен, и т.д.:

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS      RESTARTS   AGE
memory-demo-2   0/1       OOMKilled   1          37s

kubectl get pod memory-demo-2 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-2   1/1       Running   2          40s

Посмотрим подробную информацию об истории Pod'a:

kubectl describe pod memory-demo-2 --namespace=mem-example

Вывод показывает, что контейнер постоянно запускается и падает:

... Normal  Created   Created container with id 66a3a20aa7980e61be4922780bf9d24d1a1d8b7395c09861225b0eba1b1f8511
... Warning BackOff   Back-off restarting failed container

Посмотрим детальную информацию о нодах на кластере:

kubectl describe nodes

В выводе содержится запись о том, что контейнер убивается по причине нехватки памяти:

Warning OOMKilling Memory cgroup out of memory: Kill process 4481 (stress) score 1994 or sacrifice child

Удалим Pod:

kubectl delete pod memory-demo-2 --namespace=mem-example

Установка слишком большого для нод запроса памяти

Запросы и ограничения памяти связаны с контейнерами, но полезно также рассматривать эти параметры и для Pod'а. Запросом памяти для Pod'a будет сумма всех запросов памяти контейнеров, имеющихся в Pod'е. Также и лимитом памяти будет сумма всех ограничений, установленных для контейнеров.

Планирование Pod'a основано на запросах. Pod запускается на ноде лишь в случае, если нода может удовлетворить запрос памяти Pod'a.

В данном упражнении мы создадим Pod, чей запрос памяти будет превышать ёмкость любой ноды в кластере. Ниже представлен конфигурационный файл для Pod'a с одним контейнером, имеющим запрос памяти в 1000 Гб (что наверняка превышает ёмкость любой имеющейся ноды):

apiVersion: v1
kind: Pod
metadata:
  name: memory-demo-3
  namespace: mem-example
spec:
  containers:
  - name: memory-demo-3-ctr
    image: polinux/stress
    resources:
      limits:
        memory: "1000Gi"
      requests:
        memory: "1000Gi"
    command: ["stress"]
    args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"]

Создадим Pod:

kubectl apply -f https://k8s.io/examples/pods/resource/memory-request-limit-3.yaml --namespace=mem-example

Проверим статус Pod'a:

kubectl get pod memory-demo-3 --namespace=mem-example

Вывод показывает, что Pod имеет статус PENDING. Это значит, что он не запланирован ни на одной ноде, и такой статус будет сохраняться всё время:

kubectl get pod memory-demo-3 --namespace=mem-example
NAME            READY     STATUS    RESTARTS   AGE
memory-demo-3   0/1       Pending   0          25s

Посмотрим подробную информацию о Pod'е, включающую события:

kubectl describe pod memory-demo-3 --namespace=mem-example

Вывод показывает невозможность запуска контейнера из-за нехватки памяти на нодах:

Events:
  ...  Reason            Message
       ------            -------
  ...  FailedScheduling  No nodes are available that match all of the following predicates:: Insufficient memory (3).

Удалим Pod:

kubectl delete pod memory-demo-3 --namespace=mem-example

Единицы измерения памяти

Ресурсы памяти измеряются в байтах. Их можно задавать просто целым числом либо целым числом с одним из следующих окончаний: E, P, T, G, M, K, Ei, Pi, Ti, Gi, Mi, Ki. Например, представленные здесь варианты задают приблизительно одну и ту же величину:

128974848, 129e6, 129M , 123Mi

Если лимит памяти не задан

Если вы не задали ограничение памяти для контейнера, возможны следующие варианты:

  • У контейнера отсутствует верхняя граница для памяти, которую он может использовать. Такой контейнер может занять всю память, доступную на ноде, где он запущен, что, в свою очередь, может вызвать OOM Killer. Также контейнеры без ограничений по ресурсам имеют более высокие шансы быть убитыми в случае вызова OOM Kill.

  • Контейнер запущен в пространстве имён, в котором настроена величина ограничений по умолчанию. Тогда контейнеру автоматически присваивается это стандартное значение лимита. Администраторы кластера могут использовать LimitRange для задания стандартной величины ограничений по памяти.

Мотивация для использования запросов и ограничений памяти

При помощи задания величины запросов и лимитов памяти для контейнеров, запущенных на вашем кластере, можно эффективно распоряжаться имеющимися на нодах ресурсами. Задание Pod'у небольшого запроса памяти даёт хорошие шансы для него быть запланированным. Ограничение памяти, превышающее величину запроса памяти, позволяет достичь 2 вещей:

  • Pod может иметь всплески активности, в течение которых ему может потребоваться дополнительная память.

  • Величина памяти, доступная Pod'у при повышении активности, ограничена некоторой разумной величиной.

Очистка

Удалим пространство имён. Эта операция удалит все Pod'ы, созданные в рамках данного упражнения:

kubectl delete namespace mem-example

Что дальше

Для разработчиков приложений

Для администраторов кластера