Масштабирование приложения Node.js на MongoDB в Kubernetes с помощью Helm: подготовка среды и настройка MongoDB

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

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

В этом мануале мы развернем приложение Node.js с базой данных MongoDB в кластере Kubernetes с помощью чартов Helm. Мы используем официальный чарт реплик MongoDB для создания объекта StatefulSet, состоящего из трех подов, headless сервиса и трех элементов PersistentVolumeClaims. Также мы создадим чарт для развертывания приложения Node.js с несколькими репликами, используя собственный образ приложения. Настройка, которую мы выполним здесь, будет отражать функциональность кода, описанного в мануале Контейнеризация приложения Node.js для разработки, и станет хорошей отправной точкой для создания гибкого приложения Node.js на MongoDB, которое можно быстро масштабировать.

 Требования

Kubernetes 1.10+ с включенным RBAC (управление доступом на основе ролей).

1: Клонирование и упаковка приложения

Чтобы использовать приложение в Kubernetes, нужно упаковать его, иначе агент kubelet не сможет получить образ. Однако перед упаковкой нужно будет изменить URI соединения MongoDB в коде приложения, чтобы приложение могло подключаться к членам набора реплик, который мы создадим с помощью чарта mongodb-replicaset.

Для начала нужно клонировать репозиторий node-mongo-docker-dev с GitHub. Этот репозиторий содержит код из установки, описанной в серии Контейнеризация приложения Node.js для разработки. Это тестовое приложение Node.js с базой данных MongoDB, на котором вы можете потренироваться и понять, как настраивать среду разработки с помощью Docker Compose.

Клонируйте репозиторий в каталог node_project:

git clone https://github.com/do-community/node-mongo-docker-dev.git node_project

Перейдите в каталог node_project:

cd node_project

Каталог node_project содержит файлы и каталоги приложения, которое работает с пользовательским вводом. Оно было оптимизировано для работы с контейнерами: конфиденциальная информация о конфигурации была удалена из кода приложения и собрана в отдельный файл, откуда она загружается во время выполнения.

Читайте также: Архитектурное проектирование приложений в Kubernetes

Когда мы развернем чарт mongodb-replicaset, он создаст:

  • Объект StatefulSet с тремя подами – членами набора реплик MongoDB. Каждый под будет иметь связанный PersistentVolumeClaim и поддерживать фиксированный идентификатор в случае перепланирования.
  • Набор реплик MongoDB, состоящий из подов в StatefulSet. В набор войдут один первичный и два вторичных пода. Данные будут реплицироваться с первичного пода на вторичный, что обеспечивает высокую доступность приложения.

Чтобы приложение могло взаимодействовать с репликами базы данных, URI соединения MongoDB в нашем коде должен включать как имена хостов набора реплик, так и имя самого набора. Поэтому эти значения необходимо добавить в URI.

В клонированном репозитории есть файл, в котором указана информация о подключении к базе данных, он называется db.js. Откройте этот файл в nano или другом редакторе:

nano db.js

Читайте также: Переменные, области и поднятие переменных в JavaScript

В настоящее время файл содержит константы, на которые ссылается URI соединения во время выполнения. Значения этих констант вводятся с помощью свойства process.env, которое возвращает объект с информацией о пользовательской среде. Динамическая установка значений позволяет нам отделить код от базовой инфраструктуры, что необходимо в stateless среде.

Константы для URI соединения и сама строка URI в настоящее время выглядят так:

...
const {
MONGO_USERNAME,
MONGO_PASSWORD,
MONGO_HOSTNAME,
MONGO_PORT,
MONGO_DB
} = process.env;
...
const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;
...

В соответствии с подходом 12FA мы не должны жестко кодировать имена хостов наших экземпляров реплик или набора реплик в этой строке URI. Вместо этого можно расширить существующую константу MONGO_HOSTNAME и добавить несколько имен хостов – членов нашего набора реплик. Также нужно добавить константу набора реплик в раздел опций строки URI.

Добавьте MONGO_REPLICASET в объект константы URI и в строку соединения:

...
const {
MONGO_USERNAME,
MONGO_PASSWORD,
MONGO_HOSTNAME,
MONGO_PORT,
MONGO_DB,
MONGO_REPLICASET
} = process.env;
...
const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?replicaSet=${MONGO_REPLICASET}&authSource=admin`;
...

Опция replicaSet в разделе опций URI позволяет передавать имя набора реплик, которое (наряду с именами хостов, определенными в константе MONGO_HOSTNAME) позволит нам подключаться к членам набора.

Сохраните и закройте файл, когда вы закончите его редактировать.

Изменив информацию о соединении с БД для работы с наборами реплик, вы можете упаковать свое приложение, собрать образ с помощью команды docker build и отправить его в Docker Hub.

Создайте образ с помощью docker build и флага -t, который позволяет указать запоминающееся имя образа. В этом случае отметьте образ своим именем пользователя в Docker Hub и назовите его node-replicas (или выберите другое имя):

docker build -t your_dockerhub_username/node-replicas .

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

Процесс займет минуту или две. После завершения проверьте ваши образы:

docker images

Вы увидите следующий вывод:

REPOSITORY                              TAG                 IMAGE ID            CREATED             SIZE
your_dockerhub_username/node-replicas   latest              56a69b4bc882        7 seconds ago       90.1MB
node                                    10-alpine           aa57b0242b33        6 days ago          71MB

Затем войдите в вашу учетную запись Docker Hub:

docker login -u your_dockerhub_username

При появлении запроса введите пароль своей учетной записи. В результате такого входа в домашнем каталоге вашего пользователя sudo появится файл ~/.docker/config.json с учетными данными Docker Hub.

Загрузите образ приложения на Docker Hub с помощью команды docker push. Не забудьте заменить your_dockerhub_username своим собственным именем пользователя Docker Hub:

docker push your_dockerhub_username/node-replicas

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

2: Создание секретов для набора реплик MongoDB

Чарт stable/mongodb-replicaset предоставляет различные варианты использования секретов. Мы создадим два секрета для развертывания чарта:

  • Секрет для файла набора реплик, который будет работать как общий пароль между членами набора, позволяя им проходить аутентификацию.
  • Секрет для администратора MongoDB, который будет работать как пользователь root в базе данных admin. Эта роль позволит вам создавать других пользователей с ограниченными правами при развертывании приложения в рабочей среде.

Имея эти объекты, мы сможем установить предпочтительные значения параметров в отдельном файле значений и создать объект StatefulSet и реплику MongoDB с помощью чарта.

Во-первых, давайте создадим файл для ключей. Мы будем использовать команду openssl с опцией rand, чтобы сгенерировать 756-байтовую случайную строку:

openssl rand -base64 756 > key.txt

Выходные данные, сгенерированные этой командой, будут закодированы в base64, что обеспечит равномерную передачу данных, и будут перенаправлены в файл key.txt (следуя рекомендациям по аутентификации чарта mongodb-replicaset). Длина самого ключа должна составлять от 6 до 1024 символов, включая только символы из набора base64.

Теперь вы можете создать секрет keyfilesecret с помощью kubectl create:

kubectl create secret generic keyfilesecret --from-file=key.txt

Это создаст объект Secret в пространстве имен default, так как мы не создали отдельное пространство имен для этой установки.

Вы увидите следующий вывод, если секрет был создан:

secret/keyfilesecret created

Удалите key.txt:

rm key.txt

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

Затем создайте секрет для администратора MongoDB. Сначала нужно преобразовать желаемое имя пользователя и пароль в base64.

Преобразуйте имя пользователя вашей базы данных:

echo -n 'your_database_username' | base64

Запишите значение, которое вы видите в выводе.

Далее конвертируйте пароль:

echo -n 'your_database_password' | base64

Также запишите значение в выводе.

Откройте файл секрета:

nano secret.yaml

Примечание: Объекты Kubernetes обычно определяются с помощью YAML, где для отступа нельзя использовать табы – вместо этого обязательно нужно ставить два пробела. Если вы хотите проверить форматирование файлов YAML, вы можете использовать linter или проверить синтаксис с помощью kubectl create с флагами —dry-run и —validate:

kubectl create -f your_yaml_file.yaml --dry-run --validate=true

В общем, лучше как-то проверить синтаксис перед созданием ресурсов с помощью kubectl.

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

apiVersion: v1
kind: Secret
metadata:
name: mongo-secret
data:
user: your_encoded_username
password: your_encoded_password

Здесь мы используем имена ключей, которые нужны чарту mongodb-replicaset: user и password. Мы назвали объект mongo-secret, но вы можете назвать его как угодно.

Сохраните и закройте файл.

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

kubectl create -f secret.yaml

Вы увидите следующий вывод:

secret/mongo-secret created

Опять же, вы можете удалить secret.yaml или ограничить к нему доступ и добавить его в.gitignore.

Создав объекты Secret, вы можете добавить параметры для чарта mongodb-replicaset и создать развертывание MongoDB.

3: Настройка чарта MongoDB и создание развертывания

Helm поставляется с поддерживаемым репозиторием stable, который содержит необходимый нам чарт mongodb-replicaset. Чтобы использовать его с только что созданными секретами, мы создадим файл mongodb-values.yaml с параметрами конфигурации, а затем установим чарт с помощью этого файла.

Файл mongodb-values.yaml будет в значительной степени отражать стандартный файл values.yaml в репозитории mongodb-replicaset. Однако мы внесем в наш файл следующие изменения:

  • Мы установим значение true для параметра auth, чтобы экземпляры БД запускались с включенной авторизацией. Все клиенты должны будут проходить аутентификацию для доступа к ресурсам и операциям БД.
  • Мы добавим информацию о секретах, которые мы создали на предыдущем этапе, чтобы чарт использовал эти значения для создания файла ключей и администратора.
  • Мы уменьшим размер томов PersistentVolumes, связанных с каждым подом в StatefulSet, до минимального жизнеспособного блока, 1 ГБ (вы можете изменить его в соответствии с вашими требованиями).

Перед созданием файла mongodb-values.yaml сначала необходимо убедиться, что у вас есть готовый StorageClass, настроенный для оркестровки ресурсов хранения. Каждый из подов базе данных StatefulSet имеет липкий идентификатор и связанный с ним PersistentVolumeClaim, который будет динамически предоставлять PersistentVolume. Если под перенесен, PersistentVolume будет подключен к любой ноде, на который запланирован под (но если под или StatefulSet окончательно удален, каждый связанный с ним том нужно удалять вручную).

Сейчас вам нужно будет создать StorageClass и настроить provisioner по вашему выбору. Подробнее о том, как это сделать, см. официальную документацию.

Когда StorageClass будет настроен, откройте mongodb-values.yaml:

nano mongodb-values.yaml

В этом файле вы установите значения, которые будут:

  • Включать авторизацию.
  • Ссылаться на секреты keyfilesecret и mongo-secret.
  • Указывать 1Gi для PersistentVolumes.
  • Устанавливать имя набора реплик db.
  • Указывать 3 реплики для набора.
  • Закреплять в образе mongo последнюю версию (сейчас это 4.1.9).

Вставьте следующий код в файл:

replicas: 3
port: 27017
replicaSetName: db
podDisruptionBudget: {}
auth:
enabled: true
existingKeySecret: keyfilesecret
existingAdminSecret: mongo-secret
imagePullSecrets: []
installImage:
repository: unguiculus/mongodb-install
tag: 0.7
pullPolicy: Always
copyConfigImage:
repository: busybox
tag: 1.29.3
pullPolicy: Always
image:
repository: mongo
tag: 4.1.9
pullPolicy: Always
extraVars: {}
metrics:
enabled: false
image:
repository: ssalaues/mongodb-exporter
tag: 0.6.1
pullPolicy: IfNotPresent
port: 9216
path: /metrics
socketTimeout: 3s
syncTimeout: 1m
prometheusServiceDiscovery: true
resources: {}
podAnnotations: {}
securityContext:
enabled: true
runAsUser: 999
fsGroup: 999
runAsNonRoot: true
init:
resources: {}
timeout: 900
resources: {}
nodeSelector: {}
affinity: {}
tolerations: []
extraLabels: {}
persistentVolume:
enabled: true
#storageClass: "-"
accessModes:
- ReadWriteOnce
size: 1Gi
annotations: {}
serviceAnnotations: {}
terminationGracePeriodSeconds: 30
tls:
enabled: false
configmap: {}
readinessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
livenessProbe:
initialDelaySeconds: 30
timeoutSeconds: 5
failureThreshold: 3
periodSeconds: 10
successThreshold: 1

Параметр persistentVolume.storageClass закомментирован: удалив комментарий и установив значение «-» отключит динамическую оркестровку. Поскольку здесь мы оставляем это значение неопределенным, диаграмма выберет provisioner по умолчанию.

Также обратите внимание на accessMode, связанный с ключом persistentVolume: ReadWriteOnce означает, что подготовленный том будет доступен для чтения и записи только одной ноде. Просмотрите документацию, чтобы больше узнать о различных режимах доступа. Также смотрите таблицу конфигурации, включенную в репозиторий.

Сохраните и закройте файл.

Перед развертыванием чарта mongodb-replicaset нужно обновить репозиторий stable с помощью команды helm repo update:

helm repo update

Это позволит получить последнюю информацию о графике из стабильного хранилища.

Наконец, установите чарт с помощью следующей команды:

helm install --name mongo -f mongodb-values.yaml stable/mongodb-replicaset

Примечание: Перед установкой чарта можно запустить helm install с параметрами —dry-run и —debug, чтобы проверить сгенерированные манифесты для релиза:

helm install --name your_release_name -f your_values_file.yaml --dry-run --debug your_chart

Обратите внимание, что мы называем релиз mongo. Это имя будет относиться к конкретному развертыванию чарта с указанными параметрами конфигурации. Мы указали на эти опции, добавив флаг -f и наш файл mongodb-values.yaml.

Также обратите внимание, что поскольку мы не включили флаг —namespace в helm install, наши объекты чартов будут созданы в пространстве имен по умолчанию.

Создав релиз, вы увидите вывод о его состоянии, а также информацию о созданных объектах и ​​инструкции по взаимодействию с ними:

NAME:   mongo
LAST DEPLOYED: Tue Apr 16 21:51:05 2019
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/ConfigMap
NAME                              DATA  AGE
mongo-mongodb-replicaset-init     1     1s
mongo-mongodb-replicaset-mongodb  1     1s
mongo-mongodb-replicaset-tests    1     0s
...

Теперь вы можете проверить поды с помощью следующей команды:

kubectl get pods

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

NAME                         READY   STATUS     RESTARTS   AGE
mongo-mongodb-replicaset-0   1/1     Running    0          67s
mongo-mongodb-replicaset-1   0/1     Init:0/3   0          8s

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

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

NAME                         READY   STATUS    RESTARTS   AGE
mongo-mongodb-replicaset-0   1/1     Running   0          2m33s
mongo-mongodb-replicaset-1   1/1     Running   0          94s
mongo-mongodb-replicaset-2   1/1     Running   0          36s

Статус Running указывает, что поды связаны с нодами и что контейнеры, связанные с этими подами, работают. READY указывает, сколько контейнеров запущено в поде. Для получения дополнительной информации, пожалуйста, обратитесь к документации по жизненным циклам пода.

Примечание: Если вы видите неожиданные результаты в столбце STATUS, помните, что вы можете устранить неполадки пода с помощью следующих команд:

kubectl describe pods your_pod
kubectl logs your_pod

Каждый из подов в StatefulSet имеет имя, которое состоит из имени StatefulSet и индекса пода. Поскольку мы создали три реплики, члены StatefulSet имеют индекс 0-2, и у каждого есть стабильная запись DNS, состоящая из таких элементов: $(statefulset-name)-$(ordinal).$(service name).$(namespace).svc.cluster.local.

В данном случае StatefulSet и Headless Service, созданные чартом mongodb-replicaset, имеют одинаковые имена:

kubectl get statefulset
NAME                       READY   AGE
mongo-mongodb-replicaset   3/3     4m2s

kubectl get svc
NAME                              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)     AGE
kubernetes                        ClusterIP   10.245.0.1   <none>        443/TCP     42m
mongo-mongodb-replicaset          ClusterIP   None         <none>        27017/TCP   4m35s
mongo-mongodb-replicaset-client   ClusterIP   None         <none>        27017/TCP   4m35s

Это означает, что первый член StatefulSet будет иметь следующую запись DNS:

mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local

Поскольку нам нужно, чтобы приложение подключалось к каждому экземпляру MongoDB, важно владеть этой информацией, чтобы напрямую взаимодействовать с подами, а не с сервисами. Когда мы будем создавать пользовательский чарт, мы передадим DNS записи для каждого пода, используя переменные среды. Об этом речь пойдет в следующем мануале.

Tags: , , , ,