Сколько RAM нужно для k3s
«k3s лёгкий, ему хватит гигабайта» — самая дорогая фраза в русскоязычных мануалах. Кластер поднимется, kubectl get nodes покажет Ready, а на третий день первый же деплой закончится кодом выхода 137 и половиной подов в статусе Evicted. Разберём k3s требования к памяти по цифрам: сколько занимает каждый компонент, почему нода считает себя полупустой и как измерить аппетит своего приложения.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Короткий ответ: сколько памяти брать под k3s
Замеры сделаны на чистой Ubuntu 24.04 (ядро 6.8, cgroup v2), одиночный сервер, k3s v1.34.6+k3s1 из стабильного канала; между версиями 1.32–1.34 цифры плавают в пределах 5%.
| RAM ноды | Остаётся под ваши поды | Что реально влезает |
|---|---|---|
| 1 ГБ | 250–350 МБ | ничего боевого: один nginx и надежда |
| 2 ГБ | 1,2–1,3 ГБ | стенд: 4–6 лёгких подов, Redis на 64 МБ |
| 4 ГБ | 3,1–3,3 ГБ | боевой узел: 10–15 подов, Postgres, кэш |
| 8 ГБ | 7,1–7,3 ГБ | 20–30 подов, CI-раннер, сборка образов |
| 16 ГБ | ~15 ГБ | плюс Prometheus, Grafana, Longhorn |
Накладные расходы k3s почти не растут с размером сервера: фиксированный налог около 600 МБ, который на машине с 1 ГБ съедает две трети памяти, а на 8 ГБ — восемь процентов. Отсюда правило: 1 ГБ под k3s не берут никогда, 2 ГБ — только под стенд, рабочий минимум для продакшена — 4 ГБ.
Воркер дешевле: агент не тащит apiserver, scheduler и хранилище состояния, процесс k3s agent в покое держит 230–290 МБ. Итого 300–350 МБ вместо 600, и воркер на 2 ГБ — рабочая единица.
Из чего складывается расход
Что занимает память после установки по умолчанию, RSS в покое:
| Компонент | Где живёт | RSS |
|---|---|---|
k3s server: apiserver, scheduler, controller-manager, kine/SQLite, kubelet, flannel | процесс | 430–520 МБ |
containerd | отдельный процесс | 55–75 МБ |
| контроллер NetworkPolicy (kube-router) | внутри процесса k3s | +30–45 МБ |
| Traefik v3 | под в kube-system | 60–80 МБ |
| metrics-server | под | 35–55 МБ |
| CoreDNS | под | 16–22 МБ |
local-path-provisioner и svclb-traefik-* | поды | 13–20 МБ |
Считать лучше по cgroup, а не ps:
systemd-cgtop -m -b -n 1 --depth=2
cat /sys/fs/cgroup/system.slice/k3s.service/memory.current
cat /sys/fs/cgroup/kubepods.slice/memory.current
Первая цифра — вся служба с containerd и подами, вторая — только поды; разница и есть налог control-plane. Сверху ещё 90–130 МБ на systemd, sshd и journald. Оговорка, на которой спотыкаются при замере: сумма RSS из таблицы больше прироста used в free -m, потому что RSS считает общие страницы каждому процессу отдельно. Реальный прирост на чистой системе — со 120 МБ до 640–700 МБ.
Расход, который в таблицах не пишут, — плата за каждый под: свой containerd-shim-runc-v2 (11–14 МБ RSS) плюс контейнер rancher/mirrored-pause:3.6 (0,5 МБ). Двадцать подов — 230–290 МБ ещё до того, как контейнеры выделили первый байт:
# comm обрезан ядром до 15 символов, поэтому не containerd-shim-runc-v2
ps -eo rss,comm | awk '$2=="containerd-shim"{s+=$1} END {printf "%.0f MB\n", s/1024}'
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть k3sAllocatable против Capacity: почему нода врёт
Главная ловушка k3s. Смотрим ноду с 2 ГБ, kubectl describe node k3s-uk-1:
Capacity:
cpu: 2
memory: 1996996Ki
pods: 110
Allocatable:
cpu: 2
memory: 1894596Ki
pods: 110
Разница ровно 102400Ki, то есть 100Mi — дефолтный порог вытеснения memory.available<100Mi. Больше не вычтено ничего, хотя формула Kubernetes такая:
Allocatable = Capacity − kube-reserved − system-reserved − eviction-hard
k3s по умолчанию не задаёт ни kube-reserved, ни system-reserved. Ноль и ноль. Планировщик считает, что под ваши поды доступно 1,85 ГБ из 1,95 ГБ — хотя 600 МБ уже съедены самим k3s, containerd и системой. Про них он не знает и будет размещать поды до упора; когда память кончится, разбираться будет OOM-киллер ядра.
Лечится резервированием в /etc/rancher/k3s/config.yaml:
kubelet-arg:
- "system-reserved=cpu=200m,memory=256Mi"
- "kube-reserved=cpu=200m,memory=512Mi"
- "eviction-hard=memory.available<200Mi"
- "eviction-soft=memory.available<400Mi"
- "eviction-soft-grace-period=memory.available=2m"
- "max-pods=50"
После systemctl restart k3s Allocatable на той же ноде становится 1023556Ki — около 1 ГБ. Цифра неприятная, но честная. Мягкий порог с грейс-периодом даёт kubelet шанс вытеснить под через SIGTERM, а не убить жёстко. max-pods=50 вместо дефолтных 110 страхует от сбойного Deployment: 110 подов на ноде с 2–4 ГБ невозможны, одни shim-процессы съели бы 1,2 ГБ.
Как выглядит нехватка памяти
Симптомов три, лечатся они по-разному.
Контейнер превысил limits.memory. Убивает cgroup-контроллер ядра, страдает только он. В kubectl describe pod api-7c9f8d6b4-x2mlp:
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
137 — это 128 + 9, то есть SIGKILL, дальше CrashLoopBackOff. Решение — поднять лимит, а не память ноды.
Память кончилась на всей ноде. Вытесняет kubelet, и падают чужие поды. kubectl get events -A --field-selector reason=Evicted:
Warning Evicted pod/worker-5d4f8 The node was low on resource: memory.
Threshold quantity: 100Mi, available: 76Mi. Container worker was using
1053396Ki, request is 0, has larger consumption of memory.
request is 0 — это и есть критерий выбора жертвы: под без requests.memory получает QoS BestEffort и oom_score_adj = 1000, а у Guaranteed в /proc/<pid>/oom_score_adj будет -997. Одновременно нода получает MemoryPressure: True и таинт node.kubernetes.io/memory-pressure:NoSchedule — новые поды на неё больше не приезжают.
Сработал OOM-киллер ядра. Память кончилась быстрее, чем kubelet успел среагировать — типично для сборки образа или миграции БД. Ищем в dmesg -T | grep -i "out of memory":
Memory cgroup out of memory: Killed process 5821 (python3)
total-vm:1284460kB, anon-rss:498124kB, oom_score_adj:999
Если под нож попал сам k3s server, будет обрыв в journalctl -u k3s -n 100 и недоступный API — тот случай, когда «кластер просто умер». Вытесненные поды не исчезают сами: kubectl delete pod -A --field-selector=status.phase=Failed. Остальные причины незапуска — в статье «k3s: под не запускается».
Как измерить, сколько нужно вашему приложению
Точный источник — cgroup v2: он хранит и текущее потребление, и пик. Берём UID пода и идём в его срез, дефисы в имени заменены подчёркиваниями:
kubectl get pod api-7c9f8d6b4-x2mlp -o jsonpath='{.metadata.uid}{"\n"}'
cd /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod3f7a1c22_9b04_4e1a_8d55_1c0e4a2b9f10.slice
cat memory.current memory.peak memory.max memory.events
memory.peak — пик за всё время жизни пода, самая ценная цифра для лимита. memory.events покажет, сколько раз вмешалось ядро:
low 0
high 1842
max 27
oom 4
oom_kill 2
Ненулевой oom_kill означает, что контейнер уже убивали, даже если под сейчас Running и в describe чисто. high в тысячах — процесс упирается в потолок. Топ прожорливых по кластеру:
for f in /sys/fs/cgroup/kubepods.slice/*/*/memory.peak; do echo "$(cat $f) $f"; done | sort -rn | head -10
С metrics-server есть путь попроще, но менее точный — значения усредняются за окно и пиков не показывают: kubectl top pod -A --sort-by=memory. Без него та же команда вернёт error: Metrics API not available — не поломка, а следствие вашей же экономии.
Дальше арифметика: requests.memory = p95 реального потребления, limits.memory = memory.peak × 1,3. Для типового веб-приложения это requests: {memory: 192Mi, cpu: 100m} и limits: {memory: 384Mi}. Requests нужны планировщику, limits — ядру, чтобы приложение не утащило машину целиком. Отдельно закладывайте память на редкое, но жадное: миграции БД, npm ci в init-контейнере, генерация отчётов. Эти пики и убивают ноду, подобранную по среднему.
Как ужать k3s: что отключать и сколько это даёт
Реальная разница free -m до и после на той же ноде с 2 ГБ:
| Что делаем | Экономия | Чем платите |
|---|---|---|
disable: [traefik] | 60–80 МБ | нужен свой Ingress или NodePort |
disable: [metrics-server] | 35–55 МБ | нет kubectl top и HPA по памяти |
disable: [local-storage] | 10–14 МБ | нет StorageClass по умолчанию |
disable-network-policy: true | 30–45 МБ | объекты NetworkPolicy не работают |
disable-cloud-controller + disable-helm-controller | 10–15 МБ | на голом VPS почти ничего |
GOGC=40 в юните k3s | 8–15% от RSS k3s | +3–5% CPU на сборку мусора |
Про последнюю строку: Go по умолчанию (GOGC=100) даёт куче вырасти вдвое, прежде чем собрать мусор — там, где памяти в обрез, это чистая потеря. Прикручивается drop-in-юнитом:
mkdir -p /etc/systemd/system/k3s.service.d
cat >/etc/systemd/system/k3s.service.d/mem.conf <<'EOF'
[Service]
Environment=GOGC=40
Environment=GOMEMLIMIT=600MiB
EOF
systemctl daemon-reload && systemctl restart k3s
GOMEMLIMIT низко ставить нельзя: если рабочая куча не помещается в потолок, процесс уходит в непрерывный GC, ест 100% CPU и не убивается ядром — отладить такое сложнее, чем честный OOM. А --kube-apiserver-arg=watch-cache=false, который советуют «для экономии», не берите: на паре десятков объектов кэш занимает единицы мегабайт, зато каждый LIST уходит прямо в SQLite.
Про swap. Kubernetes годами требовал его выключать, теперь kubelet умеет LimitedSwap: failSwapOn: false и memorySwap.swapBehavior: LimitedSwap в /etc/rancher/k3s/kubelet.yaml, только для подов Burstable. Честно: он спасает от мгновенного OOM, но задержки растут в десятки раз, и для БД это хуже падения.
Финальная честность: всё ужимание вместе даёт 130–170 МБ, а раздетый k3s всё равно занимает 430–460 МБ. Если не хватает именно этих 150 МБ, вы не тюнингом заняты, а откладываете неизбежное. Если Kubernetes не оправдан вовсе — есть разбор «k3s против Docker Swarm»: Swarm обходится в 120 МБ на ноду.
Какой сервер взять под k3s в MAATRIX
Считайте снизу вверх: 600 МБ на k3s, 12 МБ накладных на каждый под, пики приложений и 25–30% запаса на деплой — при rolling update старые и новые поды живут одновременно, и память по обновляемому Deployment удваивается.
| Задача | Конфигурация | Что помещается |
|---|---|---|
| Учебный стенд | 2 vCPU / 2 ГБ / 30 ГБ NVMe | k3s и 4–6 лёгких подов, без БД |
| Боевой минимум | 4 vCPU / 4 ГБ / 50 ГБ NVMe | 10–15 подов, Postgres с shared_buffers=512MB, Redis |
| Комфортный узел | 4 vCPU / 8 ГБ / 80 ГБ NVMe | 20–30 подов, CI-раннер, сборка образов, запас на пики |
| Сервер + два воркера | 4/8 ГБ + 2 × 2/4 ГБ | разнос нагрузки, обновление нод без простоя |
| HA control-plane | 3 × 4 vCPU / 8 ГБ | встроенный etcd вместо SQLite |
Четыре уточнения. Мониторинг — отдельный расход: kube-prometheus-stack в покое занимает 1,2–2 ГБ, из них Prometheus 0,7–1,5 ГБ; на ноде с 4 ГБ это половина кластера ради графиков. Сетевое хранилище дорого: Longhorn требует около 1 ГБ на ноду только под instance-manager, так что при реплицируемых томах берите 8 ГБ. HA поднимает планку: встроенный etcd добавляет 150–300 МБ на каждый из трёх серверов; тройка по 2 ГБ — не отказоустойчивость, а три точки отказа синхронно. Диск обязательно NVMe: kine/SQLite и etcd упираются в задержку fsync, на медленном хранилище apiserver сыплет etcdserver: request timed out, а выглядит это как нехватка RAM.
По локации под k3s мы обычно советуем UK, Лондон: 10–20 мс до европейских точек обмена, GDPR-соседство для клиентов из ЕС и чистый IP, с которым образы из Docker Hub, ghcr.io и quay.io тянутся напрямую — без зеркал в registries.yaml и ответов 403. FR закрывает то же для континентальной Европы, US берут, когда поды ходят к зарубежным ИИ-API, RU — когда данные обязаны лежать в России по 152-ФЗ. Пошаговая установка — в статье «Как установить и настроить k3s на VPS».
Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT; зарубежная карта для лондонской площадки не нужна. Не уверены, хватит ли 4 ГБ? Пришлите список того, что запускаете, с пиками по памяти — скажем честно, где хватит одной ноды, а где нужен воркер.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть k3sОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Заведётся ли k3s на VPS с 1 ГБ RAM?
Заведётся, нода станет Ready. Но control-plane заберёт 600 МБ, под контейнеры останется 250–350 МБ, и первая же миграция БД закончится кодом 137. Отключение Traefik и metrics-server даёт 100–130 МБ и проблему не решает.
Почему kubectl describe node показывает почти 2 ГБ доступной памяти на ноде с 2 ГБ?
k3s не задаёт kube-reserved и system-reserved, поэтому Allocatable = Capacity минус только порог вытеснения 100Mi. Планировщик не знает про 600 МБ, занятые самим k3s; выставьте резервы через kubelet-arg.
Чем Exit Code 137 отличается от Evicted?
137 — это SIGKILL: контейнер превысил собственный limits.memory, убило ядро, пострадал только он. Evicted — память кончилась на всей ноде, и kubelet вытеснил под, чаще чужой и без requests.memory.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.