Введение: что мы будем делать и зачем
Kubernetes решает задачу оркестрации контейнеров: распределяет нагрузку по узлам, перезапускает упавшие поды, масштабирует сервисы и управляет сетевым доступом. kubeadm - официальный инструмент для инициализации кластера, который берет на себя генерацию сертификатов, настройку control plane и подготовку команды для присоединения worker-узлов.
В результате вы получите production-ready кластер с одним control plane и несколькими worker-узлами. Все команды проверены на Ubuntu 22.04 LTS. Если вы уже работали с Kubernetes и хотите избежать типичных ошибок при настройке, обратите внимание на разбор 10 частых ошибок при эксплуатации кластера.
Минимальные требования к узлам: 2 vCPU, 2 GB RAM и 20 GB диска для control plane; 1 vCPU, 1 GB RAM и 20 GB диска для каждого worker-узла. Для production-нагрузок рекомендуется удвоить эти значения. Все узлы должны работать на одинаковой версии ОС и иметь сетевую связность между собой.
Архитектура кластера и требования к узлам
Кластер Kubernetes делится на два типа узлов: control plane управляет состоянием, worker-узлы запускают рабочие нагрузки. Понимание ролей компонентов помогает быстрее диагностировать проблемы.
Компоненты control plane и worker-узлов
kube-apiserver - единая точка входа для всех операций. Через него проходят kubectl-запросы, аутентификация и валидация объектов. etcd - распределенное key-value хранилище, в котором сохраняется всё состояние кластера: конфигурации, секреты, статусы подов. Потеря данных etcd означает потерю кластера, поэтому резервное копирование этого компонента критично.
kube-scheduler назначает поды на узлы на основе доступных ресурсов и правил affinity. kube-controller-manager запускает контроллеры, которые следят за соответствием текущего состояния желаемому: replication controller, endpoints controller, namespace controller и другие.
На worker-узлах работают два компонента: kubelet - агент, который получает задания от control plane и управляет контейнерами через container runtime; kube-proxy - сетевой прокси, который настраивает правила маршрутизации трафика к сервисам. Container runtime (в нашем случае containerd) непосредственно запускает и останавливает контейнеры.
Минимальные требования к оборудованию и ОС
Для тестового кластера достаточно: control plane - 2 vCPU, 2 GB RAM, 20 GB диска; worker - 1 vCPU, 1 GB RAM, 20 GB диска. Для production-среды закладывайте минимум 4 vCPU и 8 GB RAM на control plane, 4 vCPU и 8 GB RAM на каждый worker. Дисковая подсистема etcd чувствительна к задержкам, поэтому используйте SSD.
ОС: Ubuntu 22.04 LTS или аналогичная. Все узлы должны работать на одной версии ядра и ОС. Смешение версий приводит к несовместимости сетевых модулей и container runtime. Перед началом настройки отключите swap и настройте сетевые параметры ядра - эти шаги описаны в следующем разделе.
Подготовка узлов кластера
Подготовка ОС - этап, на котором чаще всего возникают ошибки. Пропущенный шаг с отключением swap или модулями ядра приводит к падению kubelet на старте. Выполните команды ниже на всех узлах кластера.
Отключение swap и настройка модулей ядра
kubelet требует отключенный swap. При активном swap планировщик Kubernetes не может корректно учитывать доступную память, а поды могут быть вытеснены на диск, что резко деградирует производительность. Отключите swap на постоянной основе:
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstabПроверьте, что swap отключен:
free -hВ строке Swap должно быть значение 0. Затем загрузите модули ядра overlay и br_netfilter. Первый нужен для работы файловой системы контейнеров, второй - для обработки сетевого трафика через iptables:
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilterПроверьте загрузку модулей:
lsmod | grep -E 'overlay|br_netfilter'Настройка параметров сети (iptables)
Для корректной маршрутизации трафика между подами ядро должно обрабатывать bridge-трафик через iptables. Создайте файл с параметрами sysctl:
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --systemПараметр net.bridge.bridge-nf-call-iptables включает обработку bridge-трафика правилами iptables. Без него сетевые политики Kubernetes не будут применяться к подам. Параметр net.ipv4.ip_forward разрешает пересылку пакетов между сетевыми интерфейсами, что необходимо для работы CNI-плагинов.
Установка и настройка containerd
containerd - container runtime, который Kubernetes использует для запуска контейнеров. Критичный момент - настройка systemd cgroup driver. Несовпадение cgroup driver у containerd и kubelet приводит к нестабильной работе подов и утечкам ресурсов.
Установка containerd и настройка cgroup driver
Установите containerd из репозитория Ubuntu:
sudo apt update
sudo apt install -y containerdСоздайте дефолтный конфиг и измените параметр SystemdCgroup:
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.tomlПерезапустите сервис:
sudo systemctl restart containerd
sudo systemctl enable containerd
sudo systemctl status containerdСтатус должен показывать active (running). Параметр SystemdCgroup = true указывает containerd использовать systemd как менеджер cgroups. Это согласует управление ресурсами между kubelet и container runtime.
Проверка работоспособности containerd
Установите crictl - CLI для взаимодействия с container runtime через CRI:
VERSION="v1.28.0"
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz
sudo tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C /usr/local/bin
rm crictl-$VERSION-linux-amd64.tar.gzНастройте endpoint для crictl:
cat <<EOF | sudo tee /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOFПроверьте, что containerd отвечает:
sudo crictl infoВывод покажет информацию о runtime и версии. Для полной проверки загрузите тестовый образ:
sudo crictl pull nginx:alpineОбраз должен скачаться без ошибок. После этого можно переходить к установке компонентов Kubernetes.
Инициализация control plane с помощью kubeadm
На этом этапе вы установите kubeadm, kubelet и kubectl, затем инициализируете control plane и получите команду для присоединения worker-узлов.
Установка kubeadm, kubelet и kubectl
Добавьте репозиторий Kubernetes и установите пакеты:
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectlКоманда apt-mark hold фиксирует версии пакетов, чтобы случайное обновление не сломало кластер. Проверьте установку:
kubeadm version
kubectl version --clientЗапуск kubeadm init и настройка kubectl
Инициализируйте control plane с указанием CIDR для pod-сети. Для Flannel используйте 10.244.0.0/16, для Calico - 192.168.0.0/16:
sudo kubeadm init --pod-network-cidr=10.244.0.0/16Процесс занимает 2-3 минуты. В выводе вы увидите секцию с командой kubeadm join - сохраните её, она понадобится для подключения worker-узлов. Настройте kubectl для текущего пользователя:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configПроверьте статус узла:
kubectl get nodesУзел будет в статусе NotReady до установки CNI-плагина. Это ожидаемое поведение: без сетевого плагина поды не могут получить IP-адреса.
Настройка сетевого плагина (CNI)
CNI-плагин обеспечивает связность подов и сервисов. Без него поды не могут общаться между собой, а DNS-резолвинг не работает. Выбор CNI влияет на производительность сети и доступные функции безопасности.
Выбор CNI: Flannel или Calico?
Flannel - простой плагин, который создает overlay-сеть на основе VXLAN. Подходит для небольших кластеров до 50 узлов. Calico - более функциональный: поддерживает network policies, BGP-маршрутизацию, шифрование трафика. Для production-среды выбирайте Calico, если вам нужны сетевые политики и изоляция подов.
Для этого руководства используем Flannel как наиболее простой вариант. Если вы планируете масштабировать кластер и внедрять сетевые политики, переходите на Calico сразу - миграция CNI в работающем кластере сложнее, чем выбор правильного плагина на старте.
Установка CNI и проверка статуса
Установите Flannel:
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.ymlДождитесь, пока все поды в namespace kube-system перейдут в статус Running:
kubectl get pods -n kube-system -wПосле этого проверьте статус узла:
kubectl get nodesControl plane узел должен перейти в статус Ready в течение 1-2 минут. Если статус долго остается NotReady, проверьте логи flannel-пода:
kubectl logs -n kube-system -l app=flannelПодключение worker-узлов к кластеру
Worker-узлы выполняют рабочие нагрузки. Для их подключения нужно повторить подготовку ОС, установить containerd и компоненты Kubernetes, затем выполнить команду kubeadm join.
Подготовка worker-узла и выполнение kubeadm join
На каждом worker-узле выполните шаги из разделов «Подготовка узлов кластера» и «Установка и настройка containerd», затем установите kubeadm, kubelet и kubectl. После этого выполните команду kubeadm join, сохраненную при инициализации control plane:
sudo kubeadm join 192.168.1.100:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>Токен действителен 24 часа. Если он истек, сгенерируйте новый на control plane:
kubeadm token create --print-join-commandНа control plane проверьте, что узел присоединился:
kubectl get nodesНовый узел должен появиться в списке и перейти в статус Ready после установки CNI-компонентов на нём.
Проверка работоспособности кластера
Разверните тестовое приложение и проверьте связность. Создайте deployment с nginx:
kubectl create deployment nginx-test --image=nginx:alpine
kubectl scale deployment nginx-test --replicas=3Проверьте, что поды распределились по узлам:
kubectl get pods -o wideСоздайте service для доступа к приложению:
kubectl expose deployment nginx-test --port=80 --type=NodePortПолучите порт и проверьте доступность:
kubectl get svc nginx-test
curl http://<worker-ip>:<node-port>Ответ nginx подтверждает, что сеть работает, поды запущены, а service корректно маршрутизирует трафик. Обратите внимание: service проверяется по опубликованным endpoints, а не только по selector. Если selector настроен верно, но endpoints пусты, трафик не дойдет до подов. Проверьте endpoints командой:
kubectl get endpoints nginx-testВ столбце ENDPOINTS должны отображаться IP-адреса подов. Пустой список указывает на проблему с labels или readiness-пробами.
Базовая настройка безопасности кластера
После развертывания кластер уязвим для несанкционированного доступа. Два обязательных шага: шифрование данных etcd и настройка RBAC с принципом наименьших привилегий. Детальный план аудита безопасности описан в руководстве по аудиту безопасности Kubernetes.
Включение TLS-шифрования etcd
По умолчанию etcd хранит данные в открытом виде. Любой, кто получит доступ к файлам etcd, сможет прочитать секреты, конфигурации и токены. Включите шифрование на уровне API-сервера. Создайте файл EncryptionConfiguration:
cat <<EOF | sudo tee /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: $(head -c 32 /dev/urandom | base64)
- identity: {}
EOFДобавьте параметр в манифест kube-apiserver:
sudo sed -i '/- kube-apiserver/a\ - --encryption-provider-config=/etc/kubernetes/encryption-config.yaml' /etc/kubernetes/manifests/kube-apiserver.yamlkube-apiserver автоматически перезапустится. Проверьте, что шифрование работает:
kubectl get secrets --all-namespaces -o json | grep -q 'aescbc' && echo "Encryption active"Учтите: шифрование применяется только к новым данным. Существующие секреты нужно пересоздать, чтобы они были зашифрованы.
Настройка RBAC и ограничение доступа
RBAC управляет доступом к ресурсам кластера. Создайте ServiceAccount для приложения, Role с минимальными правами и RoleBinding для их связывания:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role
namespace: default
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-binding
namespace: default
subjects:
- kind: ServiceAccount
name: app-sa
namespace: default
roleRef:
kind: Role
name: app-role
apiGroup: rbac.authorization.k8s.io
EOFОтключите анонимный доступ к API-серверу. Добавьте флаг в манифест kube-apiserver:
sudo sed -i '/- kube-apiserver/a\ - --anonymous-auth=false' /etc/kubernetes/manifests/kube-apiserver.yamlПосле перезапуска kube-apiserver анонимные запросы будут отклоняться. Проверьте, что kubectl продолжает работать - он использует сертификаты из kubeconfig, а не анонимный доступ.
Заключение и дальнейшие шаги
Вы развернули production-ready кластер Kubernetes: подготовили узлы, настроили containerd с systemd cgroup driver, инициализировали control plane через kubeadm, установили CNI-плагин, подключили worker-узлы и включили базовую безопасность. Кластер готов к развертыванию рабочих нагрузок.
Следующие шаги для укрепления кластера: настройте мониторинг с Prometheus и Grafana, настройте резервное копирование etcd, внедрите сетевые политики. Если вы планируете обновлять кластер в будущем, изучите пошаговое руководство по обновлению Kubernetes через kubeadm. Для проектирования отказоустойчивой архитектуры обратитесь к руководству по проектированию промышленных кластеров.