Visitors have accessed this post 13146 times.

Создание кластера Kubernetes на VPS с помощью Kubespray

14
2
13146
20 ноября 2020 9:37
Автор: Rebrain Me
Kubernetes

Visitors have accessed this post 13146 times.

Автор — Андрей Трошин

k8s, Kubernetes

Не для кого не секрет, что сегодня этот оркестратор у всех на слуху. Из каждого IT-утюга доносится «k8s, кластер, CI\CD» и всякие другие неприличные и малопонятные слова. Давайте разберемся и попробуем собрать кластер Kubernetes на VPS.

Варианты установки

  • Стандартный репозиторий OS
  • Kubespray

В качестве хост-системы будем использовать CentOS 7 на всех узлах нашего k8s кластера (если у вас другая хост-система — ищите информацию на официальном сайте Kubernetes). В нашем случае выбираем вариант установки с помощью Kubespray. Kubespray — это набор Ansible ролей для установки и конфигурации Kubernetes. В свою очередь, Ansible — это инструмент для автоматизации настройки и развертывания программного обеспечения.

Подготовка

В дальнейшем мы будем использовать кластер для публикации разных сервисов и поэтому установка будет на VPS. Но мануал по установке будет также успешно работать, если вы захотите поднять k8s у себя на виртуальных машинах.

Что нам потребуется

  • Master node (OS CentOS 7: 1vCPU, RAM 2Gb, HDD 10Gb)
  • Worker node (OS CentOS 7: 1vCPU, RAM 2Gb, HDD 10Gb)
  • Worker node (OS CentOS 7: 1vCPU, RAM 2GB, HDD 10Gb)
  • Ingress node (OS CentOS 7: 2vCPU, RAM 8Gb, HDD 10Gb)

Эта конфигурация — это учебный вариант. С таким количеством узлов кластера в продуктиве, конечно же, запускаться не стоит. С другой стороны, это уже не minikube, а разнесенный по узлам кластер, хоть и маленький 🙂

Делаем k8s

Итак, у нас в распоряжении 4 узла кластера и для дальнейших действий на всех узлах необходимо сделать следующее:

  • Отключить Swap
  • Отключить Firewall
  • Сгенерировать и скопировать ssh-ключи (необходимо для того, что бы Ansible ходил по хостам и делал свои дела)

Поехали.

Отключаем SWAP.

swapoff -a

Отключаем Firewall.

firewall-cmd --state
systemctl stop firewalld
systemctl disable firewalld

Заходим на master ноду по ssh и генерируем ssh-ключ, копируем его на все хосты кластера (включая master хост, на котором был сгенерирован ключ). По очереди подставляем IP каждого узла кластера на место remote_host.

ssh-keygen
ssh-copy-id root@remote_host

Продолжим подготавливать master. Для дальнейшей работы потребуется pip и собственно git.

curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
python get-pip.py
yum install git

С подготовительной частью мы закончили. Возвращаемся на master ноду и дальнейшие операции до конца установки кластера будем совершать с нее. Скачиваем репозиторий к себе на хост и переходим в директорию kubespray.

git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray

Далее устанавливаем python-библиотеку requests и другие зависимости из файла requirements.txt.

pip install --ignore-installed requests==2.23.0
pip install -r requirements.txt

В результате у нас получилось следующее:

  • На master ноде установили Ansible.
  • Мы сгенерировали и скопировали ssh-ключ на все узлы кластера.
  • Установили все необходимые зависимости.

Приступаем к конфигурации Kubespray. Для того чтобы Kubespray понимал, на какие узлы ему устанавливать k8s, необходимо создать директорию. В ней мы опишем конфигурацию будущего кластера. Копируем /inventory/sample/ в новую директорию и переименовываем, в моем случае — в root/, и в ней открываем файл inventory.ini.

[root@master-1 inventory]# ll
total 12
drwxr-xr-x. 2 root root 4096 Nov  5 15:08 local
drwxr-xr-x. 3 root root 4096 Nov  5 15:18 root
drwxr-xr-x. 3 root root 4096 Nov  5 15:18 sample
[root@master-1 inventory]# cd root/
[root@master-1 root]# ll
total 8
drwxr-xr-x. 4 root root 4096 Nov  5 15:08 group_vars
-rw-r--r--. 1 root root  994 Nov  5 15:08 inventory.ini
[root@master-1 root]#

Типичный inventory.ini:

# ## Configure 'ip' variable to bind kubernetes services on a
# ## different ip than the default iface
# ## We should set etcd_member_name for etcd cluster. The node that is not a etcd member do not need to set the value, or can set the empty string value.
[all]
# node1 ansible_host=95.54.0.12  # ip=10.3.0.1 etcd_member_name=etcd1
# node2 ansible_host=95.54.0.13  # ip=10.3.0.2 etcd_member_name=etcd2
# node3 ansible_host=95.54.0.14  # ip=10.3.0.3 etcd_member_name=etcd3
# node4 ansible_host=95.54.0.15  # ip=10.3.0.4 etcd_member_name=etcd4
# node5 ansible_host=95.54.0.16  # ip=10.3.0.5 etcd_member_name=etcd5
# node6 ansible_host=95.54.0.17  # ip=10.3.0.6 etcd_member_name=etcd6

# ## configure a bastion host if your nodes are not directly reachable
# bastion ansible_host=x.x.x.x ansible_user=some_user

[kube-master]
# node1
# node2

[etcd]
# node1
# node2
# node3

[kube-node]
# node2
# node3
# node4
# node5
# node6

[calico-rr]

[k8s-cluster:children]
kube-master
kube-node
calico-rr

Конфигурация не сложная, и на первый взгляд понятно, что в секции [all] идет присвоение IP-адресов ко всем нодам. В остальных секциях идет компановка кластера. Ниже приведен пример inventory.ini c моими IP-адресами (у вас они могут отличаться).

# ## Configure 'ip' variable to bind kubernetes services on a
# ## different ip than the default iface
# ## We should set etcd_member_name for etcd cluster. The node that is not a etcd member do not need to set the value, or can set the empty string value.
[all]
master-1.root.local.io ansible_host=192.168.0.5 ip=192.168.0.5
ingress-1.root.local.io ansible_host=192.168.0.4 ip=192.168.0.4
node-1.root.local.io ansible_host=192.168.0.6 ip=192.168.0.6
node-2.root.local.io ansible_host=192.168.0.3 ip=192.168.0.3
# ## configure a bastion host if your nodes are not directly reachable
# bastion ansible_host=x.x.x.x ansible_user=some_user

[kube-master]
master-1.root.local.io

[etcd]
master-1.root.local.io

[kube-node]
node-1.root.local.io
node-2.root.local.io
ingress-1.root.local.io

[kube-ingress-1]
ingress-1.root.local.io

[k8s-cluster:children]
kube-node
kube-master

Разберем, что следует из такой конфигурации:

  • Наш кластер состоит из 4 узлов.
  • Одна master-нода с компонентами Сontrol Plane.
  • Одна нода с Ingress для маршрутизации трафика и две Worker ноды, на которых будут запускаться наши сервисы.
  • Control Plane (API server, etcd, Sheduler, Controle manager) собран на одной ноде. Это очень нехорошо и обычно в секции [kube-master] более одного узла, а в секции [etcd] — более трех узлов. Почему 3 узла в etcd? Потому что quorum собирается не меньше, чем в 3 узла и с нечетным общим количеством узлов в etcd. Но в нашем учебном случае такой вариант оптимальный.

Сохраняем изменения в inventory.ini и идем в директорию group_vars/. Создаем там файл kube-ingress.yml с содержимым:

node_labels:
  node-role.kubernetes.io/ingress: ""
node_taints:
  - "node-role.kubernetes.io/ingress=:NoSchedule"

Это для корректной настройки Ingress контроллера. Далее редактируем файл k8s-cluster/addons.yml. Добавляем helm, сбор метрик для мониторинга и функционал Nginx ingress controller:

  • helm_enabled: true
  • metrics_server_enabled: true
  • Убираем комментирование с Nginx ingress controller deployment
ingress_nginx_enabled: true
ingress_nginx_host_network: false
ingress_publish_status_address: ""
ingress_nginx_nodeselector:
  kubernetes.io/os: "linux"
ingress_nginx_tolerations:
  - key: "node-role.kubernetes.io/master"
    operator: "Equal"
    value: ""
    effect: "NoSchedule"
ingress_nginx_namespace: "ingress-nginx"
ingress_nginx_insecure_port: 80
ingress_nginx_secure_port: 443
ingress_nginx_configmap:
  map-hash-bucket-size: "128"
  ssl-protocols: "SSLv2"
ingress_nginx_configmap_tcp_services:
  9000: "default/example-go:8080"
ingress_nginx_configmap_udp_services:
  53: "kube-system/coredns:53"
ingress_nginx_extra_args:
  - --default-ssl-certificate=default/foo-tls

Также редактируем k8s-cluster/k8s-cluster.yml. Меняем network plugin на flannel и имя нашего кластера на root.local:

  • kube_network_plugin: flannel
  • cluster_name: root.local

Немножко подправим k8s-cluster/k8s-net-flannel.yml. Тут обычный regexp на сеть нашего провайдера VPS. У меня она 192.168.0.0/24. У вас может отличаться. Исправляем:

  • flannel_interface_regexp: ‘192\.168\.0\.\d{1,9}’

Готово! Конфигурация закончена. Мы в рамках пяти конфигурационных файлов, смогли описать все необходимое для сборки. Вспомним:

  • inventory.ini — назначаем IP-адреса и компонуем кластер.
  • kube-ingress.yml — определяем Ingress.
  • addons.yml — включаем в сборку необходимые компоненты. Добавляем helm, сбор метрик для мониторинга и функционал Nginx ingress controller.
  • k8s-cluster.yml — выбираем network plugin и задаем имя нашему кластеру.
  • k8s-net-flannel.yml — настраиваем network plugin.

Теперь осталоcь запустить playbook Ansible, который соберет кластер, и дождаться его выполнения. Переходим в корень директории kubespray и выполняем в терминале команду:

ansible-playbook -u root -i inventory/root/inventory.ini cluster.yml -b --diff

Разберем параметры. Запускаем ansible-playbook от имени root с инвентарем inventary.ini. Дальше Ansible начнет свою работу по настройке кластера. Обычно это занимает от 10 до 20 минут. Если в это время у вас вдруг отвалиться соединение ssh или еще какая беда случится и прервется сборка, то ее можно запустить повторно и это не вызовет ошибок. В итоге, должно получиться как на скрине ниже.

Кластер собран и готов к работе. Проверяем состав нод командой kubectl get nodes.

[root@master-1 kubespray]# kubectl get nodes
NAME                      STATUS   ROLES    AGE    VERSION
ingress-1.root.local.io   Ready    <none>   2d1h   v1.19.3
master-1.root.local.io    Ready    master   2d1h   v1.19.3
node-1.root.local.io      Ready    <none>   2d1h   v1.19.3
node-2.root.local.io      Ready    <none>   2d1h   v1.19.3

Видим, что кластер имеет одну мастер-ноду, две рабочие ноды и ингресс.

Но тут, если посмотреть в ROLE, можно заметить, что нашему Ingress-1 не назначена роль ingress. Исправляем kubectl label node ingress-1.root.local.io node-role.

kubernetes.io/ingress=ay]# kubectl label node ingress-1.root.local.io node-role. 
node/ingress-1.root.local.io labeled
[root@master-1 kubespray]#

И повторно смотрим, чтобы назначилась нужная нам роль. Для расширенного просмотра информации по нодам можно использовать команду kubectl get nodes -owide.

[root@master-1 kubespray]# kubectl get nodes
NAME                      STATUS   ROLES     AGE    VERSION
ingress-1.root.local.io   Ready    ingress   2d1h   v1.19.3
master-1.root.local.io    Ready    master    2d1h   v1.19.3
node-1.root.local.io      Ready    <none>    2d1h   v1.19.3
node-2.root.local.io      Ready    <none>    2d1h   v1.19.3
[root@master-1 kubespray]#

Поздравляю! У вас получилось собрать k8s кластер и теперь можно развернуть в нем простенькое приложение. Но это уже тема следующих статей.

От редакции

Если вам интересно посещать бесплатные онлайн-мероприятия по DevOps, Kubernetes, Docker, GitlabCI и др. и задавать вопросы в режиме реального времени, подключайтесь к каналу DevOps by REBRAIN

*Анонсы мероприятий каждую неделю

Практикумы для специалистов по инфраструктуре и разработчиков — https://rebrainme.com.

Наш Youtube-канал — https://www.youtube.com/channel/UC6uIx64IFKMVmj12gKtSgBQ.

Агентство Fevlake, проектируем и поддерживаем IT-инфраструктуры с 2012 года — https://fevlake.com.

Комментарии (3)
Введено символов из возможных
Не отвечать
ttserg

Cпасибо за статью. 2 обслуживающему персоналу — по какой причине код не влазит в форму, например выдача метки .
2 автор, этот вариант развернет ингресс на всех нодах , а нужен ли он нам везде, в том числе и на мастерах….. Я бы предложил поиграться с лейблом kubernetes.io/os: , точнее вместо него завести другой, соответствующий узко ингрессу.

ttserg

2 сервис — функционал редактирования коммента скоро добавить? И спасибо автору за статью, ждем обещанного продолжения

Вам также может понравится

Программный RAID в Linux. Часть 2
array(1) { [0]=> object(WP_Term)#11558 (16) { ["term_id"]=> int(6) ["name"]=> string(5) "Linux" ["slug"]=> string(5) "linux" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(6) ["taxonomy"]=> string(8) "category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(28) ["filter"]=> string(3) "raw" ["cat_ID"]=> int(6) ["category_count"]=> int(28) ["category_description"]=> string(0) "" ["cat_name"]=> string(5) "Linux" ["category_nicename"]=> string(5) "linux" ["category_parent"]=> int(0) } } Linux

Автор — Максим Рязанов

Давайте продолжим нашу тему про RAID. Напомню, что первую часть статьи можно найти тут.
Обслуживание RAID
Очистка RAID — Scrubbing
Рекомендуется регулярно выполнять scrubbing данных, чтобы проверять и исправлять ошибки. В зависимости от размера / конфигурации массива очистка может занять несколько часов.

Чтобы...

5
434
18 декабря 2020
Как мы боролись со спамом — чтобы быстро и бесплатно. И забороли
array(1) { [0]=> object(WP_Term)#976 (16) { ["term_id"]=> int(1) ["name"]=> string(7) "Default" ["slug"]=> string(7) "default" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(1) ["taxonomy"]=> string(8) "category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(1) ["filter"]=> string(3) "raw" ["cat_ID"]=> int(1) ["category_count"]=> int(1) ["category_description"]=> string(0) "" ["cat_name"]=> string(7) "Default" ["category_nicename"]=> string(7) "default" ["category_parent"]=> int(0) } } Default

Автор - Сергей Попов

Привет, друзья. Сегодня я расскажу, как мы боролись со спамом в одной коммерческой организации. Наверняка, вы все знаете, что такое SPAM, но добавлю немного истории.

Слово «спам» имеет интересное происхождение. После Второй мировой войны в США оказалось слишком много мясных консервов от фирмы-производителя SPAM. И для...

25
0
7 декабря 2020
Упрощаем работу с оповещениями в zabbix — настраиваем  теги
array(1) { [0]=> object(WP_Term)#11558 (16) { ["term_id"]=> int(7) ["name"]=> string(6) "DevOps" ["slug"]=> string(6) "devops" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(7) ["taxonomy"]=> string(8) "category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(19) ["filter"]=> string(3) "raw" ["cat_ID"]=> int(7) ["category_count"]=> int(19) ["category_description"]=> string(0) "" ["cat_name"]=> string(6) "DevOps" ["category_nicename"]=> string(6) "devops" ["category_parent"]=> int(0) } } DevOps

Автор - Евгений Генеральчик

В любой системе мониторинга крайне сложно найти золотую середину по оповещениям. Это та идеальная ситуация, когда алертов достаточно и они приходят своевременно для качественного реагирования на них. И при этом вы не тонете под лавиной алертов, когда уже невозможно выделить среди них действительно важные.

Как же...

2
0
14 августа 2020