# Запуск реплікованого застосунку зі збереженням стану

LLMS index: [llms.txt](/llms.txt)

---

<!-- overview -->

Ця сторінка показує, як запустити реплікований застосунок зі збереженням стану (<a class='glossary-tooltip' title='StatefulSet керує розгортанням і масштабуванням групи обʼєктів Pod з постійним сховищем та постійними ідентифікаторами для кожного обʼєкта Pod.' data-bs-toggle='tooltip' data-bs-placement='top' href='/uk/docs/concepts/workloads/controllers/statefulset/' target='_blank' aria-label='StatefulSet'>StatefulSet</a>). Цей застосунок — реплікована база даних MySQL. У топології цього прикладу є один головний сервер і кілька реплік, що використовують асинхронну реплікацію на основі рядків.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примітка:</h4><strong>Ця конфігурація не для операційної експлуатації</strong>. Налаштування MySQL залишаються на небезпечних стандартних значеннях, щоб зосередитися на загальних патернах запуску застосунків зі збереженням стану в Kubernetes.</div>


## Перш ніж ви розпочнете {#before-you-begin}

- <p>Вам треба мати кластер Kubernetes, а також інструмент командного рядка kubectl має бути налаштований для роботи з вашим кластером. Рекомендується виконувати ці настанови у кластері, що має щонайменше два вузли, які не виконують роль вузлів управління. Якщо у вас немає кластера, ви можете створити його, за допомогою <a href="https://minikube.sigs.k8s.io/docs/tutorials/multi_node/">minikube</a> або використовувати одну з цих пісочниць:</p>
<ul>
<li><a href="https://labs.iximiuz.com/playgrounds?category=kubernetes&filter=all">iximiuz Labs</a></li>
<li><a href="https://killercoda.com/playgrounds/scenario/kubernetes">Killercoda</a></li>
<li><a href="https://kodekloud.com/public-playgrounds">KodeKloud</a></li>
</ul>

- <p>Вам потрібно мати або <a href="/uk/docs/concepts/storage/dynamic-provisioning/">динамічний провізор PersistentVolume</a> з типовим <a href="/uk/docs/concepts/storage/storage-classes/">StorageClass</a>, або <a href="/uk/docs/concepts/storage/persistent-volumes/#provisioning">статично надавати PersistentVolume</a> самостійно, щоб задовольнити <a href="/uk/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims">PersistentVolumeClaims</a>, що використовуються тут.</p>

- Це завдання передбачає, що ви знаєте про [Постійні томи](/docs/concepts/storage/persistent-volumes/) та [StatefulSets](/docs/concepts/workloads/controllers/statefulset/), а також інші основні поняття, такі як [Pod](/docs/concepts/workloads/pods/), [Service](/docs/concepts/services-networking/service/) та [ConfigMap](/docs/tasks/configure-pod-container/configure-pod-configmap/).
- Трохи знань MySQL корисні, але цей посібник має на меті представити загальні патерни, які можуть бути корисними для інших систем.
- Ви використовуєте простір імен default або інший простір імен, в якому відсутні обʼєкти, що конфліктують.
- У вас має бути AMD64-сумісний процесор.

## Цілі

- Розгорнути репліковану топологію MySQL за допомогою StatefulSet.
- Направити трафік від клієнта MySQL.
- Спостерігати стійкість до перерв у роботі.
- Масштабувати StatefulSet вгору та вниз.

<!-- lessoncontent -->

## Розгортання MySQL {#deploy-mysql}

Приклад розгортання складається з ConfigMap, двох Service та StatefulSet.

### Створення ConfigMap {#configmap}

Створіть ConfigMap із наступного файлу конфігурації YAML:


















<div class="highlight code-sample">
    <div class="copy-code-icon">
    <a href="https://raw.githubusercontent.com/kubernetes/website/main/content/uk/examples/application/mysql/mysql-configmap.yaml" download="application/mysql/mysql-configmap.yaml"><code>application/mysql/mysql-configmap.yaml</code>
    </a><img src="/images/copycode.svg" class="icon-copycode" onclick="copyCode('application-mysql-mysql-configmap-yaml')" title="Копіювати application/mysql/mysql-configmap.yaml до буферу обміну"></img></div>
    <div class="includecode" id="application-mysql-mysql-configmap-yaml"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ConfigMap</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">primary.cnf</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">    # Застосовує цю конфігурацію до primary.
</span></span></span><span class="line"><span class="cl"><span class="sd">    [mysqld]
</span></span></span><span class="line"><span class="cl"><span class="sd">    log-bin</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replica.cnf</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">    # Застосовує цю конфігурацію до реплік.
</span></span></span><span class="line"><span class="cl"><span class="sd">    [mysqld]
</span></span></span><span class="line"><span class="cl"><span class="sd">    super-read-only</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span></code></pre></div></div>
</div>

```shell
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-configmap.yaml
```

Цей ConfigMap надає перевизначення для `my.cnf`, що дозволяє вам незалежно керувати конфігурацією на головному сервері MySQL та його репліках. У цьому випадку ви хочете, щоб головний сервер міг обслуговувати логи реплікацій реплік, а репліки відхиляли будь-які записи, які надходять не через реплікацію.

Немає нічого особливого у самому ConfigMap, що зумовлює застосування різних частин до різних Podʼів. Кожен Pod вирішує, яку частину дивитися при ініціалізації, на основі інформації, що надає контролер StatefulSet.

### Створення Service {#services}

Створіть Service із наступного файлу конфігурації YAML:


















<div class="highlight code-sample">
    <div class="copy-code-icon">
    <a href="https://raw.githubusercontent.com/kubernetes/website/main/content/uk/examples/application/mysql/mysql-services.yaml" download="application/mysql/mysql-services.yaml"><code>application/mysql/mysql-services.yaml</code>
    </a><img src="/images/copycode.svg" class="icon-copycode" onclick="copyCode('application-mysql-mysql-services-yaml')" title="Копіювати application/mysql/mysql-services.yaml до буферу обміну"></img></div>
    <div class="includecode" id="application-mysql-mysql-services-yaml"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># Service headless для стабільних записів DNS членів StatefulSet.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clusterIP</span><span class="p">:</span><span class="w"> </span><span class="l">None</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Service клієнт для підʼєднання до екземпляру MySQL для читання.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># Для запису ви повинні підключитися до основного: mysql-0.mysql.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql-read</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">readonly</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;true&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span></code></pre></div></div>
</div>

```shell
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-services.yaml
```

Service headless надає місце для DNS-записів, які <a class='glossary-tooltip' title='Контролер — цикл управління, що спостерігає за загальним станом кластера через apiserver і вносить зміни в намаганні наблизити поточний стан до бажаного.' data-bs-toggle='tooltip' data-bs-placement='top' href='/uk/docs/concepts/architecture/controller/' target='_blank' aria-label='контролери'>контролери</a> StatefulSet створюють для кожного Podʼа, що є частиною набору. Оскільки headless Service називається `mysql`, Podʼи доступні за допомогою зіставлення `<pod-name>.mysql` з будь-якого іншого Podʼа в тому ж Kubernetes кластері та просторі імен.

Клієнтський Service, з назвою `mysql-read`, є звичайним Service з власним кластерним IP, який розподіляє підключення між всіма Podʼами MySQL, які повідомляють про готовність. Набір потенційних точок доступу включає головний сервер MySQL та всі репліки.

Зверніть увагу, що лише запити на читання можуть використовувати балансувальник Service. Оскільки існує лише один головний сервер MySQL, клієнти повинні підключатися безпосередньо до головного Podʼа MySQL (через його DNS-запис у головному Service) для виконання записів.

### Створення StatefulSet {#statefulset}

Наостанок, створіть StatefulSet із наступного файлу конфігурації YAML:


















<div class="highlight code-sample">
    <div class="copy-code-icon">
    <a href="https://raw.githubusercontent.com/kubernetes/website/main/content/uk/examples/application/mysql/mysql-statefulset.yaml" download="application/mysql/mysql-statefulset.yaml"><code>application/mysql/mysql-statefulset.yaml</code>
    </a><img src="/images/copycode.svg" class="icon-copycode" onclick="copyCode('application-mysql-mysql-statefulset-yaml')" title="Копіювати application/mysql/mysql-statefulset.yaml до буферу обміну"></img></div>
    <div class="includecode" id="application-mysql-mysql-statefulset-yaml"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">StatefulSet</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">serviceName</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">initContainers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">init-mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mysql:5.7</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">bash</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="s2">&#34;-c&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          set -ex
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Generate mysql server-id from pod ordinal index.
</span></span></span><span class="line"><span class="cl"><span class="sd">          [[ $HOSTNAME =~ -([0-9]+)$ ]] || exit 1
</span></span></span><span class="line"><span class="cl"><span class="sd">          ordinal=${BASH_REMATCH[1]}
</span></span></span><span class="line"><span class="cl"><span class="sd">          echo [mysqld] &gt; /mnt/conf.d/server-id.cnf
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Add an offset to avoid reserved server-id=0 value.
</span></span></span><span class="line"><span class="cl"><span class="sd">          echo server-id=$((100 + $ordinal)) &gt;&gt; /mnt/conf.d/server-id.cnf
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Copy appropriate conf.d files from config-map to emptyDir.
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [[ $ordinal -eq 0 ]]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            cp /mnt/config-map/primary.cnf /mnt/conf.d/
</span></span></span><span class="line"><span class="cl"><span class="sd">          else
</span></span></span><span class="line"><span class="cl"><span class="sd">            cp /mnt/config-map/replica.cnf /mnt/conf.d/
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/mnt/conf.d</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">config-map</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/mnt/config-map</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">clone-mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">gcr.io/google-samples/xtrabackup:1.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">bash</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="s2">&#34;-c&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          set -ex
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Skip the clone if data already exists.
</span></span></span><span class="line"><span class="cl"><span class="sd">          [[ -d /var/lib/mysql/mysql ]] &amp;&amp; exit 0
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Skip the clone on primary (ordinal index 0).
</span></span></span><span class="line"><span class="cl"><span class="sd">          [[ `hostname` =~ -([0-9]+)$ ]] || exit 1
</span></span></span><span class="line"><span class="cl"><span class="sd">          ordinal=${BASH_REMATCH[1]}
</span></span></span><span class="line"><span class="cl"><span class="sd">          [[ $ordinal -eq 0 ]] &amp;&amp; exit 0
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Clone data from previous peer.
</span></span></span><span class="line"><span class="cl"><span class="sd">          ncat --recv-only mysql-$(($ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Prepare the backup.
</span></span></span><span class="line"><span class="cl"><span class="sd">          xtrabackup --prepare --target-dir=/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">subPath</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/mysql/conf.d</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mysql:5.7</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">MYSQL_ALLOW_EMPTY_PASSWORD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">3306</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">subPath</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/mysql/conf.d</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">requests</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">500m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">1Gi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">livenessProbe</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">exec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;mysqladmin&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;ping&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">initialDelaySeconds</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">10</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">timeoutSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">readinessProbe</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">exec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="c"># Перевіряє, чи можемо ми виконувати запити через TCP (skip-networking вимкнено).</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;mysql&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;-h&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;127.0.0.1&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;-e&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;SELECT 1&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">initialDelaySeconds</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">periodSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">timeoutSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">xtrabackup</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">gcr.io/google-samples/xtrabackup:1.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">xtrabackup</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">3307</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">bash</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="s2">&#34;-c&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          set -ex
</span></span></span><span class="line"><span class="cl"><span class="sd">          cd /var/lib/mysql
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Determine binlog position of cloned data, if any.
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [[ -f xtrabackup_slave_info &amp;&amp; &#34;x$(&lt;xtrabackup_slave_info)&#34; != &#34;x&#34; ]]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            # XtraBackup already generated a partial &#34;CHANGE MASTER TO&#34; query
</span></span></span><span class="line"><span class="cl"><span class="sd">            # because we&#39;re cloning from an existing replica. (Need to remove the tailing semicolon!)
</span></span></span><span class="line"><span class="cl"><span class="sd">            cat xtrabackup_slave_info | sed -E &#39;s/;$//g&#39; &gt; change_master_to.sql.in
</span></span></span><span class="line"><span class="cl"><span class="sd">            # Ignore xtrabackup_binlog_info in this case (it&#39;s useless).
</span></span></span><span class="line"><span class="cl"><span class="sd">            rm -f xtrabackup_slave_info xtrabackup_binlog_info
</span></span></span><span class="line"><span class="cl"><span class="sd">          elif [[ -f xtrabackup_binlog_info ]]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            # We&#39;re cloning directly from primary. Parse binlog position.
</span></span></span><span class="line"><span class="cl"><span class="sd">            [[ `cat xtrabackup_binlog_info` =~ ^(.*?)[[:space:]]+(.*?)$ ]] || exit 1
</span></span></span><span class="line"><span class="cl"><span class="sd">            rm -f xtrabackup_binlog_info xtrabackup_slave_info
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;CHANGE MASTER TO MASTER_LOG_FILE=&#39;${BASH_REMATCH[1]}&#39;,\
</span></span></span><span class="line"><span class="cl"><span class="sd">                  MASTER_LOG_POS=${BASH_REMATCH[2]}&#34; &gt; change_master_to.sql.in
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Check if we need to complete a clone by starting replication.
</span></span></span><span class="line"><span class="cl"><span class="sd">          if [[ -f change_master_to.sql.in ]]; then
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;Waiting for mysqld to be ready (accepting connections)&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            until mysql -h 127.0.0.1 -e &#34;SELECT 1&#34;; do sleep 1; done
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">            echo &#34;Initializing replication from clone position&#34;
</span></span></span><span class="line"><span class="cl"><span class="sd">            mysql -h 127.0.0.1 \
</span></span></span><span class="line"><span class="cl"><span class="sd">                  -e &#34;$(&lt;change_master_to.sql.in), \
</span></span></span><span class="line"><span class="cl"><span class="sd">                          MASTER_HOST=&#39;mysql-0.mysql&#39;, \
</span></span></span><span class="line"><span class="cl"><span class="sd">                          MASTER_USER=&#39;root&#39;, \
</span></span></span><span class="line"><span class="cl"><span class="sd">                          MASTER_PASSWORD=&#39;&#39;, \
</span></span></span><span class="line"><span class="cl"><span class="sd">                          MASTER_CONNECT_RETRY=10; \
</span></span></span><span class="line"><span class="cl"><span class="sd">                        START SLAVE;&#34; || exit 1
</span></span></span><span class="line"><span class="cl"><span class="sd">            # In case of container restart, attempt this at-most-once.
</span></span></span><span class="line"><span class="cl"><span class="sd">            mv change_master_to.sql.in change_master_to.sql.orig
</span></span></span><span class="line"><span class="cl"><span class="sd">          fi
</span></span></span><span class="line"><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          # Start a server to send backups when requested by peers.
</span></span></span><span class="line"><span class="cl"><span class="sd">          exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c \
</span></span></span><span class="line"><span class="cl"><span class="sd">            &#34;xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">subPath</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/mysql/conf.d</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">requests</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">cpu</span><span class="p">:</span><span class="w"> </span><span class="l">100m</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">memory</span><span class="p">:</span><span class="w"> </span><span class="l">100Mi</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">conf</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">emptyDir</span><span class="p">:</span><span class="w"> </span>{}<span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">config-map</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">configMap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">volumeClaimTemplates</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">accessModes</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;ReadWriteOnce&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">resources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">requests</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">10Gi</span><span class="w">
</span></span></span></code></pre></div></div>
</div>

```shell
kubectl apply -f https://k8s.io/examples/application/mysql/mysql-statefulset.yaml
```

Ви можете спостерігати за процесом запуску, виконавши:

```shell
kubectl get pods -l app=mysql --watch
```

Через деякий час ви повинні побачити, що всі 3 Podʼи стануть `Running`:

```none
NAME      READY     STATUS    RESTARTS   AGE
mysql-0   2/2       Running   0          2m
mysql-1   2/2       Running   0          1m
mysql-2   2/2       Running   0          1m
```

Натисніть **Ctrl+C**, щоб скасувати перегляд.


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примітка:</h4>Якщо ви не бачите жодних змін, переконайтеся, що у вас увімкнений динамічний PersistentVolume провізор, як зазначено у <a href="#before-you-begin">передумовах</a>.</div>


Цей маніфест використовує різноманітні техніки для керування Podʼами як частиною StatefulSet. У наступному розділі підкреслено деякі з цих технік, щоб пояснити, що відбувається під час створення Podʼів StatefulSet.

## Розуміння ініціалізації Podʼа зі збереженням стану {#understanding-statefulset-pod-initialization}

Контролер StatefulSet запускає Podʼи по одному, в порядку їх індексів. Він чекає, доки кожен Pod не повідомить про готовність, перш ніж запустити наступний.

Крім того, контролер призначає кожному Podʼу унікальне, стабільне імʼя у формі `<statefulset-name>-<ordinal-index>`, в результаті отримуємо Podʼи з іменами `mysql-0`, `mysql-1` та `mysql-2`.

Шаблон Podʼа у вищенаведеному маніфесті StatefulSet використовує ці властивості, щоб здійснити впорядкований запуск реплікації MySQL.

### Створення конфігурації {#generating-configuration}

Перед запуском будь-яких контейнерів у специфікації Podʼа, спочатку Pod запускає будь-які
[контейнери ініціалізації](/docs/concepts/workloads/pods/init-containers/) у визначеному порядку.

Перший контейнер ініціалізації, `init-mysql`, генерує спеціальні конфігураційні файли MySQL на основі порядкового індексу.

Скрипт визначає свій власний порядковий індекс, витягуючи його з кінця імені Podʼа, яке повертається командою `hostname`. Потім він зберігає порядковий індекс (з числовим зміщенням для уникнення зарезервованих значень) у файлі з назвою `server-id.cnf` в теці `conf.d` MySQL. Це перетворює унікальний, стабільний ідентифікатор, наданий StatefulSet, у домен ідентифікаторів серверів MySQL, які вимагають таких же властивостей.

Скрипт у контейнері `init-mysql` також застосовує або `primary.cnf`, або `replica.cnf` з ConfigMap, копіюючи вміст у `conf.d`. Оскільки у топології цього прикладу є лише один головний сервер MySQL та будь-яка кількість реплік, скрипт призначає порядковий індекс `0` головному серверу, а всі інші — репліками. Разом з гарантією контролера StatefulSet щодо [порядку розгортання](/docs/concepts/workloads/controllers/statefulset/#deployment-and-scaling-guarantees), це забезпечує готовність головного сервера MySQL перед створенням реплік, щоб вони могли почати реплікацію.

### Клонування наявних даних {#cloning-existing-data}

Загалом, коли новий Pod приєднується до набору як репліка, він повинен припускати, що головний сервер MySQL може вже містити дані. Також він повинен припускати, що логи реплікації можуть не бути повністю отримані з самого початку. Ці консервативні припущення є ключем до можливості запуску робочого StatefulSet масштабуватися вгору та вниз з часом, а не залишатися фіксованим на своєму початковому розмірі.

Другий контейнер ініціалізації, з назвою `clone-mysql`, виконує операцію клонування на реплікаційному Podʼі першого разу, коли він запускається на порожньому PersistentVolume. Це означає, що він копіює всі наявні дані з іншого працюючого Podʼа, таким чином, його локальний стан є достатньо консистентним для початку реплікації з головного сервера.

MySQL сам по собі не надає механізму для цього, тому приклад використовує популярний відкритий інструмент — Percona XtraBackup. Під час клонування MySQL сервер може мати знижену продуктивність. Щоб мінімізувати вплив на головний сервер MySQL, скрипт вказує кожному Podʼу отримувати дані з Podʼа, чий порядковий індекс на одиницю менший. Це працює через те, що контролер StatefulSet завжди гарантує, що Pod `N` є готовим перед запуском Podʼа `N+1`.

### Початок реплікації {#starting-replication}

Після успішного завершення контейнерів ініціалізації запускаються звичайні контейнери. Podʼи MySQL складаються з контейнера `mysql`, в якому працює фактичний сервер `mysqld`, та контейнера `xtrabackup`, який діє як [sidecar](/blog/2015/06/the-distributed-system-toolkit-patterns).

Sidecar `xtrabackup` переглядає клоновані файли даних і визначає, чи необхідно ініціалізувати реплікацію MySQL на репліці. У цьому випадку він чекає, доки `mysqld` буде готовий, а потім виконує команди `CHANGE MASTER TO` та `START SLAVE` з параметрами реплікації, отриманими з клонованих файлів XtraBackup.

Після того як репліка починає реплікацію, вона запамʼятовує свій головний сервер MySQL і автоматично підʼєднується, якщо сервер перезавантажується або зʼєднання втрачається. Також, оскільки репліки шукають головний сервер за його стабільним DNS-імʼям (`mysql-0.mysql`), вони автоматично знаходять головний сервер навіть якщо він отримує новий IP Podʼа через перепланування.

Нарешті, після початку реплікації, контейнер `xtrabackup` слухає зʼєднання з інших Podʼів, які запитують клон даних. Цей сервер залишається активним нескінченно довго в разі, якщо StatefulSet масштабується вгору, або якщо наступний Pod втрачає свій PersistentVolumeClaim і потрібно повторно виконати клонування.

## Надсилання клієнтського трафіку {#sending-client-traffic}

Ви можете надіслати тестові запити до головного сервера MySQL (хост `mysql-0.mysql`) запустивши тимчасовий контейнер з образом `mysql:5.7` і використовуючи бінарний файл клієнта `mysql`.

```shell
kubectl run mysql-client --image=mysql:5.7 -i --rm --restart=Never --\
  mysql -h mysql-0.mysql <<EOF
CREATE DATABASE test;
CREATE TABLE test.messages (message VARCHAR(250));
INSERT INTO test.messages VALUES ('hello');
EOF
```

Використовуйте хост `mysql-read`, щоб надіслати тестові запити до будь-якого сервера, який повідомляє про готовність:

```shell
kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-read -e "SELECT * FROM test.messages"
```

Ви повинні отримати вивід схожий на цей:

```none
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted
```

Щоб продемонструвати, що Service `mysql-read` розподіляє підключення між
серверами, ви можете запустити `SELECT @@server_id` у циклі:

```shell
kubectl run mysql-client-loop --image=mysql:5.7 -i -t --rm --restart=Never --\
  bash -ic "while sleep 1; do mysql -h mysql-read -e 'SELECT @@server_id,NOW()'; done"
```

Ви повинні бачити, що `@@server_id` змінюється випадковим чином, оскільки може бути вибрана інша точка доступу при кожній спробі підключення:

```none
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         100 | 2006-01-02 15:04:05 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         102 | 2006-01-02 15:04:06 |
+-------------+---------------------+
+-------------+---------------------+
| @@server_id | NOW()               |
+-------------+---------------------+
|         101 | 2006-01-02 15:04:07 |
+-------------+---------------------+
```

Ви можете натиснути **Ctrl+C**, коли захочете зупинити цикл, але цікаво тримати його запущеним в іншому вікні, щоб бачити ефект від наступних кроків.

## Симуляція відмови Podʼа та Вузла {#simulate-pod-and-node-downtime}

Щоб продемонструвати підвищену доступність читання з пулу реплік замість одного сервера, залиште цикл `SELECT @@server_id`, запущений вище, активним, тоді як ви змушуєте Pod вийти зі стану Ready.

### Збій проби готовності {#break-readiness-probe}

[Проба готовності](/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes) для контейнера `mysql` виконує команду `mysql -h 127.0.0.1 -e 'SELECT 1'`, щоб переконатися, що сервер запущений і може виконувати запити.

Одним зі способів змусити цю пробу готовності збоїти — це пошкодити цю команду:

```shell
kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql /usr/bin/mysql.off
```

Ця дія втручається у файлову систему контейнера Podʼа `mysql-2` і перейменовує команду `mysql`, щоб проба готовності не могла її знайти. Через кілька секунд Pod повинен повідомити про один зі своїх контейнерів як неготовий, що можна перевірити за допомогою:

```shell
kubectl get pod mysql-2
```

Перевірте значення `1/2` у стовпці `READY`:

```none
NAME      READY     STATUS    RESTARTS   AGE
mysql-2   1/2       Running   0          3m
```

На цьому етапі ви повинні бачити продовження роботи вашого циклу `SELECT @@server_id`, хоча він більше не повідомляє про `102`. Нагадаємо, що скрипт `init-mysql` визначив `server-id` як `100 + $ordinal`, тож ідентифікатор сервера `102` відповідає Поду `mysql-2`.

Тепер відновіть Pod і він повинен знову зʼявитися у виводі циклу через кілька секунд:

```shell
kubectl exec mysql-2 -c mysql -- mv /usr/bin/mysql.off /usr/bin/mysql
```

### Видалення Podʼів {#delete-pods}

Контролер StatefulSet також створює Podʼи знову, якщо вони видалені, подібно до того, як це робить ReplicaSet для Podʼів без збереження стану.

```shell
kubectl delete pod mysql-2
```

Контролер StatefulSet помічає, що Podʼа `mysql-2` більше не існує, і створює новий з тією ж назвою та повʼязаний з тим самим PersistentVolumeClaim. Ви повинні побачити, що ідентифікатор сервера `102` зникне з виводу циклу протягом певного часу і потім повернеться самостійно.

### Виведення вузла з експлуатації {#drain-node}

Якщо у вашому кластері Kubernetes є кілька вузлів, ви можете симулювати відмову вузла(наприклад, під час оновлення вузлів) за допомогою команди [drain](/docs/reference/generated/kubectl/kubectl-commands/#drain).

Спочатку визначте, на якому вузлі знаходиться один із Podʼів MySQL:

```shell
kubectl get pod mysql-2 -o wide
```

Назва вузла повинна зʼявитися у останньому стовпчику:

```none
NAME      READY     STATUS    RESTARTS   AGE       IP            NODE
mysql-2   2/2       Running   0          15m       10.244.5.27   kubernetes-node-9l2t
```

Потім виведіть вузол з експлуатації, виконавши наступну команду, яка забороняє новим Podʼам запускатися на цьому вузлі та видаляє будь-які існуючі Podʼи. Замість `<node-name>` підставте назву вузла, яку ви знайшли на попередньому кроці.

<div class="alert alert-caution" role="note"><h4 class="alert-heading">Увага:</h4>Виведення вузла з експлуатації може вплинути на інші завдання та застосунки, що працюють на тому ж вузлі. Виконуйте наступний крок тільки на тестовому кластері.</div>


```shell
# Дивіться вище поради щодо впливу на інші завдання
kubectl drain <node-name> --force --delete-emptydir-data --ignore-daemonsets
```

Тепер ви можете спостерігати, як Pod переплановується на іншому вузлі:

```shell
kubectl get pod mysql-2 -o wide --watch
```

Це має виглядати приблизно так:

```none
NAME      READY   STATUS          RESTARTS   AGE       IP            NODE
mysql-2   2/2     Terminating     0          15m       10.244.1.56   kubernetes-node-9l2t
[...]
mysql-2   0/2     Pending         0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:0/2        0          0s        <none>        kubernetes-node-fjlm
mysql-2   0/2     Init:1/2        0          20s       10.244.5.32   kubernetes-node-fjlm
mysql-2   0/2     PodInitializing 0          21s       10.244.5.32   kubernetes-node-fjlm
mysql-2   1/2     Running         0          22s       10.244.5.32   kubernetes-node-fjlm
mysql-2   2/2     Running         0          30s       10.244.5.32   kubernetes-node-fjlm
```

І знову, ви повинні побачити, що ідентифікатор сервера `102` зник з виводу циклу `SELECT @@server_id` протягом певного часу, а потім повернувся.

Тепер знову дозвольте вузлу приймати навантаження:

```shell
kubectl uncordon <node-name>
```

## Масштабування кількості реплік {#scaling-the-number-of-replicas}

Коли ви використовуєте реплікацію MySQL, ви можете збільшувати кількість запитів на читання, додаючи репліки. Для StatefulSet це можна зробити однією командою:

```shell
kubectl scale statefulset mysql --replicas=5
```

Спостерігайте, як нові Podʼи запускаються, виконавши:

```shell
kubectl get pods -l app=mysql --watch
```

Коли вони будуть готові, ви побачите, що ідентифікатори серверів `103` та `104` починають зʼявлятися у виводі циклу `SELECT @@server_id`.

Ви також можете перевірити, що ці нові сервери мають дані, які ви додали до них до того, як вони існували:

```shell
kubectl run mysql-client --image=mysql:5.7 -i -t --rm --restart=Never --\
  mysql -h mysql-3.mysql -e "SELECT * FROM test.messages"
```

```none
Waiting for pod default/mysql-client to be running, status is Pending, pod ready: false
+---------+
| message |
+---------+
| hello   |
+---------+
pod "mysql-client" deleted
```

Зменшити кількість реплік також можна однією командою:

```shell
kubectl scale statefulset mysql --replicas=3
```


<div class="alert alert-info" role="note"><h4 class="alert-heading">Примітка:</h4><p>Хоча збільшення створює нові PersistentVolumeClaims автоматично, зменшення автоматично не видаляє ці PVC.</p>
<p>Це дає вам можливість залишити ці ініціалізовані PVC для швидшого збільшення, або вилучити дані перед їх видаленням.</p>
</div>


Ви можете перевірити це, виконавши:

```shell
kubectl get pvc -l app=mysql
```

Це покаже, що всі 5 PVC все ще існують, попри те, що StatefulSet був зменшений до 3:

```none
NAME           STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE
data-mysql-0   Bound     pvc-8acbf5dc-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-1   Bound     pvc-8ad39820-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-2   Bound     pvc-8ad69a6d-b103-11e6-93fa-42010a800002   10Gi       RWO           20m
data-mysql-3   Bound     pvc-50043c45-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
data-mysql-4   Bound     pvc-500a9957-b1c5-11e6-93fa-42010a800002   10Gi       RWO           2m
```

Якщо ви не збираєтеся повторно використовувати додаткові PVC, ви можете їх видалити:

```shell
kubectl delete pvc data-mysql-3
kubectl delete pvc data-mysql-4
```

## Очищення

1. Припинить цикл `SELECT @@server_id`, натиснувши **Ctrl+C** в його терміналі або виконавши наступне з іншого терміналу:

   ```shell
   kubectl delete pod mysql-client-loop --now
   ```

2. Видаліть StatefulSet. Це також розпочне завершення Podʼів.

   ```shell
   kubectl delete statefulset mysql
   ```

3. Перевірте, що Podʼи зникають. Вони можуть зайняти деякий час для завершення роботи.

   ```shell
   kubectl get pods -l app=mysql
   ```

   Ви будете знати, що Podʼи завершилися, коли вищезазначена команда поверне:

   ```none
   No resources found.
   ```

4. Видаліть ConfigMap, Services та PersistentVolumeClaims.

   ```shell
   kubectl delete configmap,service,pvc -l app=mysql
   ```

5. Якщо ви вручну створювали PersistentVolumes, вам також потрібно вручну видалити їх, а також звільнити відповідні ресурси. Якщо ви використовували динамічний провізор, він автоматично видаляє PersistentVolumes, коли бачить, що ви видалили PersistentVolumeClaims. Деякі динамічні провізори (такі як ті, що стосуються EBS та PD) також звільняють відповідні ресурси при видаленні PersistentVolumes.

## Що далі

- Дізнайтеся більше про [масштабування StatefulSet](/docs/tasks/run-application/scale-stateful-set/).
- Дізнайтеся більше про [налагодження StatefulSet](/docs/tasks/debug/debug-application/debug-statefulset/).
- Дізнайтеся більше про [видалення StatefulSet](/docs/tasks/run-application/delete-stateful-set/).
- Дізнайтеся більше про [примусове видалення Podʼів StatefulSet](/docs/tasks/run-application/force-delete-stateful-set-pod/).
- Подивіться в [сховищі Helm чартів](https://artifacthub.io/) інші приклади застосунків зі збереженням стану.
