Kubernetes (K8s) - это система оркестрации с открытым исходным кодом, разработанная для автоматизации развертывания, масштабирования и управления контейнеризованными приложениями. Она позволяет упростить работу с контейнерами, обеспечивая автоматическое управление жизненным циклом приложений, балансировку нагрузки и масштабирование.
В Kubernetes узлы (Nodes) делятся на два основных типа: главные узлы (Master Nodes или Control Plane) и рабочие узлы (Worker Nodes). Главные узлы отвечают за управление кластером Kubernetes, в то время как рабочие узлы выполняют фактическую работу, запуская контейнеры с приложениями.
Так как эта статья может устареть, перед установкой лучше узнать актуальную информацию в официальных репозиториях или на сайте kubespray:
В отличие от kubeadm, в котором дополнительные компоненты приходится ставить вручную, kubespray позволяет их добавить в кластер при установке автоматически. Поэтому кластер созданный с помощью kubespray, уже с самого начала может не быть “голым” и подходить для использования в production среде.
Так же kubespray дает возможность установить кластер с несколькими Control Plane для отказоустойчивости. Но при использовании kubespray не все параметры k8s узлов выставляются автоматически, вот что делает сам kubespray, помимо установки компонентов k8s:
/etc/modules-load.d/k8s.conf
.modprobe br_netfilter overlay
.sysctl
параметры (например, net.ipv4.ip_forward=1
).А вот что нужно будет сделать вручную на узлах перед использованием kubespray:
Каждый узел Kubernetes должен иметь соответствующую DNS запись, чтобы узлы могли обращаться к друг другу по именам.
Для начала обновите дистрибутив и отключите файл подкачки (SWAP):
sudo dnf update
# Отключение файла подкачки
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
Так же для корректной работы требуется отключить SELinux:
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
Открытие портов на Master Node:
# Добавляем основные порты для Master Node
sudo firewall-cmd --permanent --zone=public --add-port=443/tcp
sudo firewall-cmd --permanent --zone=public --add-port=6443/tcp
sudo firewall-cmd --permanent --zone=public --add-port=2379-2380/tcp
sudo firewall-cmd --permanent --zone=public --add-port=10250/tcp
sudo firewall-cmd --permanent --zone=public --add-port=10259/tcp
sudo firewall-cmd --permanent --zone=public --add-port=10257/tcp
# Если используется CNI Calico (BGP)
sudo firewall-cmd --permanent --zone=public --add-protocol=ipip
sudo firewall-cmd --permanent --zone=public --add-port=9099/tcp
sudo firewall-cmd --permanent --zone=public --add-port=5473/tcp
sudo firewall-cmd --permanent --zone=public --add-port=179/tcp
sudo firewall-cmd --permanent --zone=public --add-port=4789/udp
## Разрешить весь трафик через интерфейсы Calico
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i cali+ -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i cali+ -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -o cali+ -j ACCEPT
## Разрешить весь трафик между подами
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.233.0.0/18 destination address=10.233.0.0/18 accept'
# Опционально: открываем NodePort диапазон
sudo firewall-cmd --permanent --zone=public --add-port=30000-32767/tcp
# Перезагружаем firewalld
sudo firewall-cmd --reload
# Проверяем открытые порты
sudo firewall-cmd --zone=public --list-ports
Открытие портов на Worker Node:
# Добавляем порты для Worker Node
sudo firewall-cmd --permanent --zone=public --add-port=443/tcp
sudo firewall-cmd --permanent --zone=public --add-port=10250/tcp
sudo firewall-cmd --permanent --zone=public --add-port=30000-32767/tcp
# Если используется CNI Calico (BGP)
sudo firewall-cmd --permanent --zone=public --add-protocol=ipip
sudo firewall-cmd --permanent --zone=public --add-port=9099/tcp
sudo firewall-cmd --permanent --zone=public --add-port=5473/tcp
sudo firewall-cmd --permanent --zone=public --add-port=179/tcp
sudo firewall-cmd --permanent --zone=public --add-port=4789/udp
## Разрешить весь трафик через интерфейсы Calico
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -i cali+ -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i cali+ -j ACCEPT
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -o cali+ -j ACCEPT
## Разрешить весь трафик между подами
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.233.0.0/18 destination address=10.233.0.0/18 accept'
# Для CoreDNS (если поды используют DNS)
sudo firewall-cmd --permanent --zone=public --add-port=53/udp
sudo firewall-cmd --permanent --zone=public --add-port=53/tcp
# Перезагружаем firewalld
sudo firewall-cmd --reload
# Проверяем открытые порты
sudo firewall-cmd --zone=public --list-ports
Помимо узлов самого K8s кластера, нам будет еще нужен узел с которого мы будем управлять созданием кластера посредством kubespray.
На нем нужно подготовить среду для работы python-venv :
sudo dnf makecache && sudo dnf update -y
sudo dnf install python3 python3-devel python3-pip git
После чего, нужно с kubespray узла обеспечить доступ по ssh без ввода пароля до всех k8s нод
ssh-keygen -t ed25519 -f ~/.ssh/kubespray
ssh-copy-id -i ~/.ssh/kubespray.pub service@master-1.k8s.rhel
ssh-copy-id -i ~/.ssh/kubespray.pub service@master-2.k8s.rhel
ssh-copy-id -i ~/.ssh/kubespray.pub service@master-3.k8s.rhel
ssh-copy-id -i ~/.ssh/kubespray.pub service@worker-1.k8s.rhel
ssh-copy-id -i ~/.ssh/kubespray.pub service@worker-2.k8s.rhel
Клонируем репозиторий Kubespray:
cd
git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
Создадим виртуальное окружение Python в каталоге kubespray-venv:
python3 -m venv kubespray-venv
source ~/kubespray/kubespray-venv/bin/activate
Установка необходимых зависимостей для проекта, перечисленных в файле requirements.txt:
pip install -U -r requirements.txt
Перейдём к установке Kubernetes-кластера. Подготовим наш инвентарь, шаблон находится в папке sample, мы его скопируем под новым именем и будем использовать при установке:
cp -rfp inventory/sample inventory/k8s
В новых версиях я не нашел возможность сгенерировать inventory автоматические поэтому, решил заполнить его вручную. Вот пример моей конфигурации файла inventory/k8s/inventory.ini:
[all]
master-1.k8s.rhel ip=172.16.1.101
master-2.k8s.rhel ip=172.16.1.102
master-3.k8s.rhel ip=172.16.1.103
worker-1.k8s.rhel ip=172.16.1.111
worker-2.k8s.rhel ip=172.16.1.112
[kube_control_plane]
master-1.k8s.rhel
master-2.k8s.rhel
master-3.k8s.rhel
[etcd]
master-1.k8s.rhel etcd_member_name=etcd1
master-2.k8s.rhel etcd_member_name=etcd2
master-3.k8s.rhel etcd_member_name=etcd3
[kube_node]
worker-1.k8s.rhel
worker-2.k8s.rhel
[calico_rr]
[k8s_cluster:children]
kube_control_plane
kube_node
[all:vars]
ansible_user=service
ansible_ssh_private_key_file=~/.ssh/kubespray
# Если все узлы Kubernetes в одной L2 сети, то для повышения производительности можно использовать
calico_ipv4pool_ipip=Never
# Для выхода подов в Интернет
calico_ipv4pool_nat_outgoing=true
Что за что отвечает?
Группа/Параметр | Назначение |
---|---|
[all] |
Все ноды кластера (должны быть перечислены здесь в первую очередь!). |
ip= |
Обязательный параметр — IP ноды в сети. |
[kube_control_plane] |
Ноды с компонентами kube-apiserver, kube-scheduler, kube-controller-manager. |
[etcd] |
Ноды с etcd (хранилище состояния кластера). Рекомендуется 3 или 5 нод. |
etcd_member_name |
Уникальное имя для etcd (например, etcd1 , etcd2 ). |
[kube_node] |
Worker-ноды, где будут запускаться Pod'ы. |
[k8s_cluster:children] |
Техническая группа, объединяющая мастеров и воркеров. |
Открываем файл inventory/k8s/group_vars/all/all.yml и редактируем следующие значения:
# При использовании нескольких мастеров нужно использовать встроенный LB
# Если отключить этот параметр, придется разворачивать LB для Master Nodes самостоятельно
loadbalancer_apiserver_localhost: true
# Если ввести параметр true, то в pod'ах будут использоваться DNS из списка upstream_dns_servers
# Если ввести параметр false, то в pod'ах будет использоваться тот же DNS что настроен на узле
disable_host_nameservers: false
upstream_dns_servers:
- 172.16.0.50
- 172.16.0.1
# Если у вас уже настроен NTP-client на узлах, то лучше оставить оба значения false
# В противном же случае нужно установить true и задать желаемые NTP серверы
ntp_enabled: false
ntp_manage_config: false
ntp_servers:
- "0.pool.ntp.org iburst"
- "1.pool.ntp.org iburst"
- "2.pool.ntp.org iburst"
- "3.pool.ntp.org iburst"
# Если при инициализации кластера нужна подробный ввод, в т.ч. с секретами, нужно поставить true
# В production среде лучше использовать false
unsafe_show_logs: false
В файле inventory/k8s/group_vars/k8s_cluster/k8s-cluster.yml так же содержаться важные значения:
kube_network_plugin: calico
kube_proxy_mode: ipvs
cluster_name: k8s.rhel
При использовании calico + ipvs нужно зайти в файл inventory/k8s/group_vars/k8s-cluster/k8s-net-calico.yml и добавить:
calico_ipvs_strict_arp: true
# Стандартную сеть Calico 192.168.0.0/16 было решено сменить
calico_ipv4pool_cidr: "10.233.64.0/18"
Зайдите в файл inventory/k8s/group_vars/k8s_cluster/addons.yml и включите желаемые дополнения, которые будут установлены в кластер:
# Стандартные дополенения (их лучше включать всегда при bare-metal установке)
helm_enabled: true
metrics_server_enabled: true
ingress_nginx_enabled: true
metallb_enabled: true
# Сетевые политики Calico
network_policy_enabled: true
# Стандартный Dashboard
dashboard_enabled: true
Проверка конфигурации Ansible на ошибки:
# Проверка доступности узлов через Ansible
ansible -i inventory/k8s/inventory.ini all -m ping
# Проверка прав sudo на узлах
ansible -i inventory/k8s/inventory.ini all -m ping --become --become-user=root
Перед запуском основного плейбука нужно совершить проверочный запуск для отлова большинства ошибок
ansible-playbook -i inventory/k8s/inventory.ini \
--become cluster.yml \
--tags=preinstall \
--check
Мы выполнили необходимые настройки для установки кластера, теперь пробуем запустить ansible с нужными нам параметрами.
ansible-playbook -i inventory/k8s/inventory.ini \
--become cluster.yml \
--become-user=root
И если все хорошо, после завершения работы Ansible (примерно минут 10 - 15), мы получим готовый кластер.
После чего с любой из Master Nodes можно управлять кластером выполнив команды:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Кластер уже создан, но может возникнуть надобность добавить в него еще парочку узлов, для этого не нужно пересоздавать кластер используя playbook cluster.yml, для этого есть специальный - scale.yml.
Значит ход действий при добавлении новых узлов в кластер будет следующий:
Плейбук нужно запустить следующим образом:
ansible-playbook -i inventory/k8s/inventory.ini --become scale.yml --become-user=root
Здесь все еще проще, нужно для начало очистить ноду от подов, а затем выполнить playbook для удаления из кластера:
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
ansible-playbook -i inventory/k8s/inventory.ini --become remove-node.yml \
-e "node=<node-name>" --become-user=root
Если нужно изменить параметры (например, CNI с Calico на Cilium, версию Kubernetes, параметры etcd):
Примеры изменений:
# Изменить версию Kubernetes
kube_version: "v1.34.1"
# Сменить CNI (например, с Calico на Cilium)
kube_network_plugin: "cilium"
# Изменить параметры etcd
etcd_deployment_type: "host" # или "docker", "kubeadm"
Применение изменений:
ansible-playbook -i inventory/k8s/inventory.ini cluster.yml --become cluster.yml --become-user=root
Важно:
Некоторые изменения (например, смена CNI) могут потребовать перезапуска кластера или миграции.
Изменение
kube_version
может потребовать апгрейда через минорные версии (например,1.27 → 1.28
, но не1.25 → 1.28
сразу).