Что вы получите в итоге и кому подходит это руководство
К концу материала у вас будет рабочий кластер Kubernetes на собственных серверах или виртуальных машинах: control plane поднят, worker-ноды в статусе Ready, CNI установлен, StorageClass отвечает на заявки PVC, приложения можно деплоить обычным kubectl apply. Ставим через kubeadm с containerd в роли container runtime. Это стандартный путь для продакшена, когда нужен контроль над версиями каждого компонента и полное понимание того, что происходит внутри.
Руководство рассчитано на DevOps-инженеров и системных администраторов, которые уверенно работают в Linux и понимают, что такое контейнер, образ и namespace. Базовый синтаксис systemd, работу с репозиториями пакетов и правку конфигов считаем известными, поэтому на этих деталях не задерживаемся.
Минимальная топология для продакшена: один control plane и два worker-узла. Для отказоустойчивости control plane потребуется три узла и балансировщик перед API server. Официальный туториал Kubernetes по canary-развёртыванию, например, прямо требует кластер минимум с двумя узлами, которые не выполняют роль control plane (документация Kubernetes). Двух worker-нод хватает, чтобы проверить отказоустойчивость деплоя: поды разъедутся по разным машинам, и падение одной не остановит сервис.
Порядок работы такой: подготовка узлов, отключение swap, настройка модулей ядра и firewall, установка containerd, kubeadm init, выбор и установка CNI, присоединение worker-нод, хранилище, проверка работоспособности, разбор типичных ошибок. Если вам нужен более короткий вариант с акцентом только на базовую установку, посмотрите пошаговое руководство по установке кластера Kubernetes с kubeadm: там те же этапы, но без развёрнутого блока про хранилище и диагностику.
Выбор дистрибутива Kubernetes для продакшена в 2026 году
Дистрибутив определяет, сколько ручной работы ляжет на вас и что вы получите из коробки. kubeadm даёт bare-кластер: вы сами ставите CNI, сами выбираете storage, сами настраиваете обновления. k3s упаковывает control plane в один бинарник и запускается быстрее, но по умолчанию тянет за собой Flannel и local-path-provisioner. RKE2 собирает Kubernetes с усиленными настройками безопасности и CIS-профилем, что важно для проектов с требованиями комплаенса.
По версиям ориентир на 2026 год такой: Kubernetes 1.37 требует container runtime, совместимый с CRI, а Dockershim удалён из проекта начиная с 1.24. Отдельно стоит знать про Dynamic Resource Allocation: это стабильная функция с версии 1.35, впервые появилась в 1.30, и отключить её больше нельзя (описание DRA в документации Kubernetes). Для железа вроде GPU или FPGA это меняет способ выделения устройств подами, но на процесс первичной установки кластера не влияет.
Сравнительная таблица: kubeadm, k3s, RKE2
| Критерий | kubeadm | k3s | RKE2 |
|---|---|---|---|
| Сложность установки | Средняя: все компоненты ставите сами | Низкая: один бинарник или скрипт | Низкая: установочный скрипт |
| Минимум для control plane | 2 CPU, 2 GB RAM (для продакшена 4 CPU, 8 GB RAM) | 1 CPU, 1 GB RAM | 2 CPU, 2 GB RAM |
| Минимум для worker | 1 CPU, 2 GB RAM | 1 CPU, 512 MB RAM | 1 CPU, 2 GB RAM |
| Отказоустойчивость | 3 control plane и внешний балансировщик | 3 ноды с встроенным etcd | 3 ноды с встроенным etcd |
| CNI из коробки | Нет | Flannel | Canal (Calico + Flannel) |
| Хранилище из коробки | Нет | local-path-provisioner | local-path-provisioner |
| Безопасность по умолчанию | Базовые настройки, всё настраиваете сами | Умеренная | Усиленная, профиль CIS |
| Типовой сценарий | Продакшен с полным контролем | Edge, тесты, небольшие кластеры | Продакшен с требованиями безопасности |
Для типового продакшена в 2026 году выбираем kubeadm и containerd. Полный контроль над CNI, хранилищем и версиями компонентов стоит потраченных вечеров. k3s берите, если кластер живёт на слабом железе или на периферии. RKE2 оправдан там, где аудит требует CIS-профиль и минимум ручных правок безопасности.
Почему Docker больше не используется и что это значит для установки
Старые гайды начинаются с установки Docker Engine и содержат флаг --cri-socket. С Kubernetes 1.24 Dockershim удалён из проекта, и прямой интеграции с Docker Engine в Kubernetes больше нет: эта связка не входит в состав проекта с 1.24 (документация по container runtime). Kubernetes 1.37 требует CRI-совместимого runtime, и containerd или CRI-O закрывают это требование. Docker Engine в новых версиях сам использует containerd внутри, но kubelet должен обращаться к самому containerd, а не к обёртке Docker.
Проверить, что runtime настроен правильно, можно двумя командами:
kubectl get nodes -o wide crictl info
В колонке CONTAINER-RUNTIME будет containerd со версией, а crictl info вернёт конфигурацию рантайма. Если таблица пустая или узлы висят в NotReady, ищите причину в journalctl -u kubelet: чаще всего kubelet не может подключиться к сокету containerd.
Требования к ресурсам и подготовка узлов
Минимальные требования для учебного контура: control plane 2 CPU, 2 GB RAM и 20 GB на диске, worker 1 CPU, 2 GB RAM и 20 GB. Для продакшена эти цифры умножайте: 4 CPU и 8 GB RAM для control plane, 4 CPU и 8-16 GB RAM для worker. etcd чувствителен к задержкам диска, поэтому под control plane берите SSD или NVMe, а не сетевой HDD. Если своих серверов нет, подойдут облачные VDS: Timeweb Cloud даёт серверы, базы данных, хранилище и Kubernetes, ресурсы можно менять по ходу проекта.
Подготовка выполняется на каждом узле без исключений. Пропущенный шаг на одной worker-ноде приводит к тому, что она не присоединится к кластеру.
Отключение swap и настройка модулей ядра
Сначала swap. Kubelet рассчитан на работу с реальной памятью и не стартует при включённом swap:
swapoff -a sed -i '/ swap / s/^/#/' /etc/fstab
Первая команда гасит swap в текущей сессии, вторая комментирует его в fstab, чтобы после перезагрузки он не вернулся. Проверьте результат командой free -h: в строке Swap должно быть нулевое значение.
Теперь модули ядра и параметры сети. Ядро Linux по умолчанию не пропускает IPv4-пакеты между интерфейсами, а большинство сетевых функций Kubernetes рассчитывают на включённую пересылку (документация Kubernetes).
cat <<EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter cat <<EOF | tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system
Модуль overlay обеспечивает работу файловых систем контейнеров, br_netfilter пропускает трафик через iptables. Параметр net.ipv4.ip_forward = 1 включает маршрутизацию пакетов, без него поды не выйдут за пределы своего узла.
Настройка firewall и необходимых портов
Firewall настраивайте до установки кластера. Закрытый порт 6443 приведёт к тому, что worker-ноды не присоединятся, а вы будете искать причину в kubelet.
| Узел | Порт и протокол | Назначение |
|---|---|---|
| Control plane | 6443/tcp | API server |
| Control plane | 2379-2380/tcp | etcd client и peer |
| Control plane и worker | 10250/tcp | kubelet API |
| Control plane | 10259/tcp | kube-scheduler |
| Control plane | 10257/tcp | kube-controller-manager |
| Worker | 30000-32767/tcp | NodePort Service |
| Calico | 179/tcp | BGP |
| Calico | 4789/udp | VXLAN |
Пример для ufw на control plane:
ufw allow 6443/tcp ufw allow 2379:2380/tcp ufw allow 10250/tcp ufw allow 10259/tcp ufw allow 10257/tcp ufw reload
Вариант для firewalld:
firewall-cmd --permanent --add-port=6443/tcp firewall-cmd --permanent --add-port=2379-2380/tcp firewall-cmd --permanent --add-port=10250/tcp firewall-cmd --reload
SELinux на RHEL-подобных системах держите включённым, но в режиме permissive хотя бы на время установки, иначе kubelet упрётся в отказы доступа к каталогам. AppArmor на Ubuntu оставляйте в штатном режиме: политики Kubernetes с ним совместимы.
Установка и настройка containerd
Сначала kubelet и kubectl. Подключите официальный репозиторий пакетов Kubernetes для нужной минорной версии, затем:
apt-get update apt-get install -y kubelet kubeadm kubectl apt-mark hold kubelet kubeadm kubectl
Команда apt-mark hold фиксирует версии: автоматическое обновление kubelet при плановом обновлении пакетов ломает совместимость компонентов. Обновляйте kubelet, kubeadm и kubectl вместе и осознанно.
Дальше containerd. Ставьте из официального репозитория проекта, минимальная рабочая версия для Kubernetes 1.37 - 1.7.x, но берите свежую стабильную ветку. После установки сгенерируйте конфиг и включите cgroup driver systemd:
mkdir -p /etc/containerd containerd config default | tee /etc/containerd/config.toml sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml systemctl restart containerd systemctl enable containerd
Почему именно systemd: kubelet и container runtime обязаны использовать один и тот же cgroup driver, и при systemd в роли init-системы рекомендуют драйвер systemd, а не cgroupfs. С версии 1.22 kubeadm по умолчанию выставляет systemd, если драйвер не задан явно (документация по container runtime). При ручной правке containerd.conf этого значения нет, и рассинхрон драйверов даёт периодические падения подов с ошибками cgroup.
Проверка после перезапуска: systemctl status containerd должен показать active (running), а crictl info вернуть заполненную конфигурацию runtime.
Установка control plane с kubeadm
Инициализация control plane: команда и параметры
Перед init зафиксируйте два значения: адрес API server и диапазон адресов подов. Диапазон потом менять сложно, он должен совпадать с настройками CNI.
kubeadm init \ --control-plane-endpoint=10.0.0.10:6443 \ --pod-network-cidr=192.168.0.0/16 \ --upload-certs
Параметр --control-plane-endpoint задаёт адрес, по которому узлы видят API server. Для одного control plane это IP машины, для HA - DNS-имя балансировщика, и тогда переезд узла не потребует перенастройки kubelet на всех worker-нодах. Диапазон 192.168.0.0/16 подходит Calico, для Flannel используйте 10.244.0.0/16. Флаг --upload-certs выгружает сертификаты в Secret, чтобы упростить добавление второго и третьего control plane.
Вывод команды обязательно сохраните: в конце kubeadm печатает команду kubeadm join с токеном и хешем сертификата. Именно ей присоединяются worker-ноды, и после перезапуска терминала восстановить её из истории не всегда получится.
Настройка kubectl и проверка статуса
Kubeconfig лежит в /etc/kubernetes/admin.conf и принадлежит root. Скопируйте его для своего пользователя:
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config kubectl get nodes
На этом этапе control plane будет в статусе NotReady. Это нормально: без CNI узел не готов принимать поды. Поды CoreDNS останутся в Pending по той же причине. Посмотреть, что происходит, можно так:
kubectl get pods -n kube-system kubectl describe node control-plane-1 journalctl -u kubelet -n 100 --no-pager
Если kubelet вообще не поднялся, ищите в его логах ошибку подключения к containerd или сообщение про включённый swap.
Установка CNI-плагина и настройка сети
CNI отвечает за адресацию подов и их связность между узлами. Без него кластер существует только формально: узлы NotReady, поды в Pending, DNS не работает. Подробный разбор модели сети с адресами подов, Service и диагностикой собран в отдельном материале про сети в Kubernetes, CNI и Service network.
Выбор CNI: Calico, Cilium или Flannel
| CNI | NetworkPolicy | eBPF | BGP | Сложность |
|---|---|---|---|---|
| Calico | Да | Опциональный dataplane | Да | Средняя |
| Cilium | Да | Да, по умолчанию | Опционально | Средняя и высокая |
| Flannel | Нет | Нет | Нет | Низкая |
Для продакшена берите Calico: NetworkPolicy из коробки, предсказуемая маршрутизация, BGP при соединении с физической сетью. Cilium выбирайте, если нужны eBPF-ускорение, наблюдаемость на уровне L7 и вы готовы разбираться с его моделью. Flannel оставьте для лабораторий и кластеров, где политики безопасности не нужны. Детальные замеры и критерии выбора собраны в сравнении какой CNI-плагин выбрать в 2026 году.
Установка Calico и проверка сетевой связности
Скачайте манифест Calico нужной версии из официального репозитория проекта и перед применением проверьте в нём переменную CALICO_IPV4POOL_CIDR: она должна совпадать с --pod-network-cidr, который вы указали при kubeadm init. Расхождение - самая частая причина неработающей сети после установки.
kubectl apply -f calico.yaml kubectl get pods -n kube-system -l k8s-app=calico-node kubectl get nodes
Дождитесь, когда поды calico-node перейдут в Running, а узлы сменить NotReady на Ready. Затем проверьте реальную связность между подами:
kubectl run test1 --image=busybox --restart=Never -- sleep 3600 kubectl run test2 --image=busybox --restart=Never -- sleep 3600 kubectl get pods -o wide kubectl exec -it test1 -- ping -c 3 <IP пода test2>
Ответные пакеты без потерь означают, что overlay-сеть работает, kube-proxy прописал маршруты, а CNI распределил адреса по узлам. Проблемы с MTU в overlay-сетях и разбор маршрутизации в bridge и overlay режимах разобраны в материале про сетевую маршрутизацию в Docker и Kubernetes.
Присоединение worker-нод к кластеру
Генерация команды join и присоединение
Токен из вывода kubeadm init живёт 24 часа. Если он истёк, сгенерируйте новый на control plane:
kubeadm token create --print-join-command
Команда выведет готовую строку вида kubeadm join 10.0.0.10:6443 --token <токен> --discovery-token-ca-cert-hash sha256:<хеш>. Выполните её под root на worker-ноде, подготовленной так же, как control plane: swap отключён, модули загружены, containerd с SystemdCgroup = true настроен.
kubectl get nodes -o wide
Все узлы должны перейти в Ready, в колонке ROLES у worker-нод будет . Проверить, что нода действительно принимает нагрузку, можно тестовым деплоем с двумя репликами.
Диагностика проблем при присоединении
Три причины покрывают большинство сбоев. Первая: неверный или просроченный токен, решается командой kubeadm token create --print-join-command. Вторая: несовпадение cgroup driver, kubelet стартует, но поды падают. Третья: firewall блокирует 6443 или 10250, соединение отваливается по таймауту.
journalctl -u kubelet -f kubectl describe node worker-1 crictl ps
Логи kubelet показывают, на каком шаге обрывается соединение: TLS-рукопожатие, регистрация ноды, получение CNI-конфига. kubectl describe node выводит условия Ready, MemoryPressure и DiskPressure с расшифровкой причины. crictl ps на самой ноде подтверждает, что рантайм жив и контейнеры запускаются.
Настройка хранилища для продакшена
Выбор StorageClass и CSI-драйвера
| Вариант | Тип | Репликация | Сценарий |
|---|---|---|---|
| local-path-provisioner | Локальные тома | Нет | Тесты, одна нода |
| Longhorn | Распределённое блочное | Да, 3 реплики | Средние кластеры на своей инфраструктуре |
| Ceph RBD и CephFS | Распределённое блочное и файловое | Да | Крупные кластеры |
| Облачный CSI | Зависит от провайдера | Да | Managed Kubernetes и облако |
local-path-provisioner привязывает том к конкретному узлу. Узел упал - под с этим PVC не переедет, поэтому в продакшене такой вариант не подходит. Longhorn закрывает типовую задачу: блочные тома с репликацией между узлами и снапшотами, без отдельного кластера хранения. Ceph берите, когда томов сотни и нужны S3-совместимое объектное хранилище и файловые шары.
Установка Longhorn и проверка PVC
kubectl apply -f longhorn.yaml kubectl -n longhorn-system get pods kubectl get storageclass
Манифест Longhorn возьмите из официального репозитория проекта. Все поды в namespace longhorn-system должны быть Running, а в списке StorageClass появится longhorn. Назначьте его классом по умолчанию, чтобы PVC без явного указания класса получали тома именно от него:
kubectl patch storageclass longhorn -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
Проверочный PVC выглядит так:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
kubectl apply -f test-pvc.yaml kubectl get pvc
Статус Bound означает, что CSI-драйвер создал том и привязал его к заявке. Если PVC висит в Pending, смотрите события: kubectl describe pvc test-pvc и логи подов csi-provisioner в longhorn-system.
Проверка работоспособности кластера и диагностика
Чек-лист проверки кластера
| Проверка | Команда | Ожидаемый результат |
|---|---|---|
| Статус узлов | kubectl get nodes | Все узлы Ready |
| Системные поды | kubectl get pods -A | Все поды Running, кроме завершённых Job |
| Адрес API | kubectl cluster-info | Выведен адрес control plane |
| DNS | kubectl run test --image=busybox -- nslookup kubernetes.default | Ответ с IP сервиса kubernetes |
| Деплой | kubectl create deployment nginx --image=nginx --replicas=2 | 2 пода Running |
| Хранилище | kubectl get pvc | Bound |
Особое внимание подам CoreDNS в namespace kube-system. Если они не в Running, DNS внутри кластера не работает, и сервисы не находят друг друга по именам, хотя сеть между подами жива.
Диагностика типовых сбоев
Узел NotReady почти всегда означает проблему с kubelet или CNI. Проверьте journalctl -u kubelet и поды calico-node. Под в Pending означает нехватку ресурсов на узлах, несовместимый nodeSelector или неготовый PVC: kubectl describe pod <имя> покажет событие FailedScheduling с причиной. Ошибки приложений ищите в kubectl logs <под> и kubectl logs <под> --previous, если контейнер уже перезапускался.
Для сетевых проблем полезен crictl: он показывает контейнеры на конкретном узле независимо от состояния API server. Команды crictl ps, crictl logs и crictl inspect дают ответ, когда kubectl недоступен.
Типичные ошибки при установке и как их избежать
- Swap не отключён. Kubelet не стартует, в логах сообщение про swap on. Решение: swapoff -a и правка /etc/fstab, затем перезапуск kubelet.
- Разные cgroup driver у kubelet и containerd. Поды запускаются и падают, в логах ошибки записи в cgroup. Решение: SystemdCgroup = true в /etc/containerd/config.toml и перезапуск containerd.
- Firewall блокирует порты. Worker-ноды не присоединяются, kubeadm join падает по таймауту. Решение: открыть 6443, 2379-2380, 10250 на control plane и 10250 на worker.
- Неверный pod-network-cidr. Узлы Ready, но поды не видят друг друга. Решение: привести CALICO_IPV4POOL_CIDR в манифесте CNI в соответствие с диапазоном, заданным при kubeadm init.
- Попытка использовать Docker Engine вместо CRI-runtime. Kubelet не находит сокет, кластер не поднимается. Решение: containerd или CRI-O, Dockershim не входит в Kubernetes с 1.24.
- CNI не установлен. Control plane и worker в NotReady, CoreDNS в Pending. Решение: применить манифест выбранного CNI и дождаться Running.
- Просроченный токен join. Новая нода не присоединяется, в логах kubelet ошибка авторизации. Решение: kubeadm token create --print-join-command и повторный запуск join.
Все семь пунктов проверяются до продакшена, а не после инцидента. Разбор более широкого набора граблей, включая requests и limits, RBAC, NetworkPolicy и обновления, собран в материале про 10 частых ошибок при настройке и эксплуатации Kubernetes.
Что дальше: подготовка кластера к деплою приложений
Кластер готов, но принимать трафик из интернета он пока не умеет. Следующий шаг: Ingress-контроллер (ingress-nginx или Traefik), затем cert-manager для автоматических TLS-сертификатов от Let's Encrypt. Без Ingress каждый сервис придётся публиковать через NodePort, а это открытый диапазон 30000-32767 на всех узлах.
Дальше мониторинг и логирование. Prometheus с Grafana закрывают метрики узлов, подов и control plane, Loki или ELK собирают логи. Отдельно настройте резервное копирование etcd: без снапшота восстановить кластер после потери control plane нельзя. Для managed-сценария, когда не хочется администрировать control plane самостоятельно, подойдёт облачный Kubernetes: Timeweb Cloud предлагает Kubernetes, серверы и хранилище в одном кабинете.
Безопасный выкат новых версий удобно отрабатывать на canary-развёртывании: стабильная версия запускается с 3 репликами, canary с 1, общий селектор Service распределяет трафик так, что примерно 25% запросов уходит на новую версию и 75% на прежнюю (туториал Kubernetes по canary-развёртыванию). Схема позволяет поймать регресс на малой доле трафика и откатить релиз, не трогая основную часть подов.
Стартовый набор для продакшена выглядит так: три control plane за балансировщиком, минимум два worker-узла, Calico, Longhorn, Ingress с TLS, Prometheus с Grafana, автоматические снапшоты etcd. Соберите кластер по шагам из этой статьи, прогоните чек-лист проверки и только после этого открывайте доступ приложениям.