Kubernetes Volumes: реплицированная MongoDB со StatefulSet

от автора

Автор статьи: Рустем Галиев

IBM Senior DevOps Engineer & Integration Architect

Привет Хабр!

В Kubernetes StatefulSet — это реплицированные группы Pod’ов, аналогичные ReplicaSet’ам.

Каждая реплика получает постоянное имя хоста с уникальным индексом (например, database-0, database-1 и т. д.).

Каждая реплика создается в порядке от самого низкого до самого высокого индекса и создание блокируется до тех пор, пока под с предыдущим индексом не станет работоспособным и доступным. Это относится и к масштабированию.

При удалении StatefulSet каждый из управляемых подов реплики также удаляется в порядке убывания. Это также относится к уменьшению количества реплик.

Оказывается, этот простой набор требований значительно упрощает развертывание приложений для хранения данных в Kubernetes. Например, сочетание стабильных имен хостов (например, database-0) и ограничений порядка означает, что все реплики, кроме первой, могут надежно ссылаться на database-0 для целей обнаружения и установления кворума репликации.

Сегодня мы развернем реплицированный кластер MongoDB с StatefulSet.

Для начала создадим реплицированный набор из трех модулей MongoDB, используя объект StatefulSet. Основной контейнер приложения использует образ контейнера mongo:3.4.24 и запускает процесс mongod. Когда вы запускаете mongod, запускается процесс MongoDB и запускается он в фоновом режиме. У процесса есть несколько параметров по умолчанию, например, сохранение данных в /data/db и запуск через порт 27017.

apiVersion: apps/v1 kind: StatefulSet metadata:   name: mongo spec:   serviceName: "mongo"   replicas: 3   selector:     matchLabels:       app: mongo   template:     metadata:       labels:         app: mongo     spec:       containers:       - name: mongodb         image: mongo:3.4.24         command:         - mongod         - --replSet         - rs0         ports:         - containerPort: 27017           name: peer

Пока не создавайте объект, так как мы будем вносить дополнительные изменения в манифест YAML.

Инициализируем кластер MongoDB с помощью init Container

Чтобы автоматизировать развертывание нашего кластера MongoDB на основе StatefulSet, мы собираемся добавить дополнительный контейнер в поды для выполнения инициализации.

Чтобы настроить этот модуль без создания нового образа Docker, мы собираемся использовать ConfigMap для добавления скрипта в существующий образ MongoDB.

Мы собираемся запустить этот скрипт, используя контейнер инициализации. Контейнеры инициализации (или init Container) — это специализированные контейнеры, которые запускаются один раз при запуске пода. Они обычно используются в таких случаях, когда есть небольшой объем работы по настройке, которую полезно выполнить до запуска основного приложения. В определении пода есть отдельный список initContainers, в котором можно определить контейнеры инициализации.

Последним этапом производства нашего кластера MongoDB является добавление проверок жизнеспособности в наши контейнеры, обслуживающие Mongo:

apiVersion: apps/v1 kind: StatefulSet metadata:   name: mongo spec:   serviceName: "mongo"   replicas: 3   selector:     matchLabels:       app: mongo   template:     metadata:       labels:         app: mongo     spec:       containers:       - name: mongodb         image: mongo:3.4.24         command:         - mongod         - --replSet         - rs0         ports:         - containerPort: 27017           name: web         livenessProbe:           exec:             command:             - /usr/bin/mongo             - --eval             - db.serverStatus()           initialDelaySeconds: 10           timeoutSeconds: 10       # This container initializes the mongodb server, then sleeps.       - name: init-mongo         image: mongo:3.4.24         command:         - bash         - /config/init.sh         volumeMounts:         - name: config           mountPath: /config       volumes:       - name: config         configMap:           name: "mongo-init"

Замените содержимое в файле mongo.yaml. На следующем шаге мы создадим ConfigMap с именем mongo-init.

Создание карты конфигурации

Обратите внимание, что Pod монтирует ConfigMap Volume (том) с именем mongo-init. Этот ConfigMap содержит скрипт, который выполняет нашу инициализацию. Во-первых, скрипт определяет, работает ли он на mongo-0. Если он находится на mongo-0, он создает ReplicaSet с помощью той же команды, которую мы ранее запускали императивно. Если он находится в другой реплике Mongo,ждет, пока ReplicaSet exists (как бы правильно сказать, не появится), а затем регистрируется как мембер этого ReplicaSet.

Следующий манифест YAML содержит полный объект ConfigMap:

apiVersion: v1 kind: ConfigMap metadata:   name: mongo-init data:   init.sh: |     #!/bin/bash      # Need to wait for the readiness health check to pass so that the     # mongo names resolve. This is kind of wonky.     until ping -c 1 ${HOSTNAME}.mongo; do       echo "waiting for DNS (${HOSTNAME}.mongo)..."       sleep 2     done      until /usr/bin/mongo --eval 'printjson(db.serverStatus())'; do       echo "connecting to local mongo..."       sleep 2     done     echo "connected to local."      HOST=mongo-0.mongo:27017      until /usr/bin/mongo --host=${HOST} --eval 'printjson(db.serverStatus())'; do       echo "connecting to remote mongo..."       sleep 2     done     echo "connected to remote."      if [[ "${HOSTNAME}" != 'mongo-0' ]]; then       until /usr/bin/mongo --host=${HOST} --eval="printjson(rs.status())" \             | grep -v "no replset config has been received"; do         echo "waiting for replication set initialization"         sleep 2       done       echo "adding self to mongo-0"       /usr/bin/mongo --host=${HOST} \          --eval="printjson(rs.add('${HOSTNAME}.mongo'))"     fi      if [[ "${HOSTNAME}" == 'mongo-0' ]]; then       echo "initializing replica set"       /usr/bin/mongo --eval="printjson(rs.initiate(\           {'_id': 'rs0', 'members': [{'_id': 0, \            'host': 'mongo-0.mongo:27017'}]}))"     fi     echo "initialized"

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

Сначала создайте объект ConfigMap:

kubectl apply -f mongo-configmap.yaml

Затем мы создадим объект StatefulSet, который теперь сможет ссылаться на ConfigMap по имени.

kubectl apply -f mongo.yaml

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

kubectl get pods

Между этим и тем, что вы увидите с ReplicaSet, есть два важных отличия. Во-первых, каждый реплицированный Pod имеет числовой индекс (0, 1,…) вместо случайного суффикса, который добавляется контроллером ReplicaSet. Во-вторых, поды медленно создаются по порядку, а не все сразу, как это было бы с ReplicaSet.

Предоставление MongoDB как сервиса

После создания StatefulSet нам также необходимо создать «безголовую» службу для управления записями DNS для StatefulSet. В Kubernetes сервис называется «безголовым», если у него нет виртуального IP-адреса кластера. Поскольку со StatefulSets каждый Pod имеет уникальный идентификатор, на самом деле не имеет смысла иметь IP-адрес для балансировки нагрузки для реплицируемого сервиса. Вы можете создать безголовый сервис, используя clusterIP: None в спецификации сервиса:

apiVersion: v1 kind: Service metadata:   name: mongo spec:   ports:   - port: 27017     name: peer   clusterIP: None   selector:     app: mongo

После создания этой службы обычно заполняются четыре записи DNS. Как обычно, создается mongo.default.svc.cluster.local, но, в отличие от стандартной службы, поиск DNS по этому имени хоста предоставляет все адреса в StatefulSet. Кроме того, создаются записи для mongo-0.mongo.default.svc.cluster​.local, а также для mongo-1.mongo и mongo-2.mongo. Каждый из них разрешается в определенный IP-адрес индекса реплики в StatefulSet. Таким образом, с помощью StatefulSets вы получаете четко определенные постоянные имена для каждой реплики в наборе. Это часто бывает очень полезно при настройке решения для хранения данных с репликацией.

Создайте службу с помощью следующей команды:

kubectl apply -f mongo-service.yaml

После того, как мы объединили StatefulSets, pvc и проверку живучести, у нас есть защищенная, масштабируемая облачная установка MongoDB, работающая в Kubernetes. Хотя в этой теме речь шла о MongoDB, шаги по созданию StatefulSet для управления другими решениями для хранения очень похожи и можно следовать аналогичным шаблонам.

В завершение хочу пригласить вас на бесплатный урок, где мои коллеги из OTUS расскажут как устроен мониторинг кластера, его компоненты и приложения в кластере. Вы изучите различные подходы мониторинга, подходы к мониторингу как приложения так и компонентов кластера, основные метрики Kubernetes. Также узнаете про кластеризацию/федерацию Prometheus, дополнительные хранилища метрик для prometheus (victoria metrics; thanos, cortex).


ссылка на оригинал статьи https://habr.com/ru/articles/726914/


Комментарии

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

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