Существует достаточно частая проблема: ошибки категории "
misleading".Когда копаешь в одно, а проблема в другом.
Инженеры Kubernetes иногда сталкиваются с крайне сбивающей с толку ошибкой при запуске пода:
MountVolume.SetUp failed... no space left on device.
Не зная причины этой ошибки, мы сперва тратим время на:
- просмотр что за PVC используются у деплоймента/пода.
- что за нода сейчас запустила этот под, нет ли нехватки места на самой ноде
- лезут в графану, в метрики и графики по нодам, подам, PV
- как разбиты разделы диска на нодах, что и сколько отдаётся эфемеркам
- чего там по айнодам (ну наконец-то пригодились эти знания! 🕺)
- и так далее
И это правильные мысли и план, это так же может быть причиной ошибки, но наш случай про другое.
Попытка найти строку "
no space left on device" в исходном коде Kubelet на GitHub также не дает результатов, потому что Kubelet не генерирует этот текст - он просто ретранслирует низкоуровневую ошибку, полученную от ядра Linux(или от рантайма, тут могу наврунькать).Истинная причина кроется в тонком различии между единицами измерения CPU и памяти в спецификации ресурсов. Это особенно критично для Проецируемых томов (Projected Volumes), таких как Service Account Token (
kube-api-access-*), которые монтируются как tmpfs (файловая система в RAM).https://kubernetes.io/docs/concepts/storage/projected-volumes/
Когда инженер по ошибке использует суффикс CPU (m) для лимита памяти, например:
resources:
limits:
memory: "512m" # Ошибка, должно быть "512Mi"!
-
Kubelet парсит "512m" для памяти как метрический милли, что равно 0.512 байта. Поскольку размер тома (tmpfs) должен быть целым числом, это значение округляется до 0 байт (или ничтожно малого значения). Kubelet использует этот 0 байт для установки максимального размера тома tmpfs.-
Kubelet использует этот лимит памяти для установки максимального размера тома tmpfs для Service Account токена- Ядро Linux получает запрос на монтирование
tmpfs с нулевым или ничтожно малым размером. Системный вызов mount завершается неудачей (из-за попытки выделить даже минимальный объём, обычно 1 страница = 4 KiB). и возвращает низкоуровневую ошибку ENOSPC ("No space left on device"), поскольку невозможно создать файловую систему (даже в RAM) с таким малым выделением ресурсов.То есть в нашем случае ошибка "
no space left on device" на самом деле означает: "Не удалось создать RAM-диск (tmpfs) требуемого объема, потому что выделенный размер (0 байт) некорректен".❕Стоит понимать, что Kubernetes API сервер не считает "512m" для памяти ошибкой валидации.
Он принимает это как легальное значение (0.512 байта), что позволяет поду быть созданным, но приводит к сбою выполнения (runtime failure) при монтировании на ноде.
Решение очевидное:
всегда используйте бинарные единицы (IEC) для ресурсов памяти: Mi (мебибайты), Gi (гибибайты).
Может есть и другие, я только эти использовал всегда.
Чтобы превентивно избавляться от такой проблемы, используйте дополнительно:
- pre-commit hooks
- валидаторы cli в CI/CD процессе
Какие-то конкретные утилиты советовать не буду, оставлю ссылки для примера:
- https://github.com/instrumenta/kubeval (давно не обновлялся, но он работает ок)
- https://github.com/yannh/kubeconform
- https://github.com/kubernetes-sigs/kubectl-validate
Кстати, аналогичные ошибки встречаются при монтировании секретов, конфигмапов и других tmpfs-бэкендов, использующих лимиты памяти контейнера.