Вступление
Данное руководство содержит инструкции по установке кластера Kubernetes версии 1.30 используя такие инструменты как kubeadm и Helm. Предполагается, что все узлы кластера будут находится в одной локальной сети Hetzner, а запросы из Интернета к пользовательским приложениям будут осуществлятся через балансировщик нагрузки Hetzner. Данная конфигурация позволяет заблокировать входящие соединения из Интернета на узлы кластера, что делает кластер более безопасным.
На кластер будет установлен Flannel в качестве CNI (Container Network Interface) и CRI-O в качестве CRI (Container Runtime Interface).
Предпочтение CRI-O было отданно по следующим причинам:
- CRI-O более безопасен чем containerd поскольку имеет меньшее количество свойств и внутренних компонентов, что значительно снижает поверхность для атаки
- CRI-O имеет прозрачную структуру компонентов опубликованную в открытом доступе
- У CRI-O активное сообщество
Устанавливая компоненты шаг за шагом вы научитесь собирать кластер как конструктор и управлять балансировщиком нагрузки Hetzner используя аннотации заданные в файле значений для контроллера входящего трафика.
Предварительные требования
- 3 виртуальные машины в облаке Hetzner (одна для узла плоскости управления, две для рабочих узлов) с установленной на них операционной системой Ubuntu 24.04, подключенные к одной общей сети Hetzner
- Helm установленный на одну из виртуальных машин
- Токен для доступа к API Hetzner созданный через web-интерфейс Hetzner
- TLS сертификат импортированный в web-интерфейсе Hetzner:
https://console.hetzner.cloud/projects/<project_id>/security/certificates
Шаг 1 - Подключение репозиториев
На этом шаге мы добавим Kubernetes и CRI-O репозитории на все узлы будующего кластера.
Устанавливаем версию Kubernetes как переменную окружения
KUBERNETES_VERSION=v1.30
Добавляем ключ и репозиторий Kubernetes
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/Release.key | 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:/$KUBERNETES_VERSION/deb/ /" | tee /etc/apt/sources.list.d/kubernetes.list
Добавляем ключ и репозиторий CRI-O
curl -fsSL https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/ /" | tee /etc/apt/sources.list.d/cri-o.list
Шаг 2 - Установка пакетов
Следующим шагом будет установка пакетов CNI и компонентов Kubernetes на все узлы кластера.
Обновляет список доступных пакетов
apt update
Устанавливаем пакеты
apt install -y cri-o kubelet kubeadm kubectl
crio --version && kubelet --version && kubeadm version && kubectl version --client
Отключаем автоматическое обновление для только что установленных пакетов
apt-mark hold kubelet kubeadm kubectl
Шаг 3 - Настройка операционной системы
Kubernetes требует некоторых предварительных настроек операционной системы. Если вы используете образ Ubuntu 24.04 предоставленный Hetzner, тогда вам не требуется отключать SWAP, так как он он выключен по умолчанию. Просто выполните команды ниже на всех узлах кластера.
Подключаем модуль ядра br_netfilter
echo "br_netfilter" >> /etc/modules-load.d/modules.conf
Разрешаем переадресацию пакетов между разными сетями
sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g' /etc/sysctl.conf
Перезагружаем узел для применения настроек
reboot
Шаг 4 - Инициализация кластера
Инициализация кластера выполняется на узле предназначенном для плоскости управления (Control Plane node).
В данном руководстве предполагается использовать только 1 узел с ролью Control Plane, но если вы разворачиваете кластер в продуктивной среде, то для обеспечения должной отказоустойчивости рекомендуется использовать как минимум 3.
Получаем конфигурационный файл инциализации со значениями по умолчанию
kubeadm config print init-defaults > InitConfiguration.yaml
Вам необходимо добавить или изменить следующие значения внутри файла:
bootstrapTokens.token: abcdef.0123456789abcdef
- вы можете получить токен начальной загрузки используя командуkubeadm token generate
localAPIEndpoint.advertiseAddress: 10.0.0.2
- этот параметр конфигурации позволяет вам определить, на каком IP-адресе и порту будет доступен локальный API сервер. По умолчанию kubeadm пытается автоматически определить IP-адрес интерфейса по умолчанию и использовать его. Установите здесь IP-адрес вашего узла управления внутри локальной сети HetznernodeRegistration.criSocket: unix:///var/run/crio/crio.sock
- путь к сокет-файлу CRI-OnodeRegistration.kubeletExtraArgs.cloud-provider: external
- необходимо установить значениеexternal
для запуска с внешними облачными провайдерамиnodeRegistration.kubeletExtraArgs.node-ip: 10.0.0.2
- IP адрес вашего узла управления внутри локальной сети HetznernodeRegistration.name: your_host
- имя узла управленияapiServer.certSANs: ['your_host.example.com', '10.0.0.2']
- SAN (Subject Alternative Names) для сертификата API. Вы можете использовать несколько значений. Например FQDN и локальный IP адрес узла управленияnetworking.podSubnet: 10.244.0.0/16
- подсеть для Pod'ов
Инициализируем кластер
kubeadm init --config InitConfiguration.yaml
Шаг 5 - Присоединение узлов к кластеру
Теперь мы можем добавить оставшиеся узлы к кластеру. Запустите команды ниже на всех остальных узлах.
Получаем конфигурационный файл со значениями по умолчанию
kubeadm config print join-defaults > JoinConfiguration.yaml
Вам необходимо добавить или изменить следующие значения внутри файла:
discovery.bootstrapToken.apiServerEndpoint: your_host.example.com:6443
- установите FQDN своего Control Plane узлаdiscovery.bootstrapToken.token: abcdef.0123456789abcdef
- задайте значение аналогичное указанному в InitConfiguration.yaml файлеdiscovery.tlsBootstrapToken: abcdef.0123456789abcdef
- задайте значение аналогичное указанному в InitConfiguration.yaml файлеnodeRegistration.criSocket: unix:///var/run/crio/crio.sock
- путь к сокет-файлу CRI-OnodeRegistration.kubeletExtraArgs.cloud-provider: external
- необходимо установить значениеexternal
для запуска с внешним облачным провайдеромnodeRegistration.kubeletExtraArgs.node-ip: 10.0.0.3
- локальный IP адрес текущего сервера внутри сети HetznernodeRegistration.name: your_host
- имя рабочего узла
Присоединяем узел к кластеру
kubeadm join --config JoinConfiguration.yaml
Назначаем роли рабочем узлам, путём добавления соотвествующей метки. Запустите эти команды на узле управления
mkdir -p $HOME/.kube
cp /etc/kubernetes/admin.conf $HOME/.kube/config
kubectl get nodes
kubectl label node <имя первого рабочего узла> node-role.kubernetes.io/worker=worker
kubectl label node <имя второго рабочего узла> node-role.kubernetes.io/worker=worker
Шаг 6 - Установка CNI плагина
В этом руководстве в качестве CNI используется Flannel. Если вы хотите использовать сетевые политики с целью организовать более высокий уровень безопасности, вам следует рассмотреть другие плагины.
Создаем пространство имён
kubectl create ns kube-flannel
Добавляем ему привелегий. Политика privileged
не имеет ограничений, все Pod'ы в пространстве имён kube-flannel смогут обойти типичные механизмы изоляции контейнеров. Например, они смогут получить доступ к сети узла.
kubectl label --overwrite ns kube-flannel pod-security.kubernetes.io/enforce=privileged
Подключаем helm репозиторий
helm repo add flannel https://flannel-io.github.io/flannel/
Вы можете скачать values.yaml
файл из оффицального репозитория kube-flannel. Для успешного запуска плагина вам необходимо установить значение podCidr
аналогично значению networking.podSubnet
в файле InitConfiguration.yaml.
Устанавливаем Flannel в ранее созданное пространство имён
helm install flannel flannel/flannel --values values.yaml -n kube-flannel
Компоненты в которых значение cloud-provider
определенно как external
, пометят узлы кластера как node.cloudprovider.kubernetes.io/uninitialized
с эффектом NoSchedule
. Эти метки устанавливают узлы в состояние ожидания повторной инициализации от облачного контроллера. Так как в нашем случае внешний контроллер ещё не установлен, узлы кластера не смогут планировать CoreDNS Pod'ы. Для снятия этого ограничения выполните команду ниже.
kubectl -n kube-system patch deployment coredns --type json -p '[{"op":"add","path":"/spec/template/spec/tolerations/-","value":{"key":"node.cloudprovider.kubernetes.io/uninitialized","value":"true","effect":"NoSchedule"}}]'
Шаг 7 - Установка облачного контроллера Hetzner
Облачный контроллер Hetzner отвечает за интеграцию кластера Kubernetes с Hetzner API. Установка облачного контроллера на кластер позволит использовать службы предоставляемые Hetzner. В нашем руководстве мы воспользуемся службой балансировки нагрузки Hetzner.
Создаем Kubernetes секрет содержащий ваш токен доступа к API Hetzner (смотрите пункт "Предварительные требования" в разделе Вступление) и некий идентификатор сети Hetzner. Идентификатором сети являются последние цифры взятые из URL вашей сети в web-интерфейсе облака Hetzner. Например, для URL https://console.hetzner.cloud/projects/98071/networks/2024666/resources
это будет 2024666
kubectl -n kube-system create secret generic hcloud --from-literal=token=<токен доступа к Hetzner API> --from-literal=network=<идентификатор сети Hetzner>
Добавляем и обновляем helm репозиторий
helm repo add hcloud https://charts.hetzner.cloud
helm repo update hcloud
Устанавливаем облачный контроллер Hetzner
helm install hccm hcloud/hcloud-cloud-controller-manager -n kube-system
Шаг 8 - Установка контроллера входящего трафика
В этом руководстве мы будем использовать Nginx Ingress в качестве контроллера входящего трафика. TLS шифрование будет осуществляться балансировщиком нагрузки Hetzner, который в свою очередь будет автоматически создан во время установки контроллера входящего трафика.
Перед началом установки нам необходимо в ручную провести инициализацию рабочих узлов кластера
kubectl taint nodes <имя первого рабочего узла> node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule-
kubectl taint nodes <имя второго рабочего узла> node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule-
Добавляем и обновляем helm репозиторий
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
Файл значений можно найти в оффициальном репозитории Ingress Nginx. Измените следующие значения в файле:
-
controller.dnsPolicy: ClusterFirstWithHostNet
-
controller.hostNetwork: true
- по умолчанию при использовании параметраhostNetwork
в значенииtrue
, для разрешения имен используется DNS узла. Если вы хотите, чтобы контроллер входящего трафика продолжал разрешать имена внутри сети Kubernetes используйтеhostNetwor
совместно сClusterFirstWithHostNet
-
controller.kind: DaemonSet
- устанавливаем контроллер как DaemonSet -
controller.service.annotations
- аннотации ниже задают параметры конфигурации для балансировщика нагрузки Hetzner. Полный список и описание доступных параметров можно найти в оффицальном репозитории облачного контроллера HetznerПример
controller: [...] service: [...] annotations: load-balancer.hetzner.cloud/name: "k8s-test-lb" load-balancer.hetzner.cloud/location: "fsn1" load-balancer.hetzner.cloud/type: "lb11" load-balancer.hetzner.cloud/ipv6-disabled: "true" load-balancer.hetzner.cloud/use-private-ip: "true" load-balancer.hetzner.cloud/protocol: "https" load-balancer.hetzner.cloud/http-certificates: "certificatename" load-balancer.hetzner.cloud/http-redirect-http: "true"
load-balancer.hetzner.cloud/name: "k8s-test-lb"
- здесь мы указываем имя балансировщика нагрузки. Имя будет отображаться в соотвествующем разделе web-интерфейса облака Hetznerload-balancer.hetzner.cloud/location: "fsn1"
- указываем местонахождение дата-центра в котором будет создан балансировщик. Для данного руководства местонахождение должно совпадать с местонахождением виртуальных машин вашего кластера Kubernetesload-balancer.hetzner.cloud/type: "lb11"
- определяем тип блансировщикаload-balancer.hetzner.cloud/ipv6-disabled: "true"
- отключаем использование протокола IPv6load-balancer.hetzner.cloud/use-private-ip: "true"
- указываем балансировщику использовать локальные IP адреса внутри Hetzner сети для обращения к узлам кластераload-balancer.hetzner.cloud/protocol: "https"
- назначаем протокол по которому будет работать балансировщикload-balancer.hetzner.cloud/http-certificates: "certificatename"
- список идентификаторов или имён сертификатов, которые будут использоваться для шифрования трафика между клиентам из Интернета и балансировщиком (смотрите пункт "Предварительные требования" в разделе Вступление)load-balancer.hetzner.cloud/http-redirect-http: "true"
- включаем перенаправление HTTP на HTTPS
-
controller.service.enableHttp: false
- отключаем HTTP для Kubernetes служб контроллера -
controller.service.targetPorts.https: http
- порт контроллера входящего трафика на который будет перенаправлять запроы балансировщик. Нам нет необходимости использовать шифрование трафика между балансировщиком и кластером Kubernetes, поэтому мы можем указать HTTP (80) порт
Устанавливаем контроллер входящего трафика
helm install ingress-nginx-controller ingress-nginx/ingress-nginx -f values.yaml -n kube-system
Шаг 9 - Установка CSI драйвера (Опционально)
На момент написания статьи, у Hetzner доступно подключение томов только в режиме ReadWriteOnce
. Всё же это может быть полезным, поэтому я рекомендую установить CSI драйвер.
Добавляем и обновляем helm репозитории
helm repo add hcloud https://charts.hetzner.cloud
helm repo update hcloud
Устанавливаем CSI драйвер предоставленный Hetzner
helm install hcloud-csi hcloud/hcloud-csi -n kube-system
Заключение
В резльтате мы имеем кластер состоящий из одного узла управления и двух рабочих узлов, с подключенным к нему баласировщиком нагрузки.
Теперь вы можете заблокировать весь входящий трафик из Интернета на всех узлах кластера Kubernetes и приступить к установке вашего приложения.