Tech Recipe Book
My Services
  • Book
    • About the author
    • Architect
      • Algorithms
        • DB index algorithms
          • How does database indexing work
        • Neural network optimization
          • Neural Network Optimization
        • Route search
          • Road network in a database to build a route
          • Traveling Salesman Problem (TSP)
      • Architecture Frameworks
        • DODAF
        • TOGAF
        • Enterprise Architecture (EA) Tools Reviews 2023 | Gartner
      • Zero Trust
      • Billing
        • SHM billing system
      • Bots
        • Discord
        • Telegram
          • Chat GPT Telegram bot
          • Получаем статистику Telegram-канала при помощи api и python или свой tgstat с регистрацией и смс
          • Как хостить телеграм-бота (и другие скрипты на Python) на Repl.it бесплатно 24/7
          • Создание Telegram бота на PHP #1: основные понятия для работы с API
          • Создание Telegram бота на PHP #2: создание первого бота для Telegram
          • Создание Telegram бота на PHP #3: примеры отправки сообщений с кнопками в Telegram
          • Создание Telegram бота на PHP #4: отправка файлов и изображений в Telegram
          • Создание Telegram бота на PHP #5: работа с хуками
      • Business intelligence
      • Cloud Storage
        • Ceph
        • Virtual Distributed File System
      • Cryptography
        • Open Source PKI Software
        • OpenPGP
          • Email Encryption
          • Kleopatra
          • Miscellaneous Tools
          • Server side applications
      • Message broker
        • Kafka
          • Kafka UI-tools
          • Kafka streams ksqlDb
        • RabbitMQ
      • DB
        • MySQL
          • Auto sharding
          • MariaDB Zabbix monitoring
          • MySQL and MariaDB replication with Zabbix monitoring
        • Postgres
          • HA PostgreSQL with Patroni, Haproxy, Keepalived
          • Mass parallel requests - Greenplum
          • PostgreSQL cluster for development and testing
        • Vitess - Scalable. Reliable. MySQL-compatible. Cloud-native. Database.
      • Identity and Access Management (IDM)
        • FreeIPA - Identity, Policy, Audit
        • FreeIPA as an Enterprise solution
        • Keycloak
          • Keycloak HA cluster
        • Open Identity Platform
        • SSO
          • Keycloak for Java app
          • OpenAM
          • OpenIG
      • Firewall
        • nftables
      • Infrastructure As a Code
        • Ansible
        • IaC Packer Ansible Teraform
        • Installing Jenkins using terraform in Kubernetes in Yandex Cloud with letsencypt
        • Teraform Crosplan Pulumi
        • Yandex IaC solutions
      • Kubernetes
        • Installation
          • Install Kubernetes cluster
          • Deploying a Kubespray cluster to OpenStack using Terraform
          • Kube deploy in Yandex cloud
        • Frameworks
          • Deckhouse
            • LDAP authentification
            • On premise Install
            • Yandex Cloud Install
          • K3S
          • OpenShift OKD
          • RKE2
          • Rancher
            • Rancher Install
        • Auth
          • Keycloak in k8s
          • LDAP
        • GUI management Lens
        • Monitoring
          • Monitoring with Falco
          • Network monitoring
          • Nginx ingress
          • Prometheus Graphana for sample Nodejs app
          • Rsource monitoring Avito
        • Exposing services
          • Exposing Kubernetes Services
          • Cilium BGP
        • CNCF
        • Helm
          • Repositories
            • Artifact Hub | official
            • Bitnami | vmware
          • Awesome helm charts and resources
          • Essential Services for Modern Organizations
          • Security and Compliance
          • Additional charts
        • Isolation
          • vcluster - Virtual Kubernetes Clusters
          • Kiosk
          • KubeArmor
          • Control Plane Hardening
          • Hierarchical namespaces
        • Security Center
          • Minesweeper
          • NeuVector by SUSE
          • SOAR in Kubernetes
          • Security Сenter for Kubernetes
        • Terraform CI security
          • Terraform plan analysis with Checkov and Bridgecrew
          • Yandex Terraform scan
        • Vulnerability management
          • Aqua
          • Sysdig
          • Kyverno
          • GitLab
          • NeuVector by SUSE
        • Image scanning
          • Snyk
          • Sysdig
          • Harbor
          • Trivy
        • Signature verification
          • Sigstore
        • Control plane security
          • Gatekeeper
            • Applying OPA Gatekeeper
          • Kyverno
            • Policy as a code. Kyverno
        • Runtime Security
          • Osquery
          • Falco
          • ClamAV
        • Network security
          • Cilium
          • Control Plane Hardening (API restriction)
          • Network policy recipes
          • Service mesh
            • Istio HA, LoadBalance, Rate limit
          • mTLS Autocert
        • Honeypot
          • Building honeypot using vcluster and Falco
        • Backup
          • Kasten K10
        • Secrets
          • Vault CSI Driver
      • Load Balance
        • Nginx
        • HAProxy
          • Proxy methods
          • HAProxy for RDP
          • Payment gateway A/B test with HAProxy
          • HAPRoxy for Percona or Galera
      • Monitoring
        • Zabbix
          • Apache Zabbix
          • Disc Quota
          • Nginx Zabbix
          • SSL certificates Zabix
          • Zabbix notifications
        • Nagios
          • Datacenter monitoring
        • Prometheus and Grafana
      • Windows
        • Sysmon enhanced Windows audit
        • Sysmon to Block Unwanted File
      • Linux
        • Rsync
        • Debian based
          • Apt-Cacher NG
          • Unattended Upgrades in Debian / Ubuntu
        • RedHat basede
          • RPM Server
        • Logs analysis
        • Build armhf qemu
      • NGFW
      • CI/CD
        • DevSecOps
          • DAST
            • Burp
              • Dastardly
            • StackHawk
            • ZAP and GitHub Actions
          • SAST
            • Checkmarx
            • OSV by Google
            • Snyk
            • SonarQube
        • GitLab Runner in Yandex Cloud
        • Dynamic Gitlab Runners in Yandex Cloud
        • GitLab runner in Kubernetes with Werf
        • Kubernetes deploy strategies
        • Kubernetes highload deploy. part 1
        • Kubernetes highload deploy. part 2
        • Kubernetes Argo Rollouts
        • Jenkins in Kubernetes
        • Ansible Semaphore
        • Image storage, scaning and signing
        • Install WireGuard with Gitlab and Terraform
        • CI/CD example fror small web app
        • Threat matrix for CI CD Pipeline
      • SIEM / SOC
        • Datadog
        • Splunk
          • Splunk — general description
        • MaxPatrol
          • MaxPatrol 8 and RedCheck Enterprise
        • QRadar IBM
        • Cloud Native Security Platform (CNAPP) - Aqua
        • OSSIM | AT&T
          • AlienVault (OSSIM) install
        • Wazuh
        • EDR
          • Cortex XDR | Palo Alto Networks
          • Cynet
          • FortiEDR | Fortinet
          • Elastic
        • Elastic
          • Install Elasticsearch, Logstash, and Kibana (Elastic Stack) on Ubuntu 22.04
          • Setting Up Elastic 8 with Kibana, Fleet, Endpoint Security, and Windows Log Collection
        • Threat Intelligence
          • MISP
          • msticpy Microsoft
          • X-Force | IBM
          • Elastic
      • VPN
        • Full-Mesh VPN fastd, tinc, VpnCloud
        • Wireguard
          • WireGuard for Internet access
          • WireGuard on MikroTik and Keenetic
          • WireGuard site to site
        • SoftEther VPN Project
        • Cisco AnyConnect client
        • OpenConnect
        • SSTP python server
      • OS hardening
        • CIS Benchmarks
      • Cloud Providers
      • OpenNebula
        • OpenNebula Edge Cloud - Open Source Cloud & Edge Computing
        • Discover OpenNebula – Open Source Cloud & Edge Computing Platform
        • OpenNebula Multi-Cloud
        • Kubernetes on OpenNebula
        • The Open Source Alternative to Nutanix
        • The Simple Alternative to OpenStack
        • OpenNebula Partner Ecosystem
      • OpenStack
        • Install manual
        • Install with DevStack
      • VM
        • Create a VHD file from a Linux disk
        • Backup / Migration
          • Coriolis
          • Proxmox Backup Server
        • oVirt
        • VMware vCenter
        • Proxmox
      • Docker
        • Container optimization
        • Ubuntu RDP container
      • LXC
        • LXD on Ubuntu 18.04
        • Install, Create and Manage LXC in Ubuntu/Debian
    • Big Data
      • OLAP data qubes
      • Storage and autoscale in Lerua
    • Machine Learning
      • Yandex YaLM 100B. GPT model
      • Kaggle Community Datasts Models
      • AI in video production
      • Image search
      • Chat bots
        • You.com
        • Chat GPT
          • Implementing GPT in NumPy
        • Jailbreak Chat
      • Coding plugins CodeWhisperer
    • Malware
      • Isiaon/Pitraix: Modern Cross-Platform Peer-to-Peer Botnet over TOR
      • theZoo A repository of LIVE malwares
    • Pentest
      • Red Team
        • MITRE ATT&CK matrix
        • C2 Frameworks
          • Brute Ratel C4
          • Cobalt Strike
          • Covenant
          • Havoc Framework
          • Merlin
          • Metasploit
          • Sillenttrinity
          • Sliver
        • Manage and report
          • Dradis Framework
          • Hexway
        • Underground
      • Social engineering
        • Social Engineer Toolkit setoolkit
      • OSINT
        • OSINT for comapny
        • Instagram fishing
      • Forensics
        • Forensics tools
      • Pentesting Methodology
      • Web
      • CI/CD Methodology
      • Cloud Methodology
        • Hacking The Cloud
      • Kubernetes Pentesting
      • Android
        • SSL Unpinning for Android applications
      • iOS
        • SSL unpinning iOS and macOS applications
      • HackBar tool
      • CyberChef Tools
      • Python virtualenv
      • IppSec - YouTube
      • Hacktricks.xyz
    • Compliance
      • 152 ФЗ. Personal data
      • PCI DSS and ГОСТ Р 57580.1-2017
      • Cloud compliance
      • ГОСТ Р 57580.1-2017 для Kubernetes
      • Kubernets as DevSecOps and NIST compliance
      • NIST SP 800-61 cyberincidece control
      • CIS Kubernetes Benchmark v1.6 - RKE2 v1.20
      • CIS Kubernetes Benchmark v1.23 - RKE2
      • Requirements for Russian Banks
      • Tools
        • Chef InSpec
        • Elastic SIEM
    • Asset management
      • CMDBuild
    • Project management
    • Incident management SRE
    • Risk management
      • IT risk management
      • BSI-Standard 200-3
    • Web Dev
      • Cookie security
      • OWASP Top 10 2021
      • Docker nginx php mysql
      • Docker tor hiddenservice nginx
      • Docker Compose wp nginx php mariadb
      • Dependency Checking
        • Nexus Analyzer
        • OWASP dependency-check
      • Yii skeeks cms
      • YiiStudio
    • Art
      • GTK Themes
      • Themes for Xfce Desktop
      • XFCE / Xubuntu Windows 95
      • Moscow events
      • Photo goods
      • Russian style gifts
    • Cryptocurrency
      • News
      • Arbitrage
      • Stocks
      • Exchange aggregators
      • Where to use
      • Prepaid cards
        • BitFree
        • Pyypl Your Money at Your Fingertips
    • IT magazines
      • WIKI and Writeups tools
        • BookStack
        • GitBook
        • MkDocs
        • Wiki.js
        • DokuWiki
    • Languages
    • Learning
      • (ISC)2
        • CISSP
      • Offensive Security
        • OSCP
        • OSEP
        • OSED
      • DevSecOps
        • Certified DevSecOps Professional (CDP)
        • Certified DevSecOps Expert (CDE)
      • Web Security Academy: PortSwigger
    • Relocation
      • London experience
      • IT visas in 2022
      • Remote work
      • Running business in UAE
    • Freenet
      • Independent online services: the philosophy of a free Internet
      • Tor Project Anonymity Online
      • I2P Anonymous Network
    • Services
      • SMS Registration
        • Registering ChatGPT in Russia
      • Local and regional eSIMs for travellers - Airalo
      • Digital busines cards
      • No KYC services and exchanges
Powered by GitBook
On this page
  • Оглавление
  • Введение
  • Установка высокодоступного Kubernetes-кластера
  • Настройка виртуальных машин
  • Настройка Ansible-машины
  • Установка кластера
  • Обслуживание кластера
  • Добавление worker node
  • Удаление worker node
  • Замена master node
  • Полезные команды

Was this helpful?

  1. Book
  2. Architect
  3. Kubernetes
  4. Installation

Install Kubernetes cluster

Last updated 1 year ago

Was this helpful?

В предыдущей я рассказывал, как построить простой кластер Kubernetes с одним мастер-узлом. Прошло время, опали листья... и мне захотелось большего, поэтому решил позариться на высокодоступные кластеры. В интернете много статей о том, как построить подобное решение, и давайте даже опустим тот факт, что многие из них уже устарели. Одно дело — установить кластер, а как же обслуживание: удаление, добавление, замена узлов? Про это и не вспоминают! В итоге оказалось, что не всё так просто, и вот, спустя больше ста установок, удалений и замен, у меня получилось собрать подробнейшее руководство по установке и, главное, обслуживанию highly available кластера с помощью Kubespray.

Оглавление

Введение

  1. Отделить плоскость управления (мастер) от рабочих узлов

  2. Реплицировать компоненты плоскости управления на несколько узлов

  3. Добавить балансировщик нагрузки на API Kubernetes

  4. Иметь достаточное количество рабочих узлов, чтобы выдержать нештатные ситуации и высокие нагрузки

Если подходить серьезно к вопросу, то наш будущий кластер будет квази high-available (в основном из-за внешнего балансировщика). Кластер будет состоять из четырёх узлов — двух мастеров и двух рабочих. Машины с характеристиками получше можно использовать как рабочие, а машины послабее — как мастера. На последних не будет запускаться рабочая нагрузка.

Сетап кластера:

  1. Kubenetes version - 1.23.7

  2. Сontainer runtime - Сontainerd 1.6.4

  3. Network plugin - Calico 3.22.3

Доступные виртуалки и их роли я распределил так:

  1. IP 185.186.142.53 - Master #1

  2. IP 185.186.142.5 - Master #2

  3. IP 46.8.19.144 - Worker #1

  4. IP 46.8.19.244 - Worker #2

У всех машин характеристики: 2 CPU 3,0 ГГц, 4 Гб RAM, 100 Гб HDD, ОС Ubuntu 20.04. На рабочих машинах заявлена частота процессора 3,6 ГГц, хотя команда grep MHz /proc/cpuinfo говорит, что фактически 3 ГГц (на мастер-узлах и того чуть меньше). По стоимости четыре машины в месяц обходятся в 2700 руб. (с учётом скидки за оплату на три месяца вперёд). Хочу опять отметить, насколько такой handmade дешевле Kubernetes-as-a-Service решений, аж на 75%!

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

Устанавливать кластер будем с домашней машины (далее — Ansible-машина). На ней должны быть Python, Ansible версии 2.4 или выше, и Jinja (это всё мы установим немного позже).

Большая головная боль при установки Kubernetes на "голое" железо - это отсутствие внешнего балансировщика. Для пользователя облачного сервиса GKE или AWS балансировщик прилагается в комплекте.

Enabling this option exposes every system daemon to the NGINX Ingress controller on any network interface, including the host's loopback. Please evaluate the impact this may have on the security of your system carefully.

  1. Сложно управлять пулом адресов. Хорошо если DNS провайдер предоставляет API для программного изменения адресов. Но это лишний софт, плюс надо учитывать TTL на стороне клиента, его в таком случае советуют ставить на минимальное значение.

альтернативное решение

Приступим к делу.

Установка высокодоступного Kubernetes-кластера

Настройка виртуальных машин

Этот раздел необходимо выполнить для каждой виртуальной машины будущего кластера. Прежде всего настроим SSH-доступ без пароля. Для этого выполните команду ssh-copy-id на своей локальной машине. Например, у меня она выглядит так:

ssh-copy-id root@185.186.142.53

Тут необходимо будет ввести пароль от удалённой машины. После успешного выполнения команды подключаемся к удалённой машине без пароля:

ssh root@185.186.142.53

Установим Python:

sudo apt update
sudo apt install python

Включим переадресацию IPv4:

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

Отключим подкачку памяти:

swapoff -a

Пример из жизни №1

Раньше я полагал, что поставщики виртуалок предоставляют чистые образы ОС. Как оказалось, это не совсем так. Я пробовал установить Kubernetes на VPS двух провайдеров. У первого всё работало замечательно, а у второго (который был предпочтителен из-за низкой цены) установка кластера прерывалась на середине из-за внезапного пропадания интернета, а точнее, невозможности скачать определённый файл.

Поиск в интернете упорно ничего не давал, подсказали только в техподдержке: предложили заглянуть в файл resolv.conf и проверить наличие в конфигурации серверов адресов 1.1.1.1 или 8.8.8.8. Как оказалось, у этого провайдера интересная преднастройка ОС, и файл /etc/resolv.conf представляет собой simlink. О чём недвусмысленно сообщал объёмный комментарий в самом файле:

# This file is managed by man:systemd-resolved(8). Do not edit.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Run "resolvectl status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 127.0.0.53
options edns0 trust-ad
search template

И в самом файле, конечно, этих резолверов обнаружено не было. Проблему удалось решить только удалением simlink и пересозданием файла с добавлением двух DNS-резолверов (в принципе, будет достаточно одного 1.1.1.1 или 8.8.8.8).

В итоге файл выглядит следующим образом:

nameserver 127.0.0.53
options edns0 trust-ad
search template

nameserver 8.8.8.8
nameserver 1.1.1.1

Удаленная машина успешно настроена и готовка к установке Kubernetes. Инструкции из этого подраздела повторите на всех оставшихся машинах будущего кластера.

Также крайне рекомендую на этом этапе сделать снимок системы, чтобы при переустановке кластера можно было откатить систему до текущего состояния. Причём даже не понадобится перенастраивать SSH-доступ без пароля. И тогда в будущем об этом подразделе можно забыть. Функциональность снимков предоставляется провайдером VPS (у меня это vmmanager).

Настройка Ansible-машины

Эти инструкции нужно выполнить только на своей локальной машине, с которой будет устанавливаться Kubernetes. Перейдите в подготовленную директорию и клонируйте репозиторий проекта Kubespray:

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

Помните, что подобные инструкции по установке любого софта без указания версии могут в будущем привести к проблемам. Либо статья устареет, либо новые версии могут быть обратно несовместимыми. Если вы уверены в себе и готовы решать проблемы и несостыковки, клонируйте мастер-ветку. У меня на данный момент tag v2.19.0. Команда для переключения:

git checkout tags/v2.19.0

Перейдём к установке Kubernetes-кластера. Подготовим наш инвентарь, шаблон находится в папке sample, мы его скопируем под новым именем и будем использовать при установке:

cp -rfp inventory/sample inventory/k8s

Должна появиться новая директория k8s*.* Теперь объявим переменную IPS массивом с перечислением наших виртуальных машин, это нужно для последующего генерирования конфигурации:

declare -a IPS=(185.186.142.5 185.186.142.53 46.8.19.144 46.8.19.244)

Сгенерируем конфигурацию в новый инвентарь:

CONFIG_FILE=inventory/k8s/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}

Был создан файл, посмотрим его содержимое:

nano inventory/k8s/hosts.yaml

У меня конфиг выглядит следующих образом

all:
  hosts:
    node1:
      ansible_host: 185.186.142.5
      ip: 185.186.142.5
      access_ip: 185.186.142.5
    node2:
      ansible_host: 185.186.142.53
      ip: 185.186.142.53
      access_ip: 185.186.142.53
    node3:
      ansible_host: 46.8.19.144
      ip: 46.8.19.144
      access_ip: 46.8.19.144
    node4:
      ansible_host: 46.8.19.244
      ip: 46.8.19.244
      access_ip: 46.8.19.244
  children:
    kube_control_plane:
      hosts:
        node1:
        node2:
    kube_node:
      hosts:
        node1:
        node2:
        node3:
        node4:
    etcd:
      hosts:
        node1:
        node2:
        node3:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
    calico_rr:
      hosts: {}

Получилась такая конфигурация кластера:

  • Мастер-узлы перечисляются в разделе kube_control_plane, в моём случае это node1 и node2.

  • Всего четыре Kubernetes-узла: node1, node2, node3 и node4.

  • Etcd-кластер будет установлен на три хоста: node1, node2 и node3. Три потому, что для etcd-кластера жизненно необходим кворум большинства, то есть всегда должно быть нечётное количество работающих узлов. Если попробуете удалить один из хостов в etcd:hosts, то Ansible-роль завершится с ошибкой о необходимости нечётного количества узлов

Установка кластера

Настройки будущего кластера находятся в папке group_vars. Чтобы не устанавливать Helm вручную на каждом мастер-узле, установим в файле addons.yaml параметр helm_enable равным true:

# Helm deployment
helm_enabled: true

В том же файле addons.yaml раскомментируем и активируем nginx-ingress, а также параметр ingress_nginx_host_network, благодаря которому, к каждому узлу будет привязана пода nginx-ingress:

# Nginx ingress controller deployment
ingress_nginx_enabled: true
ingress_nginx_host_network: true

Итоговый файл выглядит следующим образом

---
# Kubernetes dashboard
# RBAC required. see docs/getting-started.md for access details.
# dashboard_enabled: false

# Helm deployment
helm_enabled: true

# Registry deployment
registry_enabled: false
# registry_namespace: kube-system
# registry_storage_class: ""
# registry_disk_size: "10Gi"

# Metrics Server deployment
metrics_server_enabled: false
# metrics_server_container_port: 4443
# metrics_server_kubelet_insecure_tls: true
# metrics_server_metric_resolution: 15s
# metrics_server_kubelet_preferred_address_types: "InternalIP,ExternalIP,Hostname"

# Rancher Local Path Provisioner
local_path_provisioner_enabled: false
# local_path_provisioner_namespace: "local-path-storage"
# local_path_provisioner_storage_class: "local-path"
# local_path_provisioner_reclaim_policy: Delete
# local_path_provisioner_claim_root: /opt/local-path-provisioner/
# local_path_provisioner_debug: false
# local_path_provisioner_image_repo: "rancher/local-path-provisioner"
# local_path_provisioner_image_tag: "v0.0.21"
# local_path_provisioner_helper_image_repo: "busybox"
# local_path_provisioner_helper_image_tag: "latest"

# Local volume provisioner deployment
local_volume_provisioner_enabled: false
# local_volume_provisioner_namespace: kube-system
# local_volume_provisioner_nodelabels:
#   - kubernetes.io/hostname
#   - topology.kubernetes.io/region
#   - topology.kubernetes.io/zone
# local_volume_provisioner_storage_classes:
#   local-storage:
#     host_dir: /mnt/disks
#     mount_dir: /mnt/disks
#     volume_mode: Filesystem
#     fs_type: ext4
#   fast-disks:
#     host_dir: /mnt/fast-disks
#     mount_dir: /mnt/fast-disks
#     block_cleaner_command:
#       - "/scripts/shred.sh"
#       - "2"
#     volume_mode: Filesystem
#     fs_type: ext4
# local_volume_provisioner_tolerations:
#   - effect: NoSchedule
#     operator: Exists

# CSI Volume Snapshot Controller deployment, set this to true if your CSI is able to manage snapshots
# currently, setting cinder_csi_enabled=true would automatically enable the snapshot controller
# Longhorn is an extenal CSI that would also require setting this to true but it is not included in kubespray
# csi_snapshot_controller_enabled: false
# csi snapshot namespace
# snapshot_controller_namespace: kube-system

# CephFS provisioner deployment
cephfs_provisioner_enabled: false
# cephfs_provisioner_namespace: "cephfs-provisioner"
# cephfs_provisioner_cluster: ceph
# cephfs_provisioner_monitors: "172.24.0.1:6789,172.24.0.2:6789,172.24.0.3:6789"
# cephfs_provisioner_admin_id: admin
# cephfs_provisioner_secret: secret
# cephfs_provisioner_storage_class: cephfs
# cephfs_provisioner_reclaim_policy: Delete
# cephfs_provisioner_claim_root: /volumes
# cephfs_provisioner_deterministic_names: true

# RBD provisioner deployment
rbd_provisioner_enabled: false
# rbd_provisioner_namespace: rbd-provisioner
# rbd_provisioner_replicas: 2
# rbd_provisioner_monitors: "172.24.0.1:6789,172.24.0.2:6789,172.24.0.3:6789"
# rbd_provisioner_pool: kube
# rbd_provisioner_admin_id: admin
# rbd_provisioner_secret_name: ceph-secret-admin
# rbd_provisioner_secret: ceph-key-admin
# rbd_provisioner_user_id: kube
# rbd_provisioner_user_secret_name: ceph-secret-user
# rbd_provisioner_user_secret: ceph-key-user
# rbd_provisioner_user_secret_namespace: rbd-provisioner
# rbd_provisioner_fs_type: ext4
# rbd_provisioner_image_format: "2"
# rbd_provisioner_image_features: layering
# rbd_provisioner_storage_class: rbd
# rbd_provisioner_reclaim_policy: Delete

# Nginx ingress controller deployment
ingress_nginx_enabled: true
ingress_nginx_host_network: true
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"
#   - key: "node-role.kubernetes.io/control-plane"
#     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: "TLSv1.2 TLSv1.3"
# 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
# ingress_nginx_termination_grace_period_seconds: 300
# ingress_nginx_class: nginx

# ALB ingress controller deployment
ingress_alb_enabled: false
# alb_ingress_aws_region: "us-east-1"
# alb_ingress_restrict_scheme: "false"
# Enables logging on all outbound requests sent to the AWS API.
# If logging is desired, set to true.
# alb_ingress_aws_debug: "false"

# Cert manager deployment
cert_manager_enabled: false
# cert_manager_namespace: "cert-manager"
# cert_manager_tolerations:
#   - key: node-role.kubernetes.io/master
#     effect: NoSchedule
#   - key: node-role.kubernetes.io/control-plane
#     effect: NoSchedule
# cert_manager_affinity:
#  nodeAffinity:
#    preferredDuringSchedulingIgnoredDuringExecution:
#    - weight: 100
#      preference:
#        matchExpressions:
#        - key: node-role.kubernetes.io/control-plane
#          operator: In
#          values:
#          - ""
# cert_manager_nodeselector:
#   kubernetes.io/os: "linux"

# cert_manager_trusted_internal_ca: |
#   -----BEGIN CERTIFICATE-----
#   [REPLACE with your CA certificate]
#   -----END CERTIFICATE-----
# cert_manager_leader_election_namespace: kube-system

# MetalLB deployment
metallb_enabled: false
metallb_speaker_enabled: true
# metallb_ip_range:
#   - "10.5.0.50-10.5.0.99"
# metallb_pool_name: "loadbalanced"
# matallb_auto_assign: true
# metallb_speaker_nodeselector:
#   kubernetes.io/os: "linux"
# metallb_controller_nodeselector:
#   kubernetes.io/os: "linux"
# metallb_speaker_tolerations:
#   - key: "node-role.kubernetes.io/master"
#     operator: "Equal"
#     value: ""
#     effect: "NoSchedule"
#   - key: "node-role.kubernetes.io/control-plane"
#     operator: "Equal"
#     value: ""
#     effect: "NoSchedule"
# metallb_controller_tolerations:
#   - key: "node-role.kubernetes.io/master"
#     operator: "Equal"
#     value: ""
#     effect: "NoSchedule"
#   - key: "node-role.kubernetes.io/control-plane"
#     operator: "Equal"
#     value: ""
#     effect: "NoSchedule"
# metallb_version: v0.12.1
# metallb_protocol: "layer2"
# metallb_port: "7472"
# metallb_memberlist_port: "7946"
# metallb_additional_address_pools:
#   kube_service_pool:
#     ip_range:
#       - "10.5.1.50-10.5.1.99"
#     protocol: "layer2"
#     auto_assign: false
# metallb_protocol: "bgp"
# metallb_peers:
#   - peer_address: 192.0.2.1
#     peer_asn: 64512
#     my_asn: 4200000000
#   - peer_address: 192.0.2.2
#     peer_asn: 64513
#     my_asn: 4200000000

argocd_enabled: false
# argocd_version: v2.1.6
# argocd_namespace: argocd
# Default password:
#   - https://argoproj.github.io/argo-cd/getting_started/#4-login-using-the-cli
#   ---
#   The initial password is autogenerated to be the pod name of the Argo CD API server. This can be retrieved with the command:
#   kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2
#   ---
# Use the following var to set admin password
# argocd_admin_password: "password"

# The plugin manager for kubectl
krew_enabled: false
krew_root_dir: "/usr/local/krew"

Добавим в конфигурацию Ansible пользователя, под которым логинимся по SSH:

nano ansible.cfg

В группе ssh_connection добавьте параметр remote_user, равный root:

remote_user=root

Полностью файл выглядит так:

[ssh_connection]
pipelining=True
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o ConnectionAttempts=100 -o UserKnownHostsFile=/dev/null
#control_path = ~/.ssh/ansible-%%r@%%h:%%p
[defaults]
# https://github.com/ansible/ansible/issues/56930 (to ignore group names with - and .)
force_valid_group_names = ignore

host_key_checking=False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp
fact_caching_timeout = 7200
stdout_callback = default
display_skipped_hosts = no
library = ./library
callback_whitelist = profile_tasks,ara_default
roles_path = roles:$VIRTUAL_ENV/usr/local/share/kubespray/roles:$VIRTUAL_ENV/usr/local/share/ansible/roles:/usr/share/kubespray/roles
deprecation_warnings=False
inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo, .creds, .gpg
remote_user=root
[inventory]
ignore_patterns = artifacts, credentials

И наконец, команда запуска установки кластера. Выполните её в корне директории kubespray:

ansible-playbook -i inventory/k8s/hosts.yaml cluster.yml

Установка кластера может занять от получаса до полутора часов, в зависимости от скорости интернета. После установки вывод в консоли должен быть примерно таким:

Теперь нужно проверить состояние узлов. Выполните команду:

kubectl get nodes

Затем важно проверить, работают ли системные поды (coredns, kube-controller-manager, kube-scheduler и тд.):

kubectl get pods -n kube-system

Если появилась ошибка CrashLoopBackOff (пример из жизни №2)

Возможно, после установки возникнет ошибка CrashLoopBackOff у coredns и nodelocaldns:

nano /etc/kubernetes/kubelet-config.yaml

В файле необходимо подправить поле resolvConf на:

resolvConf: "/etc/resolv.conf"

И перезагрузить машину:

reboot

Выполните это на всех узлах, рабочих и управляющих. После этого поды перезапустятся и ошибка должна исчезнуть:

Готово, установка Kubernetes-кластера завершена. Теперь проверим на практике. насколько кластер высокодоступный, то бишь high-availability.

kubectl create deployment uuid-server --image=mopckou/sticky-session:0.0.5

Далее — Service с типом LoadBalancer:

kubectl expose deployment uuid-server --type=LoadBalancer --port=8080

И наконец, создадим манифест ingress с хостом uuid.awesomeservice.pro, который принимает и обрабатывает запросы:

cat <<EOF> ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: uuid-server-ingress
spec:
  rules:
    - host: uuid.awesomeservice.pro
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: uuid-server
                port:
                  number: 8080
EOF

Применим его:

kubectl apply -f ingress.yaml

Проверим созданный ingress:

kubectl get ingress

У ресурса появились выделенные адреса - все 4 узла могут принимать трафик. Но по факту трафик будет поступать только на две машины 185.186.142.53 и 185.186.142.5. Всё потому что только их я завязал на домен uuid.awesomeservice.pro у своего DNS провайдера. Теперь проверим, что сервер отвечает:

В инструментах разработчика можно посмотреть, на какой узел поступил и обработан запрос:

Ответила node1 (185.186.142.5). Ради интереса выполнил запрос в другом браузере (Safari):

В Safari запрос был обработан узлом node2 (185.186.142.53). Таким образом заодно убедились, что DNS-балансировка работает. Теперь посмотрим, как будет отработана внештатная ситуация и для чего вообще нужен высокодоступный кластер. Допустим, что один из узлов выходит из строя и становится недоступным. Я руками выключаю удалённую машину и через второй узел смотрю состояние кластера:

Теперь проверим, как запрос обработается через браузер:

Но последующие запросы будут сразу поступать на работающий узел:

В итоге мы имеем, что кластер не совсем ВЫСОКО доступный. Если в кластере из 4 узлов, один выходит из строя, то мы будем иметь 25% долгих запросов. Но при этом надо отметить, что они всё же будут успешными. Данное поведение я проверил в 2-х браузерах и ради интереса сделал запрос в Python скрипте - там, тоже самое, долгий запрос, но в итоге успешный.

На этом установка и тестирование кластера завершено. С учетом полученных знаний и подводных камней решайте, подходит ли вам данная сборка кластера.

Обслуживание кластера

Добавление worker node

Теперь займемся конфигурациями: добавим в наш инвентарь новый хост. В файле hosts.yaml аналогично добавьте три строчки с новым адресом в раздел all.hosts, и добавьте ссылку на этот хост в раздел с перечислением всех узлов в кластере — all.children.kube_node.hosts. Я добавил пятый узел с адресом 46.8.19.76.

Полный файл hosts.yaml

all:
  hosts:
    node1:
      ansible_host: 185.186.142.5
      ip: 185.186.142.5
      access_ip: 185.186.142.5
    node2:
      ansible_host: 185.186.142.53
      ip: 185.186.142.53
      access_ip: 185.186.142.53
    node3:
      ansible_host: 46.8.19.144
      ip: 46.8.19.144
      access_ip: 46.8.19.144
    node4:
      ansible_host: 46.8.19.244
      ip: 46.8.19.244
      access_ip: 46.8.19.244
    node5:
      ansible_host: 46.8.19.76
      ip: 46.8.19.76
      access_ip: 46.8.19.76
  children:
    kube_control_plane:
      hosts:
        node1:
        node2:
    kube_node:
      hosts:
        node1:
        node2:
        node3:
        node4:
        node5:
    etcd:
      hosts:
        node1:
        node2:
        node3:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
    calico_rr:
      hosts: {}

Перед тем, как запускать плейбук присоединения нового рабочего узла к кластеру, нужно выполнить команду:

ansible-playbook -i inventory/k8s/hosts.yaml facts.yml

Это сбор информации о кластере, он не займёт много времени. Теперь можно пользоваться ключом --limit, который нужен, чтобы во время добавления нового worker-узла не беспокоить уже существующие узлы. После успешного сбора информации запускаем основной плейбук присоединения worker-узла:

ansible-playbook -i inventory/k8s/hosts.yaml --limit=node5 scale.yml

Пример из жизни №3

Возможно, будет ошибка при выполнении скрипта. В моём случае надо было посмотреть, что работает resolved service. Запустим резолвер:

 systemctl enable systemd-resolved.service

Удаление worker node

Чтобы удалить рабочий узел, нужно всего лишь воспользоваться следующим плейбуком:

ansible-playbook -i inventory/k8s/hosts.yaml -e node=node5 remove-node.yml

Удаление занимает немного времени, не больше 10 минут. По завершении работы плейбука будет примерно такой вывод:

Плейбук завершится успешно только в том случае, если worker-узел жив. Если он абсолютно нерабочий, то смотрите следующий раздел про замену master-узла, там будет подробно рассказано, как удалить из кластера нерабочий узел.

Замена master node

Удалить мастер-узел, который не отвечает и не на связи, с помощью kubespray не получится. Пробовал разные варианты: запускал плейбук remove-node.yaml, запускал facts.yml, чтобы запросить актуальные данные кластера, запускал upgrade-cluster.yaml — всегда зависает на опросе недоступной ноды. Придётся удалять вручную. Эту инструкцию я долго искал по разным issue github, в итоге после небольших экспериментов получилось успешно удалить как не отвечающий мастер-узел, так и рабочий.

Сейчас кластер состоит из четырёх узлов: двух мастеров и двух рабочих машин:

Допустим, первый мастер-узел выходит из строя и не отвечает. Для этого собственноручно выключаю удалённую машину, и примерно через минуту узел помечается как "Not Ready":

Воспроизвели нештатную ситуацию, теперь давайте заменять мастер-узел. Прежде всего объявим его нерабочим:

kubectl cordon node1
kubectl drain node1 --ignore-daemonsets

Ключ --ignore-daemonsets обязателен для недоступных узлов. Возможно, консоль подзависнет на команде drain, поэтому можно прервать ожидание (Ctl+C). После этих команд узел перейдёт в состояние NotReady, SchedulingDisabled.

Удалим узел:

kubectl delete node node1

Теперь нужно обновить cluset-info, а именно, указать в поле server актуальный адрес мастер-узла:

kubectl edit cm -n kube-public cluster-info

Я заменил на 185.186.142.53 (node2), так как это единственный на данный момент живой мастер. Напомню, что на каждом мастер-узле ещё и развёрнут etcd-кластер, а это значит, что наша нештатная авария задела и etcd-узел вместе с node1 kubernetes. Поэтому нужно подчистить всю информацию, связанную с etcd-1. На каждом рабочем мастер-узле открываем конфигурацию:

nano /etc/kubernetes/manifests/kube-apiserver.yaml

И удалим нерабочий etcd-узел в строке --etcd-servers. В нашем случае IP-адреса будут одинаковы:

Аналогично, на каждом мастер-узле нужно отредактировать etcd-конфигурацию:

nano /etc/etcd.env

И удалить неактивную etcd-ноду в поле ETCD_INITIAL_CLUSTER:

После актуализации конфигураций последний штрих — это удаление ноды непосредственно из etcd-кластера. Воспользуемся консольной утилитой etcdctl для доступа к кластеру etcd. Эта утилита должна быть установлена на мастер-узлах. Прежде всего посмотрим существующие etcd-узлы:

etcdctl member list --cacert=/etc/ssl/etcd/ssl/ca.pem  --cert=/etc/ssl/etcd/ssl/admin-node2.pem  --key=/etc/ssl/etcd/ssl/admin-node2-key.pem

Вывод будет примерно таким:

Обратите внимание, что ключи --cacert, --cert и --key обязательны, и эти сертификаты уже сгенерированы kubespray и будут лежать в директории /etc/ssl/etcd/ssl/ на каждой машине, где развёрнут etcd-кластер. Теперь удалим из etcd-кластера неактивный узел:

etcdctl member remove 4f211fee4e79e7e7 --cacert=/etc/ssl/etcd/ssl/ca.pem  --cert=/etc/ssl/etcd/ssl/admin-node2.pem  --key=/etc/ssl/etcd/ssl/admin-node2-key.pem

Вывод будет такой:

Можете после этого проверить предыдущей командой, что etcd-узел удалён. И последнее: возвращаемся на локальную машину и удаляем неактивный host из файла hosts.yaml:

nano inventory/k8s/hosts.yaml

Достаточно будет удалить три строчки из children.kube_control_plane.hosts, children.kube_node.hosts и children.etcd.hosts (в all.hosts можно оставить, если потом захотите повторно добавить этот хост в кластер):

Полный hosts.yaml

all:
  hosts:
    node1:
      ansible_host: 185.186.142.5
      ip: 185.186.142.5
      access_ip: 185.186.142.5
    node2:
      ansible_host: 185.186.142.53
      ip: 185.186.142.53
      access_ip: 185.186.142.53
    node3:
      ansible_host: 46.8.19.144
      ip: 46.8.19.144
      access_ip: 46.8.19.144
    node4:
      ansible_host: 46.8.19.244
      ip: 46.8.19.244
      access_ip: 46.8.19.244
    node5:
      ansible_host: 46.8.19.76
      ip: 46.8.19.76
      access_ip: 46.8.19.76
  children:
    kube_control_plane:
      hosts:
        node2:
    kube_node:
      hosts:
        node2:
        node3:
        node4:
    etcd:
      hosts:
        node2:
        node3:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
    calico_rr:
      hosts: {}

Теперь нужно запустить Ansible-роль актуализации кластера (upgrade-cluster.yml). При вызове этой команды возникнет ошибка чётного количества etcd-серверов. Чтобы эту ошибку проигнорировать, нужно добавить специальный ключ ignore_assert_errors. Команда для запуска роли:

ansible-playbook -i inventory/k8s/hosts.yaml -e ignore_assert_errors=yes upgrade-cluster.yml

После выполнения роли вывод консоли будет такой:

nano inventory/k8s/hosts.yaml

Полный файл hosts.yaml

all:
  hosts:
    node1:
      ansible_host: 185.186.142.5
      ip: 185.186.142.5
      access_ip: 185.186.142.5
    node2:
      ansible_host: 185.186.142.53
      ip: 185.186.142.53
      access_ip: 185.186.142.53
    node3:
      ansible_host: 46.8.19.144
      ip: 46.8.19.144
      access_ip: 46.8.19.144
    node4:
      ansible_host: 46.8.19.244
      ip: 46.8.19.244
      access_ip: 46.8.19.244
    node5:
      ansible_host: 46.8.19.76
      ip: 46.8.19.76
      access_ip: 46.8.19.76
  children:
    kube_control_plane:
      hosts:
        node2:
        node1:
    kube_node:
      hosts:
        node2:
        node3:
        node4:
        node1:
    etcd:
      hosts:
        node2:
        node3:
        node1:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
    calico_rr:
      hosts: {}

Запускаем плейбук cluster.yaml. В ключе --limit указываем etcd и kube_control_plane, тогда при установке работоспособность worker-узлов затронута не будет.

ansible-playbook -i inventory/k8s/hosts.yaml --limit=etcd,kube_control_plane cluster.yml

Готово, мастер-узел успешно добавлен!

Решение возможных проблем

Если добавляется абсолютно новая виртуалка, то проблем возникнуть не должно. Но если добавляется машина с тем же IP, то даже если система только что переустановлена, возникнут, скорее всего, проблемы. У меня, например, эта ошибка повторяется постоянно:

Каждый раз проблема возникает при присоединении etcd-узла к etcd-кластеру. Посмотрим логи etcd на удалённой машине node1:

journalctl -n 100 --unit=etcd

Будет следующая ошибка:

"error":"error validating peerURLs {ClusterID:95417c89898c861a Members:[&{ID:5baef32decc40060 RaftAttributes:{PeerURLs:[https://185.186.142.53:2380] IsLearner:false} Attributes:{Name:etcd1 ClientURLs:[https://185.186.142.53:2379]}} &{ID:80fbe9a8863e9bfe RaftAttributes:{PeerURLs:[https://46.8.19.144:2380] IsLearner:false} Attributes:{Name:etcd2 ClientURLs:[https://46.8.19.144:2379]}}] RemovedMemberIDs:[]}: member count is unequal"

Дальнейшие шаги выполняем на мастер-узле (node1). Сначала заглянем в etcd-конфигурацию:

nano /etc/etcd.env

Новый etcd-узел подключается как etcd3, запомним это имя. Вот команда присоединения к etcd-кластеру:

etcdctl member add etcd3 --endpoints=185.186.142.53:2379,46.8.19.144:2379 --peer-urls=https://185.186.142.5:2380 --cacert=/etc/ssl/etcd/ssl/ca.pem  --cert=/etc/ssl/etcd/ssl/admin-node3.pem  --key=/etc/ssl/etcd/ssl/admin-node3-key.pem

Здесь в ключе --endpoints указываются уже существующие узлы etcd-кластера, в --peer-urls — адрес нового etcd-узла (обратите внимание на порты, в первом ключе 2379, во втором — 2380). Остальные ключи — это ссылка на сертификаты, которые уже сгенерированы kubespray, смотрите в соответствующую директорию etc/ssl/etcd/ssl/. Вывод консоли будет такой:

Etcdctl сообщит кластеру о новом участнике и распечатает переменные среды, необходимые для его успешного запуска. Так как служба уже запущена в фоне, то перезапускать ничего не придётся. Убедитесь, что сгенерированные в консоли переменные окружения такие же, как в /etc/etcd.env; если нет, то нужно подправить этот файл на всех удалённых машинах, где развернут etcd, и перезапустить службу.

Смотрим текущее состояние etcd-кластера:

etcdctl member list --endpoints=185.186.142.53:2379,46.8.19.144:2379  --cacert=/etc/ssl/etcd/ssl/ca.pem  --cert=/etc/ssl/etcd/ssl/admin-node3.pem  --key=/etc/ssl/etcd/ssl/admin-node3-key.pem

Etcd-узел успешно добавлен в etcd-кластер. Можно ещё глянуть, как дела со здоровьем кластера:

etcdctl endpoint health --endpoints=https://185.186.142.53:2379,https://46.8.19.144:2379,https://185.186.142.5:2379  --cacert=/etc/ssl/etcd/ssl/ca.pem  --cert=/etc/ssl/etcd/ssl/admin-node2.pem  --key=/etc/ssl/etcd/ssl/admin-node2-key.pem

Etcd-кластер чувствует себя отлично. Наконец, можно перезапустить Ansible-роль и добавить новый мастер-узел к Kubernetes-кластеру.

ansible-playbook -i inventory/k8s/hosts.yaml --limit=etcd,kube_control_plane cluster.yml

Полезные команды

Вот список полезных команд, которые наверняка понадобятся.

Просмотр журнала логов etcd, kubelet:

journalctl --unit=etcd -n 100
journalctl --unit=kubelet -n 100

Просмотр статуса и перезагрузка службы:

systemctl status kubelet
systemctl restart kubelet

Возможно, понадобится сделать снимок с etcd- узла. Вот рабочий пример:

ETCDCTL_API=3 etcdctl --endpoints https://185.186.142.53:2379 snapshot save snapshot.db --cacert=/etc/ssl/etcd/ssl/ca.pem  --cert=/etc/ssl/etcd/ssl/admin-node2.pem  --key=/etc/ssl/etcd/ssl/admin-node2-key.pem

Мониторинг ресурсов виртуалки:

wget -qO- bench.sh | bash

Высокодоступный кластер (aka high-availability) - это еще один шажок к production-ready кластеру. Как написано в документации, построить высокодоступный кластер значит:

В предыдущей статье проблему с помощью MetalLb, в этот раз попробую подход. Все 4 узла смогут принимать трафик извне, так как поды ingress-nginx будут развернуты на каждом узле (с помощью ресурса Daemonset) и использовать порт 80/443 этой машины.

И конечно нужно привести с предостережением по безопасности данного решения:

Заключительный штрих - я заказал доменное имя awesomeservice.pro на которое будут разрешаться все 4 IP адреса, таким образом задействуем DNS трафика. Балансировка осуществляется с помощью алгоритма Round Robin — алгоритма кругового обслуживания. Первый запрос передается одному серверу, затем следующий запрос передается другому, и так далее до достижения последнего сервера. Затем направление запросов начинается сначала. Это самое бюджетное и простое решение которое я нашел на данный момент. Именно из-за DNS балансировки будущий кластер выйдет почти высокодоступным. Данное решение имеет ряд неприятных минусов:

И самая большая проблема - нет отслеживания состояния серверов. Если одна машина из пула выйдет из строя, DNS сервер все равно будет отдавать этот адрес. Эту проблему и её последствия я наглядно после установки кластера.

Если ваш VPS провайдер позволяет создавать , то можно его привязать ко всем 4 удаленным машинам и тогда не нужна будет никакая DNS балансировка. Мой провайдер, к сожалению, не предоставляет такой функционал, поэтому протестировать такое решение не могу. А там, где есть данная фича VPS стоят неоправданно дорого.

В основе главы лежит эта , но на текущий момент она уже устарела, поэтому некоторые разделы будут продублированы и дополнены либо изменены.

Проблема легко гуглится, и решение подробно описано . Отредактируем конфигурацию kubelet:

На любом из мастер-узлов развернём простое приложение из предыдущей , которое по GET-ручке возвращает случайно сгенерированный UUID. Создадим три ресурса, первый — deployment:

Как видите, раньше запрос в Chrome обрабатывался узлом node1 (185.186.142.5), а после того, как тот стал недоступен, запросы пошли на узел node2 (185.186.142.53). На самом деле мне повезло, видимо истек и локальный кэш выполнил новый запрос к DNS серверу и получил второй адрес. Стоит чуть подольше потыкать запросы, и начинаются проблемы. В какой-то момент браузер всё таки получит адрес недоступного узла (185.186.142.5) и тогда запрос подвисает на 1 минуту, после чего браузер берет второй адрес из пула и наконец запрос успешно выполняется. На картинке видно, что обработка длилась чуть больше минуты.

Данный раздел был основан на этой и , а в последствии доработан.

Перед тем, как добавлять новый узел, удалённую машину нужно .

Теперь можно добавлять новый мастер-узел в кластер. Но перед эти удалённую машину. Я вместо подготовки на node1 (185.186.142.5) накатил снимок ОС с готовым сетапом для установки Kubernetes. Отредактируем hosts.yaml:

Добавим новый host (в моем случае это node1) в all.hosts в разделы children.kube_control_plane.hosts, children.kube_node.hosts и children.etcd.hosts*.* Надо отметить, что всегда добавлять новый хост в конец списка.

Прогуглив ошибку "member count is unequal", можно наткнуться на такое . В нём советуют указать машину с другим IP, отличным от прежнего. Это подтверждает тезис, что если добавлять новый хост с новым IP, то Ansible-роль отработает и мастер-узел успешно добавится к Kubernetes-кластеру. Допустим, что у нас нет другой машины. Методом научного тыка я нашёл другое решение: сначала добавим вручную etcd-узел к etcd-кластеру, а после перезапустим Ansible-роль cluster.yaml, и мастер-узел успешно добавится к Kubernetes-кластеру.

Введение
Установка высокодоступного Kubernetes-кластера
Настройка виртуальных машин
Настройка Ansible-машины
Установка кластера
Обслуживание кластера
Добавление worker node
Удаление worker node
Замена master node
Полезные команды
официальной
своей
решал
другой
цитату
балансировку
продемонстрирую
Virtual IP
↑
статья
↑
↑
↑
тут
статьи
TTL
↑
статье
issue
подготовить
↑
↑
подготовить
советуют
решение
↑
https://habr.com/ru/companies/domclick/articles/682364/
статье