StatefulSet в Kubernetes – примеры и наилучшие практики

от автора

image

Собираетесь ли вы развертывать базу данных в кластере Kubernetes? Если так – то это отличный выбор. Kubernetes – это инструмент оркестрации контейнеров, который при помощи множества инструментов координирует эксплуатацию приложений в контейнерах (подах). Один из этих контроллеров называется StatefulSet и используется для эксплуатации приложений, сохраняющих состояние.

Развертывание приложений с сохранением состояния в кластере Kubernetes порой бывает утомительной задачей. Дело в том, что приложение, сохраняющее состояние ожидает, что придется работать с архитектурой «ведущий-ведомый» (primary-replica), а имя пода будет фиксированным. Контроллер StatefulSets решает эту проблему, развертывая в кластере Kubernetes приложение с сохранением состояния.

Из этой статьи вы подробнее узнаете, что представляют собой StatefulSet в кластере Kubernetes, и в каких ситуациях из использовать, а также как развернуть приложение с сохранением состояния при помощи контроллера StatefulSets. Все это будет рассмотрено в пошаговом примере, в который мы включим все файлы манифеста (YAML). Более того, концепция StatefulSets будет продемонстрирована при помощи базы данных MySQL, и мы заодно рассмотрим, как создать фиксированное имя пода для каждой репликации MySQL, а также как обращаться к реплицированным подам через объект Services.

Что такое приложения с сохранением состояния?

Это приложения, которые сохраняют данные и помогают их отслеживать. Все базы данных, в частности, MySQL, Oracle и PostgreSQL – это примеры приложений, сохраняющих состояние. С другой стороны, в приложениях без сохранения состояния данные долго не держатся. Примеры приложений без сохранения состояния — Node.js и Nginx. Если состояние в приложении не сохраняется, то на каждый запрос приложение будет получать новые данные и обрабатывать их.

В современных веб-приложениях такие приложения без сохранения состояния соединяются с приложениями, сохраняющими состояние, чтобы обслужить пользовательский запрос. Приложение Node.js не сохраняет состояние, оно получает новые данные при каждом запросе, поступающем от пользователя. Далее это приложение соединяется для обработки данных с другим, сохраняющим состояние, например, с базой данных MySQL. База данных MySQL сохраняет данные и продолжает их обновлять, исходя из пользовательского запроса.

image

Далее подробнее поговорим о структурах StatefulSet в кластере Kubernetes — что они собой представляют, как ими пользоваться, и каковы наилучшие практики обращения с ними.

О структурах StatefulSet

StatefulSet – это контроллер Kubernetes, применяемый для эксплуатации сохраняющих состояние приложений в виде контейнеров (подов) в кластере Kubernetes. StatefulSet присваивают каждому поду идентификатор-липучку (sticky identity) – порядковый номер начиная с нуля — а не случайные ID каждой реплике пода. Новый под создается клонированием данных уже существовавшего пода. Если ранее существовавший под находился в ожидающем состоянии, то новый под создан не будет. Удаление подов происходит в обратном порядке, а не в случайном. Например, если у вас было четыре реплики, и в результате масштабирования их количество было сокращено до трех, то под номер 3 будет удален.

На приведенной ниже схеме показано, как поды нумеруются, начиная с нуля, и как в StatefulSet к поду прикрепляется том долговременного хранения данных.

image

Когда использовать StatefulSets

Есть несколько причин, по которым может быть целесообразно использовать StatefulSets. Рассмотрим два примера:

  1. Допустим, вы развернули базу данных MySQL в кластере Kubernetes и масштабировали ее до трех реплик, а клиентское приложение пытается получить доступ к кластеру MySQL, чтобы считывать и записывать данные. Запрос на считывание будет переадресовываться на три пода. Однако запрос на запись будет переадресовываться только на первый (ведущий) под, а записанные сюда данные будут синхронизироваться с другими подами. Это достижимо при помощи StatefulSets.
  2. Если удалить StatefulSet или отмасштабировать вниз, то не будут удалены тома, связанные с приложением, сохраняющим состояние. Так вашим данным обеспечивается безопасность. Если удалить или перезапустить под с MySQL, то вы сможете обращаться к данным из все того же тома, что и раньше.

Объект Deployment и StatefulSets

Также можно создавать поды (контейнеры), используя в кластере Kubernetes объект Deployment. Так вы сможете с легкостью реплицировать поды и прикреплять к подам тома, служащие в качестве хранилищ данных. То же самое можно проделать и при помощи StatefulSets. В чем же тогда преимущество работы со StatefulSets?

Дело в том, что подам, создаваемым при помощи объекта Deployment, присваиваются случайные ID. Например, вы создаете под с именем “my-app”, после чего расширяете его до трех реплик. Имена этих подов создаются примерно так:

my-app-123ab my-app-098bd my-app-890yt

Вслед за именем “my-app” добавляются случайные ID. Если под перезапускается, либо выполняется масштабирование вниз, то, опять же, объект Kubernetes Deployment присвоит иные случайные ID каждому поду. После перезапуска имена всех подов примут вид:

my-app-jk879 my-app-kl097 my-app-76hf7

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

image

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

Рассмотрим развернутый инстанс базы данных MySQL. Предположим, вы создаете поды для базы данных MySQL при помощи объекта Kubernetes Deployment и масштабируете поды. Если вы записываете данные в один под MySQL, то ни в коем случае не реплицируйте те же данные на другом поде MySQL, если под перезапускается. Это первая проблема с объектом Kubernetes Deployment применительно к приложениям, сохраняющим состояние.

image

Приложениям, сохраняющим состояние, всегда нужен липкий идентификатор. Притом, что объект Kubernetes Deployment предлагает случайные ID для каждого пода, контроллер Kubernetes StatefulSets предлагает для каждого пода порядковый номер начиная с нуля, вида: mysql-0, mysql-1, mysql-2 и т. д.

image

Для приложений, сохраняющих состояние и работающих с контроллером StatefulSet, можно задать первый под в качестве ведущего, а остальные поды сделать репликами ведущего. Первый под будет обрабатывать поступающие от пользователя как на чтение, так и на запись, а прочие поды будут всегда синхронизироваться с первым для репликации данных. Если под погибает, то создается одноименный ему новый под.

На следующей схеме показана архитектура с ведущим инстансом MySQL и репликами, где предусмотрен том для долговременного хранения данных и система репликации данных.

image

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

image

Резюмируя, обозначим, что StatefulSets обеспечивают следующие преимущества по сравнению с объектами Deployment:

  1. Порядковые номера для каждого из подов.
  2. Первый под может выступать в качестве ведущего, благодаря чему хорошо подходит для подготовки конфигурации с реплицируемой базой данных – такая конфигурация позволяет обрабатывать как чтение, так и запись.
  3. Другие поды действуют в качестве реплик.
  4. Новые поды будут создаваться лишь в случае, если более ранний под сейчас действует, причем, данные более раннего пода клонируются.
  5. Поды удаляются в порядке, обратном тому, в котором создавались.

Как создать StatefulSet в Kubernetes

В этом разделе будет рассказано, как создать под для базы данных MySQL, воспользовавшись для этого контроллером StatefulSets.

Создаем секрет

Сперва нужно создать для приложения MySQL секрет, в котором будет храниться конфиденциальная информация, в частности, имена пользователей и пароли. Здесь я создаю простой секрет. Но при работе в продакшене рекомендуется использовать хранилище Vault от HashiCorp. Секрет для MySQL создается при помощи следующего кода:

apiVersion: v1 kind: Secret metadata:   name: mysql-password type: opaque stringData:   MYSQL_ROOT_PASSWORD: password

Сохраните код в файле, который будет называться mysql-secret.yaml, и выполните код в вашем кластере Kubernetes при помощи следующего кода:

kubectl apply -f mysql-secret.yaml

Получите список секретов:

kubectl get secrets

Создаем приложение StatefulSet для MySQL

Прежде, чем создать приложение StatefulSet, проверьте ваши тома – для этого просто выведите список томов долговременного хранения:

kubectl get pv NAME                   CAPACITY   ACCESS MODES   RECLAIM     STATUS pvc-e0567   10Gi       RWO            Retain           Bound

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

kubectl get pvc  NAME                            STATUS   VOLUME                     CAPACITY   ACCESS  mysql-store-mysql-set-0   Bound    pvc-e0567d43ffc6405b   10Gi       RWO

Наконец, получим список классов хранилища:

kubectl get storageclass  NAME                    PROVISIONER               RECLAIMPOLICY   linode-block-storage    linodebs.csi.linode.com   Delete          linode-block-storage-retain (default)   linodebs.csi.linode.com  Retain         

Затем при помощи следующего кода создадим для MySQL приложение StatefulSet в кластере Kubernetes:

apiVersion: apps/v1 kind: StatefulSet metadata:   name: mysql-set spec:   selector:     matchLabels:       app: mysql   serviceName: "mysql"   replicas: 3   template:     metadata:       labels:         app: mysql     spec:       terminationGracePeriodSeconds: 10       containers:       - name: mysql         image: mysql:5.7         ports:         - containerPort: 3306         volumeMounts:         - name: mysql-store           mountPath: /var/lib/mysql         env:           - name: MYSQL_ROOT_PASSWORD             valueFrom:               secretKeyRef:                 name: mysql-password                 key: MYSQL_ROOT_PASSWORD   volumeClaimTemplates:   - metadata:       name: mysql-store     spec:       accessModes: ["ReadWriteOnce"]       storageClassName: "linode-block-storage-retain"       resources:         requests:           storage: 5Gi

Здесь нужно отметить несколько вещей:

  1. kind – это StatefulSet. kind приказывает Kubernetes создать приложение MySQL с возможностью сохранения состояния
  2. Пароль берется из объекта Secret при помощи secretKeyRef.
  3. Блоковое хранилище Linode использовалось в volumeClaimTemplates. Если здесь не упомянуть имени никакого класса хранилища, то для вашего кластера будет выбрано то хранилище, что задано по умолчанию.
  4. Количество реплик здесь равно 3 (указывается в параметре replica), поэтому будет создано три пода с именами mysql-set-0, mysql-set-1 и mysql-set-2.

Далее сохраняем код в файле под именем mysql.yaml и выполняем его при помощи следующей команды:

kubectl apply -f mysql.yaml Теперь, когда поды MySQL созданы, получаем список подов: kubectl get pods  NAME          READY   STATUS      RESTARTS   AGE mysql-set-0   1/1         Running        0                 142s mysql-set-1   1/1         Running        0                 132s mysql-set-2   1/1         Running        0                 120s

Создаем сервис для приложения со StatefulSet

Теперь создаем сервис для пода MySQL. С приложением, сохраняющим состояние, нельзя использовать балансировщик нагрузки. Вместо этого создадим для приложения MySQL «безголовый» сервис, воспользовавшись следующим кодом:

apiVersion: v1 kind: Service metadata:   name: mysql   labels:     app: mysql spec:   ports:   - port: 3306   clusterIP: None   selector:     app: mysql

Сохраняем этот код в файле под именем mysql-service.yaml и выполняем при помощи следующей команды:

kubectl apply -f mysql-service.yaml

Получаем список работающих сервисов:

kubectl get svc

Создаем клиент для MySQL

Если вы хотите обращаться к MySQL, то вам понадобится клиентский инструмент для работы с MySQL. Разверните клиент MySQL, воспользовавшись следующим кодом манифеста:

apiVersion: v1 kind: Pod metadata:   name: mysql-client spec:   containers:   - name: mysql-container     image: alpine     command: ['sh','-c', "sleep 1800m"]     imagePullPolicy: IfNotPresent

Сохраните этот код в файле под именем mysql-client.yaml и выполните при помощи следующей команды:

kubectl apply -f mysql-client.yaml

Затем введите в клиент MySQL следующий код:

kubectl exec --stdin --tty mysql-client -- sh

Наконец, установите клиентский инструмент для работы с MySQL:

apk add mysql-client

Обратитесь к приложению MySQL через клиент MySQL

Далее будем обращаться к приложению MySQL через клиент MySQL и создавать базы данных в подах.

Если вы еще не зашли в под с клиентом MySQL, введите следующий код:
kubectl exec -it mysql-client /bin/sh

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

mysql -u root -p -h host-server-name

Для доступа вам понадобится имя сервера MySQL. Синтаксис сервера MySQL в кластере Kubernetes приведен ниже:

stateful_name-ordinal_number.mysql.default.svc.cluster.local  #Example mysql-set-0.mysql.default.svc.cluster.local

Подключитесь к ведущему поду MySQL при помощи следующей команды. Когда система запросит пароль, введите тот, который вы подобрали в предыдущем разделе о создании секрета.

mysql -u root -p -h mysql-set-0.mysql.default.svc.cluster.local

Далее создайте базу данных в ведущем поде с MySQL, после этого выйдите:

create database erp; exit;

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

mysql -u root -p -h mysql-set-1.mysql.default.svc.cluster.local
mysql -u root -p -h mysql-set-2.mysql.default.svc.cluster.local

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

Наилучшие практики

Если вы планируете развертывать приложения, работающие с сохранением состояния, например, Oracle, MySQL, Elasticsearch и MongoDB, то StatefulSets – отличный вариант для вас.
Создавая приложения с сохранением состояния, учитывайте следующие факторы:

  1. Создайте для баз данных отдельное пространство имен.
  2. Все компоненты, необходимые для приложений с сохранением состояния, например, ConfigMaps, секреты и сервисы, размещайте в конкретном пространстве имен.
  3. Ваши собственные скрипты размещайте в ConfigMaps.
  4. При создании объектов «сервис» пользуйтесь безголовым сервисом, а не сервисом для балансировки нагрузки.
  5. Секреты храните в Vault или HashiCorp.
  6. Данные храните только в томе для долговременного хранения. В таком случае ваши данные не будут удалены, если под погибнет или аварийно завершит работу.

Объекты Deployment – это самые ходовые контроллеры, используемые для создания подов в Kubernetes. Эти поды легко масштабировать, для этого достаточно указать в файле манифеста количество реплик. Например, допустим, вы планируете развернуть приложение Node.js, после чего собираетесь распространить его на пять реплик. В таком случае вам хорошо подойдет объект Deployment.

На следующей схеме показано, как Deployment и StatefulSets присваивают имена подам.

image

StatefulSets создают пронумерованные по порядку конечные точки сервисов для каждого пода, созданного путем репликации. На следующей схеме показано, как создаются конечные точки сохраняющих состояние подов (пронумерованные по порядку), и как они обмениваются информацией друг с другом.

image

Заключение

В этой статье было рассказано о двух главных контроллерах, используемых в Kubernetes для создания подов: Deployment и StatefulSet. Объект Deployment очень хорош для работы с приложениями, не сохраняющими состояние, а StatefulSets – с сохраняющими. Если вы планируете развертывать приложения, сохраняющие состояние, например, MySQL и Oracle, следует воспользоваться контроллером StatefulSets, а не объектом Deployment.

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

Пользуйтесь контроллером StatefulSets в кластере Kubernetes, если нужно развертывать приложения, сохраняющие состояние, например, Oracle, MySQL, Elasticsearch и MongoDB. Притом, что клонирование и синхронизацию данных все равно потребуется обеспечивать вручную, StatefulSets в значительной степени упрощают развертывание приложений, сохраняющих состояние.


ссылка на оригинал статьи https://habr.com/ru/company/timeweb/blog/703550/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *