<?xml version="1.0" encoding="utf-8"?>

<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="uk">
  <generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator>
  <link href="https://blog.andygol.co.ua/uk/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="https://blog.andygol.co.ua/uk" rel="alternate" type="text/html" hreflang="uk" />
  <updated>2026-05-18T19:58:59+00:00</updated>
  <id>https://blog.andygol.co.ua/uk/feed.xml</id>
  <title type="html">Андрій Головін – Блог | </title>
  <subtitle>Персональний блог Андрія Головіна. По трохи про все.</subtitle>
  <author>
      <name>Андрій Головін</name>
    </author>
    <entry xml:lang="uk">
      <title type="html">Розгортання HA Kubernetes кластера з зовнішньою топологією etcd на локальній машині</title>
      <link href="https://blog.andygol.co.ua/uk/2026/01/12/ha-k8s-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80/" rel="alternate" type="text/html" title="Розгортання HA Kubernetes кластера з зовнішньою топологією etcd на локальній машині"/>
      <published>2026-01-12T06:30:00+00:00</published>
      <updated>2026-01-12T06:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2026/01/12/ha-k8s-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2026/01/12/ha-k8s-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80/">
        &lt;p&gt;У цьому посібнику ми розглянемо кроки для розгортання високодоступного (High Availability, HA) Kubernetes кластера з зовнішньою топологією розміщення бази даних налаштувань etcd, на локальній машині під керуванням macOS. Ми використаємо Multipass для створення віртуальних машин, cloud-init для їх ініціалізації, kubeadm для ініціалізації кластера, HAProxy як load balancer для вузлів панелі управління, Calico як Container Network Interface (&lt;a href=&quot;https://www.cni.dev&quot;&gt;CNI&lt;/a&gt;) та &lt;a href=&quot;https://blog.andygol.co.ua/uk/2025/12/12/k8s-кластер-з-kubeadm/#крок-7-встановлення-metallb&quot;&gt;MetalLB для балансування трафіку до робочих вузлів&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Цей посібник призначений як для самостійного вивчення, так і для виконання перших кроків розгортання кластера промислового рівня. Кожен крок пояснено детально, з описом компонентів та їх ролей.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2026/01/2026-01-12-multipass-ha-k8s-cluster.png&quot; alt=&quot;знімок екрану Multipass з 10 віртуальними машинами&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;архітектура-кластера&quot;&gt;Архітектура кластера&lt;/h2&gt;

&lt;p&gt;Високодоступні кластери Kubernetes використовуються в промислових середовищах для забезпечення безперервної роботи застосунків. Резервування ключових компонентів кластера дозволяє уникнути простоїв у разі відмови окремих вузлів управління або etcd.&lt;/p&gt;

&lt;div style=&quot;display: flex; justify-content: center; margin: 30px 0;&quot;&gt;
  &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/03kdtcJZaQ8?si=gQK1f5iuz6K5CynL&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h3 id=&quot;компоненти&quot;&gt;Компоненти&lt;/h3&gt;

&lt;p&gt;Ми будемо розгортати кластер за допомогою kubeadm на віртуальних машинах Ubuntu, створених Multipass та ініціалізованих з використанням cloud-init.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Multipass&lt;/strong&gt; — це інструмент для швидкого створення віртуальних машин Ubuntu у хмарному стилі в операційних системах Linux, macOS та Windows. Він надає простий, але потужний інтерфейс командного рядка, який дозволяє швидко отримати доступ до Ubuntu або створити власну локальну мініхмару.&lt;/p&gt;

    &lt;p&gt;Локальна розробка та тестування можуть бути складними, але &lt;a href=&quot;https://multipass.run/&quot;&gt;Multipass&lt;/a&gt; спрощує ці процеси, автоматизуючи процедуру розгортання та згортання інфраструктури. Multipass має бібліотеку готових образів, які можна використовувати для запуску спеціалізованих віртуальних машин або власних віртуальних машин, налаштованих за допомогою потужного інтерфейсу cloud-init.&lt;/p&gt;

    &lt;p&gt;Розробники можуть використовувати Multipass для створення прототипів хмарних розгортань та створення нових, налаштованих середовищ розробки Linux на будь-якій машині. Multipass — це найшвидший спосіб для користувачів Mac і Windows отримати командний рядок Ubuntu у своїх системах. Ви також можете використовувати його як пісочницю, щоб випробувати нові речі, не порушуючи роботу хост-машини та не потребуючи подвійного завантаження.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Cloud-init&lt;/strong&gt; — це загальноприйнятий в галузі метод ініціалізації, присутній в багатьох дистрибутивах, для створення хмарних інстансів на різних платформах. Він підтримується всіма основними постачальниками публічних хмарних послуг, системами забезпечення приватної хмарної інфраструктури та інсталяціями на «голому залізі».&lt;/p&gt;

    &lt;p&gt;Під час завантаження &lt;a href=&quot;https://cloud-init.io&quot;&gt;cloud-init&lt;/a&gt; визначає хмару, в якій працює, і відповідно ініціалізує систему. Хмарні інстанси під час першого завантаження автоматично забезпечуються мережею, сховищем, ключами SSH, пакунками та іншими вже налаштованими системними компонентами.&lt;/p&gt;

    &lt;p&gt;Cloud-init забезпечує необхідний звʼязок між запуском хмарного екземпляра та підключенням до нього, щоб він працював як очікується.&lt;/p&gt;

    &lt;p&gt;Для користувачів хмари cloud-init забезпечує управління конфігурацією хмарного екземпляра під час першого завантаження без необхідності встановлення потрібних компонентів вручну. Для постачальників хмарних послуг він забезпечує налаштування екземпляра, який можна інтегрувати з вашою хмарою.&lt;/p&gt;

    &lt;p&gt;Якщо ви хочете дізнатися більше про те, що таке cloud-init, для чого він потрібен і як працює, прочитайте &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/explanation/introduction.html#introduction&quot;&gt;детальний опис&lt;/a&gt; в його документації.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Kubeadm&lt;/strong&gt; — інструмент, створений для надання команд &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt; як найкращих “швидких практичних способів” для створення кластерів Kubernetes.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/reference/setup-tools/kubeadm/&quot;&gt;Kubeadm&lt;/a&gt; виконує необхідні дії для запуску мінімального життєздатного кластера. За своєю концепцією, він займається лише процесом розгортання кластера, створення екземплярів машин не є його функцією. Встановлення різноманітних додаткових компонентів, таких як Kubernetes Dashboard, засобів моніторингу та специфічних для хмарних середовищ надбудов, також не входить в перелік його завдань.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Etcd з топологією зовнішнього розміщення&lt;/strong&gt;. &lt;a href=&quot;https://andygol-etcd.netlify.app/uk/&quot;&gt;Etcd&lt;/a&gt; — це розподілене сховище ключів-значень із високою узгодженістю, яке забезпечує надійний спосіб зберігання даних, до яких потрібно отримати доступ розподіленій системі або кластеру машин. Воно коректно обробляє вибори лідерів під час мережевих розділень і може витримувати відмови машин, навіть вузлів-лідерів.&lt;/p&gt;

    &lt;p&gt;Kubernetes використовує etcd для зберігання всієї своєї конфігурації та стану кластера. Це включає інформацію про вузли, поди, конфігурацію мережі, секрети та інші ресурси Kubernetes. Топологія високодоступного кластера Kubernetes передбачає два варіанти розміщення etcd: &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/ha-topology/#stacked-etcd-topology&quot;&gt;топологія etcd зі спільним розміщенням&lt;/a&gt; (stacked) та &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/ha-topology/#external-etcd-topology&quot;&gt;зовнішнього розміщення&lt;/a&gt; (external) учасників кластера etcd. У цьому посібнику ми розглянемо топологію зовнішнього розміщення etcd, де etcd розгортається окремо від вузлів управління Kubernetes. Це забезпечує кращу ізоляцію, масштабованість та гнучкість у керуванні кластером.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;HAProxy&lt;/strong&gt;. Для розподілу трафіку до вузлів панелі управління будемо використовувати балансувальник навантаження &lt;a href=&quot;https://www.haproxy.org/&quot;&gt;HAProxy&lt;/a&gt;. Це дозволить нам забезпечити високу доступність API сервера Kubernetes. Крім того, ми розгорнемо локальні екземпляри HAProxy на кожному вузлі панелі управління для доступу до вузлів кластера etcd.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Calico&lt;/strong&gt;. Оскільки &lt;strong&gt;kubeadm&lt;/strong&gt; не створює мережу, в якій працюють поди, ми скористаємося &lt;a href=&quot;https://projectcalico.org/&quot;&gt;Calico&lt;/a&gt;, популярним інструментом, що реалізує Container Network Interface (CNI) та забезпечує мережеві політики. Це уніфікована платформа для всіх потреб Kubernetes у сфері мережевих технологій, мережевої безпеки та спостережуваності, яка працює з будь-яким дистрибутивом Kubernetes. Calico спрощує забезпечення безпеки мережі за допомогою мережевих політик.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;топологія-кластера&quot;&gt;Топологія кластера&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-mermaid&quot;&gt;graph TB
  subgraph C [Control Plane and etcd]
    direction TB
    subgraph CP [Control Plane Nodes]
        direction LR
        CP1 --&amp;gt; CP2
        CP2 --&amp;gt; CP1
        CP1 --&amp;gt; CP3
        CP3 --&amp;gt; CP2
        CP2 --&amp;gt; CP3
        CP3 --&amp;gt; CP1
    end

    subgraph E [etcd cluster]
        direction LR
        E1 --&amp;gt; E2
        E2 --&amp;gt; E1
        E1 --&amp;gt; E3
        E3 --&amp;gt; E2
        E2 --&amp;gt; E3
        E3 --&amp;gt; E1
    end

    subgraph HA [Keepalived
    10.10.0.100]
      HA1(HAProxy
      10.10.0.101) &amp;lt;-----&amp;gt;
      HA2(HAProxy
      10.10.0.102)
    end

    CP &amp;lt;--&amp;gt; HA
    E &amp;lt;--&amp;gt; CP

  end

    W1(Worker
    Node 1
    10.10.0.31)
    W2(Worker
    Node 2
    10.10.0.32)
    W3(Worker
    Node 3
    10.10.0.33)
    WN(Worker
    Node …
    10.10.0.9X)

    HA --&amp;gt; PN(Calico CNI)
    PN --&amp;gt; W1 &amp;amp; W2 &amp;amp; W3 &amp;amp; WN--&amp;gt; WLB &amp;lt;--&amp;gt; U(Users)

    CP1(Control Plane
    Node 1
    10.10.0.21)
    CP2(Control Plane
    Node 2
    10.10.0.22)
    CP3(Control Plane
    Node 3
    10.10.0.23)
    E1(etcd
    Node 1
    10.10.0.11)
    E2(etcd
    Node 2
    10.10.0.12)
    E3(etcd
    Node 3
    10.10.0.13)

    WLB(MetalLB
    10.10.0.200)
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;попередні-вимоги&quot;&gt;Попередні вимоги&lt;/h2&gt;

&lt;p&gt;Для розгортання кластера нам знадобляться:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Локальний компʼютер з встановленим &lt;strong&gt;Multipass&lt;/strong&gt;. Для macOS його можна встановити за допомогою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install multipass&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;kubectl&lt;/strong&gt; — Інструмент командного рядка Kubernetes, &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/tasks/tools/#kubectl&quot;&gt;kubectl&lt;/a&gt;, дозволяє вам виконувати команди відносно кластерів Kubernetes. Ви можете використовувати kubectl для розгортання застосунків, огляду та управління ресурсами кластера, а також перегляду логів (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brew install kubectl&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Знання основ &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/overview/&quot;&gt;Kubernetes&lt;/a&gt; та &lt;a href=&quot;https://uk.wikipedia.org/wiki/Linux&quot;&gt;Linux&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;yq&lt;/strong&gt; (&lt;a href=&quot;https://mikefarah.gitbook.io/yq&quot;&gt;версія від mikefarah&lt;/a&gt;) — легкий і переносний процесор командного рядка для роботи з YAML, JSON, INI та XML.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;налаштування-ssh-ключів&quot;&gt;Налаштування SSH ключів&lt;/h2&gt;

&lt;p&gt;Secure Shell, SSH (англ. Secure Shell — «безпечна оболонка») — мережевий протокол, що дозволяє проводити віддалене управління компʼютером. Він шифрує весь трафік, включаючи процес автентифікацій та передачу паролів та секретів. Для доступу до віртуальних машин потрібно налаштувати SSH ключі. Ми будемо використовувати ці ключі для підключення до всіх створених віртуальних машин та передачі між ними файлів.&lt;/p&gt;

&lt;h3 id=&quot;генерування-ssh-ключа&quot;&gt;Генерування SSH ключа&lt;/h3&gt;

&lt;p&gt;Щоб згенерувати пару ключів (публічний та приватний) виконайте наступну команду:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Створіть нову пару SSH ключів (якщо у вас немає)&lt;/span&gt;
ssh-keygen &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; rsa &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 4096 &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;k8s-cluster&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; ~/.ssh/k8s_cluster_key

&lt;span class=&quot;c&quot;&gt;# Перегляньте публічний ключ&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; ~/.ssh/k8s_cluster_key.pub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;оновлення-конфігурацій-cloud-init&quot;&gt;Оновлення конфігурацій cloud-init&lt;/h3&gt;

&lt;p&gt;Отриманий публічний ключ будемо використовувати у файлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-users.yaml&lt;/code&gt; в секції &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-authorized-keys&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;k8sadmin&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ssh_authorized_keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Замініть на ваш справжній публічний ключ&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Також можна використовувати підставлення змінних під час виконання&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;multipass launch &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; - &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
users:
  - name: username
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; ~/.ssh/id_rsa.pub &lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;доступ-до-віртуальних-машин-через-ssh&quot;&gt;Доступ до віртуальних машин через SSH&lt;/h3&gt;

&lt;p&gt;Після розгортання ми можемо отримати доступ до віртуальних машин за допомогою наступних команд:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass shell &amp;lt;імʼя-vm&amp;gt;
&lt;span class=&quot;c&quot;&gt;# або&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@&amp;lt;ip-vm&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Multipass має вбудовану команду &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shell&lt;/code&gt;, яка дозволяє нам підключатися до будь-якої створеної віртуальної машини без необхідності додаткових налаштувань SSH. Однак, вона використовує стандартного користувача &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt;. Якщо ви хочете підʼєднатися як користувач &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8sadmin&lt;/code&gt;, якого ми створимо за допомогою cloud-init, використовуйте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh -i ~/.ssh/k8s_cluster_key k8sadmin@&amp;lt;ip-vm&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;підготовка-середовища&quot;&gt;Підготовка середовища&lt;/h2&gt;

&lt;p&gt;Створіть теку для проєкту та створіть необхідні файли.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;k8s-ha-cluster
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;k8s-ha-cluster
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;створення-скриптів-та-конфігурацій&quot;&gt;Створення скриптів та конфігурацій&lt;/h2&gt;

&lt;h3 id=&quot;параметри-віртуальних-машин&quot;&gt;Параметри віртуальних машин&lt;/h3&gt;

&lt;p&gt;Для розгортання віртуальних машин нам потрібні наступні параметри:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Вузол&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Кількість&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpus&lt;/code&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memory&lt;/code&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disk&lt;/code&gt;&lt;/th&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;network&lt;/code&gt;&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;etcd&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3+ або&lt;br /&gt;(2n+1)&lt;sup&gt;&lt;a href=&quot;https://andygol-etcd.netlify.app/uk/docs/v3.5/faq/#what-is-faulure-tolerance&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;5G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;name=en0,mode=manual&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;панель управління&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2.5G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;8G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;name=en0,mode=manual&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;робочі вузли&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;3+&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;2G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;10G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;name=en0,mode=manual&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;HAProxy+Keepalived&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;2&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;1&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;1G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;5G&lt;/td&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;name=en0,mode=manual&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;cloud-init-конфігурації&quot;&gt;Cloud-init конфігурації&lt;/h3&gt;

&lt;p&gt;Ми будемо використовувати попередньо створені конфігурації віртуальних машин у форматі cloud-init для прискорення їх розгортання.&lt;/p&gt;

&lt;h4 id=&quot;створення-користувача&quot;&gt;Створення користувача&lt;/h4&gt;

&lt;p&gt;Створимо налаштування для користувача &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8sadmin&lt;/code&gt; в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-user.yaml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Створимо теку, якщо її ще немає&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;mkdir -p snipets&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; snipets/cloud-init-user.yaml&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Налаштування користувача&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;k8sadmin&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;sudo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ALL=(ALL)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;NOPASSWD:ALL&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sudo,users,containerd&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;homedir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/home/k8sadmin&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/bin/bash&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;lock_passwd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ssh_authorized_keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# Ваш SSH key&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$( cat ~/.ssh/k8s_cluster_key.pub )&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Про конфігурацію користувача ми вже говорили в налаштуваннях SSH ключів. Про інші параметри цього розділу можна прочитати в розділі &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/reference/examples.html#including-users-and-groups&quot;&gt;Including users and groups&lt;/a&gt; документації cloud-init.&lt;/p&gt;

&lt;h4 id=&quot;базова-конфігурація-для-вузлів-кластера&quot;&gt;Базова конфігурація для вузлів кластера&lt;/h4&gt;

&lt;p&gt;Базова конфігурація cloud-init для вузлів нашого кластера буде знаходитись в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-base.yaml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#cloud-config&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Додамо тут налаштування користувача з snipets/cloud-init-users.yaml&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Команди які виконується на ранній стадії boot для підготовки GPG ключа&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;bootcmd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mkdir -p /etc/apt/keyrings&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Конфігурація apt репозиторіїв&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kubernetes.list&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deb&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://pkgs.k8s.io/core:/stable:/v1.34/deb/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Оновлення системи&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;package_update&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;package_upgrade&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Для встановлення компонентів необхідних для роботи кластера нам потрібно &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#k8s-install-0&quot;&gt;використовувати репозиторій проєкту Kubernetes&lt;/a&gt;. В &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/reference/yaml_examples/boot_cmds.html#run-commands-in-early-boot&quot;&gt;секції &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bootcmd&lt;/code&gt;&lt;/a&gt;, яка дуже схожа на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runcmd&lt;/code&gt;, але команди з якої виконуються на самому початку процесу завантаження, ми отримуємо GPG ключ та зберігаємо його в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apt/keyrings&lt;/code&gt;, а потім додаємо репозиторій Kubernetes до &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/reference/examples.html#additional-apt-configuration-and-repositories&quot;&gt;списку джерел apt&lt;/a&gt; в секції &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt;. Рядки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package_update&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package_upgrade&lt;/code&gt; забезпечують отримання останніх оновлень системи під час завантаження, що є еквівалентом виконання команд &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get update&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get upgrade&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;У розділі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;packages&lt;/code&gt; ми вкажемо всі необхідні пакунки, які потрібно встановити на кожній віртуальній машині кластера.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Встановлення базових пакунків&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apt-transport-https&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ca-certificates&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;curl&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gnupg&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lsb-release&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;containerd&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubelet&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubeadm&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubectl&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Окрім робочих вузлів&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Окрім службових пакунків системи ми також &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/reference/examples.html#install-arbitrary-packages&quot;&gt;встановимо&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerd&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;, які є основними компонентами для роботи Kubernetes кластера.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerd&lt;/code&gt; — є рушієм виконання контейнерів, який використовується Kubernetes для запуску контейнеризованих застосунків. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; — це агент, який працює на кожному вузлі в кластері Kubernetes і відповідає за запуск контейнерів у подах. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; — це інструмент для швидкого розгортання Kubernetes кластера, а &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; — це інструмент командного рядка для взаємодії з кластером Kubernetes.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Налаштування користувача&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;k8sadmin&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sudo,users,containerd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Наш користувач є учасником групи &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerd&lt;/code&gt;, яку будемо використовувати для доступу до сокета &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/run/containerd/containerd.sock&lt;/code&gt;, щоб уможливити використання &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crictl&lt;/code&gt; без потреби використання &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Інструкція &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files&lt;/code&gt; в cloud-init дозволяє &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/reference/examples.html#writing-out-arbitrary-files&quot;&gt;створювати файли з вказаним вмістом&lt;/a&gt; під час ініціалізації віртуальної машини. Скористаємось нею для створення файлів для налаштування модулів ядра для роботи Kubernetes, увімкнення IP форвардингу, створення налаштувань для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crictl&lt;/code&gt; та скрипту яким ми будемо ініціалізувати та запускати &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt;. (Дивіться розділ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files&lt;/code&gt; у файлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init-base.yaml&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Після того як ми створили всі необхідні файли за допомогою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files&lt;/code&gt;, ми можемо &lt;a href=&quot;https://cloudinit.readthedocs.io/en/latest/reference/examples.html#run-commands-on-first-boot&quot;&gt;виконати вказані команди&lt;/a&gt; для налаштування системи в розділі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runcmd&lt;/code&gt;. Тут ми можемо вказувати команди, які б ми мали виконати вручну після першого завантаження системи. У нашому випадку ми зафіксуємо версії &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;, вимкнемо swap, створимо файл налаштувань для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerd&lt;/code&gt; та запустимо його, а також активуємо та запустимо службу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt;. (Дивіться розділ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runcmd&lt;/code&gt; у файлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init-base.yaml&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Створіть два файли:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a id=&quot;cloud-init-config-yaml&quot;&gt;&lt;/a&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-config.yaml&lt;/code&gt; — для встановлення статичної IP адреси нашим віртуальним машинам&lt;/p&gt;

    &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; snipets/cloud-init-config.yaml&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#cloud-config&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;timezone&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Europe/Kyiv&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Призначення статичної IP адреси&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Варіант з alias для bridge101 для другого мережевого інтерфесу&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/netplan/60-static-ip.yaml&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Явним чином вкажемо тип значення&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# permissions: !!str &quot;0755&quot; # https://github.com/canonical/multipass/issues/4176&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!!str&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0600&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;network:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;version: 2&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ethernets:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;enp0s2:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;# Тут вказється IP адреса конкретної віртуальної машини&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;addresses:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;# - 10.10.0.24/24&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;routes:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;- to: 10.10.0.0/24&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;scope: link&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;runcmd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Застосування налаштувань для використання статичної IP адреси&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;netplan apply&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Вибір vim як стандартного редактора (опціонально)&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;update-alternatives --set editor /usr/bin/vim.basic&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;☝️ Також тут вкажемо, що ми хочемо використовувати &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vim&lt;/code&gt; як наш стандартний редактор. Якщо ви надаєте перевагу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nano&lt;/code&gt; закоментуйте чи приберіть рядок &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;- update-alternatives --set editor /usr/bin/vim.basic&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;та, файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-base.yaml&lt;/code&gt; з базовими налаштуваннями для віртуальних машин кластера&lt;/p&gt;

    &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; &apos;EOF&apos; &amp;gt; snipets/cloud-init-base.yaml&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Виконується на ранній стадії boot для підготовки GPG ключа&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;bootcmd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mkdir -p /etc/apt/keyrings&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Конфігурація apt репозиторіїв&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kubernetes.list&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deb&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;[signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg]&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;https://pkgs.k8s.io/core:/stable:/v1.34/deb/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Оновлення системи&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;package_update&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;package_upgrade&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Встановлення базових пакунків&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apt-transport-https&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ca-certificates&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;curl&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gnupg&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lsb-release&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;containerd&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubelet&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubeadm&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubectl&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#  Налаштування модулів ядра для роботи Kubernetes&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/modules-load.d/k8s.conf&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# permissions: !!str &apos;0644&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;overlay&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;br_netfilter&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Увімкнення маршрутизації IPv4 пакетів&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/container-runtimes/#prerequisite-ipv4-forwarding-optional&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/sysctl.d/k8s.conf&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# permissions: !!str &apos;0644&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;net.bridge.bridge-nf-call-iptables  = 1&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;net.bridge.bridge-nf-call-ip6tables = 1&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;net.ipv4.ip_forward                 = 1&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Додаємо конфіг для crictl&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://github.com/containerd/containerd/blob/main/docs/cri/crictl.md#install-crictl&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/crictl.yaml&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# permissions: !!str &apos;0644&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;runtime-endpoint: unix:///run/containerd/containerd.sock&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;image-endpoint: unix:///run/containerd/containerd.sock&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;timeout: 10&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;debug: false&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Встановлюємо групу для сокета containerd&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/systemd/system/containerd.service.d/override.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;[Service]&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;ExecStartPost=/bin/sh -c &quot;chgrp containerd /run/containerd/containerd.sock &amp;amp;&amp;amp; chmod 660 /run/containerd/containerd.sock&quot;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Скрипт для запуску kubelet&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/local/bin/kubelet-start.sh&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!!str&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0755&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;#!/bin/bash&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;echo &quot;Запуск служби kubelet та очікування готовності (ліміт 300с)...&quot;&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;# Вмикаємо та запускаємо службу&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;sudo systemctl enable --now kubelet&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;WAIT_LIMIT=300       # Максимальний час очікування в секундах&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;ELAPSED_TIME=0       # Скільки вже пройшло часу&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;SLEEP_INTERVAL=1     # Початковий інтервал (1 секунда)&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;while ! systemctl is-active --quiet kubelet; do&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;if [ &quot;$ELAPSED_TIME&quot; -ge &quot;$WAIT_LIMIT&quot; ]; then&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;echo &quot;-------------------------------------------------------------&quot; &amp;gt;&amp;amp;2&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;echo &quot;АВАРІЙНИЙ ВИХІД: kubelet не запустився за $WAIT_LIMIT секунд.&quot; &amp;gt;&amp;amp;2&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;echo &quot;Останні логи помилок:&quot;                                         &amp;gt;&amp;amp;2&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;journalctl -u kubelet -n 20 --no-pager                               &amp;gt;&amp;amp;2&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;echo &quot;-------------------------------------------------------------&quot; &amp;gt;&amp;amp;2&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;exit 1&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;fi&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;echo &quot;Очікування kubelet... (минуло $ELAPSED_TIME/$WAIT_LIMIT сек,&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;echo &quot;наступна спроба через ${SLEEP_INTERVAL}с)&quot;&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;sleep $SLEEP_INTERVAL&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;# Оновлюємо лічильники&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;ELAPSED_TIME=$((ELAPSED_TIME + SLEEP_INTERVAL))&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;# Збільшуємо інтервал вдвічі для наступного разу (прогресія)&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;# Але не робимо інтервал більшим за 20 секунд, щоб не &quot;проспати&quot; готовність&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;SLEEP_INTERVAL=$((SLEEP_INTERVAL * 2))&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;if [ &quot;$SLEEP_INTERVAL&quot; -gt 20 ]; then&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;SLEEP_INTERVAL=20&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;fi&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;done&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;echo &quot;Kubelet успішно запущено за $ELAPSED_TIME секунд. Продовжуємо...&quot;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;runcmd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Фіксація версій пакунків Kubernetes&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apt-mark hold kubelet kubeadm kubectl&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Завантаження модулів ядра&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;modprobe overlay&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;modprobe br_netfilter&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Застосування sysctl параметрів&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sysctl --system&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Налаштування containerd&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Налаштування драйвера cgroup systemd&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/container-runtimes/#containerd-systemd&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://github.com/containerd/containerd/blob/main/docs/cri/config.md#cgroup-drivercrictl pull&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Перевизначення образу пісочниці (pause)&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/container-runtimes/#override-pause-image-containerd&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;mkdir -p /etc/containerd&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;containerd config default | tee /etc/containerd/config.toml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sed -i &apos;s|SystemdCgroup = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;|SystemdCgroup = &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;|g; s|sandbox_image = &quot;registry.k8s.io/pause.*&quot;|sandbox_image = &quot;registry.k8s.io/pause:3.10.1&quot;|&apos; /etc/containerd/config.toml&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;systemctl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;daemon-reload&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;systemctl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;containerd&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;systemctl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;containerd&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Вимкнення swap&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://andygol-k8s.netlify.app/uk/docs/concepts/cluster-administration/swap-memory-management/#swap-and-control-plane-nodes&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#swap-configuration&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;swapoff -a&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sed -i &apos;/ swap / s/^/#/&apos; /etc/fstab&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Увімкнення kubelet&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/usr/local/bin/kubelet-start.sh&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Права доступу до файлів, які створює cloud-init, стандартно є &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0644&lt;/code&gt;, якщо ви хочете вказати їх явно розкоментуйте відповідні рядки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;# permissions: !!str &apos;0644&apos;&lt;/code&gt;. Ми вказуємо тип даних явно (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;!!str &apos;0644&apos;&lt;/code&gt;) через проблему описану в тікеті &lt;a href=&quot;https://github.com/canonical/multipass/issues/4176&quot;&gt;# 4176&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;конфігурація-для-etcd-вузлів&quot;&gt;Конфігурація для etcd вузлів&lt;/h4&gt;

&lt;p&gt;Для вузлів etcd ми будемо використовувати базову конфігурацію з додатковими налаштуваннями, які специфічні для etcd. Створіть файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-etcd.yaml&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Розширимо cloud-init-base.yaml наступними налаштуваннями&lt;/span&gt;

&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; snipets/cloud-init-etcd.yaml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/setup-ha-etcd-with-kubeadm/#setup-up-the-cluster&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/systemd/system/kubelet.service.d/kubelet.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;apiVersion: kubelet.config.k8s.io/v1beta1&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;kind: KubeletConfiguration&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;authentication:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;anonymous:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;enabled: false&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;webhook:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;enabled: false&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;authorization:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;mode: AlwaysAllow&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;cgroupDriver: systemd&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;address: 127.0.0.1&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;containerRuntimeEndpoint: unix:///var/run/containerd/containerd.sock&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;staticPodPath: /etc/kubernetes/manifests&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/systemd/system/kubelet.service.d/20-etcd-service-manager.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;[Service]&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;ExecStart=&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;ExecStart=/usr/bin/kubelet --config=/etc/systemd/system/kubelet.service.d/kubelet.conf&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;Restart=always&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Будемо використовувати базові налаштування, які ми створили раніше та додамо специфічні для etcd налаштування у розділі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files&lt;/code&gt;, де ми створюємо файл конфігурації для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt;, який налаштовує його для роботи з etcd, як про це йдеться в розділі «&lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/setup-ha-etcd-with-kubeadm/#setup-up-the-cluster&quot;&gt;Налаштування високодоступного кластера etcd за допомогою kubeadm&lt;/a&gt;» документації Kubernetes.&lt;/p&gt;

&lt;h4 id=&quot;конфігурація-вузла-балансувальника-haproxy&quot;&gt;Конфігурація вузла балансувальника HAProxy&lt;/h4&gt;

&lt;p&gt;Для вузлів балансувальника навантаження трафіку панелі управління створимо файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-haproxy.yaml&lt;/code&gt;, який встановлюватиме зі стандартного репозиторію пакунки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haproxy&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;keepalived&lt;/code&gt; та додаватиме налаштування. Для створення віртуальної машини для HAProxy використаємо базові налаштування з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;configs/cloud-init-user.yaml&lt;/code&gt; та розширимо їх інструкціями &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files:&lt;/code&gt; для створення файлу налаштувань балансувальника — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/keepalived/keepalived.conf&lt;/code&gt;. (Див. розділ «&lt;a href=&quot;#налаштування-балансувальника-навантаження-для-вузлів-панелі-управління-haproxykeepalived&quot;&gt;Налаштування балансувальника навантаження для вузлів панелі управління (HAProxy+Keepalived)&lt;/a&gt;»)&lt;/p&gt;

&lt;h3 id=&quot;вибір-топології-мережі&quot;&gt;Вибір топології мережі&lt;/h3&gt;

&lt;p&gt;Для цієї демонстрації оберемо компактну мережу. (10.10.0.0/24)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;10.10.0.0/24      - Головна підмережа (256 адрес)
├─ 10.10.0.1      - Gateway/Bridge (на хост-машині)
├─ 10.10.0.10-19  - etcd (3+ вузли)
├─ 10.10.0.20-29  - Control Plane (3+ masters)
├─ 10.10.0.30-50  - Workers (до 20 workers)
└─ 10.10.0.100    - HAProxy/Keepalived (два вузла 10.10.0.101/10.10.0.102)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Щоб задати статичні адреси віртуальним машинам Multipass скористаємось одним з варіантів про який йдеться в статті «&lt;a href=&quot;https://blog.andygol.co.ua/uk/2025/12/26/статичні-ip-для-вм-multipass/#додавання-статичної-ip-адреси-до-другого-мережевого-інтерфейсу-віртуальної-машини&quot;&gt;Додавання статичної IP адреси віртуальним машинам Multipass на macOS&lt;/a&gt;».&lt;/p&gt;

&lt;p&gt;У файл cloud-init додамо відповідний розділ з налаштуванням статичної мережевої адреси на другому мережевому інтерфейсі (див. &lt;a href=&quot;#cloud-init-config-yaml&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-config.yaml&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;створення-etcd-кластера&quot;&gt;Створення etcd кластера&lt;/h2&gt;

&lt;p&gt;Почнемо зі створення кластера etcd, в якому наш високодоступний кластер Kubernetes буде зберігати налаштування та бажаний стан обʼєктів системи.&lt;/p&gt;

&lt;h3 id=&quot;розгортання-першого-вузла-кластера-etcd&quot;&gt;Розгортання першого вузла кластера etcd&lt;/h3&gt;

&lt;p&gt;Розпочнімо розгортання нашого кластера зі створення першого вузла etcd.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.11/24&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; ext-etcd-1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 2G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 5G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
      # Злиття всіх файлів в один обʼєкт
      . as $item ireduce ({}; . *+ $item) |

      # Вилучення kubectl зі списку пакунків
      del(.packages[] | select(. == &quot;kubectl&quot;)) |

      # Оновлення конфігурації мережі
      with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
        .content |= (
          from_yaml |
          .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] |
          to_yaml
        )
      ) &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-base.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-etcd.yaml &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Команда &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass launch --name ext-etcd-1&lt;/code&gt; почне розгортання віртуальної машини з назвою вказаною в параметрі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name/-n&lt;/code&gt;, в цьому випадку &lt;strong&gt;ext-etcd-1&lt;/strong&gt;; параметри &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cpus 2 --memory 2G --disk 5G&lt;/code&gt; відповідно визначають кількість ядер процесора, памʼяті та дискового простору; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--network name=en0,mode=manual&lt;/code&gt; створить ще один мережевий інтерфейс віртуальної машини, IP адресу якому буде призначено через змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VM_IP&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Оскільки etcd активно пише дані на диск, продуктивність роботи бази даних напряму залежить від продуктивності дискової підсистеми. Для промислового використання наполегливо рекомендуються системи зберігання з SSD. Мінімальний розмір дискового простору стандартно має бути не менше 2Гб, відповідно, для уникнення розміщення даних у свопі розмір оперативної памʼяті має покривати цю квоту. 8Гб є рекомендованим максимумом для звичайних розгортань. Вимоги для машин etcd для невеликих промислових кластерів: 2 vCPU, 8 Гб памʼяті та 50-80Гб SSD. (Див. &lt;a href=&quot;https://andygol-etcd.netlify.app/uk/docs/v3.5/op-guide/hardware/#small-cluster&quot;&gt;https://andygol-etcd.netlify.app/uk/docs/v3.5/op-guide/hardware/#small-cluster&lt;/a&gt;, &lt;a href=&quot;https://andygol-etcd.netlify.app/uk/docs/v3.5/faq/#system-requirements&quot;&gt;https://andygol-etcd.netlify.app/uk/docs/v3.5/faq/#system-requirements&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Ми зберемо параметри cloud-init на льоту обʼєднуючи наші файли заготовки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-config.yaml&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-user.yaml&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-base.yaml&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-etcd.yaml&lt;/code&gt; за допомогою &lt;strong&gt;yq&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Якщо ви не створювали тимчасову віртуальну машину для ініціалізації &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge101&lt;/code&gt; та не &lt;a href=&quot;https://blog.andygol.co.ua/uk/2025/12/26/статичні-ip-для-вм-multipass/#multipass-bridge-для-другого-мережевого-інтерфейсу&quot;&gt;додавали аліас для нього&lt;/a&gt;, після розгортання віртуальної машини саме час зробити наступне. Виконайте на вашому хості наступне:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Визначте назву bridge. Скоріш за все назва буде bridge101.&lt;/span&gt;
ifconfig &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; 20 &lt;span class=&quot;s2&quot;&gt;&quot;member: vmenet&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bridge&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;: &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1

&lt;span class=&quot;c&quot;&gt;# Додайте адресу&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig bridge101 10.10.0.1/24 &lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Перевірте що додалось&lt;/span&gt;
ifconfig bridge101 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;inet &quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Якщо ви це 👆 зробили після створення тимчасової віртуальної машини, можете її зараз вилучити&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass delete &amp;lt;temp-vm&amp;gt; &lt;span class=&quot;nt&quot;&gt;--purge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;налаштування-першого-вузла-etcd&quot;&gt;Налаштування першого вузла etcd&lt;/h4&gt;

&lt;p&gt;Увійдемо до нашого вузла за допомогою SSH.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.11
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Оскільки у нас ще немає сертифікатів ЦС нам потрібно згенерувати їх.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-ca
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ми отримаємо два файли &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.crt&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ca.key&lt;/code&gt; в теці &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/etcd/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id=&quot;etcd-kubeadmcfg-yaml&quot;&gt;&lt;/a&gt;Тепер створимо конфігураційний файл для kubeadm &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadmcfg.yaml&lt;/code&gt; використавши відповідні значення в змінних &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ETCD_HOST&lt;/code&gt; (IP адреса віртуальної машини) та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ETCD_NAME&lt;/code&gt; (її коротка назва). Зверніть увагу на значення змінної &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ETCD_INITIAL_CLUSTER_STATE&lt;/code&gt; яка вказує на те що ми створюємо новий кластер etcd. Надалі ми будемо приєднувати до нього інші вузли.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $2}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;ETCD_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;=https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:2380&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER_STATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER_STATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;new&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/kubeadmcfg.yaml
---
apiVersion: &quot;kubeadm.k8s.io/v1beta4&quot;
kind: InitConfiguration
nodeRegistration:
    name: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
localAPIEndpoint:
    advertiseAddress: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
---
apiVersion: &quot;kubeadm.k8s.io/v1beta4&quot;
kind: ClusterConfiguration
etcd:
    local:
        serverCertSANs:
        - &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
        peerCertSANs:
        - &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
        extraArgs:
        - name: initial-cluster
          value: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
        - name: initial-cluster-state
          value: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER_STATE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
        - name: name
          value: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_NAME&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
        - name: listen-peer-urls
          value: https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:2380
        - name: listen-client-urls
          value: https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:2379,https://127.0.0.1:2379
        - name: advertise-client-urls
          value: https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:2379
        - name: initial-advertise-peer-urls
          value: https://&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:2380
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Використовуючи створений файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadmcfg.yaml&lt;/code&gt;, який ми помістили в домашню теку користувача &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8sadmin&lt;/code&gt;, виконаємо генерацію сертифікатів etcd та створимо маніфест статичного поду для вузла кластера etcd.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 1. Генерація сертифікатів&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-server &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-peer &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-healthcheck-client &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs apiserver-etcd-client &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тепер у нас мають бути в наявності наступні ключі та сертифікати&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;/home/k8sadmin
└── kubeadmcfg.yaml
---
/etc/kubernetes/pki
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
└── etcd
    ├── ca.crt
    ├── ca.key
    ├── healthcheck-client.crt
    ├── healthcheck-client.key
    ├── peer.crt
    ├── peer.key
    ├── server.crt
    └── server.key
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Після створення відповідних сертифікатів настав час створити маніфест статичного пода. В результаті у нас маєте бути файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/manifests/etcd.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 2. Створення маніфесту статичного пода&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase etcd &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Отримавши маніфест &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/manifests/etcd.yaml&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; вузла має підхопити його, викачати образ контейнера та запустити под з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd&lt;/code&gt;, після чого перший вузол нашого кластера має відповідати на проби справності&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crictl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;crictl ps &lt;span class=&quot;nt&quot;&gt;--label&lt;/span&gt; io.kubernetes.container.name&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;etcd &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt; https://10.10.0.11:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   endpoint health &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;+-------------------------+--------+------------+-------+
|        ENDPOINT         | HEALTH |    TOOK    | ERROR |
+-------------------------+--------+------------+-------+
| https://10.10.0.11:2379 |   true | 7.777048ms |       |
+-------------------------+--------+------------+-------+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;розгортання-наступних-вузлів-кластера-etcd&quot;&gt;Розгортання наступних вузлів кластера etcd&lt;/h3&gt;

&lt;p&gt;Для розгортання наступних вузлів кластера etcd: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-3&lt;/code&gt; й так далі (за потреби) змінимо значення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VM_IP&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;10.10.0.12/24&quot;&lt;/code&gt; та імʼя віртуальної машини в параметрі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt; відповідно.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.12/24&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; ext-etcd-2 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 2G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 5G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
      # Злиття всіх файлів в один обʼєкт
      . as $item ireduce ({}; . *+ $item) |

      # Вилучення kubectl зі списку пакунків
      del(.packages[] | select(. == &quot;kubectl&quot;)) |

      # Оновлення конфігурації мережі
      with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
        .content |= (
          from_yaml |
          .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] |
          to_yaml
        )
      ) &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-base.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-etcd.yaml &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Після завершення розгортання вузла перевіримо чи є у нас SSH доступ до нього:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.12 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-la&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;налаштування-вузлів-etcd-та-їх-приєднання-до-кластера&quot;&gt;Налаштування вузлів etcd та їх приєднання до кластера&lt;/h4&gt;

&lt;p&gt;На нашому вузлі etcd &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-1&lt;/code&gt;, який вже працює, виконаємо наступну команду для отримання інструкцій для приєднання вузла &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt; до кластера etcd. Після команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;member add&lt;/code&gt; вкажемо назву вузла, який потрібно приєднати, а в параметрі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--peer-urls&lt;/code&gt; шлях до нього&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crictl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;crictl ps &lt;span class=&quot;nt&quot;&gt;--label&lt;/span&gt; io.kubernetes.container.name&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;etcd &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt; https://10.10.0.11:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   member add ext-etcd-2 &lt;span class=&quot;nt&quot;&gt;--peer-urls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://10.10.0.12:2380
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcdctl&lt;/code&gt; зареєструє нового учасника кластера etcd, а у відповідь ми отримаємо його ID та рядок &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;ext-etcd-1=https://10.10.0.11:2380,ext-etcd-2=https://10.10.0.12:2380&quot;&lt;/code&gt; з повним переліком вузлів кластера (разом з новим членом)&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;Member e3e9330902f761c3 added to cluster 3f0c3972eda275cb

ETCD_NAME=&quot;ext-etcd-2&quot;
ETCD_INITIAL_CLUSTER=&quot;ext-etcd-1=https://10.10.0.11:2380,ext-etcd-2=https://10.10.0.12:2380&quot;
ETCD_INITIAL_ADVERTISE_PEER_URLS=&quot;https://10.10.0.12:2380&quot;
ETCD_INITIAL_CLUSTER_STATE=&quot;existing&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;створення-kubeadmcfgyaml&quot;&gt;Створення kubeadmcfg.yaml&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;#etcd-kubeadmcfg-yaml&quot;&gt;Створіть файл налаштувань &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/kubeadmcfg.yaml&lt;/code&gt;&lt;/a&gt; на вузлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt; замінивши значення змінних на ті що ви тільки що отримали після виконання команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;… member add …&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Наступним обовʼязковим кроком є копіювання файлів ЦС з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-1&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt;. Для зручності кроки з перенесення файлів ЦС було обʼєднано в скрипт який потрібно створити на вашій хост машині.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos; &amp;gt; copy-etcd-ca.sh
#!/bin/bash

# --- Стандартні налаштування (змініть під себе) ---
DEFAULT_KEY=&quot;~/.ssh/k8s_cluster_key&quot;
DEFAULT_SRC=&quot;10.10.0.11&quot;
DEFAULT_DEST=&quot;10.10.0.12&quot;
USER=&quot;k8sadmin&quot;
CERT_PATH=&quot;/etc/kubernetes/pki/etcd&quot;

# --- Присвоєння аргументів ---
# &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; - перший аргумент (хост-джерело), &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; - другий (хост-призначення), &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$3&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; - третій (шлях до ключа)
SRC_HOST=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_SRC&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
DEST_HOST=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_DEST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
KEY=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;

echo &quot;Використовуються параметри:&quot;
echo &quot;  Джерело:     &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;  Призначення: &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;DEST_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;  SSH Ключ:    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;---------------------------------------&quot;

# 1. Підготовка файлів на джерелі
echo &quot;[1/4] Підготовка файлів на &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;...&quot;
ssh -i &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;sudo cp &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CERT_PATH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ca.* /tmp/ &amp;amp;&amp;amp; sudo chown &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; /tmp/ca.*&quot; || exit 1

# 2. Передача файлів між хостами
echo &quot;[2/4] Копіювання з &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; на &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST_HOST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;...&quot;
scp -i &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:/tmp/ca.*&quot; &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST_HOST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;:/tmp/&quot; || exit 1

# 3. Розміщення файлів на цільовому хості
echo &quot;[3/4] Розміщення файлів на &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST_HOST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;...&quot;
ssh -i &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST_HOST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;sudo mkdir -p &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CERT_PATH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;amp;&amp;amp; sudo mv /tmp/ca.* &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CERT_PATH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ &amp;amp;&amp;amp; sudo chown root:root &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CERT_PATH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ca.* &amp;amp;&amp;amp; sudo chmod 600 &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CERT_PATH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ca.key&quot; || exit 1

# 4. Очищення
echo &quot;[4/4] Видалення тимчасових файлів...&quot;
ssh -i &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; &quot;rm /tmp/ca.*&quot;

echo &quot;Файли ca.crt та ca.key успішно перенесено з &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SRC_HOST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; до &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEST_HOST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x copy-etcd-ca.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Виконайте перенесення файлів ЦС з хосту &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.11&lt;/code&gt; до &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.12&lt;/code&gt; командою (вкажіть ваші параметри за потреби):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;./сopy-etcd-ca.sh 10.10.0.11 10.10.0.12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тепер згенеруємо файли ключів та сертифікатів використовуючи створений файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadmcfg.yaml&lt;/code&gt; на вузлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 1. Генерація сертифікатів&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-server &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-peer &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs etcd-healthcheck-client &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase certs apiserver-etcd-client &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Після створення необхідних файлів ключів та сертифікатів для другого вузла etcd видалимо файл з приватним ключем ЦС &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/etcd/ca.key&lt;/code&gt;, він тут більше не потрібен.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 2. Видаляємо /etc/kubernetes/pki/etcd/ca.key&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тепер коли ми маємо потрібні сертифікати на своїх місцях створимо маніфест для розгортання статичного пода.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init phase etcd &lt;span class=&quot;nb&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadmcfg.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Через хвилину-дві переглянемо перелік вузлів нашого кластера etcd&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crictl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;crictl ps &lt;span class=&quot;nt&quot;&gt;--label&lt;/span&gt; io.kubernetes.container.name&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;etcd &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt; https://10.10.0.11:2379  member list &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;+------------------+---------+------------+-------------------------+-------------------------+------------+
|        ID        | STATUS  |    NAME    |       PEER ADDRS        |      CLIENT ADDRS       | IS LEARNER |
+------------------+---------+------------+-------------------------+-------------------------+------------+
| 86041dd24c0806ff | started | ext-etcd-1 | https://10.10.0.11:2380 | https://10.10.0.11:2379 |      false |
| e3e9330902f761c3 | started | ext-etcd-2 | https://10.10.0.12:2380 | https://10.10.0.12:2379 |      false |
+------------------+---------+------------+-------------------------+-------------------------+------------+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Для додавання наступного вузла кластера повторіть ті ж самі кроки що й для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-2&lt;/code&gt;. Звертайте увагу на те що змінна &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ETCD_INITIAL_CLUSTER_STATE&lt;/code&gt; повинна мати значення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;existing&quot;&lt;/code&gt;, а в змінній &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ETCD_INITIAL_CLUSTER&lt;/code&gt; для створення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/kubeadmcfg.yaml&lt;/code&gt; на вузлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-3&lt;/code&gt; потрібно буде зазначити всі вузли, які мають бути членами кластера. Для вузла &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext-etcd-3&lt;/code&gt; з IP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.13&lt;/code&gt; ця змінна матиме наступний вигляд:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ext-etcd-1=https://10.10.0.11:2380,ext-etcd-2=https://10.10.0.12:2380,ext-etcd-3=https://10.10.0.13:2380&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;ETCD_INITIAL_CLUSTER_STATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;existing&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;скасування-приєднання-вузла-до-кластера&quot;&gt;Скасування приєднання вузла до кластера&lt;/h4&gt;

&lt;p&gt;Якщо ви з будь-яких причин не хочете приєднувати вузол до кластера etcd, вам потрібно скасувати попередню команду приєднання. Для цього треба видалити цей вузол зі списку членів кластера за його &lt;strong&gt;ID&lt;/strong&gt;. Навіть якщо вузол ще не запущений, він вже зареєстрований в кластері в стані &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unstarted&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Знайдіть ID потрібного вузла. Його можна знайти у першому рядку виводу команди приєднання, або ж через отримання списку всіх членів кластера:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;crictl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;crictl ps &lt;span class=&quot;nt&quot;&gt;--label&lt;/span&gt; io.kubernetes.container.name&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;etcd &lt;span class=&quot;nt&quot;&gt;--quiet&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt; https://10.10.0.11:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   member list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;У виводі ви побачите рядок, який виглядає приблизно так: &lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;62f5145363dbf1b5, unstarted, ext-etcd-2, https://10.10.0.12:2380, ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;або в табличному вигляді&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;+------------------+-----------+------------+-------------------------+-------------------------+------------+
|        ID        |  STATUS   |    NAME    |       PEER ADDRS        |      CLIENT ADDRS       | IS LEARNER |
+------------------+-----------+------------+-------------------------+-------------------------+------------+
| 167ef81a292916d4 |   started | ext-etcd-2 | https://10.10.0.12:2380 | https://10.10.0.12:2379 |      false |
| 62f5145363dbf1b5 | unstarted |            | https://10.10.0.14:2380 |                         |      false |
| 86041dd24c0806ff |   started | ext-etcd-1 | https://10.10.0.11:2380 | https://10.10.0.11:2379 |      false |
| ba9a6c0afb514fec |   started | ext-etcd-3 | https://10.10.0.13:2380 | https://10.10.0.13:2379 |      false |
+------------------+-----------+------------+-------------------------+-------------------------+------------+
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Скопіюйте ID (тут, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;62f5145363dbf1b5&lt;/code&gt;) і виконайте команду видалення:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt; https://10.10.0.11:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
   member remove &amp;lt;ID_ВУЗЛА&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;💡 Так само відбувається видалення будь-якого вузла з переліку членів кластера. Якщо ви хочете замінити один вузол кластера іншим — спочатку видаліть “старий” вузол, після чого виконайте приєднання нового вузла.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Чому це важливо?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Якщо ви просто залишите вузол у статусі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unstarted&lt;/code&gt;, etcd буде постійно намагатися з ним звʼязатися, що може призвести до збільшення затримок (latency) або проблем із досягненням кворуму в майбутньому.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Порада&lt;/strong&gt;: Перед повторною спробою приєднання переконайтеся, що на новому вузлі (10.10.0.12) видалено стару теку даних etcd (data-dir), щоб він міг почати синхронізацію “з чистого аркуша” як новий член кластера.&lt;/p&gt;

&lt;h4 id=&quot;видалення-data-dir&quot;&gt;Видалення data-dir&lt;/h4&gt;

&lt;p&gt;Шлях до теки даних (data-dir) залежить від того, як саме встановлено etcd (через kubeadm чи як окремий сервіс).&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Якщо etcd працює як Static Pod (найчастіший випадок, kubeadm)&lt;/p&gt;

    &lt;p&gt;Перегляньте маніфест пода на вузлі, де etcd вже працює:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;data-dir&quot;&lt;/span&gt; /etc/kubernetes/manifests/etcd.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Зазвичай стандартний шлях у такому разі: &lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/lib/etcd&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Якщо etcd працює як системний сервіс (Systemd)&lt;/p&gt;

    &lt;p&gt;Якщо ви встановлювали etcd вручну або через бінарні файли, перевірте конфігурацію сервісу:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;systemctl &lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;etcd | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;data-dir
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Або подивіться у файлі конфігурації (якщо він є): &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/etcd/etcd.conf&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Перевірка через інформацію про запущені процеси&lt;/p&gt;

    &lt;p&gt;Ви можете побачити шлях безпосередньо в аргументах запущеного процесу:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ps &lt;span class=&quot;nt&quot;&gt;-ef&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;etcd | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;data-dir
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Коли ви видалите помилковий запис про вузол (як описано вище) і захочете спробувати знову:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Очистіть теку на вузлі перед його повторним запуском&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /var/lib/etcd/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;em&gt;Зауваження: Переконайтеся, що ви видаляєте дані саме на потрібному вузлі.&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірте права доступу&lt;/strong&gt;: Після очищення переконайтеся, що користувач, від якого працює etcd (зазвичай &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd&lt;/code&gt; або &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;), має права на запис у цю теку.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;налаштування-балансувальника-навантаження-для-вузлів-панелі-управління-haproxykeepalived&quot;&gt;Налаштування балансувальника навантаження для вузлів панелі управління (HAProxy+Keepalived)&lt;/h2&gt;

&lt;p&gt;У кластері Kubernetes з HA-архітектурою &lt;strong&gt;балансувальник має бути піднятий ДО ініціалізації першого вузла панелі управління&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Це “правило першої цеглини”: ми не можемо побудувати стіну, якщо не визначили, де вона стоятиме. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;controlPlaneEndpoint&lt;/code&gt; — це точка входу, яка має бути доступною з першої секунди життя кластера.&lt;/p&gt;

&lt;p&gt;Порядок дій, якому ми будемо слідувати:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Розгорнемо HAProxy+Keepalived (10.10.0.100)&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Не обовʼязково треба мати “живі” бекенди (вузли панелі управління) у цей момент, але балансувальник має слухати порт 6443 і бути доступним в мережі.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Додамо вузли панелі управління в конфіг HAProxy.&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Ми ще не запустили на першому вузлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt;, але додамо його та IP інших вузлів у бекенди HAProxy.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Запустимо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; на першому вузлі панелі управління.&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Коли &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; спробує “постукати” на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100:6443&lt;/code&gt;, балансувальник переспрямує цей запит на цей самий перший вузол (де API-сервер щойно піднявся), і операція ініціалізації завершиться успішно.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Приєднаємо інші вузли Control Plane.&lt;/strong&gt;
    &lt;ul&gt;
      &lt;li&gt;Використаємо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join ... --control-plane&lt;/code&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;тимчасовий-хак-якщо-не-має-можливості-підняти-haproxy-зараз&quot;&gt;Тимчасовий “хак” (якщо не має можливості підняти HAProxy зараз)&lt;/h3&gt;

&lt;p&gt;Якщо у вас не має можливості розгорнути окрему машину для балансувальника прямо зараз, ви можете застосувати “маневр з IP-адресою”:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Тимчасово призначте IP 10.10.0.100 першому вузлу Control Plane&lt;/strong&gt; як додаткову (через alias).&lt;/li&gt;
  &lt;li&gt;Виконайте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt;. Система побачить “себе” за цією адресою і завершить налаштування.&lt;/li&gt;
  &lt;li&gt;Пізніше, коли ви розгорнете справжній HAProxy, перенесіть цей IP туди.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;створення-налаштувань-haproxykeepalived&quot;&gt;Створення налаштувань HAProxy+Keepalived&lt;/h3&gt;

&lt;p&gt;Для того щоб зробити наш балансувальник дійсно відмовостійким (High Availability), необхідно налаштувати &lt;strong&gt;Keepalived&lt;/strong&gt;. Він дозволить двом вузлам &lt;strong&gt;HAProxy&lt;/strong&gt; спільно використовувати одну “перехідну” IP-адресу (Virtual IP — VIP).&lt;/p&gt;

&lt;h4 id=&quot;налаштування-netplan&quot;&gt;Налаштування Netplan&lt;/h4&gt;

&lt;p&gt;Залишимо адресу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt; для Keepalived, а в розділі мережевих налаштувань віртуальної машини HAProxy (у нас їх буде дві) зробимо наступне:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/netplan/60-static-ip.yaml&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!!str&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0600&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;network:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;version: 2&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;ethernets:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;enp0s2:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;addresses:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;- 10.10.0.101/24 # Реальна IP вузла LB1 (для другого буде .102)&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;routes:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;- to: 10.10.0.0/24&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;scope: link&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;вкажемо адресу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.101&lt;/code&gt; для основного балансувальника, а &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.102&lt;/code&gt; — для резервного.&lt;/p&gt;

&lt;h4 id=&quot;конфігурація-keepalived&quot;&gt;Конфігурація Keepalived&lt;/h4&gt;

&lt;p&gt;Додамо наступний блок у &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files&lt;/code&gt;. Ця конфігурація змусить Keepalived стежити за станом HAProxy і передавати VIP іншому вузлу, якщо сервіс або машина впаде.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/keepalived/keepalived.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;vrrp_script check_haproxy {&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;script &quot;killall -0 haproxy&quot; # Перевірка, чи живий процес&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;interval 2&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;weight 2&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;vrrp_instance VI_1 {&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;state MASTER              # На другому вузлі вкажіть BACKUP&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;interface enp0s2          # Назва вашого інтерфейсу&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;virtual_router_id 51      # Має бути однаковим для обох LB&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;priority 101              # На другому вузлі вкажіть 100&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;advert_int 1&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;authentication {&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;auth_type PASS&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;auth_pass k8s_secret  # Спільний пароль&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;virtual_ipaddress {&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;10.10.0.100/24        # Ваша VIP адреса для Cluster Endpoint&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;

          &lt;span class=&quot;s&quot;&gt;track_script {&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;check_haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;налаштування-ядра-sysctl&quot;&gt;Налаштування ядра (Sysctl)&lt;/h4&gt;

&lt;p&gt;Щоб HAProxy міг “сісти” на IP-адресу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt;, яка йому ще не належить (поки Keepalived не підняв її), потрібно дозволити &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonlocal_bind&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Додамо це у &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write_files&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/sysctl.d/99-kubernetes-lb.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;net.ipv4.ip_nonlocal_bind = 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;І додамо команди в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;runcmd&lt;/code&gt; для застосування:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;runcmd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sysctl --system&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;netplan apply&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;systemctl enable --now haproxy&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;systemctl enable --now keepalived&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;як-це-працює-разом&quot;&gt;Як це працює разом&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;HAProxy&lt;/strong&gt; слухає на порту 6443, але він “бачить” лише трафік, який приходить на VIP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Keepalived&lt;/strong&gt; тримає адресу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt; на активному вузлі (MASTER).&lt;/li&gt;
  &lt;li&gt;Коли ми запускаємо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init --control-plane-endpoint &quot;10.10.0.100:6443&quot;&lt;/code&gt;, запит іде на VIP -&amp;gt; потрапляє в HAProxy -&amp;gt; перенаправляється на перший доступний вузол Control Plane.&lt;/li&gt;
  &lt;li&gt;Якщо перший балансувальник вимкнеться, другий (BACKUP) миттєво забере собі IP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt;, і наш кластер Kubernetes продовжить роботу без розриву зʼєднання.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Для розгортання відмовостійкого балансувальника нам потрібно скласти до купи налаштування з:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-config.yaml&lt;/code&gt; — налаштування часового поясу, та мережеві налаштування&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-user.yaml&lt;/code&gt; – будемо використовувати користувача &lt;strong&gt;k8sadmin&lt;/strong&gt;, у якого групу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerd&lt;/code&gt; замінимо на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haproxy&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-lb.yaml&lt;/code&gt; – налаштування специфічні для розгортання та запуску вузлів балансувальника&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Створимо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-lb.yaml&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; &apos;EOF&apos; &amp;gt; snipets/cloud-init-lb.yaml&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;package_update&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;package_upgrade&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;haproxy&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;keepalived&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/sysctl.d/99-haproxy.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;net.ipv4.ip_nonlocal_bind = 1&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/haproxy/haproxy.cfg&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;global&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;log /dev/log local0&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;user haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;group haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;daemon&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;stats socket /run/haproxy/admin.sock mode 660 level admin&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;defaults&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;log     global&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;mode    tcp&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;option  tcplog&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout connect 5000&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout client  50000&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout server  50000&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;frontend k8s-api&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;bind *:6443&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;default_backend k8s-api-backend&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;backend k8s-api-backend&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;balance roundrobin&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;option tcp-check&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout server 2h&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout client 2h&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;# Тут ми прописуємо відомі нам параметри вузлів панелі управління&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;server cp-1 10.10.0.21:6443 check check-ssl verify none fall 3 rise 2&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;server cp-2 10.10.0.22:6443 check check-ssl verify none fall 3 rise 2&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;server cp-3 10.10.0.23:6443 check check-ssl verify none fall 3 rise 2&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;listen stats&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;bind *:8404&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;mode http&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;stats enable&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;stats uri /stats&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/keepalived/keepalived.conf&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;vrrp_script check_haproxy {&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;script &quot;killall -0 haproxy&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;interval 2&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;weight 2&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;vrrp_instance VI_1 {&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;state ${LB_STATE} # для lb1 буде MASTER, для lb2 буде BACKUP&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;interface enp0s2&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;virtual_router_id 51&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;priority ${LB_PRIORITY} # для lb1 буде 101, для lb2 буде 100&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;advert_int 1&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;authentication {&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;auth_type PASS&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;auth_pass k8s_pwd # значення k8s_pwd потрібно замінити на найдійніший пароль&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;virtual_ipaddress {&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;${LB_IP} # загальна адреса балансувальника 10.10.0.100/24&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;track_script {&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;check_haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;runcmd&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;sysctl --system&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;systemctl enable --now haproxy&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;systemctl enable --now keepalived&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;розгортання-haproxykeepalived&quot;&gt;Розгортання HAProxy+Keepalived&lt;/h3&gt;

&lt;p&gt;Створимо віртуальні машини для HAProxy+Keepalived:&lt;/p&gt;

&lt;p&gt;Для створення відмовостійкого балансувальника нам знадобиться дві майже ідентичних команди для запуску. Основна різниця між ними полягає в налаштуваннях Keepalived (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;priority&lt;/code&gt;) та індивідуальних IP-адресах вузлів.&lt;/p&gt;

&lt;p&gt;Запустимо перший вузол балансувальника&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Реальна IP адреса для інтерфейсу (netplan)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.101/24&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Параметри для keepalived.conf&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LB_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.100/24&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LB_STATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MASTER&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LB_PRIORITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;101&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; lb1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 1G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 5G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
    # 1. Злиття всіх файлів в один обʼєкт
    . as $item ireduce ({}; . *+ $item) |

    # 2. Виконання netplan apply після sysctl
    .runcmd |= (
      filter(. != &quot;netplan apply&quot;) |
      (to_entries | .[] | select(.value == &quot;sysctl --system&quot;) | .key) as $idx |
      .[:$idx+1] + [&quot;netplan apply&quot;] + .[$idx+1:]
    ) |
    .runcmd[].headComment = &quot;&quot; |

    # 3. Заміна групи користувача
    with(.users[] | select(.name == &quot;k8sadmin&quot;);
      .groups |= sub(&quot;containerd&quot;, &quot;haproxy&quot;)
    ) |

    # 4. Налаштування IP для застосування через Netplan
    with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
      .content |= (from_yaml | .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] | to_yaml)
    ) |

    # 5. Заміна змінних ${LB_...} у всіх файлах write_files
    with(.write_files[];
      .content |= sub(&quot;\${LB_STATE}&quot;, strenv(LB_STATE)) |
      .content |= sub(&quot;\${LB_PRIORITY}&quot;, strenv(LB_PRIORITY)) |
      .content |= sub(&quot;\${LB_IP}&quot;, strenv(LB_IP))
    )
  &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  snipets/cloud-init-lb.yaml&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;І другий вузол балансувальника&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Реальна IP адреса для інтерфейсу (netplan)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.102/24&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Параметри для keepalived.conf&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LB_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.100/24&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LB_STATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;BACKUP&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LB_PRIORITY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;100&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; lb2 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 1G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 5G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
    # 1. Злиття всіх файлів в один обʼєкт
    . as $item ireduce ({}; . *+ $item) |

    # 2. Виконання netplan apply після sysctl
    .runcmd |= (
      filter(. != &quot;netplan apply&quot;) |
      (to_entries | .[] | select(.value == &quot;sysctl --system&quot;) | .key) as $idx |
      .[:$idx+1] + [&quot;netplan apply&quot;] + .[$idx+1:]
    ) |
    .runcmd[].headComment = &quot;&quot; |

    # 3. Заміна групи користувача
    with(.users[] | select(.name == &quot;k8sadmin&quot;);
      .groups |= sub(&quot;containerd&quot;, &quot;haproxy&quot;)
    ) |

    # 4. Налаштування IP для застосування через Netplan
    with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
      .content |= (from_yaml | .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] | to_yaml)
    ) |

    # 5. Заміна змінних ${LB_...} у всіх файлах write_files
    with(.write_files[];
      .content |= sub(&quot;\${LB_STATE}&quot;, strenv(LB_STATE)) |
      .content |= sub(&quot;\${LB_PRIORITY}&quot;, strenv(LB_PRIORITY)) |
      .content |= sub(&quot;\${LB_IP}&quot;, strenv(LB_IP))
    )
  &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  snipets/cloud-init-lb.yaml&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Після запуску перевіримо наявність IP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Зайдемо на будь-яку машину і перевіримо, чи зʼявилася адреса &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;lb1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ip addr show enp0s2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;що-робити-після-запуску&quot;&gt;Що робити після запуску?&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірити статистику&lt;/strong&gt;: Відкриємо в оглядачі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://10.10.0.100:8404/stats&lt;/code&gt;. Ми побачимо, що бекенди (наші вузли панелі управління) позначені червоним (бо вони ще не ініціалізовані) — це нормально.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Запуск Kubernetes&lt;/strong&gt;: Тепер ми можемо запустити &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; на першому вузлі Control Plane. Оскільки VIP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.100&lt;/code&gt; вже активний і HAProxy слухає порт &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6443&lt;/code&gt;, помилка тайм-ауту не виникне.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;розгортання-control-plane&quot;&gt;Розгортання Control Plane&lt;/h2&gt;

&lt;p&gt;Зберемо налаштування cloud-init для розгортання вузлів нашої панелі управління. Вони будуть схожі на той що ми використовували для створення вузлів etcd але з деякими відмінностями.&lt;/p&gt;

&lt;p&gt;Відповідно до &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#before-you-begin&quot;&gt;рекомендацій&lt;/a&gt; виділимо вузлу панелі управління не менше ніж 2 ядра процесора та 2ГБ оперативної памʼяті. Для першого вузла панелі управління будемо використовувати IP адресу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.21&lt;/code&gt;. Також встановимо HAProxy як локальний балансувальник навантаження для доступу до вузлів etcd.&lt;/p&gt;

&lt;h3 id=&quot;налаштування-локального-балансувальника-навантаження-для-доступу-до-вузлів-etcd&quot;&gt;Налаштування локального балансувальника навантаження для доступу до вузлів etcd&lt;/h3&gt;

&lt;p&gt;Для доступу до вузлів etcd розгорнемо локальний балансувальник навантаження. Кожен API-сервер буде звертатись до свого балансувальника навантаження (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:2379&lt;/code&gt;). Такий підхід називається &lt;strong&gt;“Sidecar Load Balancing”&lt;/strong&gt; (або локальний проксі). Він забезпечує максимальну відмовостійкість: навіть якщо мережу між вузлами почне “штормити”, кожен API-сервер матиме свій локальний шлях до etcd.&lt;/p&gt;

&lt;p&gt;Оскільки ми робимо це для кластера Kubernetes, найкращий спосіб реалізувати це — використати &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/tasks/configure-pod-container/static-pod/&quot;&gt;Static Pods&lt;/a&gt;. Керівник вузла (kubelet) сам запускатиме та підтримуватиме HAProxy.&lt;/p&gt;

&lt;h4 id=&quot;підготовка-конфігурації-haproxy&quot;&gt;Підготовка конфігурації HAProxy&lt;/h4&gt;

&lt;p&gt;Для вузлів панелі управління створимо файл з налаштуваннями HAProxy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/haproxy-lbaas/haproxy.cfg&lt;/code&gt; для балансування трафіку до вузлів etcd&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; snipets/cloud-init-cp-haproxy.yaml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Налаштування HAProxy для доступу до вузів etcd&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/haproxy-lbaas/haproxy.cfg&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;global&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;log /dev/log local0&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;user haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;group haproxy&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;defaults&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;log global&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;mode tcp&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;option tcplog&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout connect 5000ms&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout client 50000ms&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;timeout server 50000ms&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;frontend etcd-local&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;bind 127.0.0.1:2379&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;description &quot;Local proxy for etcd cluster&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;default_backend etcd-cluster&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;backend etcd-cluster&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;option tcp-check&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;# Важливо: використовуємо roundrobin для розподілу навантаження&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;balance roundrobin&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;server etcd-1 10.10.0.11:2379 check inter 2000 rise 2 fall 3&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;server etcd-2 10.10.0.12:2379 check inter 2000 rise 2 fall 3&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;server etcd-3 10.10.0.13:2379 check inter 2000 rise 2 fall 3&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;створення-static-pod-для-haproxy&quot;&gt;Створення Static Pod для HAProxy&lt;/h4&gt;

&lt;p&gt;Змусимо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; запустити HAProxy. Створімо маніфест у теці статичних подів (стандартно це &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/manifests/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Створимо файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/manifests/etcd-proxy.yaml&lt;/code&gt; з маніфестом статичного пода HAProxy:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;cat &amp;lt;&amp;lt; EOF &amp;gt; snipets/cloud-init-cp-haproxy-manifest.yaml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;write_files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Маніфест статичного пода HAProxy для балансування трафіку до вузлів etcd&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/kubernetes/manifests/etcd-haproxy.yaml&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;apiVersion: v1&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;kind: Pod&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;metadata:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;name: etcd-haproxy&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;namespace: kube-system&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;labels:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;component: etcd-haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;tier: control-plane&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;spec:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;- name: etcd-haproxy&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;image: haproxy:2.8-alpine # Використовуємо легкий образ&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;resources:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;requests:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;cpu: 100m&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;memory: 100Mi&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;volumeMounts:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;- name: haproxy-config&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;mountPath: /usr/local/etc/haproxy/haproxy.cfg&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;readOnly: true&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;hostNetwork: true # Важливо: под має бачити 127.0.0.1 хоста&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;- name: haproxy-config&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;hostPath:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;path: /etc/haproxy-lbaas/haproxy.cfg&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;type: File&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;EOF&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;створення-першого-вузла-панелі-управління&quot;&gt;Створення першого вузла панелі управління&lt;/h4&gt;

&lt;p&gt;Створимо перший вузол панелі управління &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt; з IP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.21/24&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.21/24&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; cp-1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 2.5G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 8G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
      # Злиття всіх файлів в один обʼєкт
      . as $item ireduce ({}; . *+ $item) |

      # Оновлення конфігурації мережі
      with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
        .content |= (
          from_yaml |
          .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] |
          to_yaml
        )
      ) &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-base.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-cp-haproxy.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-cp-haproxy-manifest.yaml&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Після створення вузла панелі управління скопіюємо наступні файли з будь-якого вузла etcd на &lt;strong&gt;перший вузол&lt;/strong&gt; панелі управління (цього не треба буде робити для інших вузлів панелі управління впродовж перших двох годин після ініціалізації допоки Секрет з ключами не буде вилучений системою).&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 1. Підготуємо файли на початковому вузлі (10.10.0.11):&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Копіюємо у тимчасову теку і змінюємо власника на поточного користувача, щоб scp міг їх прочитати&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.11 &lt;span class=&quot;s2&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  mkdir -p /tmp/cert/ &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo cp /etc/kubernetes/pki/etcd/ca.crt /tmp/cert/ &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo cp /etc/kubernetes/pki/apiserver-etcd-client.* /tmp/cert/ &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo chown k8sadmin:k8sadmin /tmp/cert/* &quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 2. Перенесення файлів між вузлами через ваш локальний термінал:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Використовуємо лапки для обробки wildcards (*) на віддаленому боці&lt;/span&gt;
scp &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;k8sadmin@10.10.0.11:/tmp/cert/&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;k8sadmin@10.10.0.21:/tmp&apos;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 3. Розміщення файлів на цільовому вузлі (10.10.0.21):&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Створюємо теку (якщо її немає), переміщуємо файли і повертаємо права root&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.21 &lt;span class=&quot;s2&quot;&gt;&quot; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo mkdir -p /etc/kubernetes/pki/etcd/ &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo mv /tmp/cert/ca.crt /etc/kubernetes/pki/etcd/ &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo chown root:root /etc/kubernetes/pki/etcd/ca.crt &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo mv /tmp/cert/apiserver-etcd-client.* /etc/kubernetes/pki/ &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  sudo chown root:root /etc/kubernetes/pki/apiserver-etcd-client.*&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 4. Очищення тимчасових файлів:&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.11 &lt;span class=&quot;s2&quot;&gt;&quot;rm -rf /tmp/cert&quot;&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.21 &lt;span class=&quot;s2&quot;&gt;&quot;rm -rf /tmp/cert&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;перевірка-використання-static-pods-haproxy&quot;&gt;Перевірка використання Static Pods HAProxy&lt;/h4&gt;

&lt;p&gt;Ми поклали маніфест &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd-proxy.yaml&lt;/code&gt; у &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/manifests/&lt;/code&gt;. Проте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; ігнорує цю теку, доки не отримає команду на запуск (це відбудеться після ініціалізації вузла панелі управління).&lt;/p&gt;

&lt;p&gt;Крім того, на етапі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preflight&lt;/code&gt; команда &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; намагається перевірити доступність etcd &lt;strong&gt;до того&lt;/strong&gt;, як почне працювати будь-який компонент кластера. Оскільки наш HAProxy має працювати як Pod, він ще не запущений, порт &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:2379&lt;/code&gt; закритий — і ми отримаємо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connection refused&lt;/code&gt; під час спроби ініціалізації вузла панелі управління.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-log&quot;&gt;[preflight] Running pre-flight checks
	[WARNING ExternalEtcdVersion]: Get &quot;https://127.0.0.1:2379/version&quot;: dial tcp 127.0.0.1:2379: connect: connection refused
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Після ініціалізації вузла панелі управління є кілька способів перевірити працездатність звʼязку з etcd через локальний HAProxy:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка через cURL (найшвидший спосіб)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Оскільки ми використовуємо TLS, нам знадобляться сертифікати, які ми вже підготували для kubeadm. Спробуємо звернутися до etcd через локальний порт:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/apiserver-etcd-client.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/apiserver-etcd-client.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
     https://127.0.0.1:2379/health
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;strong&gt;Очікуваний результат:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{&quot;health&quot;:&quot;true&quot;}&lt;/code&gt;. Якщо у нас є ця відповідь, значить HAProxy успішно прокидає трафік до одного з вузлів нашого кластера etcd.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка стану Static Pod&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Перевіримо, чи взагалі запустився контейнер HAProxy. Оскільки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; може не працювати, якщо etcd недоступний, використовуйте інструмент середовища виконання контейнерів (в нашому випадку crictl):&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Для containerd (стандарт для сучасних K8s)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;crictl ps | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;etcd-haproxy

&lt;span class=&quot;c&quot;&gt;# Перегляд логів проксі&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;crictl logs &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;crictl ps &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; etcd-haproxy&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;У логах HAProxy ви мають бути записи про успішні перевірки справності (Health check passed) для бекенд-вузлів 10.10.0.11, .12, .13.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка через системні сокети&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Переконаймося, що HAProxy дійсно слухає порт 2379 на локальному інтерфейсі:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ss &lt;span class=&quot;nt&quot;&gt;-tulpn&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;2379
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Ми маємо побачити, що процес (haproxy) слухає &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:2379&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;налаштування-kubeadm&quot;&gt;Налаштування kubeadm&lt;/h3&gt;

&lt;p&gt;Створимо на першому візлі в домашній теці користувача &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8sadmin&lt;/code&gt; файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm-config.yaml&lt;/code&gt; для ініціалізації панелі управління&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.21 &lt;span class=&quot;s2&quot;&gt;&quot;cat &amp;lt;&amp;lt; &apos;EOF&apos; &amp;gt; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;HOME/kubeadm-config.yaml
---
apiVersion: kubeadm.k8s.io/v1beta4
kind: ClusterConfiguration
kubernetesVersion: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;v1.34.3&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
controlPlaneEndpoint: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10.10.0.100:6443&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
etcd:
  external:
    endpoints:
      - https://127.0.0.1:2379
    caFile: /etc/kubernetes/pki/etcd/ca.crt
    certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
    keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
networking:
  serviceSubnet: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10.96.0.0/16&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  podSubnet: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;10.244.0.0/16&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
  dnsDomain: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;cluster.local&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
EOF&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;запуск-ініціалізації-та-оминання-перевірки-externaletcdversion&quot;&gt;Запуск ініціалізації та оминання перевірки ExternalEtcdVersion&lt;/h3&gt;

&lt;p&gt;Коли &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; починає роботу він намагається перевірити доступ до зовнішнього кластера etcd. Однак ми «завернули» доступ до кластера у локальний HAProxy, який &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; запускає як статичний под. Однак на цей момент у нас ще не запущено &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt; який візьме на себе керування &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt;, який своєю чергою запустить под з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;haproxy&lt;/code&gt;. Тому нам потрібно вимкнути “передполітні перевірки” для etcd (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ignore-preflight-errors=ExternalEtcdVersion&lt;/code&gt;). Для ініціалізації кластера на вузлі панелі управління виконайте наступну команду:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/kubeadm-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--upload-certs&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--ignore-preflight-errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ExternalEtcdVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;На що звернути увагу під час ініціалізації:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Після запуску команди стежте за етапом &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[control-plane] Creating static pod manifest for &quot;kube-apiserver&quot;&lt;/code&gt;. Якщо API-сервер успішно запуститься, це означатиме, що він зміг підключитися до &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd&lt;/code&gt; через ваш локальний проксі.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Якщо команда знову зупиниться на помилці&lt;/strong&gt;, перевірте, чи не залишилося в системі процесів від попередніх спроб:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Якщо потрібно повністю скинути стан перед новою спробою&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm reset &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Після reset треба буде знову перезапустити kubelet, щоб піднявся проксі&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl restart kubelet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;За нормальних обставин ви побачите наступний лог роботи &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt;&lt;/p&gt;

&lt;details&gt;
  &lt;summary&gt;&lt;strong&gt;Подивитись лог&lt;/strong&gt;&lt;/summary&gt;

  &lt;pre&gt;&lt;code class=&quot;language-log&quot;&gt;[init] Using Kubernetes version: v1.34.3
[preflight] Running pre-flight checks
	[WARNING ExternalEtcdVersion]: Get &quot;https://127.0.0.1:2379/version&quot;: dial tcp 127.0.0.1:2379: connect: connection refused
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action beforehand using &apos;kubeadm config images pull&apos;
[certs] Using certificateDir folder &quot;/etc/kubernetes/pki&quot;
[certs] Generating &quot;ca&quot; certificate and key
[certs] Generating &quot;apiserver&quot; certificate and key
[certs] apiserver serving cert is signed for DNS names [cp-1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.2.176 10.10.0.100]
[certs] Generating &quot;apiserver-kubelet-client&quot; certificate and key
[certs] Generating &quot;front-proxy-ca&quot; certificate and key
[certs] Generating &quot;front-proxy-client&quot; certificate and key
[certs] External etcd mode: Skipping etcd/ca certificate authority generation
[certs] External etcd mode: Skipping etcd/server certificate generation
[certs] External etcd mode: Skipping etcd/peer certificate generation
[certs] External etcd mode: Skipping etcd/healthcheck-client certificate generation
[certs] External etcd mode: Skipping apiserver-etcd-client certificate generation
[certs] Generating &quot;sa&quot; key and public key
[kubeconfig] Using kubeconfig folder &quot;/etc/kubernetes&quot;
[kubeconfig] Writing &quot;admin.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;super-admin.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;kubelet.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;controller-manager.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;scheduler.conf&quot; kubeconfig file
[control-plane] Using manifest folder &quot;/etc/kubernetes/manifests&quot;
[control-plane] Creating static Pod manifest for &quot;kube-apiserver&quot;
[control-plane] Creating static Pod manifest for &quot;kube-controller-manager&quot;
[control-plane] Creating static Pod manifest for &quot;kube-scheduler&quot;
[kubelet-start] Writing kubelet environment file with flags to file &quot;/var/lib/kubelet/kubeadm-flags.env&quot;
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/instance-config.yaml&quot;
[patches] Applied patch of type &quot;application/strategic-merge-patch+json&quot; to target &quot;kubeletconfiguration&quot;
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/config.yaml&quot;
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory &quot;/etc/kubernetes/manifests&quot;
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 501.932756ms
[control-plane-check] Waiting for healthy control plane components. This can take up to 4m0s
[control-plane-check] Checking kube-apiserver at https://192.168.2.176:6443/livez
[control-plane-check] Checking kube-controller-manager at https://127.0.0.1:10257/healthz
[control-plane-check] Checking kube-scheduler at https://127.0.0.1:10259/livez
[control-plane-check] kube-controller-manager is healthy after 1.515332016s
[control-plane-check] kube-scheduler is healthy after 19.381430245s
[control-plane-check] kube-apiserver is healthy after 21.504025006s
[upload-config] Storing the configuration used in ConfigMap &quot;kubeadm-config&quot; in the &quot;kube-system&quot; Namespace
[kubelet] Creating a ConfigMap &quot;kubelet-config&quot; in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Storing the certificates in Secret &quot;kubeadm-certs&quot; in the &quot;kube-system&quot; Namespace
[upload-certs] Using certificate key:
7a088e936453ab3143f25cdb9827b8cac60888c75f91b9d6c2d08d23a32a2bc9
[mark-control-plane] Marking the node cp-1 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node cp-1 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: z28v5d.4vm6rzekoibear23
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the &quot;cluster-info&quot; ConfigMap in the &quot;kube-public&quot; namespace
[kubelet-finalize] Updating &quot;/etc/kubernetes/kubelet.conf&quot; to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run &quot;kubectl apply -f [podnetwork].yaml&quot; with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes running the following command on each as root:

  kubeadm join 10.10.0.100:6443 --token z28v5d.4vm6rzekoibear23 \
	--discovery-token-ca-cert-hash sha256:4c23033729b477d1fc30ae4b4041fe7dae70fa8defd5ecb57c571e969e00f8e0 \
	--control-plane --certificate-key 7a088e936453ab3143f25cdb9827b8cac60888c75f91b9d6c2d08d23a32a2bc9

Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
&quot;kubeadm init phase upload-certs --upload-certs&quot; to reload certs afterward.

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.10.0.100:6443 --token z28v5d.4vm6rzekoibear23 \
	--discovery-token-ca-cert-hash sha256:4c23033729b477d1fc30ae4b4041fe7dae70fa8defd5ecb57c571e969e00f8e0
&lt;/code&gt;&lt;/pre&gt;

&lt;/details&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Після запуску ініціалізації з ігноруванням помилок, нам важливо переконатися, що API-сервер дійсно зміг підключитися до бази даних, а не просто “висить” у стані очікування.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Головна перевірка: Стан API-сервера&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Якщо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; пройшов далі етапу preflight, він спробує запустити &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt;. Якщо API-сервер не може звʼязатися з etcd через наш проксі, він буде постійно перезавантажуватися.&lt;/p&gt;

&lt;p&gt;Перевіримо логи API-сервера:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /var/log/pods/kube-system_kube-apiserver-&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;/kube-apiserver/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;details&gt;
  &lt;summary&gt;&lt;strong&gt;Подивитись лог API-сервера&lt;/strong&gt;&lt;/summary&gt;

  &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;k8sadmin@cp-1:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; /var/log/pods/kube-system_kube-apiserver-cp-1_70e58895431aff7a0cb441009519f1c6/kube-apiserver/0.log
2026-01-07T11:10:20.602046303+02:00 stderr F W0107 09:10:20.601902       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#359 SubChannel #360]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: authentication handshake failed: context canceled&quot;&lt;/span&gt;
2026-01-07T11:10:20.617773217+02:00 stderr F W0107 09:10:20.617570       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#363 SubChannel #364]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: Error while dialing: dial tcp 127.0.0.1:2379: operation was canceled&quot;&lt;/span&gt;
2026-01-07T11:10:20.634721419+02:00 stderr F W0107 09:10:20.634607       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#367 SubChannel #368]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: authentication handshake failed: context canceled&quot;&lt;/span&gt;
2026-01-07T11:10:20.644114716+02:00 stderr F W0107 09:10:20.644002       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#371 SubChannel #372]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: Error while dialing: dial tcp 127.0.0.1:2379: operation was canceled&quot;&lt;/span&gt;
2026-01-07T11:10:20.662781139+02:00 stderr F W0107 09:10:20.662426       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#375 SubChannel #376]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: Error while dialing: dial tcp 127.0.0.1:2379: operation was canceled&quot;&lt;/span&gt;
2026-01-07T11:10:20.675026234+02:00 stderr F W0107 09:10:20.674872       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#379 SubChannel #380]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: authentication handshake failed: context canceled&quot;&lt;/span&gt;
2026-01-07T11:10:20.860920026+02:00 stderr F W0107 09:10:20.860664       1 logging.go:55] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;core] &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;Channel &lt;span class=&quot;c&quot;&gt;#383 SubChannel #384]grpc: addrConn.createTransport failed to connect to {Addr: &quot;127.0.0.1:2379&quot;, ServerName: &quot;127.0.0.1:2379&quot;, BalancerAttributes: {&quot;&amp;lt;%!p(pickfirstleaf.managedByPickfirstKeyType={})&amp;gt;&quot;: &quot;&amp;lt;%!p(bool=true)&amp;gt;&quot; }}. Err: connection error: desc = &quot;transport: Error while dialing: dial tcp 127.0.0.1:2379: operation was canceled&quot;&lt;/span&gt;
2026-01-07T11:11:10.4594371+02:00 stderr F I0107 09:11:10.459184       1 controller.go:667] quota admission added evaluator &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;: replicasets.apps
2026-01-07T11:19:59.613078541+02:00 stderr F I0107 09:19:59.612638       1 cidrallocator.go:277] updated ClusterIP allocator &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;Service CIDR 10.96.0.0/16
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

&lt;/details&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Цей лог свідчить про дуже важливий етап: наш &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt; &lt;strong&gt;успішно запустився&lt;/strong&gt;, але процес ініціалізації пройшов через “боротьбу” за звʼязок з etcd.&lt;/p&gt;

&lt;p&gt;Ось що сталося:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Етап помилок (Handshake failed)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Перші рядки логу показують помилки: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;transport: authentication handshake failed: context canceled та dial tcp 127.0.0.1:2379: operation was canceled&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;Це означає що:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;API-сервер намагався підключитися до вашого HAProxy (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:2379&lt;/code&gt;).&lt;/li&gt;
      &lt;li&gt;Зʼєднання встановлювалося, але TLS-рукостискання (handshake) обривалося.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;&lt;strong&gt;Причина&lt;/strong&gt;: Це відбувалося в той самий момент, коли &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; ще генерував або підкладав сертифікати, або коли HAProxy ще не встиг встановити стабільну сесію з бекенд-вузлами etcd. Це нормальна поведінка під час “холодного” старту панелі управління.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Етап успіху (Стабілізація)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Зверніть увагу на останні рядки: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I0107 09:11:10.459184 ... quota admission added evaluator for: replicasets.apps&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;I0107 09:19:59.612638 ... updated ClusterIP allocator for Service CIDR 10.96.0.0/16&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;Це перемога:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;API-сервер живий&lt;/strong&gt;. Якби він не зміг підключитися до etcd, він би просто завершив роботу з помилкою (CrashLoopBackOff) і ви б не побачили логів про &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cidrallocator&lt;/code&gt;.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;Повідомлення про &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClusterIP allocator&lt;/code&gt; означає, що API-сервер вже почав записувати дані в etcd і керувати ресурсами кластера.&lt;/p&gt;

    &lt;p&gt;Інтервал між записами (10 хвилин) показує стабільну фонову роботу контролерів.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Стан компонентів&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Те, що ви бачите теки для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-controller-manager&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-scheduler&lt;/code&gt; у &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/pods/&lt;/code&gt;, підтверджує, що &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; успішно пройшов фазу створення маніфестів і всі три основні компоненти Control Plane запущені.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;k8sadmin@cp-1:~&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sudo ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-la&lt;/span&gt; /var/log/pods/
total 28
drwxr-x---  7 root root   4096 Jan  8 22:23 &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
drwxr-xr-x 11 root syslog 4096 Jan  8 22:23 ..
drwxr-xr-x  3 root root   4096 Jan  8 22:23 kube-system_etcd-haproxy-cp-1_e2b3a81fe56706e845a17ba096c5dfad
drwxr-xr-x  3 root root   4096 Jan  8 22:23 kube-system_kube-apiserver-cp-1_e74afa6943effdf6bbdcfc384bd87bb6
drwxr-xr-x  3 root root   4096 Jan  8 22:23 kube-system_kube-controller-manager-cp-1_b635ba5e5439cc2c581bf61ca1e6fb9e
drwxr-xr-x  3 root root   4096 Jan  8 22:23 kube-system_kube-proxy-9qh54_9e21026d-0d6e-4f8c-a071-842149ffd24e
drwxr-xr-x  3 root root   4096 Jan  8 22:23 kube-system_kube-scheduler-cp-1_0cf013b3f4c49c84241ee3a56735a15d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Що перевірити зараз?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Оскільки API-сервер відповідає, виконайте наступні команди, щоб остаточно переконатися в справності першого вузла:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка вузлів&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl get nodes&lt;/code&gt; (&lt;em&gt;Ви маєте побачити cp-1 у статусі NotReady — це нормально, бо ми ще не встановили Calico&lt;/em&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка працездатності проксі (через HAProxy)&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl get --raw /healthz/etcd&lt;/code&gt; (&lt;em&gt;Має повернути &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ok&lt;/code&gt;&lt;/em&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка точок доступу etcd&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl describe pod kube-apiserver-cp-1 -n kube-system | grep etcd&lt;/code&gt; (&lt;strong&gt;Переконайтеся, що там фігурує лише &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://127.0.0.1:2379&lt;/code&gt;&lt;/strong&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Перевірка HAProxy&lt;/strong&gt;: перейдіть за посиланням &lt;a href=&quot;http://10.10.0.100:8404/stats&quot;&gt;http://10.10.0.100:8404/stats&lt;/a&gt; в інфопанель балансувальника навантаження панелі управління (&lt;em&gt;В розділі k8s-api-backend статус першого вузла панелі управління має бути UP&lt;/em&gt;)&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Далі рекомендується розгорнути CNI-втулок для мережі подів.&lt;/p&gt;

&lt;h2 id=&quot;встановлення-calico&quot;&gt;Встановлення Calico&lt;/h2&gt;

&lt;p&gt;Для створення мережі подів скористаємось Calico. Стаття «&lt;a href=&quot;https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises&quot;&gt;Install Calico networking and network policy for on-premises deployments&lt;/a&gt;» докладно описує процес встановлення Calico на власному обладнані.&lt;/p&gt;

&lt;p&gt;Ми будемо використовувати Tigera Operator та визначення власних ресурсів (CRDs, custom resource definitions). Застосуємо  наступні два маніфести:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/projectcalico/calico/v3.31.3/manifests/operator-crds.yaml
kubectl create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/projectcalico/calico/v3.31.3/manifests/tigera-operator.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Після чого завантажимо файл з власними ресурсами потрібними для налаштування Calico.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/projectcalico/calico/v3.31.3/manifests/custom-resources-bpf.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;та вкажемо CIDR мережі подів таке саме, що ми його вказали під час ініціалізації першого вузла панелі управління — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.244.0.0/16&lt;/code&gt; (стандартне значення в Calico — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cidr: 192.168.0.0/16&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Після внесення змін застосуємо маніфест для встановлення Calico&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; custom-resources-bpf.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Відстежуватимемо встановлення за допомогою команді &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;watch kubectl get tigerastatus&lt;/code&gt;. Через кілька хвилин (6-7 хвилин) усі компоненти Calico будуть мати значення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt; у стовпці &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AVAILABLE&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;NAME                            AVAILABLE   PROGRESSING   DEGRADED   SINCE
apiserver                       True        False         False      4m9s
calico                          True        False         False      3m29s
goldmane                        True        False         False      3m39s
ippools                         True        False         False      6m4s
kubeproxy-monitor               True        False         False      6m15s
whisker                         True        False         False      3m19s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Як тільки компоненти Calico стануть доступними, вузол панелі управління перейде в стан &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;READY&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;що-роботи-спочатку-розгортати-calico-чи-виконувати-kubeadm-join&quot;&gt;Що роботи спочатку: розгортати Calico чи виконувати kubeadm join?&lt;/h3&gt;

&lt;p&gt;Технічно можна і так, і так, але варіант №1 (CNI перед Join) є кращою практикою.&lt;/p&gt;

&lt;h4 id=&quot;варіант-1-спершу-cni-потім-join-рекомендовано&quot;&gt;Варіант 1: Спершу CNI, потім Join (Рекомендовано)&lt;/h4&gt;

&lt;p&gt;Коли ви встановлюєте CNI одразу після ініціалізації першого вузла (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt;), мережа кластера стає робочою негайно.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Статус Node&lt;/strong&gt;: Перший вузол швидко переходить у стан &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ready&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;CoreDNS&lt;/strong&gt;: Поди CoreDNS, які зазвичай висять у стані &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pending&lt;/code&gt; без мережі, запускаються.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Приєднання нових вузлів&lt;/strong&gt;: Коли ви приєднуєте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-3&lt;/code&gt;, вони одразу отримують мережеві налаштування. Системні поди на нових вузлах зможуть швидше почати комунікацію між собою.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Зручність&lt;/strong&gt;: Ви бачите реальний стан справності кожного нового вузла одразу після його приєднання.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;варіант-2-спершу-join-потім-cni&quot;&gt;Варіант 2: Спершу Join, потім CNI&lt;/h4&gt;

&lt;p&gt;Це теж робочий сценарій, але він виглядає більш “тривожно” під час процесу.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Статус Node&lt;/strong&gt;: Усі вузли (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-3&lt;/code&gt;) будуть у стані &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotReady&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;CoreDNS&lt;/strong&gt;: Всі системні мережеві компоненти будуть чекати.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Приєднання&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt; пройде успішно, оскільки для самого процесу приєднання (TLS Bootstrap та копіювання конфігів) CNI не потрібен — тут використовується фізична мережа між вузлами.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ризики&lt;/strong&gt;: Якщо під час приєднання виникне проблема з мережевою комунікацією самих подів (наприклад, health-checks системних компонентів), вам буде складніше зрозуміти — чи це проблема &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;join&lt;/code&gt;, чи просто відсутність CNI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Рекомендується дотримуватися наступного порядку:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; на &lt;strong&gt;cp-1&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;Потрібні налаштування &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Встановлення CNI&lt;/strong&gt; (наприклад, Cilium, Calico або Flannel).&lt;/li&gt;
  &lt;li&gt;Перевірка &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl get nodes&lt;/code&gt; (має бути &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ready&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Перенесення сертифікатів на нові вузли (за потреби).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt; для &lt;strong&gt;cp-2&lt;/strong&gt; та &lt;strong&gt;cp-3&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;розгортання-та-приєднання-наступних-вузлів-панелі-управління&quot;&gt;Розгортання та приєднання наступних вузлів панелі управління&lt;/h2&gt;

&lt;p&gt;Скористаймось вже готовою командою для першого вузла панелі управління. Замінимо IP адресу на відповідну для кожного вузла:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;cp-2.yaml&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.22&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;cp-3.yaml&lt;/strong&gt; — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.23&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Створимо віртуальні машини:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.22/24&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; cp-2 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 2.5G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 8G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
      # Злиття всіх файлів в один обʼєкт
      . as $item ireduce ({}; . *+ $item) |

      # Оновлення конфігурації мережі
      with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
        .content |= (
          from_yaml |
          .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] |
          to_yaml
        )
      ) &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-base.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-cp-haproxy.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-cp-haproxy-manifest.yaml&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Дочекаємось готовності вузла та приєднаємо його як ще один вузол панелі управління. Скористаймось виводом отриманим під час ініціалізації першого вузла панелі управління та додамо до нього параметр &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--ignore-preflight-errors=ExternalEtcdVersion&lt;/code&gt;, так само як ми це робили під час ініціалізації першого вузла панелі управління:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;10.10.0.100:6443 &lt;span class=&quot;nt&quot;&gt;--token&lt;/span&gt; z28v5d.4vm6rzekoibear23 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--discovery-token-ca-cert-hash&lt;/span&gt; sha256:4c23033729b477d1fc30ae4b4041fe7dae70fa8defd5ecb57c571e969e00f8e0 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--control-plane&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--certificate-key&lt;/span&gt; 7a088e936453ab3143f25cdb9827b8cac60888c75f91b9d6c2d08d23a32a2bc9 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;--ignore-preflight-errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ExternalEtcdVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Лог виконання приєднання другого вузла панелі управління виглядатиме наступним чином&lt;/p&gt;

&lt;details&gt;
  &lt;summary&gt;&lt;strong&gt;Подивитись лог приєднання другого вузла&lt;/strong&gt;&lt;/summary&gt;

  &lt;pre&gt;&lt;code class=&quot;language-log&quot;&gt;k8sadmin@cp-2:~$ sudo kubeadm join 10.10.0.100:6443 --token z28v5d.4vm6rzekoibear23 \
        --discovery-token-ca-cert-hash sha256:4c23033729b477d1fc30ae4b4041fe7dae70fa8defd5ecb57c571e969e00f8e0 \
        --control-plane --certificate-key 7a088e936453ab3143f25cdb9827b8cac60888c75f91b9d6c2d08d23a32a2bc9 \
        --ignore-preflight-errors=ExternalEtcdVersion
[preflight] Running pre-flight checks
[preflight] Reading configuration from the &quot;kubeadm-config&quot; ConfigMap in namespace &quot;kube-system&quot;...
[preflight] Use &apos;kubeadm init phase upload-config kubeadm --config your-config-file&apos; to re-upload it.
[preflight] Running pre-flight checks before initializing the new control plane instance
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action beforehand using &apos;kubeadm config images pull&apos;
[download-certs] Downloading the certificates in Secret &quot;kubeadm-certs&quot; in the &quot;kube-system&quot; Namespace
[download-certs] Saving the certificates to the folder: &quot;/etc/kubernetes/pki&quot;
[certs] Using certificateDir folder &quot;/etc/kubernetes/pki&quot;
[certs] Generating &quot;apiserver&quot; certificate and key
[certs] apiserver serving cert is signed for DNS names [cp-2 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.2.177 10.10.0.100]
[certs] Generating &quot;apiserver-kubelet-client&quot; certificate and key
[certs] Generating &quot;front-proxy-client&quot; certificate and key
[certs] Valid certificates and keys now exist in &quot;/etc/kubernetes/pki&quot;
[certs] Using the existing &quot;sa&quot; key
[kubeconfig] Generating kubeconfig files
[kubeconfig] Using kubeconfig folder &quot;/etc/kubernetes&quot;
[kubeconfig] Writing &quot;admin.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;controller-manager.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;scheduler.conf&quot; kubeconfig file
[control-plane] Using manifest folder &quot;/etc/kubernetes/manifests&quot;
[control-plane] Creating static Pod manifest for &quot;kube-apiserver&quot;
[control-plane] Creating static Pod manifest for &quot;kube-controller-manager&quot;
[control-plane] Creating static Pod manifest for &quot;kube-scheduler&quot;
[check-etcd] Skipping etcd check in external mode
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/instance-config.yaml&quot;
[patches] Applied patch of type &quot;application/strategic-merge-patch+json&quot; to target &quot;kubeletconfiguration&quot;
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/config.yaml&quot;
[kubelet-start] Writing kubelet environment file with flags to file &quot;/var/lib/kubelet/kubeadm-flags.env&quot;
[kubelet-start] Starting the kubelet
[control-plane-join] Using external etcd - no local stacked instance added
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 1.005951104s
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap
[mark-control-plane] Marking the node cp-2 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node cp-2 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[control-plane-check] Waiting for healthy control plane components. This can take up to 4m0s
[control-plane-check] Checking kube-apiserver at https://192.168.2.177:6443/livez
[control-plane-check] Checking kube-controller-manager at https://127.0.0.1:10257/healthz
[control-plane-check] Checking kube-scheduler at https://127.0.0.1:10259/livez
[control-plane-check] kube-controller-manager is healthy after 2.950097ms
[control-plane-check] kube-scheduler is healthy after 6.396945ms
[control-plane-check] kube-apiserver is healthy after 501.413278ms

This node has joined the cluster and a new control plane instance was created:

* Certificate signing request was sent to apiserver and approval was received.
* The Kubelet was informed of the new secure connection details.
* Control plane label and taint were applied to the new node.
* The Kubernetes control plane instances scaled up.


To start administering your cluster from this node, you need to run the following as a regular user:

	mkdir -p $HOME/.kube
	sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
	sudo chown $(id -u):$(id -g) $HOME/.kube/config

Run &apos;kubectl get nodes&apos; to see this node join the cluster.
&lt;/code&gt;&lt;/pre&gt;

&lt;/details&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;p&gt;Якщо між створенням першого вузла панелі управління та додаванням наступних вузлів пройшло багато часу ви можете зіткнутись з такою помилкою.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-log&quot;&gt;[preflight] You can also perform this action beforehand using &apos;kubeadm config images pull&apos;
[download-certs] Downloading the certificates in Secret &quot;kubeadm-certs&quot; in the &quot;kube-system&quot; Namespace
error: error execution phase control-plane-prepare/download-certs: error downloading certs: error downloading the secret: Secret &quot;kubeadm-certs&quot; was not found in the &quot;kube-system&quot; Namespace. This Secret might have expired. Please, run `kubeadm init phase upload-certs --upload-certs` on a control plane to generate a new one
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Оскільки ми використовуємо &lt;strong&gt;зовнішній etcd (External Etcd)&lt;/strong&gt;, стандартна процедура &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upload-certs&lt;/code&gt; не працює так, як описано в більшості інструкцій, тому що Kubernetes з міркувань безпеки не зберігає ключі до зовнішньої бази даних у своїх секретах.&lt;/p&gt;

&lt;p&gt;Якщо нам треба буде додати наступний вузол або відновити поточний потрібно виконати кілька додаткових кроків.&lt;/p&gt;

&lt;p&gt;Цей метод використовується, коли автоматичне завантаження ключів (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--certificate-key&lt;/code&gt;) неможливе або термін зберігання секрету з ключами сплив.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Підготовка файлової системи на новому вузлі&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;На новому вузлі (наприклад, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-4&lt;/code&gt;) створіть необхідні теки для сертифікатів та маніфестів.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# На новому вузлі&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /etc/kubernetes/pki/etcd
&lt;span class=&quot;nb&quot;&gt;sudo mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /etc/kubernetes/manifests
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Ручне перенесення сертифікатів&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Нам потрібно скопіювати &lt;strong&gt;9 файлів&lt;/strong&gt; з будь-якого працюючого вузла панелі управління (наприклад з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt;) на новий вузол. Шляхи мають повністю збігатись.&lt;/p&gt;

    &lt;table&gt;
      &lt;thead&gt;
        &lt;tr&gt;
          &lt;th&gt;Файл на джерелі (cp-1)&lt;/th&gt;
          &lt;th&gt;Куди класти на новому (cp-4)&lt;/th&gt;
          &lt;th&gt;Опис&lt;/th&gt;
        &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/ca.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/ca.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;CA кластера&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/ca.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/ca.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Ключ CA&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/sa.pub&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/sa.pub&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Ключ ServiceAccount (публічний)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/sa.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/sa.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Ключ ServiceAccount (приватний)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/front-proxy-ca.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/front-proxy-ca.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;CA для агрегації API&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/front-proxy-ca.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/front-proxy-ca.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Ключ Front Proxy&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/apiserver-etcd-client.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/apiserver-etcd-client.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Клієнтський серт. для etcd&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/apiserver-etcd-client.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/apiserver-etcd-client.key&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;Клієнтський ключ для etcd&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/etcd/ca.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/etcd/ca.crt&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;CA самого etcd&lt;/td&gt;
        &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;

    &lt;p&gt;&lt;strong&gt;Важливо&lt;/strong&gt;: Після копіювання встановіть правильні права на файли ключів (.key), інакше kubeadm може сваритися на безпеку:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo chmod &lt;/span&gt;600 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /etc/kubernetes/pki/apiserver-etcd-client.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /etc/kubernetes/pki/ca.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /etc/kubernetes/pki/front-proxy-ca.key  &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /etc/kubernetes/pki/sa.key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;strong&gt;Примітка&lt;/strong&gt;: Якщо ви не бажаєте вручну копіювати всі 9 файлів ключів та сертифікатів, а обмежитись лише копіюванням файлів &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/etcd/ca.crt&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/apiserver-etcd-client.key&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/apiserver-etcd-client.crt&lt;/code&gt; на цільову машину, ви можете спочатку запустити команду приєднання з параметром &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--certificate-key&lt;/code&gt; (як у випадку Stacked Etcd), що допоможе скопіювати більшість потрібних фалів тих якими опікується apiserver. Після цього скопіюйте вручну три файли для etcd та запустіть команду приєднання без параметра &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--certificate-key&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;Для прискорення процесу копіювання можна скористатись наступним скриптом&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos; &amp;gt; k8s-transit.sh
#!/bin/bash

# Як використовувати
#  &quot;Використання: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;lt;SRC_IP&amp;gt; &amp;lt;DST_IP&amp;gt; &amp;lt;USER&amp;gt; [SSH_KEY_PATH]&quot;
#  &quot;Приклад: k8s-transit.sh 10.10.0.21 10.10.0.178 k8sadmin ~/.ssh/id_rsa&quot;

# --- СТАНДАРТНІ ЗНАЧЕННЯ ---
DEFAULT_SRC=&quot;10.10.0.21&quot;
DEFAULT_DST=&quot;10.10.0.178&quot;
DEFAULT_USR=&quot;k8sadmin&quot;
DEFAULT_KEY=&quot;~/.ssh/k8s_cluster_key&quot;
# ---------------------------

SRC=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_SRC&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
DST=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_DST&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
USR=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_USR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
KEY_PATH=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DEFAULT_KEY&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;

# Формуємо опцію для SSH-ключа
SSH_OPTS=&quot;&quot;
if [ -f &quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eval echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$KEY_PATH&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; ]; then
    SSH_OPTS=&quot;-i &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$KEY_PATH&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
fi

echo &quot;🚀 Використовуємо конфігурацію:&quot;
echo &quot;   Джерело (SRC): &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SRC&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;   Ціль (DST):    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;   Користувач:    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$USR&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;   Ключ SSH:      &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KEY_PATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;стандартно&apos;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
echo &quot;----------------------------------------------------&quot;

# Список файлів
FILES=(
    &quot;/etc/kubernetes/pki/ca.crt&quot;
    &quot;/etc/kubernetes/pki/ca.key&quot;
    &quot;/etc/kubernetes/pki/sa.pub&quot;
    &quot;/etc/kubernetes/pki/sa.key&quot;
    &quot;/etc/kubernetes/pki/front-proxy-ca.crt&quot;
    &quot;/etc/kubernetes/pki/front-proxy-ca.key&quot;
    &quot;/etc/kubernetes/pki/apiserver-etcd-client.crt&quot;
    &quot;/etc/kubernetes/pki/apiserver-etcd-client.key&quot;
    &quot;/etc/kubernetes/pki/etcd/ca.crt&quot;
)

# 1. Підготовка тек
ssh &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SSH_OPTS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; -t &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$USR&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &quot;sudo mkdir -p /etc/kubernetes/pki/etcd&quot;

# 2. Транзит файлів
for FILE in &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;FILES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;; do
    echo &quot;Передаю: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
    ssh &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SSH_OPTS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$USR&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SRC&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &quot;sudo cat &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; | ssh &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SSH_OPTS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$USR&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &quot;sudo tee &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$FILE&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; /dev/null&quot;
done

# 3. Налаштування власника та прав доступу
echo &quot;🔒 Зміна власника на root та налаштування прав на &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;...&quot;
ssh &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$SSH_OPTS&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; -t &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$USR&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$DST&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &quot; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
    sudo chown -R root:root /etc/kubernetes/pki &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
    sudo chmod 644 /etc/kubernetes/pki/*.crt /etc/kubernetes/pki/sa.pub &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
    sudo chmod 600 /etc/kubernetes/pki/*.key /etc/kubernetes/pki/sa.key&quot;

echo &quot;✅ Успішно завершено!&quot;
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF

&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x k8s-transit.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Налаштування доступу до etcd (Local Proxy)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Оскільки ми використовуємо архітектуру, де кожен вузол спілкується з etcd через локальний проксі (HAProxy), переконаймося, що маніфест проксі вже лежить на новому вузлі.&lt;/p&gt;

    &lt;p&gt;Якщо ні — скопіюймо файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd-haproxy.yaml&lt;/code&gt; з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt; в теку &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/manifests/&lt;/code&gt; на новому вузлі. &lt;em&gt;Це гарантує, що як тільки запуститься kubelet, він підніме зв’язок з базою даних.&lt;/em&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Генерація команди Join (на працюючому вузлі)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;На працюючому вузлі (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt;) згенеруємо токен і хеш. Нам не потрібен ключ сертифіката, бо ми вже перенесли файли вручну.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# На cp-1&lt;/span&gt;
kubeadm token create &lt;span class=&quot;nt&quot;&gt;--print-join-command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;em&gt;В результаті ми отримаємо команду вигляду:&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join 10.10.0.100:6443 --token &amp;lt;token&amp;gt; --discovery-token-ca-cert-hash &amp;lt;hash&amp;gt;&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Запуск приєднання (на новому вузлі)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Запустімо отриману команду на новому вузлі, додавши прапорець &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--control-plane&lt;/code&gt;. &lt;strong&gt;⚠️ Не додавайте&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--certificate-key&lt;/code&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# На новому вузлі&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;10.10.0.100:6443 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--token&lt;/span&gt; &amp;lt;ваш_токен&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--discovery-token-ca-cert-hash&lt;/span&gt; sha256:&amp;lt;ваш_хеш&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--control-plane&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--ignore-preflight-errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ExternalEtcdVersion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;details&gt;
      &lt;summary&gt;&lt;strong&gt;Подивитись лог приєднання вузла&lt;/strong&gt;&lt;/summary&gt;

      &lt;pre&gt;&lt;code class=&quot;language-log&quot;&gt;k8sadmin@cp-4:~$ sudo kubeadm join 10.10.0.100:6443 --token efwfq8.puh8nqqk3a3dqqdx --discovery-token-ca-cert-hash sha256:4c23033729b477d1fc30ae4b4041fe7dae70fa8defd5ecb57c571e969e00f8e0 --control-plane --ignore-preflight-errors=ExternalEtcdVersion
[preflight] Running pre-flight checks
[preflight] Reading configuration from the &quot;kubeadm-config&quot; ConfigMap in namespace &quot;kube-system&quot;...
[preflight] Use &apos;kubeadm init phase upload-config kubeadm --config your-config-file&apos; to re-upload it.
[preflight] Running pre-flight checks before initializing the new control plane instance
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action beforehand using &apos;kubeadm config images pull&apos;
[certs] Using certificateDir folder &quot;/etc/kubernetes/pki&quot;
[certs] Generating &quot;front-proxy-client&quot; certificate and key
[certs] Generating &quot;apiserver&quot; certificate and key
[certs] apiserver serving cert is signed for DNS names [cp-4 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.2.179 10.10.0.100]
[certs] Generating &quot;apiserver-kubelet-client&quot; certificate and key
[certs] Valid certificates and keys now exist in &quot;/etc/kubernetes/pki&quot;
[certs] Using the existing &quot;sa&quot; key
[kubeconfig] Generating kubeconfig files
[kubeconfig] Using kubeconfig folder &quot;/etc/kubernetes&quot;
[kubeconfig] Writing &quot;admin.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;controller-manager.conf&quot; kubeconfig file
[kubeconfig] Writing &quot;scheduler.conf&quot; kubeconfig file
[control-plane] Using manifest folder &quot;/etc/kubernetes/manifests&quot;
[control-plane] Creating static Pod manifest for &quot;kube-apiserver&quot;
[control-plane] Creating static Pod manifest for &quot;kube-controller-manager&quot;
[control-plane] Creating static Pod manifest for &quot;kube-scheduler&quot;
[check-etcd] Skipping etcd check in external mode
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/instance-config.yaml&quot;
[patches] Applied patch of type &quot;application/strategic-merge-patch+json&quot; to target &quot;kubeletconfiguration&quot;
[kubelet-start] Writing kubelet configuration to file &quot;/var/lib/kubelet/config.yaml&quot;
[kubelet-start] Writing kubelet environment file with flags to file &quot;/var/lib/kubelet/kubeadm-flags.env&quot;
[kubelet-start] Starting the kubelet
[control-plane-join] Using external etcd - no local stacked instance added
[kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s
[kubelet-check] The kubelet is healthy after 503.676345ms
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap
[mark-control-plane] Marking the node cp-4 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node cp-4 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[control-plane-check] Waiting for healthy control plane components. This can take up to 4m0s
[control-plane-check] Checking kube-apiserver at https://192.168.2.179:6443/livez
[control-plane-check] Checking kube-controller-manager at https://127.0.0.1:10257/healthz
[control-plane-check] Checking kube-scheduler at https://127.0.0.1:10259/livez
[control-plane-check] kube-scheduler is healthy after 9.840373ms
[control-plane-check] kube-controller-manager is healthy after 11.326085ms
[control-plane-check] kube-apiserver is healthy after 21.681901ms

This node has joined the cluster and a new control plane instance was created:

* Certificate signing request was sent to apiserver and approval was received.
* The Kubelet was informed of the new secure connection details.
* Control plane label and taint were applied to the new node.
* The Kubernetes control plane instances scaled up.


To start administering your cluster from this node, you need to run the following as a regular user:

	mkdir -p $HOME/.kube
	sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
	sudo chown $(id -u):$(id -g) $HOME/.kube/config

Run &apos;kubectl get nodes&apos; to see this node join the cluster.
&lt;/code&gt;&lt;/pre&gt;

    &lt;/details&gt;
    &lt;p&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Якщо у вас &lt;strong&gt;Stacked Etcd&lt;/strong&gt; наступної команди для отримання нових інструкцій для приєднання вузлів до кластера буде цілком достатньо. В такій архітектурі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; повністю автоматизує процес: він завантажує всі необхідні сертифікати (включаючи ті, що потрібні для створення нового вузла члена etcd) у секрет &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm-certs&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# На вузлі cp-1&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.21
&lt;span class=&quot;nb&quot;&gt;sudo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sE&lt;/span&gt;
&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubeadm token create &lt;span class=&quot;nt&quot;&gt;--print-join-command&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--control-plane&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--certificate-key&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubeadm init phase upload-certs &lt;span class=&quot;nt&quot;&gt;--upload-certs&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Це працює для Stacked Etcd:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upload-certs&lt;/code&gt;: На відміну від External Etcd, у Stacked-режимі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; знає про всі локальні сертифікати etcd і упаковує їх у пакунок.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--certificate-key&lt;/code&gt;: Ви отримуєте ключ розшифрування, який дозволяє новому вузлу автоматично завантажити цей пакунок у свою теку &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Автоматизація&lt;/strong&gt;: Новому вузлу не потрібно нічого копіювати вручну — він сам створить і сертифікати Kubernetes, і локальну копію etcd, приєднавши її до наявних учасників.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ця команда чудово працює, однак: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init phase upload-certs --upload-certs&lt;/code&gt; завантажує сертифікати в секрет лише на &lt;strong&gt;2 години&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Якщо ви плануєте додавати вузли пізніше, вам доведеться запускати цю команду знову. Якщо ж ви робите це “тут і зараз” — це ідеальний і найшвидший спосіб.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Порада&lt;/strong&gt;: Якщо на новому вузлі вже були невдалі спроби приєднання, перед запуском нової команди обовʼязково зробіть &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo kubeadm reset -f&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;відʼєднання-та-заміна-вузла-панелі-управління&quot;&gt;Відʼєднання та заміна вузла панелі управління&lt;/h3&gt;

&lt;p&gt;Виведення вузла панелі управління, якщо він вийшов з ладу, або для його заміни, в архітектурі з &lt;strong&gt;External Etcd&lt;/strong&gt; має суттєву перевагу: оскільки база даних знаходиться поза межами вузлів Kubernetes, ви ризикуєте лише доступністю API, а не цілісністю даних.&lt;/p&gt;

&lt;p&gt;Для видалення вузла панелі управління (наприклад, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt;) з кластера виконайте наступні кроки.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Підготовка кластера&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Виконуйте ці дії на іншому справному вузлі (наприклад, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt;) або спілкуйтесь з панеллю управління через адресу балансувальника HAProxy.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Видаліть навантаження з вузла&lt;/strong&gt;: Дозвольте завершити поточні завдання та забороніть призначення нових подів&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl cordon cp-2
kubectl drain cp-2 &lt;span class=&quot;nt&quot;&gt;--ignore-daemonsets&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--delete-emptydir-data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Видаліть вузол з реєстру Kubernetes&lt;/strong&gt;: Це видалить обʼєкт Node та відповідні сертифікати (якщо використовується авторотація).&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl delete node cp-2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Очищення самого вузла (на cp-2)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Тепер перейдіть на вузол, який потрібно відʼєднати.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Скидання налаштувань kubeadm&lt;/strong&gt;: Це видалить маніфести статичних подів (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;controller-manager&lt;/code&gt; тощо) та файли конфігурації в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes&lt;/code&gt;.&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm reset &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Очищення IPVS/Iptables:&lt;/strong&gt;&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; nat &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; mangle &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;iptables &lt;span class=&quot;nt&quot;&gt;-X&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Якщо використовували IPVS:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ipvsadm &lt;span class=&quot;nt&quot;&gt;--clear&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Видалення залишкових файлів&lt;/strong&gt;: Бажано видалити теку з сертифікатами, щоб при повторному приєднанні не виникло конфліктів.&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /etc/kubernetes/pki
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Очищення Etcd&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Ось тут з’являється головна відмінність між архітектурами.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Для External Etcd&lt;/strong&gt;:&lt;/p&gt;

    &lt;p&gt;Оскільки etcd працює окремо, &lt;strong&gt;нічого робити не потрібно&lt;/strong&gt;. Зовнішній кластер etcd навіть не знає, що API-сервер на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt; перестав працювати. Ви просто видалили одного з клієнтів бази даних.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Для Stacked Etcd:&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Це найважливіший крок. Оскільки etcd працював локально на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt;, він був частиною кворуму. Якщо ви просто вимкнете вузол, etcd-кластер вважатиме його “впавшим” і чекатиме на повернення, що може негативно вплинути на кворум.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Потрібно зайти на інший майстер-вузол.&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Знайти ID члена etcd для cp-2:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system etcd-cp-1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  member list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Видалити цей ID:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system etcd-cp-1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;https://127.0.0.1:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/pki/etcd/server.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  member remove &amp;lt;MEMBER_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Оновлення Load Balancer&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Якщо у вас перед панеллю управління стоїть зовнішній балансувальник (наприклад, HAProxy або хмарний LB), обовʼязково &lt;strong&gt;видаліть IP-адресу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt; з конфігурації backend&lt;/strong&gt;.&lt;/p&gt;

    &lt;p&gt;Якщо ви використовуєте локальний HAProxy на кожному вузлі (як ми налаштовували раніше), то інші вузли просто перестануть звертатися до &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt;, але конфігурацію на робочих вузлах (workers) варто оновити, щоб вони не витрачали час на спроби зʼєднатися з відсутнім вузлом панелі управління.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Заміна вузла (якщо потрібно)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Якщо ви хочете замінити &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-2&lt;/code&gt; новим вузлом з такою ж назвою:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Підготуйте нову ОС.&lt;/li&gt;
      &lt;li&gt;Виконайте кроки з попередньої інструкції (копіювання 9 файлів сертифікатів + маніфест проксі).&lt;/li&gt;
      &lt;li&gt;Запустіть &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;розгортання-worker-nodes&quot;&gt;Розгортання Worker Nodes&lt;/h2&gt;

&lt;p&gt;Для розгортання робочих вузлів будемо використовувати налаштування з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-config.yaml&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-user.yaml&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;snipets/cloud-init-base.yaml&lt;/code&gt;. Під час процесу обʼєднання файлів приберемо &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; з переліку пакунків для встановлення. Для робочих вузлів використовуватимемо IP адреси з діапазону 10.10.0.30 — 10.10.0.99.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VM_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.10.0.31/24&quot;&lt;/span&gt;

multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; wn-1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 2G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 10G &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; &amp;lt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; yq eval-all &lt;span class=&quot;s1&quot;&gt;&apos;
      # Злиття всіх файлів в один обʼєкт
      . as $item ireduce ({}; . *+ $item) |

      # Вилучення kubectl зі списку пакунків
      del(.packages[] | select(. == &quot;kubectl&quot;)) |

      # Оновлення конфігурації мережі
      with(.write_files[] | select(.path == &quot;/etc/netplan/60-static-ip.yaml&quot;);
        .content |= (
          from_yaml |
          .network.ethernets.enp0s2.addresses += [strenv(VM_IP)] |
          to_yaml
        )
      ) &apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-config.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-user.yaml &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      snipets/cloud-init-base.yaml &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;По завершенні створення віртуальної машини увійдемо до неї через ssh&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.31
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Та виконаємо її приєднання до кластера командою, яку ми отримали під час ініціалізації панелі управління:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;10.10.0.100:6443 &lt;span class=&quot;nt&quot;&gt;--token&lt;/span&gt; jfugsz.51c9pp44fwifcu94 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--discovery-token-ca-cert-hash&lt;/span&gt; sha256:736ca5cf47fca3f4c1fd9c414f6ba70e4fefdb2f52deec5e2526f5ebccf838d6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Оператор Calico (tigera-operator) виконає реєстрацію вузла в мережі подів і через хвилину-дві статус робочого вузла зміниться на READY. Вузол готовий для отримання робочого навантаження.&lt;/p&gt;

&lt;h2 id=&quot;встановлення-metallb&quot;&gt;Встановлення MetalLB&lt;/h2&gt;

&lt;p&gt;Щодо встановлення MetalLB для балансування доступу до робочих вузлів скористайтесь інструкцією зі статті «&lt;a href=&quot;https://blog.andygol.co.ua/uk/2025/12/12/k8s-кластер-з-kubeadm/#крок-7-встановлення-metallb&quot;&gt;Розгортання Kubernetes-кластера на локальному компʼютері: Повне покрокове керівництво&lt;/a&gt;».&lt;/p&gt;

&lt;p&gt;Тепер ваш кластер готовий прийняти робоче навантаження.&lt;/p&gt;

&lt;h2 id=&quot;тестування-кластера&quot;&gt;Тестування кластера&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;cp1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; kubectl get nodes
multipass &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;cp1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; kubectl get pods &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;підсумки&quot;&gt;Підсумки&lt;/h2&gt;

&lt;p&gt;Цей посібник демонструє повне розгортання HA Kubernetes кластера з топологією external etcd. Кластер готовий до використання та може бути легко масштабований. Всі кроки дотримуються принципу DRY та можуть бути адаптовані для різних середовищ.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;додатки&quot;&gt;Додатки&lt;/h2&gt;

&lt;h3 id=&quot;мережева-архітектура-для-ha-kubernetes-з-external-etcd&quot;&gt;Мережева архітектура для HA Kubernetes з external etcd&lt;/h3&gt;

&lt;details&gt;
  &lt;summary&gt;&lt;strong&gt;Подивитись…&lt;/strong&gt;&lt;/summary&gt;

  &lt;h4 id=&quot;section&quot;&gt;Рекомендована топологія мережі — Підмережа: 10.10.0.0/22 (1024 адреси)&lt;/h4&gt;

  &lt;p&gt;Ця підмережа достатньо велика для розширення та логічно поділена на блоки:&lt;/p&gt;

  &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────┐
│ 10.10.0.0/22      - Головна підмережа Kubernetes        │
├─────────────────────────────────────────────────────────┤
│ 10.10.0.0/26      - Інфраструктура (64 адреси)          │
│   10.10.0.1       - Gateway (macOS host)                │
│   10.10.0.2       - DNS (опціонально)                   │
│   10.10.0.10      - HAProxy/Load Balancer               │
│   10.10.0.11-20   - Резерв для інфраструктури           │
├─────────────────────────────────────────────────────────┤
│ 10.10.0.64/26     - etcd кластер (64 адреси)            │
│   10.10.0.65      - etcd-1                              │
│   10.10.0.66      - etcd-2                              │
│   10.10.0.67      - etcd-3                              │
│   10.10.0.68-70   - Резерв для додаткових etcd          │
├─────────────────────────────────────────────────────────┤
│ 10.10.0.128/25    - Control Plane (128 адрес)           │
│   10.10.0.129     - k8s-master-1                        │
│   10.10.0.130     - k8s-master-2                        │
│   10.10.0.131     - k8s-master-3                        │
│   10.10.0.132-140 - Резерв для додаткових masters       │
├─────────────────────────────────────────────────────────┤
│ 10.10.1.0/24      - Worker Nodes (256 адрес)            │
│   10.10.1.10      - k8s-worker-1                        │
│   10.10.1.11      - k8s-worker-2                        │
│   10.10.1.12      - k8s-worker-3                        │
│   10.10.1.13      - k8s-worker-4                        │
│   10.10.1.14      - k8s-worker-5                        │
│   10.10.1.15-100  - Резерв для додаткових workers       │
├─────────────────────────────────────────────────────────┤
│ 10.10.2.0/24   - Pod Network (256 адрес) - опціонально  │
│   Використовується для тестування                       │
├─────────────────────────────────────────────────────────┤
│ 10.10.3.0/24   - Service Network - опціонально          │
│   Використовується для тестування                       │
└─────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;&lt;strong&gt;Детальний план IP адрес&lt;/strong&gt;&lt;/p&gt;

  &lt;h4 id=&quot;section-1&quot;&gt;1. Інфраструктура (10.10.0.0/26)&lt;/h4&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;IP адреса&lt;/th&gt;
        &lt;th&gt;Hostname&lt;/th&gt;
        &lt;th&gt;Роль&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.1&lt;/td&gt;
        &lt;td&gt;macOS-host&lt;/td&gt;
        &lt;td&gt;Gateway/Bridge&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.10&lt;/td&gt;
        &lt;td&gt;haproxy-lb&lt;/td&gt;
        &lt;td&gt;Load Balancer (опціонально)&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;h4 id=&quot;etcd--101006426&quot;&gt;2. etcd Кластер (10.10.0.64/26)&lt;/h4&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;IP адреса&lt;/th&gt;
        &lt;th&gt;Hostname&lt;/th&gt;
        &lt;th&gt;Роль&lt;/th&gt;
        &lt;th&gt;vCPU&lt;/th&gt;
        &lt;th&gt;RAM&lt;/th&gt;
        &lt;th&gt;Disk&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.65&lt;/td&gt;
        &lt;td&gt;etcd-1&lt;/td&gt;
        &lt;td&gt;etcd member&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;2GB&lt;/td&gt;
        &lt;td&gt;20GB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.66&lt;/td&gt;
        &lt;td&gt;etcd-2&lt;/td&gt;
        &lt;td&gt;etcd member&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;2GB&lt;/td&gt;
        &lt;td&gt;20GB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.67&lt;/td&gt;
        &lt;td&gt;etcd-3&lt;/td&gt;
        &lt;td&gt;etcd member&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;2GB&lt;/td&gt;
        &lt;td&gt;20GB&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;p&gt;&lt;strong&gt;Мінімальна конфігурація etcd:&lt;/strong&gt; 3 вузли для кворуму&lt;/p&gt;

  &lt;h4 id=&quot;control-plane-1010012825&quot;&gt;3. Control Plane (10.10.0.128/25)&lt;/h4&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;IP адреса&lt;/th&gt;
        &lt;th&gt;Hostname&lt;/th&gt;
        &lt;th&gt;Роль&lt;/th&gt;
        &lt;th&gt;vCPU&lt;/th&gt;
        &lt;th&gt;RAM&lt;/th&gt;
        &lt;th&gt;Disk&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.129&lt;/td&gt;
        &lt;td&gt;k8s-master-1&lt;/td&gt;
        &lt;td&gt;Control Plane (Primary)&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;4GB&lt;/td&gt;
        &lt;td&gt;40GB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.130&lt;/td&gt;
        &lt;td&gt;k8s-master-2&lt;/td&gt;
        &lt;td&gt;Control Plane&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;4GB&lt;/td&gt;
        &lt;td&gt;40GB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.0.131&lt;/td&gt;
        &lt;td&gt;k8s-master-3&lt;/td&gt;
        &lt;td&gt;Control Plane&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;4GB&lt;/td&gt;
        &lt;td&gt;40GB&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;p&gt;&lt;strong&gt;Мінімальна конфігурація HA:&lt;/strong&gt; 3 master вузли&lt;/p&gt;

  &lt;h4 id=&quot;worker-nodes-10101024&quot;&gt;4. Worker Nodes (10.10.1.0/24)&lt;/h4&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;IP адреса&lt;/th&gt;
        &lt;th&gt;Hostname&lt;/th&gt;
        &lt;th&gt;Роль&lt;/th&gt;
        &lt;th&gt;vCPU&lt;/th&gt;
        &lt;th&gt;RAM&lt;/th&gt;
        &lt;th&gt;Disk&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.1.10&lt;/td&gt;
        &lt;td&gt;k8s-worker-1&lt;/td&gt;
        &lt;td&gt;Worker Node&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;4GB&lt;/td&gt;
        &lt;td&gt;50GB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.1.11&lt;/td&gt;
        &lt;td&gt;k8s-worker-2&lt;/td&gt;
        &lt;td&gt;Worker Node&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;4GB&lt;/td&gt;
        &lt;td&gt;50GB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;10.10.1.12&lt;/td&gt;
        &lt;td&gt;k8s-worker-3&lt;/td&gt;
        &lt;td&gt;Worker Node&lt;/td&gt;
        &lt;td&gt;2&lt;/td&gt;
        &lt;td&gt;4GB&lt;/td&gt;
        &lt;td&gt;50GB&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;

  &lt;p&gt;&lt;strong&gt;Рекомендація:&lt;/strong&gt; Мінімум 2-3 workers для тестування, можна масштабувати до 100+&lt;/p&gt;

  &lt;h4 id=&quot;kubernetes----vm&quot;&gt;Внутрішні мережі Kubernetes (не конфліктують з VM)&lt;/h4&gt;

  &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Pod Network (CNI - Calico/Flannel/Weave)&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;--pod-network-cidr=192.168.0.0/16&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Service Network&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;--service-cidr=172.16.0.0/16&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Cluster DNS&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;--cluster-dns=172.16.0.10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;p&gt;Ці мережі &lt;strong&gt;віртуальні&lt;/strong&gt; і не конфліктують з VM адресами 10.10.x.x&lt;/p&gt;

  &lt;h3 id=&quot;section-2&quot;&gt;Альтернативні варіанти підмереж&lt;/h3&gt;

  &lt;h4 id=&quot;section-3&quot;&gt;Варіант 1: Компактна топологія (10.10.10.0/24)&lt;/h4&gt;

  &lt;p&gt;Для невеликого кластеру (до 10 вузлів):&lt;/p&gt;

  &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.10.10.0/24 - Головна підмережа (256 адрес)
├─ 10.10.10.1      - Gateway
├─ 10.10.10.10-12  - etcd (3 вузли)
├─ 10.10.10.20-22  - Control Plane (3 masters)
└─ 10.10.10.30-50  - Workers (до 20 workers)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;h4 id=&quot;section-4&quot;&gt;Варіант 2: Розширена топологія (10.10.0.0/16)&lt;/h4&gt;

  &lt;p&gt;Для великого production кластеру:&lt;/p&gt;

  &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10.10.0.0/16 - Вся мережа (65536 адрес)
├─ 10.10.1.0/24   - Інфраструктура
├─ 10.10.10.0/24  - etcd кластер
├─ 10.10.20.0/24  - Control Plane
├─ 10.10.30.0/22  - Workers (1024 адреси)
└─ 10.10.40.0/22  - Резерв для розширення
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;h4 id=&quot;section-5&quot;&gt;Варіант 3: Класична приватна мережа (172.16.0.0/16)&lt;/h4&gt;

  &lt;p&gt;Альтернатива якщо 10.10.x.x зайнято:&lt;/p&gt;

  &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;172.16.0.0/16
├─ 172.16.1.0/24   - Інфраструктура + etcd
├─ 172.16.2.0/24   - Control Plane
└─ 172.16.10.0/23  - Workers (512 адрес)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

  &lt;h3 id=&quot;dns--&quot;&gt;DNS записи (рекомендовано)&lt;/h3&gt;

  &lt;p&gt;Додайте в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hosts&lt;/code&gt; на macOS:&lt;/p&gt;

  &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Kubernetes HA Cluster&lt;/span&gt;
10.10.0.1       gateway.k8s.local
10.10.0.10      lb.k8s.local haproxy.k8s.local

&lt;span class=&quot;c&quot;&gt;# etcd cluster&lt;/span&gt;
10.10.0.65      etcd-1.k8s.local
10.10.0.66      etcd-2.k8s.local
10.10.0.67      etcd-3.k8s.local

&lt;span class=&quot;c&quot;&gt;# Control Plane&lt;/span&gt;
10.10.0.129     master-1.k8s.local k8s-master-1
10.10.0.130     master-2.k8s.local k8s-master-2
10.10.0.131     master-3.k8s.local k8s-master-3

&lt;span class=&quot;c&quot;&gt;# Workers&lt;/span&gt;
10.10.1.10      worker-1.k8s.local k8s-worker-1
10.10.1.11      worker-2.k8s.local k8s-worker-2
10.10.1.12      worker-3.k8s.local k8s-worker-3

&lt;span class=&quot;c&quot;&gt;# API endpoint (вказує на LB або будь-який master)&lt;/span&gt;
10.10.0.10      api.k8s.local kubernetes.k8s.local
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

&lt;/details&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;виправлення-проблем&quot;&gt;Виправлення проблем&lt;/h2&gt;

&lt;details&gt;
  &lt;summary&gt;&lt;strong&gt;Переглянути&lt;/strong&gt;&lt;/summary&gt;

  &lt;h3 id=&quot;pod-network-cidr&quot;&gt;Виправлення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--pod-network-cidr&lt;/code&gt;&lt;/h3&gt;

  &lt;p&gt;Q: після встановлення CNI-втулка (Flannel) я помітив що забув використати &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--pod-network-cidr=10.244.0.0/16&lt;/code&gt; під час ініціалізації HA кластера з трьома control planes. Як це виправити без видалення кластера?&lt;/p&gt;

  &lt;details&gt;
    &lt;summary&gt;&lt;strong&gt;Відповідь&lt;/strong&gt;&lt;/summary&gt;

    &lt;p&gt;Щоб виправити CIDR мережі Pod-ів у діючому HA-кластері (3 control planes) без його повного перевстановлення, вам потрібно оновити конфігурацію &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt;, налаштування &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClusterConfiguration&lt;/code&gt;, параметри &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-proxy&lt;/code&gt; та конфігурацію самого Flannel.&lt;/p&gt;

    &lt;h4 id=&quot;clusterconfiguration&quot;&gt;1. Оновлення конфігурації ClusterConfiguration&lt;/h4&gt;

    &lt;p&gt;Kubernetes зберігає налаштування ініціалізації у ConfigMap. Їх потрібно змінити, щоб майбутні операції (наприклад, приєднання нових вузлів) використовували правильний CIDR.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;Відредагуйте ConfigMap:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl edit configmap kubeadm-config &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Знайдіть розділ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;networking&lt;/code&gt; і змініть або додайте параметр &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podSubnet&lt;/code&gt;:&lt;/p&gt;

        &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;networking&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;podSubnet&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;10.244.0.0/16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 id=&quot;control-plane---3--&quot;&gt;2. Оновлення маніфестів Control Plane (на всіх 3-х вузлах)&lt;/h4&gt;

    &lt;p&gt;На кожному з трьох control plane вузлів потрібно оновити статичний маніфест &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-controller-manager&lt;/code&gt;, щоб він правильно розподіляв IP-адреси для вузлів.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;Відкрийте файл: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo vim /etc/kubernetes/manifests/kube-controller-manager.yaml&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Знайдіть прапорець &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cluster-cidr&lt;/code&gt; і встановіть його:&lt;/p&gt;

        &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--cluster-cidr=10.244.0.0/16&lt;/span&gt;
 &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--allocate-node-cidrs=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;Після збереження файлу Kubelet автоматично перезапустить компонент. Повторіть це на &lt;strong&gt;всіх трьох&lt;/strong&gt; master-вузлах.&lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 id=&quot;kube-proxy&quot;&gt;3. Оновлення налаштувань kube-proxy&lt;/h4&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-proxy&lt;/code&gt; використовує CIDR для налаштування правил IPTables/IPVS.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;Відредагуйте ConfigMap для kube-proxy:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl edit configmap kube-proxy &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Знайдіть параметр &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clusterCIDR&lt;/code&gt; і змініть його:&lt;/p&gt;

        &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;clusterCIDR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10.244.0.0/16&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Перезапустіть всі Pod-и kube-proxy для застосування змін:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl rollout restart daemonset kube-proxy &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;Далі має все запрацювати. Якщо цього не сталося, перейдіть до наступних кроків.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 id=&quot;flannel---cidr&quot;&gt;4. Перевстановлення Flannel з правильним CIDR&lt;/h4&gt;

    &lt;p&gt;Оскільки Flannel вже встановлено з неправильними параметрами (або без них), його потрібно переналаштувати.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;Завантажте актуальний маніфест Flannel з офіційного репозиторію:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; curl &lt;span class=&quot;nt&quot;&gt;-sLO&lt;/span&gt; https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Перевірте у завантаженому файлі секцію net-conf.json. Вона повинна містити:&lt;/p&gt;

        &lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Network&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;10.244.0.0/16&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Видаліть старий Flannel та встановіть оновлений:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl delete &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; kube-flannel.yml &lt;span class=&quot;c&quot;&gt;# якщо у вас є старий файл&lt;/span&gt;
kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; kube-flannel.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 id=&quot;spec--podcidr&quot;&gt;5. Оновлення Spec вузлів (PodCIDR)&lt;/h4&gt;

    &lt;p&gt;Це найкритичніший крок. Кожен вузол у кластері вже має призначений старий (або порожній) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podCIDR&lt;/code&gt; у своїх властивостях. Їх потрібно оновити вручну.&lt;/p&gt;

    &lt;p&gt;Для кожного вузла (master та worker) виконайте:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl patch node &amp;lt;node-name&amp;gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;spec&quot;:{&quot;podCIDR&quot;:&quot;10.244.X.0/24&quot;, &quot;podCIDRs&quot;:[&quot;10.244.X.0/24&quot;]}}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;em&gt;Де X — унікальне число для кожного вузла (наприклад, 0, 1, 2).&lt;/em&gt;&lt;/p&gt;

    &lt;h4 id=&quot;section-6&quot;&gt;6. Фінальне перезавантаження&lt;/h4&gt;

    &lt;p&gt;Щоб зміни вступили в силу і Pod-и отримали адреси з нового діапазону:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;&lt;strong&gt;Перезавантажте вузли&lt;/strong&gt; по черзі або видаліть всі існуючі Pod-и в усіх namespace, щоб вони перестворилися з новими IP.&lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Перевірте статус мережі:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get nodes &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.items[*].spec.podCIDR}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;&lt;strong&gt;Важливо&lt;/strong&gt;: Якщо у вас вже були запущені робочі навантаження (workloads), їхні IP-адреси не зміняться автоматично до моменту перестворення Pod-ів. На HA-кластері цей процес можна робити по черзі для кожного вузла (cordon -&amp;gt; drain -&amp;gt; reboot -&amp;gt; uncordon), щоб уникнути простою.&lt;/p&gt;

  &lt;/details&gt;

  &lt;hr /&gt;

  &lt;h3 id=&quot;etcd---&quot;&gt;Скидання etcd до первісного стану&lt;/h3&gt;

  &lt;p&gt;Q: Як очистити дані в кластері? Скинути його в первісний стан?&lt;/p&gt;

  &lt;details&gt;
    &lt;summary&gt;&lt;strong&gt;Відповідь&lt;/strong&gt;&lt;/summary&gt;

    &lt;p&gt;Щоб скинути etcd до «первісного» (порожнього) стану, необхідно повністю видалити його дані на кожному вузлі. Оскільки etcd є критичним компонентом Kubernetes, цей процес фактично знищить усю конфігурацію вашого кластера (Deployment, Services, Secrets тощо).&lt;/p&gt;

    &lt;p&gt;Покрокова інструкція для кластера, розгорнутого через kubeadm:&lt;/p&gt;

    &lt;h4 id=&quot;etcd&quot;&gt;1. Зупиніть сервіси, що залежать від etcd&lt;/h4&gt;

    &lt;p&gt;На кожній майстер-ноді (control-plane) необхідно тимчасово прибрати маніфести статичних подів, щоб Kubernetes припинив спроби перезапустити etcd та API-сервер:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo mv&lt;/span&gt; /etc/kubernetes/manifests/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.yaml /tmp/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Зачекайте кілька секунд, поки контейнери зупиняться. Перевірити можна через &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crictl ps&lt;/code&gt; або &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt;.&lt;/p&gt;

    &lt;h4 id=&quot;section-7&quot;&gt;2. Очистіть дані на всіх вузлах&lt;/h4&gt;

    &lt;p&gt;Видаліть вміст теки даних etcd (зазвичай це &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/lib/etcd&lt;/code&gt;). Це потрібно зробити на всіх вузлах, де працював etcd:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /var/lib/etcd/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;h4 id=&quot;section-8&quot;&gt;3. Ініціалізація нового “порожнього” кластера&lt;/h4&gt;

    &lt;p&gt;Тепер вам потрібно змусити etcd запуститися як новий кластер.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Варіант А (Повне скидання кластера через kubeadm):&lt;/strong&gt;&lt;/p&gt;

        &lt;p&gt;Якщо ви хочете почати все з нуля (включаючи сертифікати та конфігурацію K8s), виконайте на кожному вузлі:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm reset &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /etc/cni/net.d
&lt;span class=&quot;c&quot;&gt;# Після цього знову запустіть &apos;kubeadm init&apos; на першій ноді&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;strong&gt;Варіант Б (Тільки очищення даних etcd без перевстановлення K8s):&lt;/strong&gt;&lt;/p&gt;

        &lt;p&gt;Якщо сертифікати в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/pki/etcd/&lt;/code&gt; ще валідні, ви можете спробувати перестворити лише базу. Поверніть маніфести назад:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo mv&lt;/span&gt; /tmp/etcd.yaml /etc/kubernetes/manifests/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;

        &lt;p&gt;&lt;em&gt;Важливо&lt;/em&gt;: Якщо в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;etcd.yaml&lt;/code&gt; вказані параметри &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--initial-cluster-state=existing&lt;/code&gt;, вам може знадобитися вручну змінити їх на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt; або скористатися командою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init phase etcd local&lt;/code&gt;, щоб згенерувати коректний маніфест для нової ініціалізації.&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;

    &lt;h4 id=&quot;etcdctl---&quot;&gt;4. Очищення через etcdctl (якщо кластер працює)&lt;/h4&gt;

    &lt;p&gt;Якщо кластер «живий», але ви просто хочете видалити всі дані (ключі), не зупиняючи сервіси:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;etcdctl &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--endpoints&lt;/span&gt; https://127.0.0.1:2379 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cacert&lt;/span&gt; /etc/kubernetes/pki/etcd/ca.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cert&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.crt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--key&lt;/span&gt; /etc/kubernetes/pki/etcd/peer.key &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  del &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--prefix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Це видалить усі обʼєкти Kubernetes, але збереже саму структуру кластера etcd.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Попередження&lt;/strong&gt;: Після повного очищення даних &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/lib/etcd&lt;/code&gt; API-сервер не зможе запуститися, поки ви не виконаєте повторну ініціалізацію через &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt;.&lt;/p&gt;

  &lt;/details&gt;

  &lt;hr /&gt;

  &lt;h3 id=&quot;kubeconfig&quot;&gt;Kubeconfig&lt;/h3&gt;

  &lt;p&gt;Q: Як правильно перенести kubeconfig на локальний хост та додати кластер до переліку інших кластерів?&lt;/p&gt;

  &lt;details&gt;
    &lt;summary&gt;&lt;strong&gt;Відповідь&lt;/strong&gt;&lt;/summary&gt;

    &lt;p&gt;Щоб правильно перенести kubeconfig та об’єднати його з вашими існуючими кластерами, найкраще уникати ручного редагування основного файлу конфігурації, оскільки це часто призводить до помилок у відступах YAML.&lt;/p&gt;

    &lt;h4 id=&quot;section-9&quot;&gt;1. Завантаження файлу на локальний хост&lt;/h4&gt;

    &lt;p&gt;Використовуйте ssh для читання файлу безпосередньо в новий локальний файл. Замініть 10.10.0.21 на IP вашого cp-1.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# На вашій локальній машині&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; ~/.ssh/k8s_cluster_key k8sadmin@10.10.0.21 &lt;span class=&quot;s2&quot;&gt;&quot;sudo cat /etc/kubernetes/admin.conf&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; kubeconfig-new.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;h4 id=&quot;section-10&quot;&gt;2. Очищення конфігурації (Важливо)&lt;/h4&gt;

    &lt;p&gt;Файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin.conf&lt;/code&gt;, згенерований &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt;, зазвичай має стандартні назви (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes-admin&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes&lt;/code&gt;). Якщо у вас кілька кластерів, ці назви будуть конфліктувати.&lt;/p&gt;

    &lt;p&gt;Відкрийте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig-new.yaml&lt;/code&gt; у редакторі та перейменуйте ключові поля. Наприклад, якщо ваш кластер називається &lt;strong&gt;prod-cluster&lt;/strong&gt;:&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clusters.name&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes&lt;/code&gt; ➡️ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prod-cluster&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contexts.name&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes-admin@kubernetes&lt;/code&gt; ➡️ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin@prod-cluster&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contexts.context.cluster&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes&lt;/code&gt; ➡️ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prod-cluster&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contexts.context.user&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes-admin&lt;/code&gt; ➡️ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin-prod&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;users.name&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes-admin&lt;/code&gt; ➡️ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin-prod&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;&lt;strong&gt;Порада&lt;/strong&gt;: Якщо ви використовуєте Load Balancer (10.10.0.100), переконайтеся, що в полі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt; вказана саме ця IP-адреса, а не внутрішній IP &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp-1&lt;/code&gt;.&lt;/p&gt;

    &lt;h4 id=&quot;kubeconfig-1&quot;&gt;3. Обʼєднання з основним kubeconfig&lt;/h4&gt;

    &lt;p&gt;Замість того, щоб копіювати текст, скористайтеся можливістю &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; обʼєднувати декілька файлів через змінну оточення.&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;Тимчасово об’єднайте файли в пам’яті:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBECONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~/.kube/config:./kubeconfig-new.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Запишіть результат у новий файл (змерджений):&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl config view &lt;span class=&quot;nt&quot;&gt;--flatten&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; ~/.kube/config_new
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Замініть старий конфіг новим:&lt;/p&gt;

        &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mv&lt;/span&gt; ~/.kube/config_new ~/.kube/config
&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;600 ~/.kube/config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;

    &lt;h4 id=&quot;section-11&quot;&gt;4. Перевірка та перемикання&lt;/h4&gt;

    &lt;p&gt;Тепер ви можете побачити всі свої кластери:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Список контекстів&lt;/span&gt;
kubectl config get-contexts

&lt;span class=&quot;c&quot;&gt;# Перемикання на новий кластер&lt;/span&gt;
kubectl config use-context admin@prod-cluster

&lt;span class=&quot;c&quot;&gt;# Перевірка зв&apos;язку&lt;/span&gt;
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;strong&gt;Альтернатива (якщо ви використовуєте кілька файлів)&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Якщо ви не хочете змішувати все в один файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.kube/config&lt;/code&gt;, ви можете просто тримати їх окремо в папці &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.kube/configs/&lt;/code&gt; і додавати шлях до них у ваш &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.bashrc&lt;/code&gt; або &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;KUBECONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.kube/config:&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.kube/configs/prod-cluster.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Або інший варіант.&lt;/p&gt;

    &lt;p&gt;Набір команд &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl config&lt;/code&gt; дозволяє маніпулювати конфігурацією набагато чистіше. Оскільки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; завжди створює стандартні імена (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes-admin&lt;/code&gt;), найкраща стратегія — це &lt;strong&gt;iмпортувати файл з новою назвою context&lt;/strong&gt;, а &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; автоматично підтягне повʼязані з ним кластер та користувача.&lt;/p&gt;

    &lt;p&gt;&lt;strong&gt;Скрипт для імпорту та перейменування&lt;/strong&gt;&lt;/p&gt;

    &lt;p&gt;Цей підхід не редагує &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;temp-k8s.yaml&lt;/code&gt;, а відразу вливає його в основний конфіг з новими іменами.&lt;/p&gt;

    &lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# 1. Завантажуємо файл&lt;/span&gt;
ssh k8sadmin@10.10.0.21 &lt;span class=&quot;s2&quot;&gt;&quot;sudo cat /etc/kubernetes/admin.conf&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; temp-k8s.yaml

&lt;span class=&quot;c&quot;&gt;# 2. Визначаємо назву нового кластера&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;NEW_NAME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;k8s-cluster-prod&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 3. Додаємо кластер, користувача та контекст у ваш основний ~/.kube/config&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Ми витягуємо дані з тимчасового файлу за допомогою &apos;view&apos;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Додаємо кластер&lt;/span&gt;
kubectl config set-cluster &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NEW_NAME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--server&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubectl config view &lt;span class=&quot;nt&quot;&gt;--kubeconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;temp-k8s.yaml &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.clusters[0].cluster.server}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--certificate-authority-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubectl config view &lt;span class=&quot;nt&quot;&gt;--kubeconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;temp-k8s.yaml &lt;span class=&quot;nt&quot;&gt;--raw&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.clusters[0].cluster.certificate-authority-data}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Додаємо користувача&lt;/span&gt;
kubectl config set-credentials &lt;span class=&quot;s2&quot;&gt;&quot;admin-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NEW_NAME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--client-certificate-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubectl config view &lt;span class=&quot;nt&quot;&gt;--kubeconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;temp-k8s.yaml &lt;span class=&quot;nt&quot;&gt;--raw&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.users[0].user.client-certificate-data}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--client-key-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubectl config view &lt;span class=&quot;nt&quot;&gt;--kubeconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;temp-k8s.yaml &lt;span class=&quot;nt&quot;&gt;--raw&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.users[0].user.client-key-data}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Створюємо контекст&lt;/span&gt;
kubectl config set-context &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NEW_NAME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NEW_NAME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;admin-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NEW_NAME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 4. Видаляємо тимчасовий файл&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;temp-k8s.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/details&gt;

&lt;/details&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="DevOps"/>
        <category term="Infrastructure"/>
        <category term="Multipass"/>
        <category term="cloud-init"/>
        <category term="Netplan"/>
        <category term="etcd"/>
        <category term="k8s"/>
        <category term="Kubernetes"/>
        <summary type="html">У цьому посібнику ми розглянемо кроки для розгортання високодоступного (High Availability, HA) Kubernetes кластера з зовнішньою топологією розміщення бази даних налаштувань etcd, на локальній машині під керуванням macOS. Ми використаємо Multipass для створення віртуальних машин, cloud-init для їх ініціалізації, kubeadm для ініціалізації кластера, HAProxy як load balancer для вузлів панелі управління, Calico як Container Network Interface (CNI) та MetalLB для балансування трафіку до робочих вузлів.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Додавання статичної IP адреси віртуальним машинам Multipass на macOS</title>
      <link href="https://blog.andygol.co.ua/uk/2025/12/26/%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D1%96-ip-%D0%B4%D0%BB%D1%8F-%D0%B2%D0%BC-multipass/" rel="alternate" type="text/html" title="Додавання статичної IP адреси віртуальним машинам Multipass на macOS"/>
      <published>2025-12-26T06:30:00+00:00</published>
      <updated>2025-12-26T06:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2025/12/26/%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D1%96-ip-%D0%B4%D0%BB%D1%8F-%D0%B2%D0%BC-multipass</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2025/12/26/%D1%81%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%BD%D1%96-ip-%D0%B4%D0%BB%D1%8F-%D0%B2%D0%BC-multipass/">
        &lt;p&gt;Цей посібник містить докладний опис того як додати статичну IP адресу до віртуальної машини Multipass на macOS. Загальний опис того як це зробити ви можете знайти в офіційній документації в розділі &lt;a href=&quot;https://documentation.ubuntu.com/multipass/latest/how-to-guides/manage-instances/configure-static-ips/&quot;&gt;Configure static IPs&lt;/a&gt;, однак на macOS без певних модифікацій повторити ці рекомендації не виходить.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/X_PqiMvaE08?si=1JVrvxjHMduGariC&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;&lt;a id=&quot;default-eth&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;додавання-ip-адреси-до-першого-мережевого-інтерфейсу-віртуальної-машини&quot;&gt;Додавання IP адреси до першого мережевого інтерфейсу віртуальної машини&lt;/h2&gt;

&lt;h3 id=&quot;пошук-multipass-bridge&quot;&gt;Пошук Multipass bridge&lt;/h3&gt;

&lt;p&gt;Для доступу до віртуальних машин Multipass (на macOS) створює міст з назвою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge100&lt;/code&gt;. Цей міст використовується для доступу віртуальних машин до мережі хоста. IP адреси призначаються через DHCP і при (пере)створенні віртуальної машини їй надається наступна IP адреса з мережі моста.&lt;/p&gt;

&lt;p&gt;Виконання команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass networks&lt;/code&gt; має показати вам перелік доступних мережевих інтерфейсів.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-multipass-networks.png&quot; alt=&quot;multipass networks&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Як ви можете бачити в цьому переліку нашого моста немає. Результат &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass networks&lt;/code&gt; підтверджує головне обмеження Multipass на macOS: він “бачить” лише фізичні мережеві адаптери (Wi-Fi, Ethernet, USB). Створені віртуальні мости (bridge100) він ігнорує, оскільки вони не мають апаратного профілю, який очікує драйвер віртуалізації Apple.&lt;/p&gt;

&lt;p&gt;Спробуємо знайти міст іншим шляхом.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Знайдіть bridge (зазвичай bridge100)&lt;/span&gt;
ifconfig | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt; 2 &lt;span class=&quot;s2&quot;&gt;&quot;^bridge&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Відподіь має бути схожа на наступне:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-grep-bridge.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Запамʼятайте назву bridge&lt;/strong&gt; (наприклад, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge100&lt;/code&gt;). Ми будема далі її використовувати.&lt;/p&gt;

&lt;p&gt;В моєму випадку міст доступний за адресою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.2.1/24&lt;/code&gt; і DHCP сервер виділяє адреси віртуальним машинам з діапазону &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.2.X&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifconfig -v bridge100&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-ifconfig-bridge100.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;налаштування-bridge-на-хості&quot;&gt;Налаштування bridge на хості&lt;/h3&gt;

&lt;p&gt;Припустимо, що нам потрібно надавати віртуальним машинам статичні IP адреси для мережі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.0/24&lt;/code&gt;. Для цього ми будемо створювати аліас до наявного у нас мосту.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Додайте IP адресу до bridge&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig bridge100 10.10.0.1/24 &lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Перевірте що додалось&lt;/span&gt;
ifconfig bridge100 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;inet &quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Очікуваний результат:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;inet 192.168.2.1 netmask 0xffffff00 broadcast 192.168.2.255
inet 10.10.0.1 netmask 0xffffff00 broadcast 10.10.0.255
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-bridge100-alias.png&quot; alt=&quot;bridge100 alias&quot; /&gt;&lt;/p&gt;

&lt;p&gt;або за допомогою скрипта&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;TARGET_BRIDGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; 20 &lt;span class=&quot;s2&quot;&gt;&quot;member: vmenet&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bridge&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;: &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET_BRIDGE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Помилка: міст не знайдено. Перевірте, чи запущена ВМ.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ВМ знайдена на &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET_BRIDGE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;. Призначаємо 10.10.0.1...&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig &lt;span class=&quot;nv&quot;&gt;$TARGET_BRIDGE&lt;/span&gt; 10.10.0.1/24 &lt;span class=&quot;nb&quot;&gt;alias
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;створення-cloud-init-конфігурації-для-vm&quot;&gt;Створення cloud-init конфігурації для VM&lt;/h3&gt;

&lt;p&gt;Створимо наступний конфігураційний файл cloud-init, що містить налаштування для мережі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.10.0.0/24&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; multipass-static-ip.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;
#cloud-config

write_files:
  - path: /etc/netplan/60-static-ip.yaml
    permissions: &apos;0600&apos;
    content: |
      network:
        version: 2
        ethernets:
          default:
            dhcp4: true
            addresses:
              - 10.10.0.10/24
            routes:
              - to: default
                via: 10.10.0.1
                metric: 200

runcmd:
  - netplan apply

hostname: test-vm
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Для налаштування роботи мережі в Ubuntu використовується &lt;a href=&quot;https://netplan.io&quot;&gt;Netplan&lt;/a&gt;, ми додаємо настройки для нього у файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/netplan/60-static-ip.yaml&lt;/code&gt;. Вміст файлу знаходиться у полі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;content:&lt;/code&gt; Зверніть увагу на рядок &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;permissions: &apos;0600&apos;&lt;/code&gt;, ним ми встановлюємо права достпу на читання-запис тільки для користувача root. За наявності занадто дозвільних прав Netplan сповістить про це і не застосує налаштування. Команда &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netplan apply&lt;/code&gt; застосовує налаштування з теки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/netplan/&lt;/code&gt;. Поле &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostname&lt;/code&gt; містить назву для нашої віртуальної машини, яка буде додано у файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/hostname&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;запуск-vm-та-перевірка-роботи&quot;&gt;Запуск VM та перевірка роботи&lt;/h3&gt;

&lt;p&gt;Створимо нашу віртуальну машину&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Створіть VM з cloud-init конфігурацією&lt;/span&gt;
multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; multipass-static-ip.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Multipass створить віртуальну машину з назвою вказаною в параметрі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name/-n&lt;/code&gt; та використає налаштування &lt;a href=&quot;https://cloud-init.io&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt;&lt;/a&gt; з файла, вказаного в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cloud-init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Перевіримо мережеві налаштування нашої віртуальної машини&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Перевірте IP адреси на VM&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ip addr show enp0s1

&lt;span class=&quot;c&quot;&gt;# або скористайтесь netplan&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; netplan status

&lt;span class=&quot;c&quot;&gt;# Має показати два IP:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - 192.168.2.x (DHCP)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# - 10.10.0.10 (static)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm-netplan-status.png&quot; alt=&quot;test-vm netplan status&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Тепер настав час переврити звʼязок&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# З хоста до VM&lt;/span&gt;
ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 10.10.0.10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm-ping-from-host.png&quot; alt=&quot;ping з хоста до VM&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# З VM до хоста&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 10.10.0.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm-ping-from-vm-to-host.png&quot; alt=&quot;ping з VM до хоста&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Перевірте доступ в інтернет з VM&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm-ping-from-vm-to-internet.png&quot; alt=&quot;ping в інтернет з VM&quot; /&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Статична IP адреса працює!&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;технічні-деталі&quot;&gt;Технічні деталі&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Вивід переліку файлів конфігурації&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-la&lt;/span&gt; /etc/netplan

&lt;span class=&quot;c&quot;&gt;# Отримання обʼєднаної конфігурації мережі&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;netplan get
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ми бачимо що у теці &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/netplan&lt;/code&gt; знаходяться файли &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50-cloud-init.yaml&lt;/code&gt;, який створюється cloud-init під час ініціалізації віртуальної машини, та файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;60-static-ip.yaml&lt;/code&gt;, який ми передали в налаштуваннях&lt;/p&gt;

&lt;p&gt;Виконання команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo netplan get&lt;/code&gt; надасть нам обʼєднану конфігурацію мережі. Порівняйте її з вмістом файлу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;50-cloud-init.yaml&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Отримайте вміст 50-cloud-init.yaml&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo cat&lt;/span&gt; /etc/netplan/50-cloud-init.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Зверніть увагу на назву мережевого інтерйфесу що його використовує &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cloud-init&lt;/code&gt;. Саме його ми використовували в наших налаштуваннях (не &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enp0s1&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;network&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ethernets&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# назва мережевого інтерфейсу&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;macaddress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;52:54:00:ae:24:22&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dhcp-identifier&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mac&quot;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dhcp4&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a id=&quot;enp0s2&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;додавання-статичної-ip-адреси-до-другого-мережевого-інтерфейсу-віртуальної-машини&quot;&gt;Додавання статичної IP адреси до другого мережевого інтерфейсу віртуальної машини&lt;/h2&gt;

&lt;p&gt;Окрім додавання статчної IP адреси до першого мережевого інтерфейсу ми можемо робити це й для інших мережевих інтерфейсів вірутальної машини. Для цього на хості потрібно додати/створити відповідний мережевий міст.&lt;/p&gt;

&lt;h3 id=&quot;multipass-bridge-для-другого-мережевого-інтерфейсу&quot;&gt;Multipass bridge для другого мережевого інтерфейсу&lt;/h3&gt;

&lt;p&gt;Запустимо тимчасову ВМ для створення нового моста (bridge101)&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; sandbox-vm &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Параметр &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--network name=en0,mode=manual&lt;/code&gt; створить новий мережевий інтерфейс &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enp0s2&lt;/code&gt; у віртуальній машині, який буде привʼзаний до нового bridge. Однак, після створення цей інтерфейс буде неактивним, оскільки йому не було призначено IP-адресу.&lt;/p&gt;

&lt;p&gt;Недоліком чи перевагою такого підходу є те, що після видалення всіх віртуальних машин, привʼязаних до цього bridge його буде видалено з системи автоматично. Він існує допоки є віртуальні машини привʼязані до нього.&lt;/p&gt;

&lt;p&gt;Ця команда дозволяє дізнатись назву цього bridge:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ifconfig &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; 20 &lt;span class=&quot;s2&quot;&gt;&quot;member: vmenet&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bridge&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;: &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Скоріш за все назва буде &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge101&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Додамо аліас до мосту&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Додайте IP адресу до bridge&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig bridge101 10.10.1.1/24 &lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Перевірте що додалось&lt;/span&gt;
ifconfig bridge101 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;inet &quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Очікуваний результат:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;inet 10.10.1.1 netmask 0xffffff00 broadcast 10.10.1.255
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-bridge101-alias.png&quot; alt=&quot;bridge101 alias&quot; /&gt;&lt;/p&gt;

&lt;p&gt;або&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;TARGET_BRIDGE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;ifconfig &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-B&lt;/span&gt; 20 &lt;span class=&quot;s2&quot;&gt;&quot;member: vmenet&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;bridge&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt;: &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tail&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET_BRIDGE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Помилка: міст не знайдено. Перевірте, чи запущена ВМ.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;ВМ знайдена на &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET_BRIDGE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;. Призначаємо 10.10.1.1...&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ifconfig &lt;span class=&quot;nv&quot;&gt;$TARGET_BRIDGE&lt;/span&gt; 10.10.1.1/24 &lt;span class=&quot;nb&quot;&gt;alias
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;створення-cloud-init-конфігурації-для-другого-мережевого-інтерфейсу-vm&quot;&gt;Створення cloud-init конфігурації для другого мережевого інтерфейсу VM&lt;/h3&gt;

&lt;p&gt;Так само, як і в першому випадку створимо конфігурацію cloud-init для налаштування мережевого інтерфейсу віртуальної машини.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; multipass-static-ip1.yaml &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;sh&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&apos;
#cloud-config

write_files:
  - path: /etc/netplan/60-custom-network.yaml
    permissions: &apos;0600&apos;
    content: |
      network:
        version: 2
        ethernets:
          enp0s2:
            addresses:
              - 10.10.1.20/24
            # МИ ВИДАЛЯЄМО &quot;via: 10.10.0.1&quot; (default gateway)
            # Замість цього просто дозволяємо прямий доступ до мережі 10.10.1.0/24
            routes:
              - to: 10.10.1.0/24
                scope: link
runcmd:
  - netplan apply
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;запуск-та-перевірка-роботи-віртуальної-машини&quot;&gt;Запуск та перевірка роботи віртуальної машини&lt;/h3&gt;

&lt;p&gt;Запустимо віртуальну машину з назвою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test-vm1&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Створіть VM з cloud-init конфігурацією&lt;/span&gt;
multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; test-vm1 &lt;span class=&quot;nt&quot;&gt;--network&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;en0,mode&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;manual &lt;span class=&quot;nt&quot;&gt;--cloud-init&lt;/span&gt; multipass-static-ip1.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Дочекаємось завершення процесу створення та запуску VM. Після цього ми можемо видалити нашу тимчасову віртуальну машину, яку ми використовували для того, щоб система створила новий мережевий міст.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass delete sandbox-vm &lt;span class=&quot;nt&quot;&gt;--purge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Створення віртуальної машини &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test-vm1&lt;/code&gt; відбувається так само як і &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test-vm&lt;/code&gt; з тією відмінністю, що вона матиме два мережевих інтерфеси.&lt;/p&gt;

&lt;p&gt;Тепер перевіримо налаштування мережевих інтерфейсів віртуальної машини&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Перевірте IP адреси на VM&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ip addr show

&lt;span class=&quot;c&quot;&gt;# або скористайтесь netplan&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; netplan status
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ви маєте побачити два IP:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;192.168.2.x (DHCP) на enp0s1&lt;/li&gt;
  &lt;li&gt;10.10.1.20 (static) на enp0s2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm1-netplan-status.png&quot; alt=&quot;test-vm1 netplan status&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Перевіримо роботу звʼязку&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# З хоста до VM1&lt;/span&gt;
ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 10.10.1.20
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm1-ping-from-host.png&quot; alt=&quot;ping з хоста до VM1&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# З VM1 до хоста&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 10.10.1.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm1-ping-from-vm-to-host.png&quot; alt=&quot;ping з VM1 до хоста&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Трафік між VM та VM1&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 10.10.0.10
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 10.10.1.20
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm1-ping-from-vm1-to-vm.png&quot; alt=&quot;Трафік між VM та VM1&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Перевірте доступ в інтернет з VM1&lt;/span&gt;
multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; test-vm1 &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/12/2025-12-26-test-vm-ping-from1-vm-to-internet.png&quot; alt=&quot;Перевірте доступ в інтернет з VM1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Статична IP адреса на другому мережевому інтерфейсі працює!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;очищення&quot;&gt;Очищення&lt;/h2&gt;

&lt;p&gt;Видаліть віртуальні машини командою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass delete &amp;lt;name-vm&amp;gt; --purge&lt;/code&gt; або всі разом — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass delete --all --purge&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Після видалення всіх віртуальних машин, які були привʼязані до мосту &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge101&lt;/code&gt; його буде вилучено автоматично.&lt;/p&gt;

&lt;p&gt;Рузультатом виконання &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ifconfig -v bridge101&lt;/code&gt; буде&lt;/p&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;ifconfig: interface bridge101 does not exist
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Для видалення аліасу з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge100&lt;/code&gt; виконайте &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo ifconfig bridge100 -alias 10.10.0.1&lt;/code&gt;, також можна очистити кеш &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo arp -d -a&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;підсумки&quot;&gt;Підсумки&lt;/h2&gt;

&lt;p&gt;За допомогою цих настанов ви маєте можливість додавати статичні адреси віртуальним машинам Multipass. Це може бути корисно у випадках коли вам потрібно використовувати пул заздалегідь виділених адрес.&lt;/p&gt;

&lt;p&gt;⚠️ Це керівництво було створено та протестовано для роботи на macOS. Робота з іншими операційними системами може мати відмінності, залежно від функцій та підходів, які ви будете використовувати.&lt;/p&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="DevOps"/>
        <category term="Infrastructure"/>
        <category term="Multipass"/>
        <category term="cloud-init"/>
        <category term="Netplan"/>
        <summary type="html">Цей посібник містить докладний опис того як додати статичну IP адресу до віртуальної машини Multipass на macOS. Загальний опис того як це зробити ви можете знайти в офіційній документації в розділі Configure static IPs, однак на macOS без певних модифікацій повторити ці рекомендації не виходить.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Розгортання Kubernetes-кластера на локальному компʼютері: Повне покрокове керівництво</title>
      <link href="https://blog.andygol.co.ua/uk/2025/12/12/k8s-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80-%D0%B7-kubeadm/" rel="alternate" type="text/html" title="Розгортання Kubernetes-кластера на локальному компʼютері: Повне покрокове керівництво"/>
      <published>2025-12-12T06:30:00+00:00</published>
      <updated>2025-12-12T06:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2025/12/12/k8s-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80-%D0%B7-kubeadm</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2025/12/12/k8s-%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80-%D0%B7-kubeadm/">
        &lt;blockquote&gt;
  &lt;p&gt;💡 Такої детальної покрокової інструкції немає в офіційній документації Kubernetes! Те, що ви знайдете там, — це окремі рекомендації. Тут же все зібране в одному місці, щоб ви могли швидко та легко створити свій перший кластер.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;що-ми-будемо-робити&quot;&gt;Що ми будемо робити?&lt;/h2&gt;

&lt;p&gt;Ми створимо повноцінний Kubernetes-кластер на вашому локальному компʼютері. За наявності у вас окремого обладнання (компʼютерів), можна адаптувати це керівництво для фізичних машин. Для цього ми використаємо:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Multipass&lt;/strong&gt; — інструмент для створення віртуальних машин Ubuntu&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;kubeadm&lt;/strong&gt; — основний інструмент для ініціалізації кластера&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Flannel&lt;/strong&gt; — CNI-втулок для створення мережі між Pod’ами&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MetalLB&lt;/strong&gt; — балансувальник навантаження для нашого кластера&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Наш кластер складатиметься з &lt;strong&gt;трьох вузлів&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1 вузла панелі управління (control plane)&lt;/li&gt;
  &lt;li&gt;2 робочих worker-вузлів&lt;/li&gt;
&lt;/ul&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/ji3nKGN16hQ?si=ZHCl9hJyLgmTPLCn&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h3 id=&quot;передумови&quot;&gt;Передумови&lt;/h3&gt;

&lt;p&gt;Перед початком встановіть &lt;a href=&quot;https://canonical.com/multipass&quot;&gt;Multipass&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;multipass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;крок-1-створення-віртуальних-машин&quot;&gt;Крок 1: Створення віртуальних машин&lt;/h2&gt;

&lt;h3 id=&quot;визначаємо-список-вузлів&quot;&gt;Визначаємо список вузлів&lt;/h3&gt;

&lt;p&gt;Створимо масив з іменами трьох віртуальних машин. Це як простий список — просто записуємо, які машини нам потрібні.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=(&lt;/span&gt;k8s-control k8s-worker-1 k8s-worker-2&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;створюємо-вм&quot;&gt;Створюємо ВМ&lt;/h3&gt;

&lt;p&gt;Тепер за допомогою multipass створимо три віртуальні машини з Ubuntu на борту.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass launch &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--cpus&lt;/span&gt; 2 &lt;span class=&quot;nt&quot;&gt;--memory&lt;/span&gt; 4G &lt;span class=&quot;nt&quot;&gt;--disk&lt;/span&gt; 20G
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;За допомогою циклу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for NODE in &quot;${NODES[@]}&quot;&lt;/code&gt; проходимо по кожному імені в масиві; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass launch --name $NODE&lt;/code&gt; створює віртуальну машину з відповідним імʼям та наступними параметрами:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cpus 2&lt;/code&gt; — виділяємо 2 процесорних ядра (мінімум для K8s)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--memory 4G&lt;/code&gt; — виділяємо 4 ГБ оперативної памʼяті&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--disk 20G&lt;/code&gt; — виділяємо 20 ГБ дискового простору&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multipass автоматично завантажить Ubuntu. Це займе кілька хвилин ☕.&lt;/p&gt;

&lt;h2 id=&quot;крок-2-підготовка-всіх-вузлів&quot;&gt;Крок 2: Підготовка всіх вузлів&lt;/h2&gt;

&lt;p&gt;Коли наші віртуальні машини створені, тепер почнемо налаштування. Ці кроки потрібно виконати на всіх трьох машинах.&lt;/p&gt;

&lt;h3 id=&quot;21-оновлення-системи&quot;&gt;2.1. Оновлення системи&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [1/7] Оновлення системи на всіх вузлах ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;
    sudo apt-get update &amp;amp;&amp;amp;
    sudo apt-get upgrade -y
  &quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass exec $NODE&lt;/code&gt; — виконує команду на віртуальній машині&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get update&lt;/code&gt; — оновлює список доступних пакетів (як каталог програм)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get upgrade -y&lt;/code&gt; — встановлює всі оновлення, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-y&lt;/code&gt; означає “так, згоден” у відповідях на всі питання&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Перед початком роботи важливо мати актуальну систему з останніми виправленнями безпеки та оновленнями пакетів.&lt;/p&gt;

&lt;p&gt;Після виконання цієї команди всі пакети в системі будуть оновлені до останніх версій.&lt;/p&gt;

&lt;h3 id=&quot;22-вимкнення-firewall&quot;&gt;2.2. Вимкнення firewall&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [2/7] Вимкнення firewall на всіх вузлах ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;ufw disable
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;UFW — це вбудований брандмауер Ubuntu. Для навчального кластера ми вимикаємо брандмауер, щоб уникнути проблем з мережевим звʼязком між вузлами.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;У промисловій експлуатації&lt;/strong&gt; потрібно правильно налаштувати firewall та відкрити відповідні порти!&lt;/p&gt;

&lt;h3 id=&quot;23-завантаження-модулів-ядра&quot;&gt;2.3. Завантаження модулів ядра&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [3/7] Налаштування kernel-модулів ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;
    echo -e &apos;overlay&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;br_netfilter&apos; | sudo tee /etc/modules-load.d/k8s.conf
    sudo modprobe overlay
    sudo modprobe br_netfilter
  &quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Для своєї роботи Kubernetes потребує щоб були увімкнені ці два модулі ядра Linux:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;overlay&lt;/code&gt; — для файлової системи контейнерів (для роботи з шарами образів контейнерів)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;br_netfilter&lt;/code&gt; — для мережевого звʼязку між контейнерами&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Перший рядок записує ці модулі у конфігураційний файл, щоб вони завантажувались автоматично при старті. Наступні два рядки завантажують їх зараз.&lt;/p&gt;

&lt;h3 id=&quot;24-налаштування-мережевих-параметрів&quot;&gt;2.4. Налаштування мережевих параметрів&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [4/7] Налаштування мережевих параметрів ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;
    cat &amp;lt;&amp;lt;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
  &quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тут ми налаштовуємо параметри мережі на рівні ядра операційної системи:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;bridge-nf-call-iptables — дозволяє iptables обробляти трафік, що проходить через мережеві мости (для IPv4 та IPv6)&lt;/li&gt;
  &lt;li&gt;ip_forward — дозволяє маршрутизацію пакетів між мережевими інтерфейсами (як поштове відділення, що пересилає листи)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Команда sysctl –system застосовує ці параметри негайно.&lt;/p&gt;

&lt;p&gt;Це критично важливо для роботи мережі Kubernetes!&lt;/p&gt;

&lt;h3 id=&quot;25-встановлення-containerd&quot;&gt;2.5. Встановлення containerd&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [5/7] Встановлення containerd ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; containerd
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Containerd — це рушій для запуску та виконання контейнерів, тобто програма, яка фактично запускає та керує контейнерами. Це як двигун для автомобіля — без нього нічого не поїде. Kubernetes підтримує різні рушії, containerd — найпопулярніший та є рекомендованим варіантом.&lt;/p&gt;

&lt;h3 id=&quot;26-налаштування-containerd&quot;&gt;2.6. Налаштування containerd&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [6/7] Налаштування containerd і CRI ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;
    sudo mkdir -p /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml

    # Оновлюємо sandbox image
    sudo sed -i &apos;s/registry.k8s.io&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/pause:3.8/registry.k8s.io&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/pause:3.10.1/&apos; /etc/containerd/config.toml

    # Увімкнення режиму cgroup через systemd
    sudo sed -i &apos;s/SystemdCgroup = false/SystemdCgroup = true/&apos; /etc/containerd/config.toml

    # Додаємо конфіг для crictl
    sudo tee /etc/crictl.yaml &amp;lt;&amp;lt;EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF

    sudo systemctl restart containerd
    sudo systemctl enable containerd
  &quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Що відбувається:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Генеруємо стандартний конфігураційний файл за допомогою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;containerd config default&lt;/code&gt; та записуємо його в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/containerd/config.toml&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Kubernetes використовує спеціальний (інфраструктурний) “pause” контейнер для кожного Podʼа. Оновимо його до версії 3.10.1 (остання на момент написання)&lt;/li&gt;
  &lt;li&gt;Вмикаємо управління cgroups через systemd. Cgroups — це механізм обмеження ресурсів (CPU, памʼять) для контейнерів. Systemd — стандартний спосіб керування цим в Ubuntu&lt;/li&gt;
  &lt;li&gt;Налаштовуємо crictl (інструмент для відладки контейнерів). Ми вказуємо йому, як підключатися до containerd&lt;/li&gt;
  &lt;li&gt;Перезапускаємо containerd з новими налаштуваннями та додаємо його в автозапуск&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;27-встановлення-kubernetes-компонентів&quot;&gt;2.7. Встановлення Kubernetes компонентів&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;=== [7/7] Встановлення Kubernetes компонентів ===&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; bash &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;
    sudo apt-get install -y apt-transport-https ca-certificates curl gpg

    curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
      | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

    echo &apos;deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /&apos; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;
      | sudo tee /etc/apt/sources.list.d/kubernetes.list

    sudo apt-get update
    sudo apt-get install -y kubelet kubeadm kubectl
    sudo apt-mark hold kubelet kubeadm kubectl
    sudo systemctl enable kubelet
  &quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Підготовка&lt;/strong&gt;: Встановлюємо інструменти для безпечного завантаження пакетів (HTTPS, сертифікати, GPG)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Додавання ключа&lt;/strong&gt;: Завантажуємо криптографічний ключ від офіційного репозиторію Kubernetes. Це як печатка, що підтверджує автентичність пакетів&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Додавання репозиторію&lt;/strong&gt;: Додаємо офіційний репозиторій Kubernetes версії 1.34 до списку джерел пакетів&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Встановлення:&lt;/strong&gt;&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelet&lt;/code&gt; — агент на кожному вузлі, який запускає контейнери&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm&lt;/code&gt; — інструмент для ініціалізації кластера&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; — клієнт командного рядка для управління кластером&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;‼️ &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-mark hold&lt;/code&gt; запобігає автоматичному оновленню зафіксованих пакетів. Версії всіх компонентів у кластері мають збігатися! Додаємо kubelet в автозапуск&lt;/p&gt;

&lt;h3 id=&quot;28-готово-&quot;&gt;2.8. Готово! ✅&lt;/h3&gt;

&lt;p&gt;Всі ці кроки можна обʼєднати в один скрипт, який виконає їх послідовно на всіх вузлах та підготує їх до створення кластера.&lt;/p&gt;

&lt;p&gt;Всі три машини готові стати частиною Kubernetes-кластера!&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;h2 id=&quot;крок-3-ініціалізація-control-plane&quot;&gt;Крок 3: Ініціалізація Control Plane&lt;/h2&gt;

&lt;h3 id=&quot;підключаємось-до-control-plane&quot;&gt;Підключаємось до control plane&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass shell k8s-control
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ця команда відкриває термінал всередині віртуальної машини &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s-control&lt;/code&gt;. Тепер ми працюємо безпосередньо на цій машині, як якби сиділи за нею локально.&lt;/p&gt;

&lt;h3 id=&quot;отримання-ip-адреси&quot;&gt;Отримання IP-адреси&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;CONTROL_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostname -I&lt;/code&gt; — показує всі IP-адреси цієї машини&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;awk &apos;{print $1}&apos;&lt;/code&gt; — витягує першу адресу зі списку&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$()&lt;/code&gt; — зберігає результат у змінну CONTROL_IP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Нам потрібна IP-адреса, щоб worker-вузли знали, куди підключатися.&lt;/p&gt;

&lt;h3 id=&quot;ініціалізуємо-кластер-&quot;&gt;Ініціалізуємо кластер 🚀&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm init &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--pod-network-cidr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;10.244.0.0/16 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--apiserver-advertise-address&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CONTROL_IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Kubeadm — це інструмент для швидкого створення Kubernetes-кластера. Команда &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; ініціалізує панель управління (control plane), налаштовує API server, scheduler, controller manager та інші компоненти. В кінці виводиться команда &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt;, яку потрібно виконати на worker-вузлах, щоб приєднати їх до кластера.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Параметри:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--pod-network-cidr=10.244.0.0/16&lt;/code&gt; — діапазон IP-адрес для Podʼів (для Flannel)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--apiserver-advertise-address&lt;/code&gt; — IP для підключення інших вузлів&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⏱️ Це займе 1-2 хвилини.&lt;/p&gt;

&lt;p&gt;📝 &lt;strong&gt;Збережіть команду &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt;, яку виведе команда!&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;налаштовуємо-kubectl&quot;&gt;Налаштовуємо kubectl&lt;/h3&gt;

&lt;p&gt;Крім виводу команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt;, виводиться інструкція для налаштування kubectl на панелі управління для керування кластером від імені поточного користувача.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; ~/.kube
&lt;span class=&quot;nb&quot;&gt;sudo cp&lt;/span&gt; /etc/kubernetes/admin.conf ~/.kube/config
&lt;span class=&quot;nb&quot;&gt;sudo chown&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;:&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; ~/.kube/config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;kubectl потребує конфігураційний файл для підключення до кластера.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir -p ~/.kube&lt;/code&gt; — створюємо папку для конфігурації (якщо її ще немає)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp admin.conf ~/.kube/config&lt;/code&gt; — копіюємо файл з правами адміністратора&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chown&lt;/code&gt; — змінюємо власника файлу на поточного користувача (щоб не потрібно було використовувати sudo для kubectl)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;крок-4-встановлення-мережевого-втулка-cni--flannel&quot;&gt;Крок 4: Встановлення мережевого втулка (CNI) — Flannel&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/flannel-io/flannel#deploying-flannel-manually&quot;&gt;Flannel&lt;/a&gt;&lt;/strong&gt; — мережевий втулок (&lt;a href=&quot;https://www.cni.dev&quot;&gt;Container Network Interface, CNI&lt;/a&gt;), що дозволяє Podʼам на різних вузлах спілкуватися між собою.&lt;/p&gt;

&lt;p&gt;Kubernetes сам по собі не знає, як організувати мережу. Flannel створює overlay-мережу, де всі Podʼи наче в одній локальній мережі 🌐&lt;/p&gt;

&lt;p&gt;Ця команда завантажує та застосовує YAML-маніфест, який створює всі необхідні ресурси (DaemonSet, ConfigMap, ServiceAccount тощо).&lt;/p&gt;

&lt;h2 id=&quot;крок-5-приєднання-worker-вузлів&quot;&gt;Крок 5: Приєднання Worker-вузлів&lt;/h2&gt;

&lt;p&gt;Під час ініціалізації &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt; вивела команду &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm join&lt;/code&gt;, яку потрібно виконати на кожному worker-вузлі.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;k8s-worker-1 k8s-worker-2&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;kubeadm &lt;span class=&quot;nb&quot;&gt;join &lt;/span&gt;192.168.2.26:6443 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--token&lt;/span&gt; bsw6fd.e7624wl2688fybjx &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--discovery-token-ca-cert-hash&lt;/span&gt; sha256:7850aa1c6181277e284a08b81256979db25698a89982f0885540376a5376e0bd
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;⚠️ &lt;strong&gt;ВАЖЛИВО:&lt;/strong&gt; У вашому випадку IP, токен та хеш будуть &lt;strong&gt;іншими&lt;/strong&gt;! Використовуйте команду, яку ви отримали від &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Що тут:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipass exec $NODE --&lt;/code&gt; — виконуємо команду на кожному worker-вузлі&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.2.14:6443&lt;/code&gt; — адреса API server (порт 6443 — стандартний)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--token&lt;/code&gt; — тимчасовий токен автентифікації (згенерований під час виконання &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeadm init&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--discovery-token-ca-cert-hash&lt;/code&gt; — SHA256-хеш сертифікату CA для перевірки автентичності (захист від атак типу man-in-the-middle)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;крок-6-перевірка-кластера&quot;&gt;Крок 6: Перевірка кластера&lt;/h2&gt;

&lt;p&gt;Повертаємося на вузол панелі управління (control plane) та перевіряємо статус вузлів.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;multipass shell k8s-control
kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ви повинні побачити три вузли зі статусом &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ready&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NAME            STATUS   ROLES           AGE   VERSION
k8s-control     Ready    control-plane   5m    v1.34.0
k8s-worker-1    Ready    &amp;lt;none&amp;gt;          2m    v1.34.0
k8s-worker-2    Ready    &amp;lt;none&amp;gt;          2m    v1.34.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Якщо статус &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NotReady&lt;/code&gt;, почекайте хвилину — Flannel ще налаштовується ⏳&lt;/p&gt;

&lt;h2 id=&quot;крок-7-встановлення-metallb&quot;&gt;Крок 7: Встановлення MetalLB&lt;/h2&gt;

&lt;h3 id=&quot;що-таке-metallb&quot;&gt;Що таке MetalLB?&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://metallb.io&quot;&gt;MetalLB&lt;/a&gt;&lt;/strong&gt; — це load balancer для bare-metal кластерів. В керованих (managed) розгортаннях Kubernetes від хмарних провайдерів (AWS, GCP) сервіси типу LoadBalancer створюються автоматично. У нашому локальному кластері MetalLB надає цю функціональність. 🎯&lt;/p&gt;

&lt;h3 id=&quot;застосовуємо-маніфести&quot;&gt;Застосовуємо маніфести&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/metallb/metallb/v0.15.3/config/manifests/metallb-native.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;чекаємо-готовності&quot;&gt;Чекаємо готовності&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nb&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--namespace&lt;/span&gt; metallb-system &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--for&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;condition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ready pod &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--selector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;metallb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ця команда чекає, поки всі Pod’и MetalLB не перейдуть у стан готовності.&lt;/p&gt;

&lt;h3 id=&quot;маркування-worker-вузлів&quot;&gt;Маркування worker-вузлів&lt;/h3&gt;

&lt;p&gt;Для того щоб призначити мітки всім вузлам, імена яких починаються з &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;k8s-worker-&lt;/code&gt;, можна використати команду &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl get nodes&lt;/code&gt; разом із фільтрацією за допомогою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;grep&lt;/code&gt; та подальшим циклом &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for&lt;/code&gt; для застосування мітки до кожного знайденого вузла.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubectl get nodes &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.items[*].metadata.name}&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;[[:space:]]&apos;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;\n&apos;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;^k8s-worker-&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Applying label to node: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  kubectl label node &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; metallb-role&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;worker &lt;span class=&quot;nt&quot;&gt;--overwrite&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Додаємо мітку &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metallb-role=worker&lt;/code&gt; до worker-вузлів. Мітки (labels) — це key-value пари, що допомагають організовувати та вибирати ресурси в Kubernetes. Ми використаємо цю мітку, щоб MetalLB працював тільки на worker-вузлах.&lt;/p&gt;

&lt;h3 id=&quot;налаштування-ip-пулу-та-l2advertisement&quot;&gt;Налаштування IP-пулу та L2Advertisement&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;CONTROL_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $1}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;BASE_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$CONTROL_IP&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f1-3&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BASE_IP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.200-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BASE_IP&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: worker-nodes-l2
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool
  nodeSelectors:
  - matchLabels:
      metallb-role: worker
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Що відбувається:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Отримання базової IP&lt;/strong&gt;: Якщо IP control plane — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.2.14&lt;/code&gt;, то &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BASE_IP&lt;/code&gt; буде &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;192.168.2&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;IPAddressPool&lt;/strong&gt; — Визначає діапазон IP-адрес, які MetalLB може видавати LoadBalancer-сервісам. У нашому випадку — від .200 до .250 (51 адреса)&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;L2Advertisement&lt;/strong&gt; — Налаштовує L2 (layer 2) режим. MetalLB оголошує IP-адреси через ARP-протокол, щоб мережа знала, де знаходяться ці адреси&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;nodeSelectors&lt;/strong&gt; — Обмежує роботу MetalLB тільки вузлами з міткою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;metallb-role=worker&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;крок-8-демонстрація-&quot;&gt;Крок 8: Демонстрація 🎉&lt;/h2&gt;

&lt;h3 id=&quot;створюємо-deployment&quot;&gt;Створюємо Deployment&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create deployment hello &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--image&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;nginxdemos/hello:plain-text &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--replicas&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Створюємо 3 репліки простого nginx-застосунку.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deployment&lt;/code&gt; — обʼєкт Kubernetes, що керує набором ідентичних Podʼів&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--image&lt;/code&gt; — Docker-образ для контейнерів (простий nginx з текстовим виводом)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--replicas=3&lt;/code&gt; — запустити 3 копії (для демонстрації балансування)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--port=80&lt;/code&gt; — відкрити порт 80 у контейнері&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;створюємо-loadbalancer-service&quot;&gt;Створюємо LoadBalancer Service&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl expose deployment hello &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;LoadBalancer &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Service типу LoadBalancer — MetalLB виділить зовнішню IP-адресу для доступу до нашого застосунку! Service — це абстракція, що надає стабільний спосіб доступу до групи Podʼів:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;expose deployment&lt;/code&gt; — ця команда створює Service для нашого Deployment&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--type=LoadBalancer&lt;/code&gt; — цей параметр вказує, що потрібно використовувати зовнішній load balancer (MetalLB виділить IP)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--port=80&lt;/code&gt; — порт, на якому Service буде доступний&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;тестуємо-балансування&quot;&gt;Тестуємо балансування&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;EXTERNAL_IP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;kubectl get svc hello &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{.status.loadBalancer.ingress[0].ip}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;LB IP: &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$EXTERNAL_IP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;i &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;1..10&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Request &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:&quot;&lt;/span&gt;
  curl &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; http://&lt;span class=&quot;nv&quot;&gt;$EXTERNAL_IP&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Server address&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;---&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Для тестування балансування нашого навантаження в змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXTERNAL_IP&lt;/code&gt; зберігаємо зовнішню IP-адресу, яку виділив MetalLB для сервісу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt;; потім виконуємо 10 запитів до цієї IP-адреси та виводимо рядок з адресою сервера, який обробив кожен запит.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Результат:&lt;/strong&gt; Ви побачите, що запити розподіляються між трьома різними Podʼами. Це балансування навантаження в дії! 🔄&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Request 1:
Server address: 10.244.1.2:80
---
Request 2:
Server address: 10.244.2.3:80
---
Request 3:
Server address: 10.244.1.4:80
---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;-важливо-про-swap&quot;&gt;🚨 Важливо про SWAP&lt;/h2&gt;

&lt;p&gt;У стандартній конфігурації Multipass на віртуальних машинах &lt;strong&gt;swap вимкнений&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Kubelet стандартно &lt;strong&gt;відмовляється працювати&lt;/strong&gt;, якщо swap увімкнений, тому що це може призвести до непередбачуваної поведінки Pod’ів. (контейнери можуть “виштовхуватись” на диск, що сповільнює їх роботу).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;У нашому випадку все ок&lt;/strong&gt; — swap вимкнений автоматично! ✅&lt;/p&gt;

&lt;p&gt;Але якщо ви налаштовуєте K8s на інших системах, де swap увімкнений:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Варіант 1:&lt;/strong&gt; Вимкніть swap&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;swapoff &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# І закоментуйте swap у /etc/fstab&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Варіант 2:&lt;/strong&gt; Налаштуйте kubelet для &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/cluster-administration/swap-memory-management/&quot;&gt;роботи зі swap&lt;/a&gt; (експериментально з K8s 1.28+)&lt;/p&gt;

&lt;h2 id=&quot;корисні-команди&quot;&gt;Корисні команди&lt;/h2&gt;

&lt;h3 id=&quot;перевірка-статусу&quot;&gt;Перевірка статусу&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get nodes
kubectl get pods &lt;span class=&quot;nt&quot;&gt;-A&lt;/span&gt;
kubectl get svc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;перегляд-логів&quot;&gt;Перегляд логів&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl logs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; kube-system &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;flannel
kubectl logs &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; metallb-system &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;metallb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;видалення-ресурсів&quot;&gt;Видалення ресурсів&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl delete svc hello
kubectl delete deployment hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;зупинка-кластера&quot;&gt;Зупинка кластера&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Зупинити всі ВМ&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass stop &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Запустити знову&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass start &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;видалення-кластера&quot;&gt;Видалення кластера&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;NODE &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;NODES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
  &lt;/span&gt;multipass delete &lt;span class=&quot;nv&quot;&gt;$NODE&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;multipass purge
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;підсумки&quot;&gt;Підсумки&lt;/h2&gt;

&lt;p&gt;Вітаю! 🎉 Ми щойно створили повноцінний Kubernetes-кластер на своєму локальному компʼютері!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Що ми зробили:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;✅ Створили 3 віртуальні машини&lt;/li&gt;
  &lt;li&gt;✅ Налаштували containerd та kernel-модулі&lt;/li&gt;
  &lt;li&gt;✅ Ініціалізували control plane&lt;/li&gt;
  &lt;li&gt;✅ Приєднали worker-вузли&lt;/li&gt;
  &lt;li&gt;✅ Встановили Flannel для мережі&lt;/li&gt;
  &lt;li&gt;✅ Налаштували MetalLB для LoadBalancer&lt;/li&gt;
  &lt;li&gt;✅ Розгорнули тестовий застосунок з балансуванням&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Тепер у вас є локальне середовище для:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Експериментів з Kubernetes&lt;/li&gt;
  &lt;li&gt;Тестування маніфестів&lt;/li&gt;
  &lt;li&gt;Ознайомлення з архітектурою кластера&lt;/li&gt;
  &lt;li&gt;Підготовки до сертифікацій (CKA, CKAD)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Сподіваюсь, це керівництво було корисним!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Якщо у вас виникли питання або проблеми — пишіть у коментарях. Діліться цим постом з колегами, яким це може бути корисно 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Kubernetes! ☸️&lt;/strong&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;застереження-&quot;&gt;Застереження 🚨&lt;/h2&gt;

&lt;p&gt;Цей кластер призначений для &lt;strong&gt;розробки та тестування&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Для промисловій експлуатації потрібні додаткові налаштування:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Firewall та Network Policies&lt;/li&gt;
  &lt;li&gt;RBAC (Role-Based Access Control)&lt;/li&gt;
  &lt;li&gt;Secrets Management&lt;/li&gt;
  &lt;li&gt;Моніторинг та логування&lt;/li&gt;
  &lt;li&gt;Резервне копіювання etcd&lt;/li&gt;
  &lt;li&gt;High Availability control plane&lt;/li&gt;
  &lt;li&gt;Сканування на вразливості&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Скрипт для підготовки вузлів &lt;a href=&quot;https://gist.github.com/Andygol/2944d08862d58e3c5a12becacd55b620&quot;&gt;https://gist.github.com/Andygol/2944d08862d58e3c5a12becacd55b620&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="Kubernetes"/>
        <category term="Containers"/>
        <category term="Orchestration"/>
        <category term="DevOps"/>
        <category term="Infrastructure"/>
        <category term="kubeadm"/>
        <category term="flannel"/>
        <category term="containerd"/>
        <summary type="html">💡 Такої детальної покрокової інструкції немає в офіційній документації Kubernetes! Те, що ви знайдете там, — це окремі рекомендації. Тут же все зібране в одному місці, щоб ви могли швидко та легко створити свій перший кластер.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Від Docker до Kubernetes: коли контейнери перестають бути простими</title>
      <link href="https://blog.andygol.co.ua/uk/2025/10/30/%D0%B2%D1%96%D0%B4-docker-%D0%B4%D0%BE-kubernetes/" rel="alternate" type="text/html" title="Від Docker до Kubernetes: коли контейнери перестають бути простими"/>
      <published>2025-10-30T08:30:00+00:00</published>
      <updated>2025-10-30T08:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2025/10/30/%D0%B2%D1%96%D0%B4-docker-%D0%B4%D0%BE-kubernetes</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2025/10/30/%D0%B2%D1%96%D0%B4-docker-%D0%B4%D0%BE-kubernetes/">
        &lt;p&gt;Ти це нарешті зробив.&lt;/p&gt;

&lt;p&gt;Після днів, а може й тижнів роботи — твій застосунок повністю &lt;strong&gt;докеризований&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;У тебе є &lt;strong&gt;Node.js API&lt;/strong&gt;, &lt;strong&gt;React-фронтенд&lt;/strong&gt;, &lt;strong&gt;Postgres база даних&lt;/strong&gt; — кожен компонент охайно запакований у свій контейнер. Один &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt;, і все магічно оживає. Твоя локальна інфраструктура — мов оркестр: кожен контейнер грає свою партію, і ти — диригент.&lt;/p&gt;

&lt;p&gt;Ти задоволений. Відчуваєш себе справжнім DevOps-чарівником.&lt;/p&gt;

&lt;h2 id=&quot;час-для-виходу-в-люди&quot;&gt;Час для виходу в люди&lt;/h2&gt;

&lt;p&gt;На локальній машині все працює як годинник. Та коли настав час розгорнути все це на реальних серверах, ілюзія простоти швидко розсипається.&lt;/p&gt;

&lt;p&gt;Ти хочеш &lt;strong&gt;надійності&lt;/strong&gt;.&lt;br /&gt;
Ти хочеш &lt;strong&gt;масштабованості&lt;/strong&gt;.&lt;br /&gt;
І здається, рішення очевидне:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;«Я просто запущу ті самі контейнери на кількох серверах.»&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Просто? Не зовсім.&lt;/p&gt;

&lt;h2 id=&quot;контейнери-в-хаосі&quot;&gt;Контейнери в хаосі&lt;/h2&gt;

&lt;p&gt;Ти стикаєшся з першим головним болем:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Як фронтенд знайде свій API, якщо IP контейнерів змінюються після кожного перезапуску?&lt;/li&gt;
  &lt;li&gt;Що буде, коли сервер впаде посеред ночі? Хто перезапустить контейнери?&lt;/li&gt;
  &lt;li&gt;Як оновити образ API, не зупинивши решту системи?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ти починаєш писати Bash-скрипти, вручну копіюєш образи через SSH, намагаєшся якось балансувати трафік.&lt;br /&gt;
Але кожне оновлення — це ризик.&lt;br /&gt;
Кожен збій — це паніка.&lt;/p&gt;

&lt;p&gt;Твій колись елегантний Docker-сетап перетворюється на хаотичну мережу костилів і ручних ритуалів.&lt;/p&gt;

&lt;h2 id=&quot;kubernetes-не-просто-docker-на-стероїдах&quot;&gt;Kubernetes — не просто Docker на стероїдах&lt;/h2&gt;

&lt;p&gt;Тут з’являється &lt;strong&gt;Kubernetes&lt;/strong&gt; (або просто &lt;strong&gt;K8s&lt;/strong&gt;)&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Можливо, ти вже чув: «Це щось складне», або «Це для корпорацій типу Google».&lt;br /&gt;
І дійсно — спочатку він здається надмірно громіздким.&lt;br /&gt;
Але це тому, що &lt;strong&gt;Kubernetes вирішує зовсім іншу проблему&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Docker — це спосіб &lt;strong&gt;упакувати&lt;/strong&gt; застосунок.
Kubernetes — це спосіб &lt;strong&gt;керувати&lt;/strong&gt; цими застосунками &lt;strong&gt;в масштабі&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Він не просто запускає контейнери — він керує їхнім життєвим циклом: розміщення, масштабування, відновлення після збоїв, оновлення, доступність.&lt;/p&gt;

&lt;p&gt;І головна ідея — у &lt;strong&gt;зміні мислення&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;декларативне-мислення-що-а-не-як&quot;&gt;Декларативне мислення: “що”, а не “як”&lt;/h2&gt;

&lt;p&gt;Раніше ти працював &lt;strong&gt;імперативно&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Запусти цей контейнер тут. Зупини цей — там.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kubernetes вимагає мислити &lt;strong&gt;декларативно&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Хочу, щоб працювало три копії мого API з образом v1.2.&lt;br /&gt;
Кожна має мати 500 МБ пам’яті.&lt;br /&gt;
Вони мають бути доступні з іменем api-service.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ти не наказуєш — ти &lt;strong&gt;описуєш бажаний стан&lt;/strong&gt;.&lt;br /&gt;
Kubernetes сам вирішує, як його досягти.&lt;br /&gt;
Його основна задача — &lt;strong&gt;постійно спостерігати за реальною системою і зводити її до бажаного стану&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;як-kubernetes-вирішує-справжні-проблеми-експлуатації&quot;&gt;Як Kubernetes вирішує справжні проблеми експлуатації&lt;/h2&gt;

&lt;h3 id=&quot;автоматичне-розміщення-scheduling--bin-packing&quot;&gt;Автоматичне розміщення (Scheduling &amp;amp; Bin Packing)&lt;/h3&gt;

&lt;p&gt;Kubernetes бачить усі твої сервери (вони називаються &lt;strong&gt;вузлами&lt;/strong&gt; — &lt;strong&gt;Nodes&lt;/strong&gt;) і сам вирішує, де краще запустити контейнери. Він аналізує ресурси — CPU, RAM, дисковий простір і розподіляє навантаження оптимально. Жодних ручних призначень, жодних «цей контейнер — на цей сервер».&lt;/p&gt;

&lt;h3 id=&quot;самовідновлення-self-healing&quot;&gt;Самовідновлення (Self-Healing)&lt;/h3&gt;

&lt;p&gt;Контейнер впав? Kubernetes це помічає.&lt;br /&gt;
Фактичний стан — 2 копії, бажаний — 3.&lt;br /&gt;
Він просто запускає новий.&lt;/p&gt;

&lt;p&gt;Жодних нічних викликів, жодних скриптів-відновлювачів.&lt;br /&gt;
&lt;strong&gt;Система лікує себе сама&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;горизонтальне-масштабування&quot;&gt;Горизонтальне масштабування&lt;/h3&gt;

&lt;p&gt;Трафік зростає — потрібно більше копій API.&lt;br /&gt;
Ти змінюєш один рядок у YAML:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;І Kubernetes сам піднімає додаткові контейнери, розподіляючи їх між вузлами.&lt;/p&gt;

&lt;p&gt;А якщо не хочеш робити це вручну — просто вкажи правила автоматичного масштабування. K8s сам збільшить кількість контейнерів, коли зросте навантаження, і зменшить — коли спаде.&lt;/p&gt;

&lt;h3 id=&quot;виявлення-сервісів-і-балансування-навантаження&quot;&gt;Виявлення сервісів і балансування навантаження&lt;/h3&gt;

&lt;p&gt;У Kubernetes контейнери не шукають один одного за IP.&lt;br /&gt;
Ти створюєш &lt;strong&gt;Service&lt;/strong&gt; — абстракцію, яка надає стабільне ім’я (наприклад, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api-service&lt;/code&gt;) і внутрішню IP-адресу.&lt;/p&gt;

&lt;p&gt;Фронтенд просто звертається до &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api-service&lt;/code&gt;, а Kubernetes сам визначає, на який контейнер API піде запит. Трафік автоматично розподіляється між справними екземплярами.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Жодних жорстко записаних в код IP, жодних ручних налаштувань балансування.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;автоматичні-оновлення-та-відкати&quot;&gt;Автоматичні оновлення та відкати&lt;/h3&gt;

&lt;p&gt;Хочеш оновити свій API до версії v1.3?&lt;br /&gt;
Змінюєш версію образу в YAML — і все.&lt;/p&gt;

&lt;p&gt;Kubernetes виконує &lt;strong&gt;rolling update&lt;/strong&gt; — поступово запускає нові контейнери v1.3 і зупиняє старі v1.2.&lt;br /&gt;
Жодного простою. Користувачі навіть не помічають, як відбулось оновлення.&lt;/p&gt;

&lt;p&gt;А якщо щось пішло не так — Kubernetes автоматично відкотиться до попередньої стабільної версії.&lt;/p&gt;

&lt;h2 id=&quot;kubernetes-як-операційна-система-для-твоїх-застосунків&quot;&gt;Kubernetes як операційна система для твоїх застосунків&lt;/h2&gt;

&lt;p&gt;Kubernetes — це не ще один інструмент DevOps. Це ціла &lt;strong&gt;операційна система для розподілених застосунків&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Він керує ресурсами, сервісами, оновленнями, трафіком, відновленням — усім, що раніше вимагало десятків ручних скриптів і безсонних ночей.&lt;/p&gt;

&lt;p&gt;Ти більше не витрачаєш час на підтримку серверів. Ти зосереджуєшся на тому, що справді має значення — на розробці продукту.&lt;/p&gt;

&lt;h2 id=&quot;керованість&quot;&gt;Керованість&lt;/h2&gt;

&lt;p&gt;Kubernetes не обіцяє простоти — він дає &lt;strong&gt;керованість&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Він дає тобі змогу описати, &lt;em&gt;як має виглядати твоя система&lt;/em&gt;, а потім бере на себе все інше: розміщення, відновлення, масштабування, балансування, оновлення.&lt;/p&gt;

&lt;p&gt;Docker був першим кроком.&lt;br /&gt;
Kubernetes — це наступний рівень зрілості інфраструктури.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Контейнери зробили розробку простішою.&lt;br /&gt;
Kubernetes робить експлуатацію передбачуваною.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Kubernetes — це переносима, розширювана та відкрита платформа для управління контейнеризованими навантаженнями та службами, яка полегшує як декларативне, так і автоматичне налаштування. Вона має велику та швидко зростаючу екосистему. Сервіси, підтримка та інструменти Kubernetes широко доступні.&lt;br /&gt; &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/overview/&quot;&gt;https://andygol-k8s.netlify.app/uk/docs/concepts/overview/&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="Docker"/>
        <category term="Kubernetes"/>
        <category term="Containers"/>
        <category term="Orchestration"/>
        <category term="Scaling"/>
        <category term="DevOps"/>
        <category term="Infrastructure"/>
        <category term="Automation"/>
        <category term="Networking"/>
        <summary type="html">Ти це нарешті зробив.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Хвилинні зміни в OpenStreetMap не такі й хвилинні</title>
      <link href="https://blog.andygol.co.ua/uk/2025/05/08/%D1%84%D0%B0%D0%B9%D0%BB%D0%B8-%D0%B7%D0%BC%D1%96%D0%BD-osm/" rel="alternate" type="text/html" title="Хвилинні зміни в OpenStreetMap не такі й хвилинні"/>
      <published>2025-05-08T08:30:00+00:00</published>
      <updated>2025-05-08T08:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2025/05/08/%D1%84%D0%B0%D0%B9%D0%BB%D0%B8-%D0%B7%D0%BC%D1%96%D0%BD-osm</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2025/05/08/%D1%84%D0%B0%D0%B9%D0%BB%D0%B8-%D0%B7%D0%BC%D1%96%D0%BD-osm/">
        &lt;p&gt;OpenStreetMap — це чудовий приклад проєкту зі спільного створення відкритої бази даних геопросторової інформації. Накопичення інформації це тільки частина справи. Її збирають для того, щоб нею можна було користуватись. Ви можете отримати дані для поточної ділянки на мапі скориставшись меню &lt;a href=&quot;https://www.openstreetmap.org/export&quot;&gt;Експорт&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.openstreetmap.org/export#map=12/48.4680/34.9894&quot;&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-osm-export-tab-12-uk.png&quot; alt=&quot;Дніпро, Україна. OpenStreetMap, z12&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Однак можливості завантажити дані для поточної ділянки мають певні обмеження. Ви не зможете зробити це, якщо ви переглядаєте мапу в масштабі z11 та менше 👇. Бачите повідомлення в жовтому прямокутнику?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.openstreetmap.org/export#map=11/48.4681/34.9894&quot;&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-osm-export-tab-11-uk.png&quot; alt=&quot;Дніпро, Україна. OpenStreetMap, z11&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;планета-osm&quot;&gt;Планета OSM&lt;/h2&gt;

&lt;p&gt;У випадку, коли вам потрібні дані для більшої території, або навіть для всієї планети, вам пряма дорога до місця де проєкт пропонує дані для завантаження — дамп планети, &lt;a href=&quot;https://planet.openstreetmap.org/&quot;&gt;https://planet.openstreetmap.org/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://planet.osm.org&quot;&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-planet-osm-org.png&quot; alt=&quot;planet.osm.org&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Тут ви можете завантажити дані для всієї планети, як у вигляді &lt;a href=&quot;https://planet.openstreetmap.org/planet/&quot;&gt;зрізу даних&lt;/a&gt; на певну дату або у вигляді &lt;a href=&quot;https://planet.openstreetmap.org/planet/full-history/&quot;&gt;дампу з повною історією змін&lt;/a&gt;, які генерується раз на тиждень.&lt;/p&gt;

&lt;p&gt;Ці дані проєкт надає всім охочим безплатно, однак ви можете &lt;a href=&quot;https://supporting.openstreetmap.org/&quot;&gt;підтримати проєкт&lt;/a&gt;, зробивши пожертву чи ставши членом Фундації OpenStreetMap.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://supporting.openstreetmap.org/donate/&quot;&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-donate-osm.png&quot; alt=&quot;https://supporting.openstreetmap.org/donate/&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;файли-змін-даних&quot;&gt;Файли змін даних&lt;/h2&gt;

&lt;p&gt;Для тих, хто не так глибоко знайомий з проєктом, diffs — &lt;a href=&quot;https://planet.openstreetmap.org/replication/&quot;&gt;це набори змін&lt;/a&gt;, що відбулись в даних, які публікуються OpenStreetMap кожного дня, години та хвилини. Ці файли містять перелік усіх змін, внесених до бази даних за обраний проміжок часу.&lt;/p&gt;

&lt;p&gt;Розробники можуть використовувати ці diffs для оновлення своїх копій бази даних, які потім використовуються для геокодування, рендерингу мап та інших інструментів майже в режимі реального часу.&lt;/p&gt;

&lt;p&gt;Однією з ключових особливостей, що дозволяє стежити за змінами в цій величезній базі даних, є концепція “хвилинних змін” (minute diffs). На перший погляд, назва натякає на майже миттєве відображення кожної зміненої деталі. Але чи справді все так?
Звучить ідеально, правда? Але є кілька нюансів, які варто враховувати:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;“Майже” реальний час&lt;/strong&gt; — ключове слово. Хоча файли змін генеруються щохвилини, їхня фактична поява може затримуватися. Залежно від завантаженості серверів, процес генерації та публікації може зайняти трохи більше часу. Тож, хоча ви й можете побачити зміни протягом кількох хвилин, це не завжди буде рівно одна хвилина.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Обробка файлів змін&lt;/strong&gt; — не миттєва. Отримання файлу змін — це лише перший крок. Потім програмне забезпечення повинно завантажити, розпарсити та застосувати ці зміни до локальної копії бази даних. Цей процес також займає певний час, який залежить від розміру файлу змін та продуктивності вашого обладнання. У хвилини пікового внесення змін до OpenStreetMap розмір таких файлів може значно зростати, уповільнюючи процес оновлення.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Порядок застосування змін&lt;/strong&gt; має значення. Для забезпечення консистентності даних, зміни повинні застосовуватися в правильному порядку. Це означає, що якщо ви пропустили кілька хвилинних diffs (наприклад, через проблеми з мережею), вам доведеться обробити їх усі послідовно, перш ніж ваша база даних знов буде актуальною.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;використання-файлів-змін&quot;&gt;Використання файлів змін&lt;/h2&gt;

&lt;p&gt;Ви отримали дамп планети та завантажили його у свій сервіс для створення мапи, чи прокладання маршрутів. Це лише початкова частина всього процесу. В поточному швидкоплинному світі інформація змінюється дуже швидко і той хто володіє цими змінами матиме значну перевагу перед іншими.&lt;/p&gt;

&lt;p&gt;Файл дампу планети в OpenStreetMap генерується раз на тиждень&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;На момент написання, 08 травня 2025 року, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;planet-latest.osm.bz2&lt;/code&gt; було створено &lt;em&gt;2025-05-02 23:50&lt;/em&gt;, та його розмір був — 150G; ви також можете отримати дані у двійковому форматі pbf — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;planet-latest.osm.pbf&lt;/code&gt;, &lt;em&gt;2025-05-02 23:50&lt;/em&gt;, 81G; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;planet-250428.osm.pbf&lt;/code&gt;, &lt;em&gt;2025-05-02 23:50&lt;/em&gt;, 81G).&lt;/p&gt;

  &lt;p&gt;Цей, останній дамп містить дані станом на &lt;em&gt;25 квітня 2025 року&lt;/em&gt;, його було опубліковано — &lt;em&gt;02 травня 2025 року&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Виходить що на момент публікації дамп вже є на 7 діб застарілим, додайте до цього ще пару днів, коли ви захочете його завантажити, тож у вас на момент початку розгортання дані будуть застарілим на 1-2 тижні, додайте до цього ще час потрібний для завантаження дампу у вашу систему і ви можете виявити, що дані на момент початку їх використання втратили свою актуальність приблизно на 3-5 тижнів (якщо ми говоримо про масштаб планети). Для того щоб наздогнати це відставання та мати актуальні дані варто скористатись файлами реплікації &lt;a href=&quot;https://planet.openstreetmap.org/replication/&quot;&gt;https://planet.openstreetmap.org/replication/&lt;/a&gt;, нашими денними, годинними та хвилинними файлами змін.&lt;/p&gt;

&lt;h3 id=&quot;процес-реплікації&quot;&gt;Процес реплікації&lt;/h3&gt;

&lt;p&gt;Кожен файл змін містить має додатковий файл з метаданими про сам файл змін — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-planet-osm-state-txt.png&quot; alt=&quot;state.txt&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state.txt&lt;/code&gt; містить інформацію про часовий відбиток та номер в послідовності наборів змін, за цим номером ми можемо отримати сам файл зі змінами (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;717.osc.gz&lt;/code&gt;, &lt;em&gt;2025-05-08 17:01&lt;/em&gt;, 101K). Для зручності, номер набору змін розбивається на триплети &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;006/590/717&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Відомості про найсвіжіший файл набору змін міститься в корені відповідних змін, для хвилинних це — &lt;a href=&quot;https://planet.openstreetmap.org/replication/minute/state.txt&quot;&gt;https://planet.openstreetmap.org/replication/minute/state.txt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Для того щоб наздогнати стан ваших локальних даних з поточними даними, що є в OpenStreetMap, вам потрібно визначити часовий відбиток, що є останнім у вашому локальному дампі. Наприклад, у дампі станом на 24 березня 2025 останній відбиток часу був &lt;em&gt;2025-03-24T00:59:53Z&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-osmium-dump-info.png&quot; alt=&quot;osmium planet damp info&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Тепер коли у вас є цей час вам потрібно знайти відповідний файл змін (денних чи годинних) та його номер. В цьому вам може допомогти цей сервіс:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://replicate-sequences.osm.mazdermind.de/?2013-01-01T10:00:00Z
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;або&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;s2&quot;&gt;&quot;https://replicate-sequences.osm.mazdermind.de/?&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;stat&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%Y&quot;&lt;/span&gt; planet-latest.osm.pbf&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; +&lt;span class=&quot;s2&quot;&gt;&quot;%FT%TZ&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;пошук-файлу-змін-вручну&quot;&gt;Пошук файлу змін вручну&lt;/h3&gt;

&lt;p&gt;Для денних та годинних змін ви також можете скористатись наступним підходом:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Початковий часовий відбиток (останній з файлу дампа) перетворюємо в Unix epoch time&lt;/p&gt;

    &lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2025-03-24T00:59:53Z&quot;&lt;/span&gt; +%s
&lt;span class=&quot;c&quot;&gt;# або явно зазначивши формат часового відбитку&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-j&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%Y-%m-%dT%H:%M:%SZ&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2025-03-24T00:59:53Z&quot;&lt;/span&gt; +%s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;отримуємо час Unix в секундах &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1742777993&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Отримуємо часовий відбиток з останнього &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state.txt&lt;/code&gt; (відповідно для денних та годинних змін) разом з поточним номером в послідовності змін.&lt;/li&gt;
  &lt;li&gt;Обчислюємо різницю в часі (в днях та годинах), отримане значення віднімаємо від номера послідовності та отримуємо посилання для завантаження файлу змін з якого ми почнемо наздоганяти відставання в наших локальних даних.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;DUMP_EPOCH_TS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;2025-03-24T00:59:53Z&quot;&lt;/span&gt; +%s&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;REFERENCE_DATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;wget &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; - https://planet.openstreetmap.org/replication/day/state.txt 2&amp;gt;/dev/null | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;timestamp&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;=&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/T/ /;s/Z//; s/\\//g&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;REFERENCE_SEQ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;wget &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; - https://planet.openstreetmap.org/replication/day/state.txt 2&amp;gt;/dev/null | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;sequenceNumber&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;cut&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;=&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f2&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;LAST_EPOCH_TS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$REFERENCE_DATE&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; +%s&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Тут приклад для обчислення різниці в днях 86400 сек = 24 год&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Замініть на 3600 для обчислення різниці в годинах&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;DIFF_TS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$LAST_EPOCH_TS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; DUMP_EPOCH_TS&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;86400&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;TARGET_SEQ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;#$REFERENCE_SEQ - $DIFF_TS) + 1 ))&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;seq_padded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%09d&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$TARGET_SEQ&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://planet.openstreetmap.org/replication/day/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seq_padded&lt;/span&gt;:0:3&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seq_padded&lt;/span&gt;:3:3&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;seq_padded&lt;/span&gt;:6:3&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.state.txt&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Змінна &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$url&lt;/code&gt; міститиме посилання на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state.txt&lt;/code&gt; файл для початку процесу реплікації. Цей файл потрібен для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;osmosis&lt;/code&gt; чи подібного інструменту. (Докладніше про сам процес реплікації даних дивіться: &lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Planet.osm/diffs&quot;&gt;https://wiki.openstreetmap.org/wiki/Planet.osm/diffs&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Описаний вище підхід добре підходить для файлів денних та годинних змін, однак не спрацьовує для хвилинних змін.&lt;/p&gt;

&lt;h3 id=&quot;проблеми-з-обчисленням-хвилинних-змін&quot;&gt;Проблеми з обчисленням хвилинних змін&lt;/h3&gt;

&lt;p&gt;Описаний вище підхід для обчислення номера файлу змін для початку реплікації не працює для хвилинних змін. Чому? Тому що хвилинні зміни можуть створюватись більше ніж 60 секунд, через це відбувається “проковзування” в часовому ряді.&lt;/p&gt;

&lt;p&gt;Так, якщо взяти діапазон хвилинних змін 6590000 — 6590227, отримаємо 233 хвилини та всього 227 файлів з хвилинними змінами.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/05/2025-05-08-osm-minutes-diffs.png&quot; alt=&quot;osm-diffs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;На графіках ви можете побачити, скільки часу було витрачено на створення хвилинних змін, та в які хвилини вони були створені. Тут ми бачимо, що о 7:59, 8:02 та 8:05 змін не було створено, відбулося “проковзування”&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Sequence&lt;/th&gt;
      &lt;th&gt;Timestamp&lt;/th&gt;
      &lt;th&gt;Epoch,&lt;br /&gt;seconds&lt;/th&gt;
      &lt;th&gt;Epoch,&lt;br /&gt;minutes&lt;/th&gt;
      &lt;th&gt;Time, &lt;br /&gt; sec&lt;/th&gt;
      &lt;th&gt;Clock&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;194&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:12:08&lt;/td&gt;
      &lt;td&gt;1746681128&lt;/td&gt;
      &lt;td&gt;29111352&lt;/td&gt;
      &lt;td&gt;65&lt;/td&gt;
      &lt;td&gt;8:12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;193&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:11:05&lt;/td&gt;
      &lt;td&gt;1746681065&lt;/td&gt;
      &lt;td&gt;29111351&lt;/td&gt;
      &lt;td&gt;63&lt;/td&gt;
      &lt;td&gt;8:11&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;192&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:10:01&lt;/td&gt;
      &lt;td&gt;1746681001&lt;/td&gt;
      &lt;td&gt;29111350&lt;/td&gt;
      &lt;td&gt;64&lt;/td&gt;
      &lt;td&gt;8:10&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;191&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:09:01&lt;/td&gt;
      &lt;td&gt;1746680941&lt;/td&gt;
      &lt;td&gt;29111349&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
      &lt;td&gt;8:09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;190&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:08:01&lt;/td&gt;
      &lt;td&gt;1746680881&lt;/td&gt;
      &lt;td&gt;29111348&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
      &lt;td&gt;8:08&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;189&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:07:02&lt;/td&gt;
      &lt;td&gt;1746680822&lt;/td&gt;
      &lt;td&gt;29111347&lt;/td&gt;
      &lt;td&gt;59&lt;/td&gt;
      &lt;td&gt;8:07&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;188&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 &lt;strong&gt;8:06:00&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1746680760&lt;/td&gt;
      &lt;td&gt;29111346&lt;/td&gt;
      &lt;td&gt;62&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;8:06&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;187&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:04:59&lt;/td&gt;
      &lt;td&gt;1746680699&lt;/td&gt;
      &lt;td&gt;29111344&lt;/td&gt;
      &lt;td&gt;61&lt;/td&gt;
      &lt;td&gt;8:04&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;186&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:04:00&lt;/td&gt;
      &lt;td&gt;1746680640&lt;/td&gt;
      &lt;td&gt;29111344&lt;/td&gt;
      &lt;td&gt;59&lt;/td&gt;
      &lt;td&gt;8:04&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;185&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 &lt;strong&gt;8:03:00&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1746680580&lt;/td&gt;
      &lt;td&gt;29111343&lt;/td&gt;
      &lt;td&gt;60&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;8:03&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;184&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:01:59&lt;/td&gt;
      &lt;td&gt;1746680519&lt;/td&gt;
      &lt;td&gt;29111341&lt;/td&gt;
      &lt;td&gt;61&lt;/td&gt;
      &lt;td&gt;8:01&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;183&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 8:00:58&lt;/td&gt;
      &lt;td&gt;1746680458&lt;/td&gt;
      &lt;td&gt;29111340&lt;/td&gt;
      &lt;td&gt;61&lt;/td&gt;
      &lt;td&gt;8:00&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;182&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 &lt;strong&gt;8:00:00&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1746680400&lt;/td&gt;
      &lt;td&gt;29111340&lt;/td&gt;
      &lt;td&gt;58&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;8:00&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;181&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 7:58:56&lt;/td&gt;
      &lt;td&gt;1746680336&lt;/td&gt;
      &lt;td&gt;29111338&lt;/td&gt;
      &lt;td&gt;64&lt;/td&gt;
      &lt;td&gt;7:58&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;6590&lt;strong&gt;180&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2025-05-08 7:57:51&lt;/td&gt;
      &lt;td&gt;1746680271&lt;/td&gt;
      &lt;td&gt;29111337&lt;/td&gt;
      &lt;td&gt;65&lt;/td&gt;
      &lt;td&gt;7:57&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;В цій таблиці також видно, що впродовж 15 хвилин відбулося 3 “проковзування”.&lt;/p&gt;

&lt;p&gt;Чи є це критичним для повсякденного використання? Раз таке відбувається вже давно то схоже, що — ні. Наявні інструменти обробки змін цим не переймаються. Так, це може викликати трохи надлишкового трафіку, якщо для хвилинних змін ви відступите трохи далі в минуле ніж треба, однак це не вплине на фінальний результат. Ви отримаєте власний набір даних, що буде синхронізований з даними OpenStreetMap з відставанням до 2 хвилин.&lt;/p&gt;

&lt;h3 id=&quot;чи-можна-це-виправити-можливо&quot;&gt;Чи можна це виправити? Можливо&lt;/h3&gt;

&lt;p&gt;Обробка даних з привʼязкою в часі завжди є нетривіальним завданням. Для виправлення цього стану:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;В метадані наборів змін в полі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timestamp&lt;/code&gt; потрібно вказувати час &lt;strong&gt;початку&lt;/strong&gt; (момент в часі на який припав початок створення набору змін), цей час в ідеалі має бути цілим (чи дуже близьким до цього) часом у хвилинах, годинах та днях:
    &lt;ul&gt;
      &lt;li&gt;13:00:00, 13:01:00, 13:02:00 — для хвилин;&lt;/li&gt;
      &lt;li&gt;14:00:00, 15:00:00 — для годин; та&lt;/li&gt;
      &lt;li&gt;00:00:00 — для днів.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Часом завершення процесу створення змін буде час модифікації самого файлу, хоча його можна також вказати в метаданих набору змін (це той час, який зараз міститься в &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timestamp&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Створення наступного набору змін має починатись в обумовлений час незалежно від того чи завершилось завдання зі створення попереднього набору змін. Певний час вони можуть виконуватись паралельно.&lt;/li&gt;
  &lt;li&gt;У разі відсутності змін — створювати пустий файл набору змін для підтримання монотонного порядку нумерування змін, щоб не було пропусків в часовому ряді.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Якщо вам дошкуляє такий стан справ, ви можете подати пропозиції щодо його виправлення супровідникам проєкту, можливо це буде виправлено. Як швидко? Важко сказати.&lt;/p&gt;

&lt;h2 id=&quot;підсумки&quot;&gt;Підсумки&lt;/h2&gt;

&lt;p&gt;Файли змін залишаються надзвичайно цінним інструментом для підтримки актуальності даних OpenStreetMap. Однак важливо розуміти їхні обмеження та не сприймати назву буквально, особливо це стосується хвилинних змін.&lt;/p&gt;

&lt;p&gt;Для розробників, які використовують хвилинні файли змін, це означає необхідність мати надійну інфраструктуру для їхньої обробки, враховувати можливі затримки та бути готовими до обробки великих обсягів даних у періоди активного редагування.&lt;/p&gt;

&lt;p&gt;А для звичайних користувачів це просто цікавий факт про те, як працює “за лаштунками” улюблена ними відкрита мапа. Наступного разу, коли ви внесете невелику правку і не побачите її на своєму сервері з мапою миттєво, не хвилюйтеся — можливо, ваш “хвилинний” файл просто ще в дорозі!&lt;/p&gt;

&lt;p&gt;Що ви думаєте про хвилинні зміни? Чи стикалися ви з затримками в оновленні даних? Поділіться своїм досвідом у коментарях!&lt;/p&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="OpenStreetMap"/>
        <category term="Diffs"/>
        <category term="Replication"/>
        <category term="Data"/>
        <summary type="html">OpenStreetMap — це чудовий приклад проєкту зі спільного створення відкритої бази даних геопросторової інформації. Накопичення інформації це тільки частина справи. Її збирають для того, щоб нею можна було користуватись. Ви можете отримати дані для поточної ділянки на мапі скориставшись меню Експорт.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Доступ до файлової системи хосту для Persistent Volume в Kind</title>
      <link href="https://blog.andygol.co.ua/uk/2025/04/05/%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%B0-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-%D1%85%D0%BE%D1%81%D1%82%D1%83-%D1%8F%D0%BA-pv/" rel="alternate" type="text/html" title="Доступ до файлової системи хосту для  Persistent Volume в Kind"/>
      <published>2025-04-05T08:30:00+00:00</published>
      <updated>2025-04-05T08:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2025/04/05/%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%B0-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-%D1%85%D0%BE%D1%81%D1%82%D1%83-%D1%8F%D0%BA-pv</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2025/04/05/%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%D0%B0-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0-%D1%85%D0%BE%D1%81%D1%82%D1%83-%D1%8F%D0%BA-pv/">
        &lt;p&gt;Кубернетіс вже перестає бути чимось таким з чим працюють лише інженери платформи. Все більше і більше застосунків загортаються в контейнери та запускаються в контейнерних середовищах. Що робити, якщо з певних причин у вас немає доступу до хмарної платформи, але треба вести розробку застосунку, який працюватиме в хмарі? Ви можете скористатись &lt;a href=&quot;https://kind.sigs.k8s.io&quot;&gt;Kind&lt;/a&gt; для локального розгортання &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/overview/&quot;&gt;Кубернетіс&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://kind.sigs.k8s.io&quot;&gt;Kind&lt;/a&gt; — це інструмент для запуску локальних кластерів Kubernetes з використанням «вузлів» контейнерів Docker. В першу чергу kind був розроблений для тестування самого Kubernetes, але може бути використаний для локальної розробки або CI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Для запуску Kind вам знадобиться &lt;a href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt;, &lt;a href=&quot;https://podman.io/&quot;&gt;podman&lt;/a&gt; або інший рушій для роботи з контейнерами. Ви можете звернутись до &lt;a href=&quot;https://kind.sigs.k8s.io/docs/user/quick-start/&quot;&gt;Швидкого початку роботи з Kind&lt;/a&gt; на офіційному сайті.&lt;/p&gt;

&lt;h2 id=&quot;створення-кластера&quot;&gt;Створення кластера&lt;/h2&gt;

&lt;p&gt;Отже, ми маємо встановлений Kind та середовище для роботи з контейнерами, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; – &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/reference/kubectl/&quot;&gt;інструмент командного рядка&lt;/a&gt; для виконання маніпуляцій з кластером.&lt;/p&gt;

&lt;p&gt;Для створення локального кластера скористаємось командою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind create cluster&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/04/2025-04-05-kind-create-cluster.png&quot; alt=&quot;kind create cluster&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ви завжди можете отримати потрібну довідку скориставшись командою виду &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind [command] --help&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2025/04/2025-04-05-kind-help.png&quot; alt=&quot;kind --help&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Ми створили свій локальний кластер з іменем &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind-kind&lt;/code&gt;. Це стандартне імʼя, яке використовує kind, якщо параметр &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n назва&lt;/code&gt; або &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name назва&lt;/code&gt; не вказано.&lt;/p&gt;

&lt;p&gt;Крім використання ключів для &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kind&lt;/code&gt; ми можемо використати маніфест для створення кластера з потрібними нам параметрами.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: my-super-cluster
nodes:
- role: control-plane
- role: worker
  extraMounts:
  - hostPath: /path/to/local/data
    containerPath: /data
# - role: worker
# - role: worker
#   extraMounts:
#   - hostPath: /path/to/local/data/dump
#     containerPath: /data/dump
#   - hostPath: /path/to/local/data/diff
#     containerPath: /data/diff
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;☝️ тут ми можемо зазначити кількість потрібних вузлів, їх роль та, головне, в нашому випадку, — шлях у локальній файловій системі, який ми будемо монтувати у вузли нашого кластера та використовувати, як систему зберігання для наших Постійних Томів (Persistent Volumes). Див &lt;a href=&quot;https://kind.sigs.k8s.io/docs/user/configuration/#extra-mounts&quot;&gt;Extra Mounts&lt;/a&gt; в документації Kind.&lt;/p&gt;

&lt;p&gt;Застосуємо нашу конфігурацію для створення кластера
&lt;a name=&quot;create-cluster&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kind create cluster &lt;span class=&quot;nt&quot;&gt;--config&lt;/span&gt; kind-config.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;Creating cluster &quot;kind&quot; ...
 ✓ Ensuring node image (kindest/node:v1.32.2) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to &quot;kind-my-super-cluster&quot;
You can now use your cluster with:

kubectl cluster-info --context kind-my-super-cluster

&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#&lt;/span&gt;community 🙂
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Перевіримо, що файлову систему хосту змонтовано у вузол worker нашого кластера.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container inspect osm-cluster-worker &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  | jq &lt;span class=&quot;s1&quot;&gt;&apos;[{&quot;Name&quot;: .[0].Name,
          &quot;BindMounts&quot;: (
            .[] |
            .Mounts[] |
            select(.Type == &quot;bind&quot;)
        )}]&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;І бачимо, що все ОК, файлову систему змонтовано.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/my-super-cluster-worker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;BindMounts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bind&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/host_mnt/path/to/local/data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Destination&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Mode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;RW&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Propagation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rprivate&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/my-super-cluster-worker&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;BindMounts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;bind&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/lib/modules&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Destination&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/lib/modules&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Mode&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ro&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;RW&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Propagation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;rprivate&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;створення-persistentvolume-та-persistentvolumeclaim&quot;&gt;Створення PersistentVolume та PersistentVolumeClaim&lt;/h2&gt;

&lt;p&gt;Створимо маніфест для Постійного Тому&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PersistentVolume&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-super-cluster-pv&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;100Gi&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;accessModes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;volumeMode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Filesystem&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;hostPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/data&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;storageClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-storageclass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;А також створимо Заявку PersistentVolumeClaim яку будемо використовувати для монтування Постійного Тому в робочі навантаження.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;my-super-cluster-pvc&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;accessModes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;100Gi&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;storageClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-storageclass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тепер найголовніше 🥁, потрібно створити StorageClass, який дозволить нам явно повʼязати Заявку PVC з Постійним Томом PV.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Примітка:&lt;/strong&gt; Зверніть увагу що kind &lt;a href=&quot;#create-cluster&quot;&gt;створює&lt;/a&gt; стандартний StorageClass під час створення кластера. Однак цей StorageClass не задовольняє нашим вимогам маючи &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reclaimPolicy:Delete&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;NAME                 PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
standard (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  80m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Це означає, що вміст нашого Постійного Тому буде очищатись після його розмонтування з пода, а це не те що нам треба.&lt;/p&gt;

&lt;p&gt;Створимо наш StorageClass в кластері.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; -  &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: my-storageclass
provisioner: rancher.io/local-path
parameters:
  nodePath: /data
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;storageclass.storage.k8s.io/my-storageclass created
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Перевіримо наш StorageClass.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;NAME                 PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
my-storageclass      rancher.io/local-path   Retain          WaitForFirstConsumer   false                  5m27s
standard (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  91m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;І зробимо його типовим, на про всяк випадок&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl patch storageclass my-storageclass &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;metadata&quot;: {&quot;annotations&quot;:{&quot;storageclass.kubernetes.io/is-default-class&quot;:&quot;true&quot;}}}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;а StorageClass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;standard&lt;/code&gt; навпаки, зробимо звичайним.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl patch storageclass standard &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;metadata&quot;: {&quot;annotations&quot;:{&quot;storageclass.kubernetes.io/is-default-class&quot;:&quot;false&quot;}}}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Переглянемо поточні відомості про StorageClass&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get storageclass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;NAME                        PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
my-storageclass (default)   rancher.io/local-path   Retain          WaitForFirstConsumer   false                  12m
standard                    rancher.io/local-path   Delete          WaitForFirstConsumer   false                  98m
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;використання-persistentvolumeclaim-в-поді&quot;&gt;Використання PersistentVolumeClaim в поді&lt;/h2&gt;

&lt;p&gt;Застосуємо маніфести PV та PVC в кластері.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; pv.yaml &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; pvc.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Створимо под, який використовує Заявку на постійний том для зберігання даних.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl apply &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; - &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
apiVersion: v1
kind: Pod
metadata:
  name: debug-pod
spec:
  containers:
  - name: debug-container
    image: busybox:latest
    command: [&quot;sh&quot;, &quot;-c&quot;, &quot;sleep 3600&quot;]
    volumeMounts:
    - mountPath: &quot;/data&quot;
      name: my-super-cluster
  volumes:
  - name: my-super-cluster
    persistentVolumeClaim:
      claimName: my-super-cluster-pvc
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Перевіримо що Заявка PVC має привʼязку до PV та використовується в нашому тестовому поді&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get pv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;NAME                  CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                          STORAGECLASS      VOLUMEATTRIBUTESCLASS   REASON   AGE
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;my-super-cluster-pv   100Gi      RWO            Retain           Bound    default/my-super-cluster-pvc   my-storageclass   &amp;lt;unset&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                          &lt;/span&gt;9m20s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl get pvc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;NAME                   STATUS   VOLUME                CAPACITY   ACCESS MODES   STORAGECLASS      VOLUMEATTRIBUTESCLASS   AGE
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;my-super-cluster-pvc   Bound    my-super-cluster-pv   100Gi      RWO            my-storageclass   &amp;lt;unset&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;                 &lt;/span&gt;8m48s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Зверніть увагу що статус PV та PVC має значення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bound&lt;/code&gt;, що означає що Заявку було успішно звʼязано з Постійним Томом.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl describe pod
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;Name:             debug-pod
Namespace:        default
Priority:         0
Service Account:  default
Node:             kind-control-plane/172.20.0.4
Start Time:       Fri, 04 Apr 2025 18:17:09 +0300
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;Labels:           &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;Annotations:      &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Status:           Running
IP:               10.244.0.5
IPs:
  IP:  10.244.0.5
Containers:
  debug-container:
    Container ID:  containerd://d030a6edfc13c314853f22efc505990bbbb8e3954ed1c9887b9c7b3be575a0be
    Image:         busybox:latest
    Image ID:      docker.io/library/busybox@sha256:37f7b378a29ceb4c551b1b5582e27747b855bbfaa73fa11914fe0df028dc581f
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;    Port:          &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;    Host Port:     &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;    Command:
      sh
      -c
      sleep 3600
    State:          Running
      Started:      Fri, 04 Apr 2025 18:17:13 +0300
    Ready:          True
    Restart Count:  0
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;    Environment:    &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;    Mounts:
      /data from my-super-cluster (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-5wdzj (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True
  Initialized                 True
  Ready                       True
  ContainersReady             True
  PodScheduled                True
Volumes:
  my-super-cluster:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  my-super-cluster-pvc
    ReadOnly:   false
  kube-api-access-5wdzj:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;    ConfigMapOptional:       &amp;lt;nil&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;    DownwardAPI:             true
QoS Class:                   BestEffort
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;Node-Selectors:              &amp;lt;none&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;go&quot;&gt;Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  8m36s  default-scheduler  Successfully assigned default/debug-pod to kind-control-plane
  Normal  Pulling    8m36s  kubelet            Pulling image &quot;busybox:latest&quot;
  Normal  Pulled     8m32s  kubelet            Successfully pulled image &quot;busybox:latest&quot; in 3.395s (3.395s including waiting). Image size: 1855985 bytes.
  Normal  Created    8m32s  kubelet            Created container: debug-container
  Normal  Started    8m32s  kubelet            Started container debug-container
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Наш под було успішно створено і він працює.&lt;/p&gt;

&lt;p&gt;Отримаємо доступ до термінала в нашому поді та перевіримо, що том змонтований у нашій файловій системі і все працює належним чином.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; debug-pod &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gp&quot;&gt;/ #&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; / | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;data
&lt;span class=&quot;go&quot;&gt;drwxr-xr-x    2 root     root          4096 Apr  4 15:17 data
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;/ #&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;touch&lt;/span&gt; /data/somefile.txt
&lt;span class=&quot;gp&quot;&gt;/ #&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; /data
&lt;span class=&quot;go&quot;&gt;total 0
-rw-r--r--    1 root     root             0 Apr  4 15:31 somefile.txt
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;/ #&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gp&quot;&gt;/ #&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тепер перегляньте файлову систему хосту змонтовану у вузол worker нашого кластера і ви побачите там тільки що створений файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;somefile.txt&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;підсумки&quot;&gt;Підсумки&lt;/h2&gt;

&lt;p&gt;Ми створили Заявку на використання Постійного Тому в робочому навантажені, яка використовує Клас Зберігання (StorageClass) для звʼязування Заявки з Томом. Постійний том використовує систему зберігання наявну на вузлі нашого кластера. Система зберігання вузла кластера базується на файловій системі хосту, на якому розгорнуто наш кластер.&lt;/p&gt;

&lt;p&gt;У такий спосіб ми можемо надійно зберігати та повторно використовувати дані розміщені в Постійному Томі в робочих навантаження нашого кластера, які за своєю природою мають обмежений &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/workloads/pods/pod-lifecycle/&quot;&gt;життєвий цикл&lt;/a&gt;. Окрім цього ми також можемо передавати в наші робочі навантаження попередньо створені дані з нашого хосту та використовувати їх в подах.&lt;/p&gt;

&lt;h2 id=&quot;очищення&quot;&gt;Очищення&lt;/h2&gt;

&lt;p&gt;Для очищення (вилучення) кластера скористайтесь наступною командою.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kind delete cluster &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; kind-my-super-cluster
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-console highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;go&quot;&gt;Deleting cluster &quot;kind-my-super-cluster&quot; ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Зачекайте допоки Kind видаліть кластер. За потреби видаліть створені файли у файловій системі хосту.&lt;/p&gt;

&lt;h2 id=&quot;додаткові-матеріали&quot;&gt;Додаткові матеріали&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://kind.sigs.k8s.io/docs/user/quick-start/&quot;&gt;Kind Quick Start&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://mauilion.dev/posts/kind-pvc/&quot;&gt;Kind Persistent Volumes&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/rancher/local-path-provisioner#storage-classes&quot;&gt;Rancher Local Path Provisioner&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/storage/volumes/&quot;&gt;Томи&lt;/a&gt;, &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/storage/persistent-volumes/&quot;&gt;Постійні Томи&lt;/a&gt;, &lt;a href=&quot;https://andygol-k8s.netlify.app/uk/docs/concepts/storage/storage-classes/&quot;&gt;Класи сховищ&lt;/a&gt; в Кубернетіс&lt;/li&gt;
&lt;/ul&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="Kubernetes"/>
        <category term="Persisten Volumes"/>
        <category term="Node"/>
        <category term="Host"/>
        <summary type="html">Кубернетіс вже перестає бути чимось таким з чим працюють лише інженери платформи. Все більше і більше застосунків загортаються в контейнери та запускаються в контейнерних середовищах. Що робити, якщо з певних причин у вас немає доступу до хмарної платформи, але треба вести розробку застосунку, який працюватиме в хмарі? Ви можете скористатись Kind для локального розгортання Кубернетіс.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Розгортаємо контейнер з приватного реєстру ghcr.io в Amazon ECS</title>
      <link href="https://blog.andygol.co.ua/uk/2024/06/26/%D1%80%D0%BE%D0%B7%D0%B3%D0%BE%D1%80%D1%82%D0%B0%D1%94%D0%BC%D0%BE-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80-%D0%B7-%D0%BF%D1%80%D0%B8%D0%B2%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE-%D1%80%D0%B5%D1%94%D1%81%D1%82%D1%80%D1%83-ghcr-%D0%B2-ecs/" rel="alternate" type="text/html" title="Розгортаємо контейнер з приватного реєстру ghcr.io в Amazon ECS"/>
      <published>2024-06-26T08:30:00+00:00</published>
      <updated>2024-06-26T08:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2024/06/26/%D1%80%D0%BE%D0%B7%D0%B3%D0%BE%D1%80%D1%82%D0%B0%D1%94%D0%BC%D0%BE-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80-%D0%B7-%D0%BF%D1%80%D0%B8%D0%B2%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE-%D1%80%D0%B5%D1%94%D1%81%D1%82%D1%80%D1%83-ghcr-%D0%B2-ecs</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2024/06/26/%D1%80%D0%BE%D0%B7%D0%B3%D0%BE%D1%80%D1%82%D0%B0%D1%94%D0%BC%D0%BE-%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%B9%D0%BD%D0%B5%D1%80-%D0%B7-%D0%BF%D1%80%D0%B8%D0%B2%D0%B0%D1%82%D0%BD%D0%BE%D0%B3%D0%BE-%D1%80%D0%B5%D1%94%D1%81%D1%82%D1%80%D1%83-ghcr-%D0%B2-ecs/">
        &lt;p&gt;У кожного настає такий момент, коли вам потрібно розгорнути контейнер з приватного репозиторію на ECS. Документація від Amazon не є взірцем зрозумілості, тому я підготував для вас (в першу чергу для себе) опис того, що для цього потрібно зробити.&lt;/p&gt;

&lt;h2 id=&quot;передумови&quot;&gt;Передумови&lt;/h2&gt;

&lt;h3 id=&quot;контейнер-в-ghcrio&quot;&gt;Контейнер в ghcr.io&lt;/h3&gt;

&lt;p&gt;Для початку, вам потрібно завантажити контейнер на &lt;a href=&quot;https://ghcr.io/&quot;&gt;GitHub Container Registry&lt;/a&gt;. Це може бути як публічний, так і приватний репозиторій.&lt;/p&gt;

&lt;p&gt;Входите в GitHub і переходите на сторінку репозиторію, в якому знаходиться ваш контейнер. В розділі &lt;strong&gt;Packages&lt;/strong&gt; обираєте артефакт, який є створеним раніше контейнером.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-container.png&quot; alt=&quot;GitHub Container Registry&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Для доступу до вашого реєстру вам знадобиться токен доступу Private Access Token. Для створення PAT перейдіть в налаштування вашого облікового запису.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-profile-settings.png&quot; alt=&quot;GitHub Profile Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Внизу сторінки оберіть &lt;strong&gt;Developer settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-profile-dev-settings.png&quot; alt=&quot;GitHub Developer Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Далі, виберіть &lt;strong&gt;Personal access tokens&lt;/strong&gt;/&lt;strong&gt;Tokens (classic)&lt;/strong&gt;/&lt;strong&gt;Generate new token (classic)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-profile-token-classic.png&quot; alt=&quot;GitHub Personal Access Tokens&quot; /&gt;&lt;/p&gt;

&lt;p&gt;або, перейдіть за цим посиланням для створення PAT — &lt;a href=&quot;https://github.com/settings/tokens/new&quot;&gt;https://github.com/settings/tokens/new&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Додайте за потреби Примітку, оберіть час дії токена (через скільки днів він перестане бути валідним), та вкажіть потрібні права доступу – в цьому випадку нам потрібно лише мати права на отримання пакетів (&lt;strong&gt;read:packages&lt;/strong&gt; — Download packages from GitHub Package Registry). Внизу сторінки натисніть кнопку &lt;strong&gt;Generate token&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-profile-new-token.png&quot; alt=&quot;GitHub New Personal Access Token&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Після цього ви побачите сторінку з вашим новим токеном. Скопіюйте його та збережіть у безпечному місці. Ми будемо його використовувати для доступу до реєстру.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-profile-created-token.png&quot; alt=&quot;GitHub Personal Access Token&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;ecs&quot;&gt;ECS&lt;/h3&gt;

&lt;p&gt;У вас має бути налаштований обліковий запис в AWS, за бажанням локально встановлений &lt;a href=&quot;https://aws.amazon.com/cli/&quot;&gt;AWS CLI&lt;/a&gt; та &lt;a href=&quot;https://github.com/aws/amazon-ecs-cli&quot;&gt;Amazon ECS CLI&lt;/a&gt;. Якщо ви не бажаєте встановлювати їх локально, ви можете використовувати &lt;a href=&quot;https://aws.amazon.com/cloudshell/&quot;&gt;AWS CloudShell&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;створення-cmk-в-aws-kms&quot;&gt;Створення CMK в AWS KMS&lt;/h2&gt;

&lt;p&gt;Для початку, нам потрібно створити &lt;a href=&quot;https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#kms_keys&quot;&gt;CMK&lt;/a&gt; (Customer Master Key) та аліасу для нього в &lt;a href=&quot;https://aws.amazon.com/kms/&quot;&gt;AWS KMS&lt;/a&gt;. CMK використовується &lt;a href=&quot;https://docs.aws.amazon.com/kms/latest/developerguide/services-secrets-manager.html&quot;&gt;AWS Secret Manager&lt;/a&gt; для &lt;a href=&quot;https://en.wikipedia.org/wiki/Hybrid_cryptosystem#Envelope_encryption&quot;&gt;шифрування конвертів&lt;/a&gt;, даних які містять чутливу інформацію. Аліас виступає як назва для вашого CMK, її простіше запамʼятати та використовувати ніж сам ідентифікатор ключа. Також ви можете використовувати аліас у вашому коді. Ви можете змінювати ключ у майбутньому (на інший), залишаючи аліас незмінним.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws kms create-key &lt;span class=&quot;nt&quot;&gt;--query&lt;/span&gt; KeyMetadata.Arn &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;у відповідь ви отримаєте ідентифікатор вашого ключа, у вигляді Amazon Resource Name (&lt;a href=&quot;https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html&quot;&gt;ARN&lt;/a&gt;) наприклад:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;arn:aws:kms:eu-central-1:123456789012:key/abc123de-4567-89fa-0bcd-efgh12345678
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Тепер ми створимо аліас для нашого ключа:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws kms create-alias &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--alias-name&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt;/ecs-ghcr &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--target-key-id&lt;/span&gt; arn:aws:kms:eu-central-1:123456789012:key/abc123de-4567-89fa-0bcd-efgh12345678
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Якщо ви не налаштовували собі локально AWS CLI, ви можете скористатись CloudShell та виконувати всі команди в командному рядку там.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-cloudshel.png&quot; alt=&quot;AWS CloudShell&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Створення CMK ви можете зробити в Консолі AWS, перейшовши в &lt;a href=&quot;https://eu-central-1.console.aws.amazon.com/kms/home?region=eu-central-1#/kms/keys&quot;&gt;AWS KMS&lt;/a&gt; та натиснувши кнопку &lt;strong&gt;Create key&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/ghcr-to-ecs-kms-create-key.png&quot; alt=&quot;AWS KMS Create Key&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ARN CMK вам знадобиться при створенні документа політики довіри на наступному кроці.&lt;/p&gt;

&lt;h2 id=&quot;створення-secret-в-aws-secrets-manager&quot;&gt;Створення Secret в AWS Secrets Manager&lt;/h2&gt;

&lt;p&gt;На цьому етапі нам потрібно створити Secret, який буде містити ваш логін на пароль (код доступу) зашифровані за допомогою CMK для витягування образу вашого контейнера з приватного реєстру.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws secretsmanager create-secret &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; ghcr_io_pat &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--description&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Secret to get packages from ghcr.io&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--kms-key-id&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt;/ecs-ghcr &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--secret-string&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{&quot;username&quot;:&quot;your_nickname&quot;, &quot;password&quot;:&quot;ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&quot;}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;У відповідь ви маєте отримати наступне:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ARN&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;arn:aws:secretsmanager:eu-central-1:123456789012:secret:ghcr_io_pat-abcdEF&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ghcr_io_pat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;VersionId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;4b43b832-df4c-48b3-b59a-bb18287e6c15&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;ARN секрету повинен бути у виводі ☝️ попередньої команди – поле ARN. Вам потрібно буде посилатися на цей ARN під час створення документа політики довіри на наступному кроці.&lt;/p&gt;

&lt;h2 id=&quot;створення-ролі-в-iam-для-виконання-завдання&quot;&gt;Створення ролі в IAM для виконання завдання&lt;/h2&gt;

&lt;p&gt;У випадку наявності у вас ролі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecsTaskExecutionRole&lt;/code&gt; можете пропустити цей крок.&lt;/p&gt;

&lt;p&gt;Спочатку вам потрібно буде створити документ політики довіри, щоб вказати виконавця, який візьме на себе роль, що в цьому випадку є ECS task:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; ecs-trust-policy.json
{
    &quot;Version&quot;: &quot;2012-10-17&quot;,
    &quot;Statement&quot;: [
        {
            &quot;Effect&quot;: &quot;Allow&quot;,
            &quot;Principal&quot;: {
                &quot;Service&quot;: &quot;ecs-tasks.amazonaws.com&quot;
            },
            &quot;Action&quot;: &quot;sts:AssumeRole&quot;
        }
    ]
}
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Створимо роль скориставшись AWS CLI та файлом &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-trust-policy.json&lt;/code&gt; з описом ролі.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws iam create-role &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; ecsTaskExecutionRole &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--assume-role-policy-document&lt;/span&gt; file://ecs-trust-policy.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Щоб додати основні дозволи до інших ресурсів сервісів AWS, які необхідні для запуску завдань Amazon ECS, прикріпіть політику ролі виконання завдань AWS ECS до новоствореної ролі:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws iam attach-role-policy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; ecsTaskExecutionRole &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--policy-arn&lt;/span&gt; arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тепер створимо документ політики дозволів, який дозволяє завданню ECS розшифрувати та отримати секрет, створений в AWS Secrets Manager.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; ecs-secret-permission.json 
{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [
        &quot;kms:Decrypt&quot;,
        &quot;secretsmanager:GetSecretValue&quot;
      ],
      &quot;Resource&quot;: [
        &quot;arn:aws:secretsmanager:eu-central-1:123456789012:secret:ghcr_io_pat-abcdEF&quot;,
        &quot;arn:aws:kms:eu-central-1:123456789012:key/abc123de-4567-89fa-0bcd-efgh12345678&quot;
      ]
    }
  ]
}
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;☝️ вкажіть свої Secret та CMK в значення &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Resource&quot;: [ &amp;lt;Secret&amp;gt;, &amp;lt;CMK&amp;gt; ]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Нарешті, додайте інтегровану політику дозволів, яка дозволить вашій задачі отримувати імʼя користувача та пароль до ghcr.io з AWS Secrets Manager. Зверніть увагу, що ви посилаєтеся на документ політики дозволів, створений на попередньому кроці. Змініть шлях до теки за необхідності, щоб визначити правильне розташування файлу:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws iam put-role-policy &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--role-name&lt;/span&gt; ecsTaskExecutionRole &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--policy-name&lt;/span&gt; ECS-SecretsManager-Permission &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--policy-document&lt;/span&gt; file://ecs-secret-permission.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;налаштування-ecs-cli-необовʼязково&quot;&gt;Налаштування ECS CLI (необовʼязково)&lt;/h2&gt;

&lt;p&gt;Інструмент командного рядка для Amazon ECS (ESC CLI) надає команди для спрощення створення кластера Amazon ECS та AWS ресурсів, необхідних для його налаштування. Після встановлення ECS CLI ви можете додатково налаштувати ваші AWS облікові дані в іменованому профілі ECS. Профілі зберігаються в файлі &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ecs/credentials&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli configure profile &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--access-key&lt;/span&gt; &amp;lt;AWS_ACCESS_KEY_ID&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--secret-key&lt;/span&gt; &amp;lt;AWS_SECRET_ACCESS_KEY&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--profile-name&lt;/span&gt; &amp;lt;PROFILE_NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ви також можете вказати стандартний профіль який використовувати для всіх команд ECS CLI:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli configure profile default &lt;span class=&quot;nt&quot;&gt;--profile-name&lt;/span&gt; &amp;lt;PROFILE_NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Якщо ви не налаштовуєте профіль ECS або не встановлюєте змінні середовища, буде використано стандартний профіль AWS. Значення ключа доступу та секретного ключа доступу можна переглянути в &lt;a href=&quot;https://console.aws.amazon.com/iam/home?#security_credential&quot;&gt;AWS Management Console&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ви можете додатково налаштувати назву кластера ECS, тип запуску та регіон AWS для використання з ECS CLI за допомогою команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli configure&lt;/code&gt;. Значення змінної &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;LAUNCH_TYPE&amp;gt;&lt;/code&gt; можна встановити у &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FARGATE&lt;/code&gt; або &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EC2&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli configure &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt; &amp;lt;CLUSTER_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--default-launch-type&lt;/span&gt; &amp;lt;LAUNCH_TYPE&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--config-name&lt;/span&gt; &amp;lt;CONFIG_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--region&lt;/span&gt; &amp;lt;AWS_REGION&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ці значення також можна визначити або перевизначити за допомогою прапорців команд, зазначених у наступних кроках.&lt;/p&gt;

&lt;h2 id=&quot;створення-кластера-amazon-ecs&quot;&gt;Створення кластера Amazon ECS&lt;/h2&gt;

&lt;p&gt;Створимо кластер Amazon ECS за допомогою команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli up&lt;/code&gt;, вказавши назву кластера, регіон AWS (наприклад, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eu-central-1&lt;/code&gt;) та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FARGATE&lt;/code&gt; як тип запуску:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli up &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt; &amp;lt;CLUSTER_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--region&lt;/span&gt; eu-central-1 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--launch-type&lt;/span&gt; FARGATE &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Використовуючи тип запуску FARGATE, ми використовуємо AWS Fargate для управління обчислювальними ресурсами від нашого імені, щоб нам не потрібно було вказувати власні екземпляри контейнера EC2. Типово ECS CLI також запустить стек AWS CloudFormation для створення нового VPC з прикріпленим Інтернет-шлюзом, 2 загальнодоступними підмережами та групою безпеки. Ви також можете вказати власні ресурси, використовуючи прапорці в команді вище.&lt;/p&gt;

&lt;h2 id=&quot;налаштування-групи-безпеки&quot;&gt;Налаштування групи безпеки&lt;/h2&gt;

&lt;p&gt;Після успішного створення кластера ECS ви повинні побачити ідентифікатори VPC та підмережі, що відображаються в терміналі. Далі отримайте опис JSON новоствореної групи безпеки та занотуйте ідентифікатор групи безпеки або &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupId&lt;/code&gt;. Замініть змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;VPC_ID&amp;gt;&lt;/code&gt; на ідентифікатор новоствореного VPC.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws ec2 describe-security-groups &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--filters&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;vpc-id,Values&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;lt;VPC_ID&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--region&lt;/span&gt; eu-central-1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;У відповідь ви маєте отримати подібний вивід:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;SecurityGroups&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default VPC security group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;GroupName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;IpPermissions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;IpProtocol&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;IpRanges&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Ipv6Ranges&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;PrefixListIds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;UserIdGroupPairs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;GroupId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;sg-04512c8a7bff9b34e&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;UserId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;123456789012&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;…&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Додайте правило для вхідного трафіку до групи безпеки, що дозволяє HTTP-трафік з будь-якої адреси IPv4. Замініть змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SG_ID&amp;gt;&lt;/code&gt; на &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GroupId&lt;/code&gt;, отриману на попередньому кроці. Це вхідне правило дозволить вам підтвердити, що ваш сервер працює у вашому завданні та що образ з приватного реєстру було успішно отримано з GHCR.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;aws ec2 authorize-security-group-ingress &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--group-id&lt;/span&gt; &amp;lt;SG_ID&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--protocol&lt;/span&gt; tcp &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt; 8080 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cidr&lt;/span&gt; 0.0.0.0/0 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--region&lt;/span&gt; eu-central-1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;створення-service-amazon-ecs&quot;&gt;Створення Service Amazon ECS&lt;/h2&gt;

&lt;p&gt;Service Amazon ECS дозволяє одночасно запускати та підтримувати кілька екземплярів визначення завдання. ECS CLI дозволяє створити Service за допомогою файлу Docker compose. Створіть наступний файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt;, який визначає контейнер, який надає порт 8080 для вхідного трафіку на сервер. Щоб посилатися на образ, який зберігається у вашому приватному реєстрі у GHCR, замініть змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;USER_NAME&amp;gt;&lt;/code&gt; на ваше імʼя користувача GitHub, змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;REPO_NAME&amp;gt;&lt;/code&gt; на імʼя вашого приватного репозиторію та змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;TAG_NAME&amp;gt;&lt;/code&gt; на теґ, який ви використовували.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; docker-compose.yml
version: &quot;3&quot;
services:
    web:
        image: ghcr.io/&amp;lt;USER_NAME&amp;gt;/&amp;lt;REPO_NAME&amp;gt;:&amp;lt;TAG_NAME&amp;gt;
        ports:
            - 8080:8080
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Так, якщо ви спробуєте розгорнути образ з прикладу на початку, значення змінної &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt; буде наступним — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ghcr.io/andygol/switch2osm-mkdocs:main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Вам також потрібно буде створити наступний файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-params.yml&lt;/code&gt;, щоб вказати додаткові параметри для вашого Service, специфічні для Amazon ECS. Зауважте, що поле &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;services&lt;/code&gt; нижче відповідає полю &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;services&lt;/code&gt; у файлі Docker Compose вище, що відповідає назві контейнера для запуску. Коли ECS CLI створює визначення завдання з файлу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt;, поля &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; будуть обʼєднані в визначення контейнера ECS, включаючи образ контейнера, який він буде використовувати, та облікові дані сховища GHCR, які йому знадобляться для доступу до нього. Замініть змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SECRET_ARN&amp;gt;&lt;/code&gt; на ARN секрету AWS Secrets Manager, який ви створили раніше. Замініть змінні &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SUB_1_ID&amp;gt;&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SUB_2_ID&amp;gt;&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;SG_ID&lt;/code&gt;&amp;gt; ідентифікаторами 2 загальнодоступних підмереж та групи безпеки, які були створені разом з кластером ECS.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; &amp;gt; ecs-params.yml
version: 1
task_definition:
  task_execution_role: ecsTaskExecutionRole
  ecs_network_mode: awsvpc
  task_size:
    mem_limit: 0.5GB
    cpu_limit: 256
  services:
    web:
        repository_credentials: 
            credentials_parameter: &quot;&amp;lt;SECRET_ARN&amp;gt;&quot;
run_params:
  network_configuration:
    awsvpc_configuration:
      subnets:
        - &quot;&amp;lt;SUB_1_ID&amp;gt;&quot;
        - &quot;&amp;lt;SUB_2_ID&amp;gt;&quot;
      security_groups:
        - &quot;&amp;lt;SG_ID&amp;gt;&quot;
      assign_public_ip: ENABLED
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Далі створіть Service ECS зі свого файлу compose за допомогою команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli compose service up&lt;/code&gt;. Ця команда шукатиме ваші файли &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-params.yml&lt;/code&gt; у поточній теці. Замініть змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;CLUSTER_NAME&amp;gt;&lt;/code&gt; на імʼя вашого кластера ECS та змінну &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;PROJECT_NAME&amp;gt;&lt;/code&gt; на бажану назву вашого Service ECS.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli compose &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--project-name&lt;/span&gt; &amp;lt;PROJECT_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt; &amp;lt;CLUSTER_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  service up &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--launch-type&lt;/span&gt; FARGATE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Майте трохи часу на розгортання вашого Service ECS. Тепер ви можете перевірити стан вашого Service ECS за допомогою команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli ps&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli compose &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--project-name&lt;/span&gt; &amp;lt;PROJECT_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt; &amp;lt;CLUSTER_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  service ps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Перейшовши до IP-адреси, зазначеної на порту 8080, ви зможете переглянути головну сторінку нашого проєкту, підтверджуючи, що ваше завдання змогло успішно витягнути образ контейнера з реєстру GHCR, використовуючи ваші облікові дані для автентифікації.&lt;/p&gt;

&lt;h2 id=&quot;очищення&quot;&gt;Очищення&lt;/h2&gt;

&lt;p&gt;Припиніть роботу вашого Service ECS за допомогою команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli compose service down&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli compose &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--project-name&lt;/span&gt; &amp;lt;PROJECT_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt; &amp;lt;CLUSTER_NAME&amp;gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  service down
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Видаліть стек AWS CloudFormation, який був створений &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli up&lt;/code&gt;, та пов’язані з ним ресурси за допомогою команди &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ecs-cli down&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ecs-cli down &lt;span class=&quot;nt&quot;&gt;--cluster&lt;/span&gt; &amp;lt;CLUSTER_NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="React"/>
        <category term="GitHub"/>
        <category term="Actions"/>
        <category term="CI/CD"/>
        <summary type="html">У кожного настає такий момент, коли вам потрібно розгорнути контейнер з приватного репозиторію на ECS. Документація від Amazon не є взірцем зрозумілості, тому я підготував для вас (в першу чергу для себе) опис того, що для цього потрібно зробити.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">Розгортаємо застосунок React на GitHub Pages</title>
      <link href="https://blog.andygol.co.ua/uk/2024/06/03/%D1%80%D0%BE%D0%B7%D0%B3%D0%BE%D1%80%D1%82%D0%B0%D1%94%D0%BC%D0%BE-react-app-%D0%BD%D0%B0-github-pages/" rel="alternate" type="text/html" title="Розгортаємо застосунок React на GitHub Pages"/>
      <published>2024-06-03T08:30:00+00:00</published>
      <updated>2024-06-03T08:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2024/06/03/%D1%80%D0%BE%D0%B7%D0%B3%D0%BE%D1%80%D1%82%D0%B0%D1%94%D0%BC%D0%BE-react-app-%D0%BD%D0%B0-github-pages</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2024/06/03/%D1%80%D0%BE%D0%B7%D0%B3%D0%BE%D1%80%D1%82%D0%B0%D1%94%D0%BC%D0%BE-react-app-%D0%BD%D0%B0-github-pages/">
        &lt;p&gt;Тут я покажу вам, як розгорнути застосунок React на GitHub Pages за допомогою GitHub Actions. GitHub Pages — це сервіс хостингу статичних сайтів, який бере HTML, CSS та JavaScript файли безпосередньо з репозиторію на GitHub та робить їх доступними у вигляді вебсайту. GitHub Actions — це сервіс CI/CD, що дозволяє автоматизувати робочий процес публікації вашого статичного сайту на GitHub Pages.&lt;/p&gt;

&lt;h2 id=&quot;створення-застосунку-react&quot;&gt;Створення застосунку React&lt;/h2&gt;

&lt;p&gt;Спочатку створіть новий застосунок React за допомогою Create React App.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx create-react-app react-app &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; typescript
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;react-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ваш git-репозиторій має бути ініціалізований автоматично.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/npx-boilerplating-1.png&quot; alt=&quot;Create React App 1&quot; /&gt;
&lt;img src=&quot;/images/2024/06/npx-boilerplating-2.png&quot; alt=&quot;Create React App 2&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;створення-репозиторію-на-github&quot;&gt;Створення репозиторію на GitHub&lt;/h2&gt;

&lt;p&gt;Перебуваючи у теці з новоствореним застосунком та створіть новий репозиторій на GitHub.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gh repo create
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/gh-repo-create.png&quot; alt=&quot;Create GitHub Repository&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;налаштування-репозиторію&quot;&gt;Налаштування репозиторію&lt;/h2&gt;

&lt;p&gt;Перейдіть до налаштувань репозиторію та увімкніть GitHub Pages. Оберіть джерело для &lt;strong&gt;Build and deployment&lt;/strong&gt; — GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/gh-repo-pages-source.png&quot; alt=&quot;GitHub Pages Settings&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Відкрийте меню &lt;strong&gt;Actions&lt;/strong&gt;/&lt;strong&gt;General&lt;/strong&gt;, прокрутіть до розділу &lt;strong&gt;Workflow permissions&lt;/strong&gt; та надайте дозволи на читання та запис для робочого процесу GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/gh-repo-actions.png&quot; alt=&quot;GitHub Actions Permissions&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;додавання-робочого-процесу-github-actions&quot;&gt;Додавання робочого процесу GitHub Actions&lt;/h2&gt;

&lt;p&gt;Створіть нову теку &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows&lt;/code&gt; у корені вашого репозиторію.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; .github/workflows
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Створіть новий файл з назвою (наприклад) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.yml&lt;/code&gt; у теці &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Додайте назву вашого робочого процесу.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploy React App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Це дозволить вам вирізняти цей робочий процес серед інших.&lt;/p&gt;

&lt;p&gt;Додайте наступний вміст до файлу &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.yml&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;closed&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Цей розділ описує події, що запускають робочий процес. У цьому випадку робочий процес буде запущений при безпосередньому внесенні змін в гілку main за допомогою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;, при виконанні pull request та при ручному запуску робочого процесу.&lt;/p&gt;

&lt;p&gt;Додайте опис завдань до робочого процесу.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Наше завдання з ім’ям &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; буде виконуватись на останній версії Ubuntu.&lt;/p&gt;

&lt;p&gt;Додайте кроки до завдання.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Checkout code&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup Node.js&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;node-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;20&apos;&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Перший крок витягує код з репозиторію. Другий крок налаштовує Node.js.&lt;/p&gt;

&lt;p&gt;Додайте більше кроків до завдання.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;- name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Install dependencies&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm ci&lt;/span&gt;

      &lt;span class=&quot;s&quot;&gt;- name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;npm run build&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Третій крок встановлює залежності, а четвертий крок збирає застосунок React.&lt;/p&gt;

&lt;p&gt;Додайте останні кроки до завдання.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;- name&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Upload artifact&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/upload-pages-artifact@v3&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;github-pages&apos;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;
&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тут ми використовуємо дію &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upload-pages-artifact&lt;/code&gt; для завантаження теки збірки як артефакту для подальшого розгортання.&lt;/p&gt;

&lt;p&gt;Створіть кроки розгортання.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
  &lt;span class=&quot;na&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github.event.pull_request.merged == &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; || github.event_name == &apos;push&apos; || github.event_name == &apos;workflow_dispatch&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;pages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;id-token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;read&lt;/span&gt;

    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github-pages&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ steps.deployment.outputs.page_url }}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Завдання розгортання буде виконуватись тільки якщо pull request обʼєднано з основною гілкою або робочий процес запускається через &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; або вручну. Воно потребує завершення завдання &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt;. Завдання розгортання має дозволи на запис до сторінок, запис токена id та читання вмісту. Ім’я середовища — &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github-pages&lt;/code&gt;, а URL — покаже вам адресу після завершення кроку розгортання.&lt;/p&gt;

&lt;p&gt;Додайте кроки до завдання розгортання.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;…&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup Pages&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/configure-pages@v5&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploy to GitHub Pages&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deployment&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/deploy-pages@v4&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;artifact_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;github-pages&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Перший крок налаштовує Pages. Другий крок розгортає теку збірки на GitHub Pages за допомогою &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy-pages&lt;/code&gt;. Ми використовуємо артефакт попередньо створений на етапі збірки (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt;) та GitHub token для автентифікації розгортання.&lt;/p&gt;

&lt;p&gt;Тепер зробіть commit змін та надішліть їх у репозиторій.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Add GitHub Actions workflow&quot;&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;налаштування-packagejson&quot;&gt;Налаштування &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Відкрийте файл &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt; і додайте параметр &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;homepage&lt;/code&gt;. Ви можете встановити його значення як &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; або надати URL вашого репо для GitHub Pages (&quot;homepage&quot;: &quot;&lt;a href=&quot;https://andygol.github.io/react-app/&quot;&gt;https://andygol.github.io/react-app/&lt;/a&gt;&quot;).&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
  &quot;name&quot;: &quot;react-app&quot;,
  &quot;version&quot;: &quot;0.1.0&quot;,
  &quot;private&quot;: true,
&lt;span class=&quot;gi&quot;&gt;+  &quot;homepage&quot;: &quot;.&quot;,
&lt;/span&gt;  &quot;dependencies&quot;: {
    …
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;розгортання-застосунку-react&quot;&gt;Розгортання застосунку React&lt;/h2&gt;

&lt;p&gt;Будь-які зміни, які ви вносите до гілки &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;, запустять робочий процес GitHub Actions. Робочий процес збере застосунок React і розгорне його на GitHub Pages.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2024/06/deployed-react-app.png&quot; alt=&quot;Deployed React App&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://andygol.co.ua/react-app/&quot;&gt;https://andygol.co.ua/react-app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ось і все! Ви успішно розгорнули застосунок React на GitHub Pages за допомогою GitHub Actions.&lt;/p&gt;

&lt;p&gt;PS. Якщо ви хочете побачити повний файл робочого процесу, перегляньте &lt;a href=&quot;https://github.com/Andygol/react-app/blob/main/.github/workflows/deploy.yml&quot;&gt;репозиторій на GitHub&lt;/a&gt;.&lt;/p&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="React"/>
        <category term="GitHub"/>
        <category term="Actions"/>
        <category term="CI/CD"/>
        <summary type="html">Тут я покажу вам, як розгорнути застосунок React на GitHub Pages за допомогою GitHub Actions. GitHub Pages — це сервіс хостингу статичних сайтів, який бере HTML, CSS та JavaScript файли безпосередньо з репозиторію на GitHub та робить їх доступними у вигляді вебсайту. GitHub Actions — це сервіс CI/CD, що дозволяє автоматизувати робочий процес публікації вашого статичного сайту на GitHub Pages.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">OpenStreetMap — не Google Maps</title>
      <link href="https://blog.andygol.co.ua/uk/2023/07/22/openstreetmap-%D0%BD%D0%B5-google-maps/" rel="alternate" type="text/html" title="OpenStreetMap — не Google Maps"/>
      <published>2023-07-22T08:30:00+00:00</published>
      <updated>2023-07-22T08:30:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2023/07/22/openstreetmap-%D0%BD%D0%B5-google-maps</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2023/07/22/openstreetmap-%D0%BD%D0%B5-google-maps/">
        &lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Тут мав би бути знімок з екрана з “Історії іграшок” де Баз намагається пояснити Вудді щось про зірки показуючи рукою вгору.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Десь років 20+ тому мені трапилось на очі коротеньке оповідання на навколокомпʼютерну тематику з елементами трилеру, в якому був момент, коли головний герой похапцем створював якісь геосервіси, застосунки, що їх використовують, майже на колінці, щоб вийти з ситуацій в якій він опинився. Тоді я не до кінця зрозумів всієї ідеї, бо з таким в житті ще не доводилось стикатись. І ось на дворі нове сторіччя/тисячоріччя у нас в кишені пристрій, який використовує ці загадкові геосервіси – смартфон.&lt;/p&gt;

&lt;p&gt;Спробуємо визначитись з цими загадковими геосервісами. Насправді вони існували задовго до появи смартфонів, та і до появи компʼютерів взагалі. Найпростіший приклад – це поштові адреси. Знаючи номер будинку, назву вулиці ви можете дістатись до точки призначення. Спланувати (продумати) маршрут ви можете самотужки, намалювавши на клаптику паперу приблизну схему, або зробивши подібне у себе в голові. Насправді ми так робимо кожного разу як кудись виходимо з дому – плануємо свій маршрут і далі дотримуємося цього плану, навіть коли йдемо до найближчого магазину, щоб купити продуктів. Якщо ви вирушаєте в малознайоме, віддалене місце про яке у вас або мало відомостей, або ви там ніколи не бували, ви спитаєте у знайомих як туди краще дістатись, яким видом транспорту доїхати, на якій зупинці вийти, в якій бік далі йти і де й куди звернути. Можна взяти таксі і водій довезе вас за вказаною адресою чи орієнтирами. Якщо вам потрібно надіслати посилку в інше місто – поштова служба надасть вам таку послугу, однак ви все одно маєте зазначити отримувача та місце куди посилку треба доправити. Як бачите, нам потрібні відомості про знаходження якогось обʼєкта на місцевості (в просторі), тобто геопросторова інформація. Ця інформація лежить в основі всіх геосервісів.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;… потім буде бурякове поле, потім лісосмуга, потім кукурудзяне поле, потім пшоничне поле, конопляне поле, за ним говоряща річка – вона і підкаже…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Геосервіси – це інформаційні та технологічні рішення, які забезпечують збір, обробку, зберігання та надання географічної (геолокаційної) інформації. Вони допомагають розуміти та використовувати просторові дані для різних цілей, таких як навігація, аналіз даних, планування, маркетинг і багато іншого. За допомогою геосервісів, користувачі можуть взаємодіяти з мапами, точними координатами, географічними обʼєктами та іншими геоданими.&lt;/p&gt;

&lt;p&gt;І от зараз вже виклик таксі, замовлення доставки піци, розрахунок потенційного охоплення цільової аудиторії вже майже неможливо уявити без використання геопросторових даних та сервісів.&lt;/p&gt;

&lt;p&gt;Мені неодноразово доводилось бачити коли люди плутали дані з сервісами. Це саме той випадок, який винесено в заголовок. Я бачив кілька різних аналізів де автори намагались порівнювати Google Maps саме з OpenStreetMap не маючи уявлення про те, що вони порівнювали. Нумо розбиратись.&lt;/p&gt;

&lt;h2 id=&quot;поговоримо-спочатку-про-google-maps&quot;&gt;Поговоримо спочатку про Google Maps&lt;/h2&gt;

&lt;p&gt;Історія Google Maps розпочалася в середині 2000-х років і є цікавим прикладом успішного поєднання інноваційної технології та практичного застосування.&lt;/p&gt;

&lt;p&gt;Google Maps було запущено 8 лютого 2005 року. Мапи були створені як проєкт команди Google, яка прагнула створити картографічний вебсервіс з високою швидкістю завантаження мап та інтерактивними можливостями.&lt;/p&gt;

&lt;p&gt;Перша версія Google Maps надавала можливість переглядати мапи, шукати місця, планувати маршрути та орієнтуватися за допомогою GPS. Інтерфейс був інтуїтивно зрозумілим та дружнім для користувачів, наскільки це можливо.&lt;/p&gt;

&lt;p&gt;Вже у червні 2005 року Google представляє Google Maps API, який дозволив розробникам інтегрувати картографічні функції до своїх вебсайті та застосунків. Це відкрило широкі можливості для використання картографії в інших проєктах.&lt;/p&gt;

&lt;p&gt;У 2007 році Google Maps був поповнений новим функціоналом - Street View, який дозволив користувачам переглядати фотографії вздовж доріг та вулиць, що надавало їм уявлення про реалістичний вигляд місцевості, зʼявилась можливість віртуально побувати в інших місцях, в яких ти ніколи не був, і може ніколи не будеш.&lt;/p&gt;

&lt;p&gt;У 2008 році була запущена мобільна версія Google Maps для смартфонів, що дозволило користувачам отримувати доступ до мап та навігації прямо на своїх мобільних пристроях.&lt;/p&gt;

&lt;p&gt;У 2010 році Google інтегрувала функціонал Google Maps з іншим продуктом - Google Earth, дозволяючи користувачам переглядати 3D-моделі та зображення з супутників.&lt;/p&gt;

&lt;p&gt;Google Maps продовжував розвиватися, додаючи нові функції, такі як покрокова навігація, режим роботи в офлайн, оцінки та відгуки про місця, рекомендації про події та установи та інше.&lt;/p&gt;

&lt;p&gt;Сьогодні, з точки зору пересічного користувача, Google Maps стали одним з найпопулярніших інструментів для навігації та дослідження світу. Вони використовуються мільйонами людей щодня для пошуку маршрутів, пошуку місць, перегляду фотографій та вивчення нових віддалених місць. Google Maps також стала важливим ресурсом для підприємств, дослідників та громадських організацій, що використовують їх послуги для розвʼязання різноманітних задач.&lt;/p&gt;

&lt;h2 id=&quot;openstreetmap&quot;&gt;OpenStreetMap&lt;/h2&gt;

&lt;p&gt;Історія OpenStreetMap (OSM) починається у 2004 році і повʼязана з бажанням створити вільну, відкриту та доступну для всіх базу даних геопросторової інформації.&lt;/p&gt;

&lt;p&gt;OpenStreetMap була заснована у Великобританії Стівом Костом (Steve Coast) у серпні 2004 року. У нього була ідея створити глобальну картографічну платформу, на якій користувачі можуть спільно створювати та редагувати геодані.&lt;/p&gt;

&lt;p&gt;Головною причиною виникнення OpenStreetMap була нестача доступної та актуальної географічної інформації в деяких регіонах світу. Комерційні глобальні сервіси картографії, такі як Google Maps, не завжди надавали покриття достатньої якості або деталізації для певних територій.&lt;/p&gt;

&lt;p&gt;OpenStreetMap висувала принцип відкритості даних та доступу до картографічної інформації. Проєкт став платформою для глобальної спільноти волонтерів, які могли долучитися до процесу створення та редагування геоданих будь-яких регіонів планети.&lt;/p&gt;

&lt;p&gt;Згодом OpenStreetMap привернув до себе увагу все більшої кількості користувачів та розбудував активну спільноту. Волонтери почали активно вносити дані про дороги, місцевості, відомості про гідрографію, підприємства та інші географічні обʼєкти.&lt;/p&gt;

&lt;p&gt;Спрямована на розвиток OpenStreetMap спільнота стала однією з найбільших та найактивніших вільних картографічних спільнот у світі. Завдяки вільному та безкоштовному доступу до даних, відкритості та колективним зусиллям, OSM стала джерелом цінної та актуальної геопросторової інформації, що використовується у різних галузях, включаючи туризм, дослідження, гуманітарну допомогу та планування міст.&lt;/p&gt;

&lt;h2 id=&quot;цілі-та-філософія&quot;&gt;Цілі та філософія&lt;/h2&gt;

&lt;p&gt;Google Maps – це комерційний продукт, створений компанією Google. Основна мета Google Maps – надавати користувачам зручні та швидкі інтерактивні мапи для навігації, пошуку місць та орієнтування. Google заробляє на цьому, відображаючи рекламу та надаючи платні послуги.&lt;/p&gt;

&lt;p&gt;З іншого боку, OpenStreetMap – це проєкт, заснований на волонтерських засадах та філософії відкритості даних. Головна мета OSM – створити вільну та загальнодоступну базу геопросторових даних для всього світу. Кожен може внести свій внесок, додавши нові дані або виправивши помилки. Такий підхід дає можливість створити докладні та актуальні мапи там, де інші джерела можуть бути обмежені або застарілі.&lt;/p&gt;

&lt;h2 id=&quot;джерела-даних&quot;&gt;Джерела даних&lt;/h2&gt;

&lt;p&gt;Google Maps використовує комерційні та пропрієтарні джерела даних, ліцензовані дані від сторонніх компаній, та алгоритми машинного навчання для обробки великих обсягів даних, які акумулюються компанією разом зі збором даних на місцевості за допомогою спеціалізованого обладнання та відстеження пристроїв клієнтів. Це дає їм перевагу у швидкості та охопленні, але приховує деталі та джерела інформації. (Так всі ми пересуваючись місцевістю не відаючи про це допомагаємо Google продавати нам послуги використовуючи дані отримані з наших смартфонів у вигляді автоматизованих звітів від застосунків чи ОС.)&lt;/p&gt;

&lt;p&gt;OpenStreetMap використовує відкриті джерела даних, такі як супутникові знімки надані постачальниками для цілей проєкту, відкриті геопросторові дані, що поширюються урядами країн на вільних засадах, дані з інших вільних відкритих джерел, а також власні внески учасників проєкту. Це дозволяє створювати більш прозорі та перевірені дані, які можуть бути корисними для громадськості, дослідників та навіть гуманітарних організацій.&lt;/p&gt;

&lt;h2 id=&quot;актуальність-даних&quot;&gt;Актуальність даних&lt;/h2&gt;

&lt;p&gt;Google Maps має доступ до ресурсів, які дозволяють оновлювати їхню базу даних з певною періодичністю, особливо там де на них є підвищений попит і на послугах можна більше заробити. Це допомагає надавати користувачам актуальну інформацію, наприклад про дорожні затори, розваги та нові заклади. (Памʼятаєте про ваші смартфони за допомогою яких Google збирає про все це дані?)&lt;/p&gt;

&lt;p&gt;OpenStreetMap, залежно від регіону, може мати різний рівень актуальності даних. Тут все залежить від кількості та активності волонтерів, які вносять зміни до бази даних. Однак там, де є активна спільнота учасників проєкту, дані можуть бути досить актуальними та детальними.&lt;/p&gt;

&lt;p&gt;Ви можете зауважити що на головній і Google Maps, і OpenStreetMap знаходяться схожі елементи:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Мапа&lt;/li&gt;
  &lt;li&gt;Поле для пошуку&lt;/li&gt;
  &lt;li&gt;Можливість побудувати маршрут&lt;/li&gt;
  &lt;li&gt;Перегляд відомостей про обʼєкт на мапі…&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;так-в-чому-ж-тоді-різниця&quot;&gt;Так в чому ж тоді різниця?&lt;/h2&gt;

&lt;p&gt;Головна різниця – це те, що Google Maps є комерційним продуктом метою якого є надання послуг клієнтам. OpenStreetMap, в першу чергу, націлений на збір та розповсюдження даних.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;– А як же API? – можете сказати ви.&lt;br /&gt;
– Це не одне API, а збірка з різноманітних API, кожне з яких відповідає за надання тієї чи іншої послуги.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;API для показу мап; API для пошуку (геокодуванню); API для прокладання маршрутів; API для ведення по маршруту та надавання покрокових інструкцій та інше – всі ці компоненти і створюють кінцевий продукт.&lt;/p&gt;

&lt;p&gt;Перевага Google в централізації, всі API розробляються та підтримуються в середині компанії. Користуючись ними ви очікуєте отримати певний обсяг послуг з певним рівнем доступності та якості (SLA), за що ви готові заплатити певну суму, без потреби розгортання всієї інфраструктури у себе чи у клієнта. Розрахунок того в скільки вам вийде користування послугами зовсім нетривіальний, бо іноді досить важко вловити як одні послуги залежать від інших та спрогнозувати очікуваний рівень витрат.&lt;/p&gt;

&lt;p&gt;Натомість API OpenStreetMap призначений для отримання, редагування та збереження даних в спільній базі геопросторових даних учасниками проєкту. Тобто єдине що гарантує вам OpenStreetMap – це те, що ви можете скористатись даними і вже використовуючи ці дані ви можете створити власну мапу, геокодер, пропонувати своїм клієнтам послуги навігації чи ще щось. Мапа, пошук, прокладання маршрутів на головній OpenStreetMap – це демонстрація того, що можна зробити з даними проєкту, всі ці елементи самі по собі є окремими незалежними проєктами і єдине, що їх повʼязує з OpenStreetMap – дані які OpenStreetMap надає всім охочім.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Мапа – тайловий сервер (&lt;a href=&quot;https://mapnik.org/&quot;&gt;Mapnik&lt;/a&gt;) та стиль подання даних (&lt;a href=&quot;https://wiki.openstreetmap.org/wiki/Uk:Стандартний_шар&quot;&gt;OSM-Carto&lt;/a&gt;) – 2 окремих проєкти, які жодним чином не залежать від OSMF (OpenStreetMap Foundation)&lt;/li&gt;
  &lt;li&gt;Бібліотека показу тайлів – &lt;a href=&quot;https://leafletjs.com&quot;&gt;leaflet.js&lt;/a&gt;, бібліотека JavaScript з відкритими сирцями для показу інтерактивних мап&lt;/li&gt;
  &lt;li&gt;Пошук – геокодер &lt;a href=&quot;https://nominatim.org/&quot;&gt;Nominatim&lt;/a&gt;, теж самостійний проєкт&lt;/li&gt;
  &lt;li&gt;Прокладання маршрутів – &lt;a href=&quot;https://project-osrm.org/&quot;&gt;OSRM&lt;/a&gt;, &lt;a href=&quot;https://valhalla.github.io/valhalla/&quot;&gt;Valhalla&lt;/a&gt;, &lt;a href=&quot;https://www.graphhopper.com/&quot;&gt;GraphHopper&lt;/a&gt;, теж сторонні проєкти&lt;/li&gt;
  &lt;li&gt;Експорт/видобування даних - &lt;a href=&quot;http://overpass-api.de/&quot;&gt;Overpass API&lt;/a&gt; (&lt;a href=&quot;https://overpass-turbo.eu/&quot;&gt;Overpass-Turbo&lt;/a&gt;), так само…&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ideditor.com/&quot;&gt;iD&lt;/a&gt; – редактор даних OSM (вбудований у Головну), зараз під управлінням OSMF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Всі ці проєкти є вільними проєктами з відкритими сирцями і за потреби, можливості, за наявності відповідної кваліфікації ви можете зібрати з цього конструктора потрібне вам рішення. Тим більше, що у вас є вибір серед різних проєктів візуалізації даних, створення мапи; різних рушіїв геокодування, пошуку; рушіїв прокладання маршрутів та навігації. Крім того, ви можете використовувати дані OpenStreetMap для проведення аналізу у власних проєктах, поєднуючи їх з іншими вашим даним, це те що ви не можете робити з Google Maps.&lt;/p&gt;

&lt;h2 id=&quot;використання-openstreetmap&quot;&gt;Використання OpenStreetMap&lt;/h2&gt;

&lt;p&gt;OpenStreetMap використовується різними компаніями та організаціями, включаючи Meta, Apple, Amazon, TomTom та іншими.&lt;/p&gt;

&lt;p&gt;Meta використовує дані OpenStreetMap для своїх функцій картографії та локаційної інтеграції. Для забезпечення своїх користувачів актуальною інформацією про місця та локації; Meta може використовувати дані OSM для показу місцезнаходження користувачів, мап та інтеграції з іншими функціями на своїх платформах.&lt;/p&gt;

&lt;p&gt;Apple також використовує дані OpenStreetMap у деяких зі своїх послуг. Наприклад, в деяких країнах або регіонах, де дані від стороніх постачальників для Apple Maps можуть бути менш повними або неактуальними, або навпаки там де дані OpenStreetMap якісніші, вони можуть використовувати їх для підтримки навігації та картографії.&lt;/p&gt;

&lt;p&gt;Amazon також використовує дані OpenStreetMap у своїх послугах, таких як AWS (Amazon Web Services). OSM може бути використаний у різних рішеннях для обробки географічних даних, аналізу та візуалізації на платформі AWS.&lt;/p&gt;

&lt;p&gt;Поміж іншими виробниками навігаційного обладнання, TomTom також співпрацює з OpenStreetMap. Вони можуть використовувати дані OSM для забезпечення навігації та картографічних послуг у своїх пристроях та додатках.&lt;/p&gt;

&lt;p&gt;Airbnb, популярна платформа для бронювання помешкань, також використовує дані OSM для відображення локацій та місць проживання на своїх мапах. Вони можуть використовувати OSM для надання точної інформації про розташування помешкань та їхнє оточення.&lt;/p&gt;

&lt;p&gt;ESRI ArcGIS Online, дозволяє користувачам інтегрувати дані OpenStreetMap до своїх географічних інформаційних систем. Користувачі можуть імпортувати дані OSM до своїх проєктів, використовувати їх для аналізу, створення мап та візуалізації.&lt;/p&gt;

&lt;p&gt;Ці приклади показують широкий спектр використання OpenStreetMap відомими компаніями та службами. Відкритість та доступність даних OSM дозволяють різним платформам та організаціям використовувати географічну інформацію для забезпечення якісних послуг та розвитку своїх продуктів.&lt;/p&gt;

&lt;p&gt;Слід зазначити, що вибір того що використовувати, сервіси Google, чи дані та екосистему OpenStreetMap, залежить від конкретних вимог, повноти та актуальності покриття даним в тій чи іншій території, потребі у швидкому внесені зміни в дані, можливості впровадження процесів контролю якості даних та наявності у вас досвіду роботи з продуктами Google чи екосистемою та даними OpenStreetMap. Вибір за вами.&lt;/p&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="OpenStreetMap"/>
        <category term="Google Maps"/>
        <category term="OSM"/>
        <category term="API"/>
        <summary type="html">Тут мав би бути знімок з екрана з “Історії іграшок” де Баз намагається пояснити Вудді щось про зірки показуючи рукою вгору.</summary>
      
    </entry>
  
    <entry xml:lang="uk">
      <title type="html">API OSM 2.0 з використанням git</title>
      <link href="https://blog.andygol.co.ua/uk/2023/05/07/osm-2-0-api-%D0%B7-%D0%B2%D0%B8%D0%BA%D0%BE%D1%80%D0%B8%D1%81%D1%82%D0%B0%D0%BD%D0%BD%D1%8F%D0%BC-git/" rel="alternate" type="text/html" title="API OSM 2.0 з використанням git"/>
      <published>2023-05-07T08:29:00+00:00</published>
      <updated>2023-05-07T08:29:00+00:00</updated>
      <id>https://blog.andygol.co.ua/uk/2023/05/07/osm-2-0-api-%D0%B7-%D0%B2%D0%B8%D0%BA%D0%BE%D1%80%D0%B8%D1%81%D1%82%D0%B0%D0%BD%D0%BD%D1%8F%D0%BC-git</id>
      <content type="html" xml:base="https://blog.andygol.co.ua/uk/2023/05/07/osm-2-0-api-%D0%B7-%D0%B2%D0%B8%D0%BA%D0%BE%D1%80%D0%B8%D1%81%D1%82%D0%B0%D0%BD%D0%BD%D1%8F%D0%BC-git/">
        &lt;p&gt;API OSM 2.0 (OpenStreetMap) – це інтерфейс, який надає доступ до геопросторових даних та історії їх змін, збережених за допомогою git. Основна структура даних в OSM має базуватись на розділенні Земної кулі на тайли, кожен з яких є окремим репозиторієм git.&lt;/p&gt;

&lt;p&gt;Формат збереження даних обʼєктів OSM – це yaml (YAML Ain’t Markup Language). Yaml – зручний для читання та збереження формат даних, що базується на використанні відступів для представлення структури даних. Використання yaml в OSM дозволить зберігати різні багаторівневі атрибути у вигляді теґів для географічних обʼєктів.&lt;/p&gt;

&lt;p&gt;Кожен обʼєкт зберігатиметься у вигляді окремого YAML-файлу, що відрізняється від попередніх версій API OSM, де дані зберігалися та передавалися у вигляді XML-файлів.&lt;/p&gt;

&lt;p&gt;Кожен тайл в OSM є самостійним репозиторієм git. Git – це система керування версіями, яка дозволяє відстежувати історію змін у файловій системі та зберігати різні версії файлів. Використання git для OSM дозволяє відстежувати всі зміни, внесені до геопросторових даних, та зберігати їх історію.&lt;/p&gt;

&lt;h2 id=&quot;опис&quot;&gt;Опис&lt;/h2&gt;

&lt;p&gt;API OSM має надавати різні методи для доступу до даних та їхньої модифікації. Деякі з них повинні включати:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Отримання даних по координатах або області.&lt;/li&gt;
  &lt;li&gt;Пошук обʼєктів за типом, атрибутами або теґами.&lt;/li&gt;
  &lt;li&gt;Отримання історії змін для конкретного обʼєкта.&lt;/li&gt;
  &lt;li&gt;Додавання нових обʼєктів або змін до наявних.&lt;/li&gt;
  &lt;li&gt;Видалення обʼєктів.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;API OSM має надавати можливість виконувати запити на основі різних протоколів для зручного отримання та взаємодії з геопросторовими даними.&lt;/p&gt;

&lt;p&gt;Загалом, API OSM, що використовує git для збереження геопросторових даних та їхньої історії змін, надає потужний механізм для спільної роботи та управління геопросторовими даними. Основні особливості API OSM з використанням git включають:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Контроль версій:&lt;/strong&gt;
Завдяки використанню git, API OSM дозволяє зберігати історію змін для кожного обʼєкта. Це означає, що ви можете відстежувати, хто, коли і які зміни вніс у конкретний обʼєкт, а також відновлювати попередні версії даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розподілена структура даних:&lt;/strong&gt;
Земна куля розбивається на тайли, які є самостійними репозиторіями git. Це дозволяє ефективно керувати та розподіляти навантаження на різних серверах. Кожен тайл містить лише обʼєкти, що знаходяться у його межах, що полегшує доступ до конкретних даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Гнучкість формату даних:&lt;/strong&gt;
Використання формату yaml для зберігання даних обʼєктів дозволяє зберігати різноманітні атрибути та теґи, які допомагають описати географічні обʼєкти докладно. Це дає можливість додавати власні теґи та атрибути для більш гнучкого представлення даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Масштабованість:&lt;/strong&gt;
Розподілена структура даних та використання git дозволяють легко масштабувати систему для обробки великих обсягів геопросторових даних. Ви можете додавати нові тайли або сервери для розширення інфраструктури та підвищення продуктивності, розбивати великі тайли на менші з часом.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Зручний доступ до даних:&lt;/strong&gt;
API OSM надає різноманітні методи для доступу до даних OSM. Ви можете отримувати дані за координатами або областями, шукати обʼєкти за типом, атрибутами або теґами OSM, а також отримувати історію змін для конкретного обʼєкта. Це надає гнучкість та можливість вибирати лише необхідні дані.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Можливість спільної роботи:&lt;/strong&gt;
Використання git дозволяє кільком користувачам працювати з даними одночасно та вносити зміни. Завдяки контролю версій і можливості обʼєднання різних гілок, ви можете легко спільно працювати над проєктами, вносити та обʼєднувати зміни.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Інтеграція з іншими інструментами:&lt;/strong&gt;
Оскільки OSM використовує git, ви можете використовувати широкий спектр інструментів, які підтримують git, для роботи з геопросторовими даними. Це можуть бути різні системи контролю версій, редактори коду, інструменти аналізу даних та інші.&lt;/p&gt;

&lt;p&gt;Крім цих можливостей API OSM з використанням git, дозволяє робити:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розширення даних:&lt;/strong&gt;
Ви можете додавати власні дані до даних OSM, розширюючи поточну базу геопросторових даних. Це дозволяє створювати власні набори даних, додавати атрибути та розширювати функціональні можливості OSM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Інтеграція з іншими сервісами:&lt;/strong&gt;
API OSM може бути легко інтегрований з іншими сервісами та інструментами, такими як геопросторові сервіси або системи аналізу даних. Це дозволяє поєднувати різні джерела даних та використовувати їх для створення потрібних рішень.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розширені можливості редагування:&lt;/strong&gt;
API OSM дозволяє вносити зміни у геопросторові дані, включаючи додавання нових обʼєктів, редагування атрибутів та видалення обʼєктів. Це дає можливість активно співпрацювати з глобальною базою даних OSM і доповнювати її знаннями про конкретні обʼєкти або регіони.&lt;/p&gt;

&lt;p&gt;Це лише кілька додаткових можливостей API OSM з використанням git. Загалом, API OSM має бути потужним інструментом для роботи з геопросторовими даними, забезпечуючи контроль версій, гнучкість формату даних та їх масштабування.&lt;/p&gt;

&lt;h2 id=&quot;yaml--для-збереження-даних&quot;&gt;YAML – для збереження даних&lt;/h2&gt;

&lt;p&gt;Версія API OSM 2.0 передбачає використання формату yaml. Кожен обʼєкт зберігається у вигляді окремого YAML-файлу і це відрізняється від попередніх версій API OSM, де дані зберігалися у вигляді XML-файлів. Використання YAML-формату для зберігання окремих файлів обʼєктів має деякі переваги:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Простота читання та редагування:&lt;/strong&gt;
YAML-формат має зрозумілий синтаксис, який легко читається, що спрощує редагування даних вручну або їх перегляд. Використання відступів замість синтаксичних елементів XML спрощує роботу з даними та їх редагування&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Гнучкість у виразності даних:&lt;/strong&gt;
YAML дозволяє зберігати додаткові властивості та інформацію про обʼєкти, яка необхідна для конкретного застосування. Він може включати прості значення, списки, асоціативні масиви та вкладені структури даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Пошук та фільтрація даних:&lt;/strong&gt;
Будучи окремими файлами, обʼєкти можуть бути легко знайдені та відфільтровані за різними параметрами. Ви можете використовувати бібліотеки або інструменти для роботи з YAML для здійснення пошуку та обробки даних за потребою.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Підтримка історії змін:&lt;/strong&gt;
Кожен обʼєкт представляться у вигляді власного файлу, що дозволяє зберігати історію змін та відстежувати розвиток даних. Git, як система контролю версій, може використовуватися для керування змінами та гілками, що сприяє ефективному співробітництву та управлінню версіями даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розширення та валідація даних:&lt;/strong&gt;
Ви можете використовувати YAML-схеми для валідації та контролю правильності даних. Це дозволяє забезпечити те, що дані відповідають заданим правилам і структурі. Також, ви можете розширювати YAML-схеми для визначення додаткових властивостей та обмежень для обʼєктів, що зберігаються.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Інтеграція з іншими інструментами:&lt;/strong&gt;
YAML є популярним форматом обміну даними, що підтримується багатьма інструментами та платформами. Ви можете використовувати різні інструменти для імпорту та експорту даних, а також для обробки та аналізу геопросторових даних у форматі YAML.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/2023/05/2023-05-07-osm-2-0-api-using-git.png&quot; alt=&quot;null island yaml&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;git-репозиторій-як-сховище-даних&quot;&gt;Git-репозиторій як сховище даних&lt;/h2&gt;

&lt;p&gt;Форма (геометрія) тайлів кожного репозиторію може бути довільною.&lt;/p&gt;

&lt;p&gt;В API OSM з використанням git, форма (геометрія) тайлів кожного репозиторію може бути довільною. Тайли можуть мати різні форми та розміри, залежно від потреб ваших геопросторових даних.&lt;/p&gt;

&lt;p&gt;Опис меж тайлів, або інші метадані, які стосуються кожного тайла, можуть бути збережені в самому репозиторії тайла. Це дозволяє зберігати важливу інформацію разом з геопросторовими даними, що допомагає забезпечити консистентність та доступність цих даних.&lt;/p&gt;

&lt;p&gt;Крім того, існує майстер-репозиторій, який включає репозиторії-тайли у вигляді субмодулів git. Майстер-репозиторій слугує як вихідний пункт для управління та координації репозиторіями-тайлів. Він може містити додаткову інформацію про тайли, таку як індекси або посилання на кожен тайл.&lt;/p&gt;

&lt;p&gt;Така архітектура дозволяє організувати та управляти геопросторовими даними, зберігаючи опис меж тайлів в самому репозиторії тайла та використовуючи майстер-репозиторій для координації та доступу до цих репозиторіїв-тайлів.&lt;/p&gt;

&lt;p&gt;Цей підхід забезпечує гнучкість та масштабованість при роботі з геопросторовими даними, дозволяючи легко управляти окремими тайлами, а також забезпечує збереження історії змін та контроль версій за допомогою git.&lt;/p&gt;

&lt;p&gt;Сегментація даних на репозиторії-тайли дозволяє запровадити:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Підтримку розподіленого співробітництва:&lt;/strong&gt;
Завдяки використанню git, API OSM забезпечує можливість розподіленого співробітництва. Різні користувачі можуть працювати з різними тайлами, внести зміни та взаємодіяти з базою даних OSM, використовуючи ті ж самі принципи що й для роботи з сирцевим кодом, а пізніше обʼєднати свої зміни за допомогою git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Синхронізація та злиття змін:&lt;/strong&gt;
Завдяки використанню git, API OSM дозволяє легко синхронізувати та зливати зміни, які були внесені різними користувачами у власні копії репозиторіїв. Це дозволяє уникнути конфліктів та забезпечити цілісність даних під час обʼєднання змін. До того ж користувачі, можуть робити власні форки, для додавання своїх (переважно непублічних) даних поверх даних OSM. З часом, коли буде прийняте рішення передати власні закриті дані до суспільного надбання, це можна бути зробити за допомогою звичайної команди git push. Зміни можуть передаватись як вгору, від локальних копій до спільних репозиторіїв, так і вниз – від загальних до локальних репозиторіїв, що дозволяє підтримувати власні репозиторії користувачів в актуальному стані, отримуючи оновлення від спільноти, докладаючи менше зусиль для синхронізації даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Історія змін:&lt;/strong&gt;
Завдяки використанню git, API OSM зберігає історію змін обʼєктів в кожному тайлі, що дозволяє прослідковувати розвиток та зміни геопросторових даних. Це корисна функція для аналізу даних, відстеження внесених змін та відновлення попередніх версій.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Підтримка версій:&lt;/strong&gt;
Git дозволяє зберігати кожну версію обʼєкта (файлу) в окремому коміті, що дає можливість легко переглядати, відновлювати та відстежувати зміни в даних з часом. Ви можете проглядати історію змін та повертатися до попередніх версій в разі потреби.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Легка реплікація та розподіл:&lt;/strong&gt;
Оскільки кожен обʼєкт зберігається у власному файлі, їх можна легко реплікувати та розподіляти на різних серверах або тайлах. Це дозволяє покращити масштабованість та доступ до даних. Git надає можливість синхронізувати та реплікувати дані між різними серверами або тайлами. Ви можете використовувати функції git, такі як створення гілок та їх злиття, для керування змінами та оновленнями даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Керування конфліктами:&lt;/strong&gt;
У випадку одночасних змін в одному обʼєкті, git надає засоби для вирішення конфліктів змін та обʼєднання різних версій. Це дозволяє кільком користувачам працювати з одними й тими ж даними та ефективно обробляти конфлікти.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Відновлення даних:&lt;/strong&gt;
Завдяки контролю версій git, API OSM дозволяє відновлювати дані до попередніх версій або відновлювати видалені обʼєкти. Це забезпечує збереження та безпеку даних.&lt;/p&gt;

&lt;h2 id=&quot;набір-змін-osm--коміт-git&quot;&gt;Набір змін OSM — коміт git&lt;/h2&gt;

&lt;p&gt;Так, в контексті збереження даних OSM в git, набір змін представляється як коміт (commit) в git. Коміт є фіксованою структурою даних, яка охоплює зміни, зроблені в конкретний момент часу.&lt;/p&gt;

&lt;p&gt;Кожен коміт в git містить інформацію про зміни, що були внесені до репозиторію. У випадку з OSM, коміт може містити зміни, повʼязані з додаванням, видаленням або зміною геопросторових обʼєктів.&lt;/p&gt;

&lt;p&gt;Коміти в git забезпечують історію змін і дозволяють відстежувати розвиток даних в часі. Кожен коміт має унікальний ідентифікатор (хеш), який служить для їх ідентифікації та відновлення певної версії даних.&lt;/p&gt;

&lt;p&gt;Один коміт може містити зміни для одного або кількох геопросторових обʼєктів. Це дозволяє фіксувати зміни в окремих обʼєктах та управляти історією змін незалежно від інших обʼєктів.&lt;/p&gt;

&lt;p&gt;Коміти можуть бути згруповані в гілки (branches) та злиті (merged) між собою, що дозволяє керувати паралельними гілками та обʼєднувати їх зміни в одну загальну версію даних.&lt;/p&gt;

&lt;p&gt;Отже, коміти в git використовуються для збереження та керування змінами в геопросторових даних OSM у версії збереження даних за допомогою git.&lt;/p&gt;

&lt;h2 id=&quot;переваги-використання-git&quot;&gt;Переваги використання git&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Команди git:&lt;/strong&gt;
Ви можете використовувати стандартні команди git для керування репозиторіями-тайлами, такі як &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git clone&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; та інші. Це дозволяє вам працювати з геопросторовими даними API OSM, використовуючи знайомий та потужний інтерфейс git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розширені функції git:&lt;/strong&gt;
API OSM може використовувати додаткові функції та можливості git, такі як гілки (branches), теги (tags) та коміти (commits). Це дозволяє вам організовувати та керувати різними версіями, створювати позначення для важливих моментів та працювати з різними гілками.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Керування доступом:&lt;/strong&gt;
API OSM може надавати можливість обмеження доступу до окремих тайлів або даних, використовуючи функції контролю доступу git. Це дозволяє контролювати, хто має право на читання, запис або інші дії з геопросторовими даними.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Резервне копіювання та відновлення:&lt;/strong&gt;
Завдяки git, API OSM дозволяє створювати резервні копії даних та відновлювати їх в разі потреби. Це забезпечує додатковий рівень захисту та безпеки для ваших геопросторових даних без потреби розробки власних рішень для цього.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розповсюдження змін:&lt;/strong&gt;
Ви можете легко поширювати зміни, внесені в репозиторії-тайл, на інші екземпляри або інсталяції API OSM використовуючи звичні методи роботи з git. Не потрібно витрачати ресурси сервера на створення дампів даних та даних для реплікації. Завдяки використанню git це відбувається шляхом отримання оновлених даних за потреби командою git pull. Це дозволяє співпрацювати з різними спільнотами або розподіленими системами, обмінюватись та синхронізувати геопросторові дані.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Інтеграція з іншими інструментами:&lt;/strong&gt;
API OSM з використанням git може бути легко інтегрований з іншими інструментами розробки, такими як системи управління проєктами, інструменти автоматизації, сервіси хмарних сховищ та інші. Це робить його важливим компонентом при розробці геопросторових застосунків та роботі з даними.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Синхронізація з іншими джерелами даних:&lt;/strong&gt;
За допомогою API OSM та git ви можете синхронізувати геопросторові дані з іншими джерелами даних. Це може бути корисно, якщо ви маєте додаткові джерела даних або хочете поєднати дані з різних джерел для створення повнішої та комплекснішої бази даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розширення та налаштування:&lt;/strong&gt;
API OSM з git дозволяє розширювати та налаштовувати його функціональність згідно з власними потребами. Ви можете додавати додаткові модулі, функції та розширення для роботи з геоданими та взаємодії з API.&lt;/p&gt;

&lt;p&gt;Використання API OSM з git надає потужні можливості управління геопросторовими даними, збереження їх історії змін та співпраці зі спільнотою.&lt;/p&gt;

&lt;h3 id=&quot;підсумок-можливостей-api-osm-з-використанням-git&quot;&gt;Підсумок можливостей API OSM з використанням git&lt;/h3&gt;

&lt;p&gt;API OSM, що використовує git для збереження геопросторових даних та історії змін, має наступні характеристики:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Кожен тайл є самостійним репозиторієм git, що містить окремий набір геопросторових даних.&lt;/li&gt;
  &lt;li&gt;Тайли можуть мати різну форму (геометрію) і опис їх меж зберігається в самому репозиторії тайла та майстер-репозиторії.&lt;/li&gt;
  &lt;li&gt;Дані про окремі обʼєкти зберігаються у форматі YAML, що дозволяє зберігати додаткові властивості та інформацію про них, наслідуючи всю гнучкість системи теґування OSM.&lt;/li&gt;
  &lt;li&gt;API підтримує розподілене співробітництво, дозволяючи різним користувачам працювати з різними тайлами та зливати зміни з використанням git.&lt;/li&gt;
  &lt;li&gt;Зміни зберігаються в історії змін git-репозиторіїв, що дозволяє прослідковувати розвиток геопросторових даних.&lt;/li&gt;
  &lt;li&gt;За допомогою стандартних команд git, таких як &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clone&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pull&lt;/code&gt; та &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push&lt;/code&gt;, можна керувати репозиторіями тайлів.&lt;/li&gt;
  &lt;li&gt;API надає розширені функції git, такі як гілки, git-теґи та коміти, що дозволяють керувати версіями та розвитком.&lt;/li&gt;
  &lt;li&gt;Існує можливість обмеження доступу до тайлів та даних за допомогою контролю доступу git.&lt;/li&gt;
  &lt;li&gt;API дозволяє відновлювати дані до попередніх версій та виконувати резервне копіювання та відновлення даних.&lt;/li&gt;
  &lt;li&gt;Зміни можна поширювати на інші екземпляри API OSM та співпрацювати зі спільнотою OpenStreetMap та іншими розробниками.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;розподіл-великих-обʼєктів-між-тайлами&quot;&gt;Розподіл великих обʼєктів між тайлами&lt;/h2&gt;

&lt;p&gt;Розподіл великих обʼєктів між тайлами є важливим аспектом при організації геопросторових даних в API OSM з використанням git. Це дозволяє ефективно управляти великими обсягами даних і забезпечувати оптимальну продуктивність та швидкість доступу до цих даних.&lt;/p&gt;

&lt;p&gt;Існує кілька підходів до розподілу великих обʼєктів між тайлами в API OSM. Ось деякі з них:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Географічний розподіл:&lt;/strong&gt;
Обʼєкти можна розподілити між тайлами, використовуючи їх географічне розташування. Наприклад, великі регіональні обʼєкти можуть бути розподілені на окремі тайли, що охоплюють відповідну територію. Це дозволяє зберігати дані ближче до їх фізичного розташування та зменшує навантаження на окремі тайли.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Розподіл за категоріями:&lt;/strong&gt;
Обʼєкти можуть бути розподілені між тайлами в залежності від їх категорій або типів. Наприклад, дорожні або гідрографічні обʼєкти можуть бути збережені в окремих тайлах, що спрощує доступ до конкретних категорій даних.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Залежність від розміру:&lt;/strong&gt;
Обʼєкти можуть бути розподілені на тайли в залежності від їх розміру або складності. Наприклад, великі або складні обʼєкти можуть бути збережені в окремих тайлах для забезпечення оптимального управління та швидкого доступу до них. Це можуть бути контури океанів та континентів, кордони країн та таке інше.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Динамічний розподіл:&lt;/strong&gt;
API OSM може автоматично розподіляти великі обʼєкти між тайлами залежно від завантаження та потреби.&lt;/p&gt;

&lt;p&gt;Альтернативним підходом може бути розподіл великих обʼєктів на основі віртуальних границь тайлів. Замість того, щоб розподіляти обʼєкти фізично між окремими репозиторіями тайлів, ви можете використовувати віртуальні границі, які обʼєднують обʼєкти, які логічно повʼязані між собою.&lt;/p&gt;

&lt;p&gt;В цьому підході, великі обʼєкти можуть бути розбиті на декілька тайлів, і ці тайли можуть використовувати спільний репозиторій або мати посилання один на одного. Це дозволяє зберігати обʼєкти, які фізично розташовані у різних тайлах, разом у віртуальній групі.&lt;/p&gt;

&lt;p&gt;Відповідно, ви можете мати кожен тайл як окремий репозиторій git, але вони можуть посилатися один на одного, щоб утворити віртуальну групу обʼєктів. Це забезпечує зручний доступ та керування великими обʼєктами, розташованими у різних тайлах, зберігаючи при цьому гнучкість та швидкодію git.&lt;/p&gt;

&lt;p&gt;Вибір підходу до розподілу великих обʼєктів між тайлами залежить від конкретних потреб та вимог до продуктивності, доступності та керованості даних.&lt;/p&gt;

&lt;h2 id=&quot;примітки&quot;&gt;Примітки&lt;/h2&gt;

&lt;p&gt;Важливо зазначити, що цей опис є загальним і певні деталі та реалізація можуть залежати від конкретної реалізації API OSM 2.0 та ваших потреб.&lt;/p&gt;

&lt;p&gt;При роботі з API OSM з використанням git, переконайтесь, що ви дотримуєтеся правил та політик OSM щодо внесення змін до глобальної бази даних. Збереження історії змін та правильне використання git допоможуть управляти цими змінами та забезпечити безпеку даних.&lt;/p&gt;

&lt;p&gt;Якщо ви маєте конкретні запитання щодо використання API OSM з git або потребуєте більш детальної інформації, будь ласка, уточніть, і я спробую надати вам більше деталей.&lt;/p&gt;
      </content>
      <author>
        <name>Андрій Головін</name>
      </author>

      
        <category term="OSM"/>
        <category term="API"/>
        <category term="git"/>
        <category term="yaml"/>
        <category term="yml"/>
        <summary type="html">API OSM 2.0 (OpenStreetMap) – це інтерфейс, який надає доступ до геопросторових даних та історії їх змін, збережених за допомогою git. Основна структура даних в OSM має базуватись на розділенні Земної кулі на тайли, кожен з яких є окремим репозиторієм git.</summary>
      
    </entry>
  
</feed>
