Советы и решение типовых проблем
Что-то обязательно пойдёт не так — это нормально, так все учатся. На этой странице собраны самые частые проблемы домашнего кластера: как их распознать, как диагностировать и как починить. Плюс резервное копирование и шпаргалка по kubectl.
Узел в статусе NotReady
Симптом: kubectl get nodes показывает NotReady, поды на этом узле в Pending или Unknown.
Шаг 1. Спросите кластер, что случилось
kubectl describe node k8s-node1
Листайте до раздела Conditions — там список состояний узла. Ищите условие, которое не в порядке:
| Condition | О чём говорит | Частая причина |
|---|---|---|
Ready = False | Узел нездоров | kubelet упал, нет связи с сетью подов |
MemoryPressure = True | Кончается ОЗУ | Слишком много подов без limits |
DiskPressure = True | Кончается диск | Образы, логи, локальные тома съели место |
PIDPressure = True | Кончаются PID'ы | Редко; утечка процессов |
NetworkUnavailable = True | Не работает сеть подов | CNI-плагин не запустился |
Там же, внизу вывода, раздел Events — последние события узла, часто прямо называющие виновника.
Шаг 2. Посмотрите журналы на самом узле
Зайдите по SSH на проблемную машину и проверьте главного агента:
# k3s (сервер или агент):
sudo journalctl -u k3s -n 100 --no-pager
sudo journalctl -u k3s-agent -n 100 --no-pager
# kubeadm (kubelet):
sudo journalctl -u kubelet -n 100 --no-pager
# следить в реальном времени:
sudo journalctl -u k3s -f
Частые строки и их значения:
container runtime is down— упал containerd:sudo systemctl status containerd;failed to load Kubelet config fileили ругань на swap — вернитесь к инструкции «Подготовка», проверьтеswapon --show;- таймауты к apiserver — проверьте сеть:
pingcontrol-plane, правильность IP и токена (в k3s —/etc/rancher/k3s/).
Шаг 3. Проверьте системные поды сети
Если kubelet жив, но поды не стартуют — часто виноват CNI:
# flannel (kubeadm):
kubectl get pods -n kube-flannel
# логи проблемного пода flannel:
kubectl logs -n kube-flannel имя-пода
sudo systemctl restart k3s (или k3s-agent, или kubelet). Если после этого узел Ready — читайте журналы и ищите первопричину в спокойной обстановке. Но если узел «отваливается» регулярно — это уже симптом: ищите, кто съедает память или диск.
Не хватает памяти: OOMKilled и Evicted
Симптомы: поды перезапускаются с причиной OOMKilled, либо зависают в статусе Evicted, либо узел показывает MemoryPressure.
Шаг 4. Найдите, кто ест память
# потребление по подам (нужен metrics-server; в k3s встроен)
kubectl top nodes
kubectl top pods -A --sort-by=memory
# почему перезапустился конкретный под
kubectl describe pod имя-пода | grep -A3 "Last State"
# Reason: OOMKilled — ядро убило процесс за превышение лимита
Шаг 5. Разберитесь с requests и limits
Ключ к стабильности — правильные значения у каждого контейнера:
- requests — сколько памяти гарантированно зарезервировано за подом. Планировщик не разместит под на узле, если свободных requests там меньше, чем просит под. Занижать requests «чтобы влезло» — откладывать проблему.
- limits — потолок. Превысил — получил OOMKilled и перезапуск.
Практический подход: запустите приложение без жёстких ограничений, дайте поработать пару дней, посмотрите реальное потребление (kubectl top pods) и выставьте requests чуть выше среднего, limits — выше пикового. Для домашних сервисов ориентиры есть в таблице на странице «Требования».
Шаг 6. Почистите «зависшие» Evicted-поды
Evicted-поды не удаляются сами и засоряют вывод:
# найти все Evicted во всех namespace
kubectl get pods -A --field-selector=status.phase=Failed
# удалить их разом
kubectl delete pods -A --field-selector=status.phase=Failed
Пода не стартует: CrashLoopBackOff и ImagePullBackOff
Два самых частых статуса проблемных подов.
ImagePullBackOff — образ не скачивается
Кластер не может получить образ контейнера. Диагностика:
kubectl describe pod имя-пода | tail -20
# в Events будет причина, типичные:
# - "not found" — опечатка в имени или теге образа
# - "unauthorized" — приватный реестр, а imagePullSecrets не задан
# - таймауты — нет интернета на узле или DNS не работает
Решения по порядку: проверьте написание image: в манифесте (точное имя и тег); проверьте с узла curl -I https://registry-1.docker.io/v2/; для своих образов, загруженных вручную через k3s ctr images import, обязательно поставьте imagePullPolicy: IfNotPresent, иначе кластер будет пытаться скачать образ из интернета и честно упадёт с ImagePullBackOff.
CrashLoopBackOff — приложение падает сразу после старта
Под стартует, приложение внутри падает, Kubernetes перезапускает, цикл повторяется с нарастающей паузой (отсюда BackOff). Порядок действий:
# логи текущего и ПРЕДЫДУЩЕГО запуска — ключевая команда
kubectl logs имя-пода
kubectl logs имя-пода --previous
# история рестартов и причина выхода
kubectl describe pod имя-пода | grep -A5 "Last State"
Типовые причины:
- ошибка в конфиге приложения — опечатка в ConfigMap, неверный адрес базы данных;
- нет доступа к тому — Permission denied на PVC (см. раздел про права на странице «Хранение»);
- приложению не хватает памяти при старте — OOMKilled ещё до начала работы, увеличьте limits;
- порт занят или неверный command/args в манифесте.
Под в Pending и не назначается на узел
kubectl describe pod имя-пода | grep -A5 Events
Частые причины из Events: Insufficient memory/cpu (на узлах нет свободных requests — уменьшите их или освободите узел), pod has unbound immediate PersistentVolumeClaims (PVC не привязался — смотрите kubectl describe pvc, обычно provisioner не запущен или кончилось место), node(s) had taint (на узле запрещающий taint, например control-plane без снятого ограничения — см. шаг 5 на странице «Установка»).
Заполнение диска и etcd
Симптомы: поды не создаются (FailedCreatePodSandBox), в журналах no space left on device, узел в DiskPressure, а в тяжёлых случаях — apiserver вообще не отвечает, потому что etcd не может писать на диск.
Шаг 7. Найдите, что заняло место
# общая картина
df -h
# самые тяжёлые каталоги (обычно /var/lib — образы, тома, etcd)
sudo du -h -d1 /var/lib 2>/dev/null | sort -rh | head
sudo du -h -d1 /var/lib/rancher 2>/dev/null | sort -rh | head # для k3s
Типовые «толстяки» в домашнем кластере:
- Образы контейнеров — каждый образ сотни мегабайт, а их копятся десятки версий;
- Логи контейнеров — болтливое приложение пишет гигабайты в
/var/log/pods; - Локальные тома —
/var/lib/rancher/k3s/storageрастёт вместе с данными приложений; - etcd — сама по себе маленькая, но без чистки истории раздувается.
Шаг 8. Почистите образы и логи
# неиспользуемые образы (k3s / containerd):
sudo k3s crictl rmi --prune
# усечь разросшиеся логи конкретного пода:
sudo truncate -s 0 /var/log/pods/*/имя-пода/*.log
Чтобы логи не разрастались в будущем, в k3s можно ограничить их размер при установке: INSTALL_K3S_EXEC="--kubelet-arg container-log-max-size=10Mi --kubelet-arg container-log-max-files=3".
Шаг 9. Присмотрите за etcd
Размер etcd смотрим в k3s так:
sudo k3s etcd-snapshot list
ls -lh /var/lib/rancher/k3s/server/db/
Если база перевалила за гигабайт на пустом кластере — полезно сделать дефрагментацию. Для k3s обычно достаточно того, что он сам периодически компактирует историю; в ванильном kubeadm-кластере дефрагментация делается через etcdctl (тема уже продвинутая — обратитесь к официальной документации kubernetes.io, раздел про etcd maintenance).
sudo systemctl restart k3s → проверьте kubectl get nodes. Если etcd не стартует после очистки диска — восстанавливайтесь из снапшота (следующий раздел). Именно поэтому бэкапы нужно настроить до того, как они понадобятся.
Резервное копирование
Бэкап домашнего кластера состоит из трёх слоёв:
- etcd — состояние самого кластера (все объекты k8s);
- манифесты — ваши YAML и helm values (в идеале — в git);
- данные томов — файлы приложений (Nextcloud, медиатека и т.д.).
Шаг 10. Снапшоты etcd в k3s
В k3s это встроено и делается одной командой:
# ручной снапшот прямо сейчас
sudo k3s etcd-snapshot save --name before-upgrade
# список снапшотов
sudo k3s etcd-snapshot list
# файлы лежат здесь:
ls -lh /var/lib/rancher/k3s/server/db/snapshots/
Настройте автоматические снапшоты при установке или добавьте параметры в /etc/systemd/system/k3s.service (после правки: sudo systemctl daemon-reload && sudo systemctl restart k3s):
# флаги запуска k3s server:
--etcd-snapshot-schedule-cron="0 3 * * *" # каждый день в 03:00
--etcd-snapshot-retention=7 # хранить 7 последних
/var/lib/rancher/k3s/server/db/snapshots/ на другую машину или NFS-шару. Простейший вариант — запись в crontab: rsync -a /var/lib/rancher/k3s/server/db/snapshots/ /mnt/nfs-backup/etcd/.
Шаг 11. Восстановление из снапшота (порядок действий)
# остановить k3s
sudo systemctl stop k3s
# восстановить кластер из выбранного снапшота
sudo k3s server --cluster-reset \
--cluster-reset-restore-path=/var/lib/rancher/k3s/server/db/snapshots/имя-снапшота
# после успешного восстановления снять режим reset
sudo systemctl start k3s
# проверить: kubectl get nodes, kubectl get pods -A
Потренируйтесь на свежем кластере: сделайте снапшот, создайте под, восстановитесь из снапшота — под должен исчезнуть. Опыт восстановления до того, как всё сгорело, бесценен.
Шаг 12. Бэкап манифестов и данных
- Манифесты: все свои YAML храните в git-репозитории (тем более когда поднимете Gitea — см. «Примеры»). Полный экспорт существующих объектов на всякий случай:
kubectl get all -A -o yaml > cluster-dump.yaml(это скорее снимок для изучения, чем полноценный бэкап — восстанавливайтесь из своих чистых манифестов). - Данные томов: для NFS — бэкапится каталог шары обычными средствами (rsync по расписанию). Для local-path — rsync каталога
/var/lib/rancher/k3s/storage/на NFS по cron. Для баз данных — их штатные дампы (pg_dump и т.п.) перед копированием файлов.
Обновление кластера
Kubernetes развивается быстро, и раз в несколько месяцев кластер стоит обновлять. Общий безопасный порядок одинаков для любого дистрибутива:
Шаг 1. Сделайте бэкап перед обновлением
# снапшот etcd (k3s)
sudo k3s etcd-snapshot save --name before-upgrade
Убедитесь, что бэкапы данных томов свежие. Обновление без бэкапа — русская рулетка.
Шаг 2. Обновите control-plane
В k3s — просто повторный запуск установочного скрипта с новой версией:
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.31.4+k3s1 sh -
В kubeadm-кластере — штатная процедура: снять hold с пакетов, обновить kubeadm, выполнить kubeadm upgrade plan и kubeadm upgrade apply, затем обновить kubelet и kubectl. Подробный порядок зависит от версии — сверяйтесь с официальной документацией kubernetes.io.
Шаг 3. Обновите рабочие узлы по одному
# выгнать поды с узла
kubectl drain k8s-node2 --ignore-daemonsets --delete-emptydir-data
# обновить k3s-agent на узле (тот же скрипт с той же версией)
# ... или пакеты в kubeadm
# вернуть узел в строй
kubectl uncordon k8s-node2
Правило: версии узлов не должны отличаться от control-plane больше чем на два минорных релиза, а лучше держать всех на одной версии.
Шпаргалка по kubectl
| Задача | Команда |
|---|---|
| Список узлов | kubectl get nodes -o wide |
| Все поды кластера | kubectl get pods -A |
| Поды в конкретном namespace | kubectl get pods -n media |
| Подробности об объекте (события!) | kubectl describe pod имя -n namespace |
| Логи пода | kubectl logs имя-пода -n namespace |
| Логи в реальном времени | kubectl logs -f имя-пода |
| Логи прошлого (упавшего) контейнера | kubectl logs имя-пода --previous |
| Зайти внутрь контейнера | kubectl exec -it имя-пода -- /bin/sh |
| Применить манифест | kubectl apply -f файл.yaml |
| Удалить по манифесту | kubectl delete -f файл.yaml |
| Перезапустить deployment | kubectl rollout restart deployment/имя |
| Статус обновления | kubectl rollout status deployment/имя |
| Откат обновления | kubectl rollout undo deployment/имя |
| Потребление ресурсов | kubectl top nodes / kubectl top pods -A |
| Всё про хранилище | kubectl get pv,pvc,sc |
| Сервисы и их типы | kubectl get svc -A |
| События кластера (кто что сломал) | kubectl get events -A --sort-by=.lastTimestamp |
| Endpoints сервиса (есть ли живые поды за ним) | kubectl get endpoints имя-сервиса |
| Быстрый тестовый под для отладки сети | kubectl run tmp --rm -it --image=busybox -- sh |
| Проброс порта на локальную машину | kubectl port-forward svc/имя 8080:80 |
| Поды на конкретном узле | kubectl get pods -A --field-selector spec.nodeName=k8s-node1 |
| Выгнать поды с узла (перед обслуживанием) | kubectl drain k8s-node1 --ignore-daemonsets --delete-emptydir-data |
| Вернуть узел в строй | kubectl uncordon k8s-node1 |
Шаг 13. Золотое правило диагностики
Когда «ничего не работает», идите по цепочке снизу вверх и проверяйте каждое звено одной командой:
- Узел жив? →
kubectl get nodes - Под запущен? →
kubectl get pods -o wide - Что говорит сам под? →
kubectl describe pod+kubectl logs - Есть ли за сервисом живые поды? →
kubectl get endpoints - Работает ли сервис изнутри кластера? →
kubectl run tmp --rm -it --image=busybox -- wget -qO- http://имя-сервиса - Работает ли Ingress? →
kubectl describe ingressи логи контроллера
90% домашних проблем находится на первых трёх шагах этой цепочки.