Как масштабировать приложение Node.js с MongoDB в Kubernetes, используя Helm

Вступление

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

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

В этом руководстве вы развернете приложениеNode.js с базой данных MongoDB в кластере Kubernetes с помощью диаграмм Helm. Вы будете использоватьofficial Helm MongoDB replica set chart для созданияStatefulSet object, состоящего из трехPods, aHeadless Service и трехPersistentVolumeClaims. Вы также создадите диаграмму для развертывания приложения Node.js с несколькими репликами, используя собственный образ приложения. Настройка, которую вы создадите в этом руководстве, будет отражать функциональность кода, описанного вContainerizing a Node.js Application with Docker Compose, и станет хорошей отправной точкой для создания устойчивого приложения Node.js с хранилищем данных MongoDB, которое может масштабироваться в соответствии с вашими потребностями.

Предпосылки

Для завершения этого урока вам понадобится:

  • Кластер Kubernetes 1.10+ с включенным управлением доступом на основе ролей (RBAC). Эта установка будет использоватьDigitalOcean Kubernetes cluster, но вы можете использоватьcreate a cluster using another method.

  • Инструмент командной строкиkubectl, установленный на вашем локальном компьютере или сервере разработки и настроенный для подключения к вашему кластеру. Вы можете узнать больше об установкеkubectl вofficial documentation.

  • Helm, установленный на вашем локальном компьютере или сервере разработки, и Tiller установлен в вашем кластере, следуя указаниям, изложенным в шагах 1 и 2How To Install Software on Kubernetes Clusters with the Helm Package Manager.

  • Docker установлен на вашем локальном компьютере или сервере разработки. Если вы работаете с Ubuntu 18.04, выполните шаги 1 и 2How To Install and Use Docker on Ubuntu 18.04; в противном случае следуйтеofficial documentation для получения информации об установке в других операционных системах. Обязательно добавьте своего пользователя без полномочий root в группуdocker, как описано в шаге 2 связанного руководства.

  • АккаунтDocker Hub. Чтобы узнать, как это настроить, обратитесь кthis introduction в Docker Hub.

[[шаг-1 -—- клонирование-и-упаковка-приложения]] == Шаг 1. Клонирование и упаковка приложения

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

Нашим первым шагом будет клонированиеnode-mongo-docker-dev repository изDigitalOcean Community GitHub account. Этот репозиторий включает код из настройки, описанной вContainerizing a Node.js Application for Development With Docker Compose, в которой используется демонстрационное приложение Node.js с базой данных MongoDB, чтобы продемонстрировать, как настроить среду разработки с помощью Docker Compose. Вы можете найти больше информации о самом приложении в серииFrom Containers to Kubernetes with Node.js.

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

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

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

cd node_project

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

Для получения дополнительных сведений о разработке современных контейнерных приложений см.Architecting Applications for Kubernetes иModernizing Applications for Kubernetes.

Когда мы развернем диаграмму Helmmongodb-replicaset, она создаст:

  • Объект StatefulSet с тремя подами - членами MongoDBreplica set. Каждый Pod будет иметь связанный PersistentVolumeClaim и будет поддерживать фиксированную идентификацию в случае перепланирования.

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

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

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

nano db.js

В настоящее время файл включаетconstants, на которые ссылается URI соединения с базой данных во время выполнения. Значения для этих констант вводятся с помощью свойстваprocess.env узла, которое возвращает объект с информацией о вашей пользовательской среде во время выполнения. Динамическая установка значений в коде нашего приложения позволяет нам отделить код от базовой инфраструктуры, что необходимо в динамической среде без сохранения состояния. Для получения дополнительной информации о таком рефакторинге кода приложения см.Step 2 ofContainerizing a Node.js Application for Development With Docker Compose и соответствующее обсуждение вThe 12-Factor App.

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

~/node_project/db.js

...
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 может быть расширена, чтобы включить несколько имен хостов - членов нашего набора реплик - поэтому мы оставим это на месте. Однако нам нужно будет добавить константу набора реплик кoptions section строки URI.

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

~/node_project/db.js

...
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 option в разделе параметров URI позволяет нам передать имя набора реплик, что вместе с именами хостов, определенными в константеMONGO_HOSTNAME, позволит нам подключиться к набору члены.

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

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

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

docker build -t your_dockerhub_username/node-replicas .

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

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

docker images

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

OutputREPOSITORY                              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

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

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

docker push your_dockerhub_username/node-replicas

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

[[step-2 -—- created-secrets-for-the-mongodb-replica-set]] == Шаг 2. Создание секретов для набора реплик MongoDB

Диаграммаstable/mongodb-replicaset предоставляет различные варианты использования секретов, и мы создадим два для использования с нашим развертыванием диаграммы:

  • Секрет для нашегоreplica set keyfile, который будет работать как общий пароль между членами набора реплик, позволяя им аутентифицировать других членов.

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

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

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

openssl rand -base64 756 > key.txt

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

Теперь вы можете создать секрет с именемkeyfilesecret, используя этот файл сkubectl create:

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

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

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

Outputsecret/keyfilesecret created

Удалитьkey.txt:

rm key.txt

В качестве альтернативы, если вы хотите сохранить файл, убедитесь, чтоrestrict its permissions и добавьте его в свой.gitignore file, чтобы он не попадал под контроль версий.

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

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

echo -n 'your_database_username' | base64

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

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

echo -n 'your_database_password' | base64

Также обратите внимание на значение в выводе.

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

nano secret.yaml

[.Примечание]##

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

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

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

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

~/node_project/secret.yaml

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

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

Outputsecret/mongo-secret created

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

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

[[step-3 -—- configuring-the-mongodb-helm-chart-and-create-a-deployment]] == Шаг 3. Настройка диаграммы управления MongoDB и создание развертывания

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

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

  • Мы установим для параметраauth значениеtrue, чтобы гарантировать, что наши экземпляры базы данных начинаются сauthorization enabled. Это означает, что все клиенты должны будут проходить аутентификацию для доступа к ресурсам и операциям базы данных.

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

  • Мы уменьшим размер PersistentVolumes, связанных с каждым модулем в StatefulSet, чтобы использоватьminimum viable DigitalOcean Block Storage unit, 1 ГБ, хотя вы можете изменить это в соответствии с вашими требованиями к хранилищу.

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

Поскольку мы работаем сDigitalOcean Kubernetes, наш класс StorageClassprovisioner по умолчанию установлен наdobs.csi.digitalocean.com -DigitalOcean Block Storage, что мы можем проверить, набрав:

kubectl get storageclass

Если вы работаете с кластером DigitalOcean, вы увидите следующий вывод:

OutputNAME                         PROVISIONER                 AGE
do-block-storage (default)   dobs.csi.digitalocean.com   21m

Если вы не работаете с кластером DigitalOcean, вам нужно будет создать StorageClass и настроитьprovisionerпо вашему выбору. Подробнее о том, как это сделать, см. Вofficial documentation.

Теперь, когда вы убедились, что у вас настроен StorageClass, откройтеmongodb-values.yaml для редактирования:

nano mongodb-values.yaml

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

  • Включить авторизацию.

  • Ссылайтесь на свои объектыkeyfilesecret иmongo-secret.

  • Укажите1Gi для ваших PersistentVolumes.

  • Установите имя набора реплик наdb.

  • Укажите реплики3 для набора.

  • Закрепите образmongo в последней версии на момент написания:4.1.9.

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

~/node_project/mongodb-values.yaml

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 - в нашем случаеdobs.csi.digitalocean.com.

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

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

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

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

helm repo update

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

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

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

[.Примечание]##

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

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

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

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

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

OutputNAME:   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

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

OutputNAME                         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 не полностью готовы:Init Containers, связанные с контейнерами Pod, все еще работают. Поскольку членами StatefulSet являютсяcreated in sequential order, каждый Pod в StatefulSet должен бытьRunning иReady, прежде чем будет создан следующий Pod.

После создания модулей и запуска всех связанных с ними контейнеров вы увидите следующий вывод:

OutputNAME                         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

RunningSTATUS указывает, что ваши модули привязаны к узлам и что контейнеры, связанные с этими модулями, работают. READY указывает, сколько контейнеров в поде работает. Для получения дополнительной информации обратитесь кdocumentation on Pod lifecycles.

[.Примечание]##

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

kubectl describe pods your_pod
kubectl logs your_pod

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

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

kubectl get statefulset
OutputNAME                       READY   AGE
mongo-mongodb-replicaset   3/3     4m2s
kubectl get svc
OutputNAME                              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)     AGE
kubernetes                        ClusterIP   10.245.0.1           443/TCP     42m
mongo-mongodb-replicaset          ClusterIP   None                 27017/TCP   4m35s
mongo-mongodb-replicaset-client   ClusterIP   None                 27017/TCP   4m35s

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

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

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

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

[[step-4 -—- created-a-custom-application-chart-and-configuring-parameters]] == Шаг 4 - Создание пользовательской диаграммы приложения и настройка параметров

Мы создадим пользовательскую диаграмму Хелма для нашего приложения Node и изменим файлы по умолчанию в стандартном каталоге диаграмм, чтобы наше приложение могло работать с только что созданным набором реплик. Мы также создадим файлы для определения объектов ConfigMap и Secret для нашего приложения.

Сначала создайте новый каталог диаграмм с именемnodeapp с помощью следующей команды:

helm create nodeapp

Это создаст каталог с именемnodeapp в вашей папке~/node_project со следующими ресурсами:

  • ФайлChart.yaml с основной информацией о вашем графике.

  • Файлvalues.yaml, который позволяет вам устанавливать определенные значения параметров, как вы это делали при развертывании MongoDB.

  • Файл.helmignore с шаблонами файлов и каталогов, которые будут игнорироваться при упаковке диаграмм.

  • Каталогtemplates/ с файлами шаблонов, которые будут генерировать манифесты Kubernetes.

  • Каталогtemplates/tests/ для тестовых файлов.

  • Каталогcharts/ для любых диаграмм, от которых зависит эта диаграмма.

Первым файлом, который мы изменим из этих файлов по умолчанию, будетvalues.yaml. Откройте этот файл сейчас:

nano nodeapp/values.yaml

Значения, которые мы здесь установим, включают:

  • Количество реплик.

  • Изображение приложения, которое мы хотим использовать. В нашем случае это будет изображениеnode-replicas, которое мы создали вStep 1.

  • ServiceType. В этом случае мы укажемLoadBalancer, чтобы создать точку доступа к нашему приложению в целях тестирования. Поскольку мы работаем с кластером DigitalOcean Kubernetes, это создастDigitalOcean Load Balancer при развертывании нашей диаграммы. В производственной среде вы можете настроить диаграмму для использованияIngress Resources иIngress Controllers для маршрутизации трафика к вашим Сервисам.

  • targetPort, чтобы указать порт на модуле, где будет отображаться наше приложение.

Мы не будем вводить переменные среды в этот файл. Вместо этого мы создадим шаблоны для объектов ConfigMap и Secret и добавим эти значения в манифест развертывания нашего приложения, расположенный в~/node_project/nodeapp/templates/deployment.yaml.

Настройте следующие значения в файлеvalues.yaml:

~/node_project/nodeapp/values.yaml

# Default values for nodeapp.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 3

image:
  repository: your_dockerhub_username/node-replicas
  tag: latest
  pullPolicy: IfNotPresent

nameOverride: ""
fullnameOverride: ""

service:
  type: LoadBalancer
  port: 80
  targetPort: 8080
...

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

Затем откройте файлsecret.yaml в каталогеnodeapp/templates:

nano nodeapp/templates/secret.yaml

В этом файле добавьте значения для констант приложенияMONGO_USERNAME иMONGO_PASSWORD. Это константы, к которым ваше приложение ожидает получить доступ во время выполнения, как указано вdb.js, файле подключения к базе данных. Добавляя значения для этих констант, не забывайте использовать значения base64 -encoded, которые вы использовали ранее вStep 2 при создании объектаmongo-secret. Если вам нужно восстановить эти значения, вы можете вернуться к шагу 2 и снова запустить соответствующие команды.

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

~/node_project/nodeapp/templates/secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-auth
data:
  MONGO_USERNAME: your_encoded_username
  MONGO_PASSWORD: your_encoded_password

Имя этого секретного объекта будет зависеть от имени вашего релиза Helm, которое вы укажете при развертывании диаграммы приложения.

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

Затем откройте файл, чтобы создать ConfigMap для вашего приложения:

nano nodeapp/templates/configmap.yaml

В этом файле мы определим оставшиеся переменные, которые ожидает наше приложение:MONGO_HOSTNAME,MONGO_PORT,MONGO_DB иMONGO_REPLICASET. Наша переменнаяMONGO_HOSTNAME будет включать запись DNS для экземпляраeach в наш набор реплик, поскольку это то, чтоMongoDB connection URI requires.

СогласноKubernetes documentation, когда приложение реализует проверки работоспособности и готовности, при подключении к подам следует использоватьSRV records. Как обсуждалось вStep 3, наши записи Pod SRV следуют этому шаблону:$(statefulset-name)-$(ordinal).$(service name).$(namespace).svc.cluster.local. Поскольку наш MongoDB StatefulSet реализует проверки работоспособности и готовности, мы должны использовать эти стабильные идентификаторы при определении значений переменнойMONGO_HOSTNAME.

Добавьте в файл следующий код, чтобы определить переменныеMONGO_HOSTNAME,MONGO_PORT,MONGO_DB иMONGO_REPLICASET. Вы можете использовать другое имя для своей базы данныхMONGO_DB, но ваши значенияMONGO_HOSTNAME иMONGO_REPLICASET должны быть записаны так, как они указаны здесь:

~/node_project/nodeapp/templates/configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-config
data:
  MONGO_HOSTNAME: "mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local,mongo-mongodb-replicaset-1.mongo-mongodb-replicaset.default.svc.cluster.local,mongo-mongodb-replicaset-2.mongo-mongodb-replicaset.default.svc.cluster.local"
  MONGO_PORT: "27017"
  MONGO_DB: "sharkinfo"
  MONGO_REPLICASET: "db"

Поскольку мы уже создали объект StatefulSet и набор реплик, перечисленные здесь имена хостов должны быть перечислены в вашем файле в точности так, как они отображаются в этом примере. Если вы уничтожите эти объекты и переименуете ваш выпуск MongoDB Helm, вам потребуется пересмотреть значения, включенные в эту ConfigMap. То же самое относится и кMONGO_REPLICASET, поскольку мы указали имя набора реплик в нашей версии MongoDB.

Также обратите внимание, что перечисленные здесь значения указаны в кавычках, что составляетthe expectation for environment variables in Helm.

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

Определив значения параметров диаграммы и создав манифесты Secret и ConfigMap, вы можете отредактировать шаблон развертывания приложения, чтобы использовать переменные среды.

[[step-5 -—- integration-environment-variables-into-your-helm-deployment]] == Шаг 5. Интеграция переменных среды в ваше развертывание Helm

Имея файлы для нашего приложения Secret и ConfigMap, мы должны убедиться, что наше приложение Deployment может использовать эти значения. Мы также настроимliveness and readiness probes, которые уже определены в манифесте развертывания.

Откройте шаблон развертывания приложения для редактирования:

nano nodeapp/templates/deployment.yaml

Хотя это файл YAML, шаблоны Helm используют синтаксис, отличный от стандартных файлов YAML Kubernetes, для создания манифестов. Дополнительные сведения о шаблонах см. ВHelm documentation.

В файле сначала добавьте ключenv в спецификации контейнера приложения, ниже ключаimagePullPolicy и вышеports:

~/node_project/nodeapp/templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
...
  spec:
    containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        env:
        ports:

Затем добавьте следующие ключи в список переменныхenv:

~/node_project/nodeapp/templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
...
  spec:
    containers:
      - name: {{ .Chart.Name }}
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        env:
        - name: MONGO_USERNAME
          valueFrom:
            secretKeyRef:
              key: MONGO_USERNAME
              name: {{ .Release.Name }}-auth
        - name: MONGO_PASSWORD
          valueFrom:
            secretKeyRef:
              key: MONGO_PASSWORD
              name: {{ .Release.Name }}-auth
        - name: MONGO_HOSTNAME
          valueFrom:
            configMapKeyRef:
              key: MONGO_HOSTNAME
              name: {{ .Release.Name }}-config
        - name: MONGO_PORT
          valueFrom:
            configMapKeyRef:
              key: MONGO_PORT
              name: {{ .Release.Name }}-config
        - name: MONGO_DB
          valueFrom:
            configMapKeyRef:
              key: MONGO_DB
              name: {{ .Release.Name }}-config
        - name: MONGO_REPLICASET
          valueFrom:
            configMapKeyRef:
              key: MONGO_REPLICASET
              name: {{ .Release.Name }}-config

Каждая переменная включает ссылку на свое значение, определяемое либоsecretKeyRef key, в случае значений Secret, либоconfigMapKeyRef для значений ConfigMap. Эти ключи указывают на файлы Secret и ConfigMap, которые мы создали на предыдущем шаге.

Затем под ключомports измените определениеcontainerPort, чтобы указать порт в контейнере, где будет отображаться наше приложение:

~/node_project/nodeapp/templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
...
  spec:
    containers:
    ...
      env:
    ...
      ports:
        - name: http
          containerPort: 8080
          protocol: TCP
      ...

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

  • Датчики готовности оценивают, готов ли Pod обслуживать трафик, останавливая все запросы к Pod до тех пор, пока проверки не пройдут успешно.

  • Датчики жизнеспособности проверяют базовое поведение приложения, чтобы определить, работает ли приложение в контейнере и работает ли оно должным образом. В случае сбоя датчика жизнеспособности Kubernetes перезапустит контейнер.

Подробнее об обоих см.relevant discussion вArchitecting Applications for Kubernetes.

В нашем случае мы будем использоватьhttpGet request, которые Helm предоставил по умолчанию, и проверим, принимает ли наше приложение запросы на конечной точке/sharks. kubelet service выполнит зонд, отправив запрос GET на сервер Node, работающий в контейнере пода приложения и прослушивая порт8080. Если код состояния для ответа находится между 200 и 400, тоkubelet сделает вывод, что контейнер исправен. В противном случае, в случае статуса 400 или 500,kubelet либо остановит трафик к контейнеру, в случае проверки готовности, либо перезапустит контейнер, в случае проверки работоспособности.

Добавьте следующую модификацию к указаннымpath для датчиков живучести и готовности:

~/node_project/nodeapp/templates/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
...
  spec:
    containers:
    ...
      env:
    ...
      ports:
        - name: http
          containerPort: 8080
          protocol: TCP
      livenessProbe:
        httpGet:
          path: /sharks
          port: http
      readinessProbe:
        httpGet:
          path: /sharks
          port: http

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

Теперь вы готовы создать релиз приложения с помощью Helm. Запустите следующийhelm install command, который включает имя выпуска и расположение каталога диаграмм:

helm install --name nodejs ./nodeapp

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

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

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

OutputNAME:   nodejs
LAST DEPLOYED: Wed Apr 17 18:10:29 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME           DATA  AGE
nodejs-config  4     1s

==> v1/Deployment
NAME            READY  UP-TO-DATE  AVAILABLE  AGE
nodejs-nodeapp  0/3    3           0          1s

...

Опять же, выходные данные будут указывать состояние выпуска вместе с информацией о созданных объектах и ​​о том, как вы можете взаимодействовать с ними.

Проверьте статус ваших стручков:

kubectl get pods
OutputNAME                              READY   STATUS    RESTARTS   AGE
mongo-mongodb-replicaset-0        1/1     Running   0          57m
mongo-mongodb-replicaset-1        1/1     Running   0          56m
mongo-mongodb-replicaset-2        1/1     Running   0          55m
nodejs-nodeapp-577df49dcc-b5fq5   1/1     Running   0          117s
nodejs-nodeapp-577df49dcc-bkk66   1/1     Running   0          117s
nodejs-nodeapp-577df49dcc-lpmt2   1/1     Running   0          117s

После того, как ваши Pod запущены и работают, проверьте свои Услуги:

kubectl get svc
OutputNAME                              TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
kubernetes                        ClusterIP      10.245.0.1                 443/TCP        96m
mongo-mongodb-replicaset          ClusterIP      None                       27017/TCP      58m
mongo-mongodb-replicaset-client   ClusterIP      None                       27017/TCP      58m
nodejs-nodeapp                    LoadBalancer   10.245.33.46   your_lb_ip        80:31518/TCP   3m22s

EXTERNAL_IP, связанный со службойnodejs-nodeapp, - это IP-адрес, по которому вы можете получить доступ к приложению извне кластера. Если вы видите статус<pending> в столбцеEXTERNAL_IP, это означает, что ваш балансировщик нагрузки все еще создается.

Как только вы увидите IP-адрес в этом столбце, перейдите к нему в своем браузере:http://your_lb_ip.

Вы должны увидеть следующую целевую страницу:

Application Landing Page

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

[[step-6 -—- testing-mongodb-replication]] == Шаг 6. Тестирование репликации MongoDB

Когда наше приложение работает и доступно через внешний IP-адрес, мы можем добавить некоторые тестовые данные и обеспечить их репликацию между членами нашего набора реплик MongoDB.

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

Application Landing Page

Щелкните по кнопкеGet Shark Info. Вы увидите страницу с формой ввода, где вы можете ввести имя акулы и описание общего характера этой акулы:

Shark Info Form

В форме добавьте начальную акулу по вашему выбору. Чтобы продемонстрировать, мы добавимMegalodon Shark в полеShark Name иAncient в полеShark Character:

Filled Shark Form

Щелкните по кнопкеSubmit. Вы увидите страницу с этой информацией об акулах, отображенной для вас:

Shark Output

Теперь вернитесь к форме информации об акулах, нажавSharks на верхней панели навигации:

Shark Info Form

Введите новую акулу по вашему выбору. Мы пойдем сWhale Shark иLarge:

Enter New Shark

Как только вы нажметеSubmit, вы увидите, что новая акула была добавлена ​​в коллекцию акул в вашей базе данных:

Complete Shark Collection

Давайте проверим, что введенные нами данные были реплицированы между первичными и вторичными членами нашего набора реплик.

Получить список ваших стручков:

kubectl get pods
OutputNAME                              READY   STATUS    RESTARTS   AGE
mongo-mongodb-replicaset-0        1/1     Running   0          74m
mongo-mongodb-replicaset-1        1/1     Running   0          73m
mongo-mongodb-replicaset-2        1/1     Running   0          72m
nodejs-nodeapp-577df49dcc-b5fq5   1/1     Running   0          5m4s
nodejs-nodeapp-577df49dcc-bkk66   1/1     Running   0          5m4s
nodejs-nodeapp-577df49dcc-lpmt2   1/1     Running   0          5m4s

Чтобы получить доступ кmongo shell на ваших модулях, вы можете использоватьkubectl exec command и имя пользователя, которое вы использовали для создания вашегоmongo-secret вStep 2. Получите доступ к оболочкеmongo на первом поде в StatefulSet с помощью следующей команды:

kubectl exec -it mongo-mongodb-replicaset-0 -- mongo -u your_database_username -p --authenticationDatabase admin

При появлении запроса введите пароль, связанный с этим именем пользователя:

OutputMongoDB shell version v4.1.9
Enter password:

Вы попадете в административную оболочку:

OutputMongoDB server version: 4.1.9
Welcome to the MongoDB shell.
...

db:PRIMARY>

Хотя сама подсказка включает эту информацию, вы можете вручную проверить, какой член набора реплик является основным сrs.isMaster() method:

rs.isMaster()

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

Outputdb:PRIMARY> rs.isMaster()
{
        "hosts" : [
                "mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local:27017",
                "mongo-mongodb-replicaset-1.mongo-mongodb-replicaset.default.svc.cluster.local:27017",
                "mongo-mongodb-replicaset-2.mongo-mongodb-replicaset.default.svc.cluster.local:27017"
        ],
        ...
        "primary" : "mongo-mongodb-replicaset-0.mongo-mongodb-replicaset.default.svc.cluster.local:27017",
        ...

Затем переключитесь на вашу базу данныхsharkinfo:

use sharkinfo
Outputswitched to db sharkinfo

Перечислите коллекции в базе данных:

show collections
Outputsharks

Вывести документы в сборник:

db.sharks.find()

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

Output{ "_id" : ObjectId("5cb7702c9111a5451c6dc8bb"), "name" : "Megalodon Shark", "character" : "Ancient", "__v" : 0 }
{ "_id" : ObjectId("5cb77054fcdbf563f3b47365"), "name" : "Whale Shark", "character" : "Large", "__v" : 0 }

Выход из оболочки MongoDB:

exit

Теперь, когда мы проверили данные на нашем первичном сервере, давайте проверим, реплицируются ли они на вторичный сервер. kubectl exec вmongo-mongodb-replicaset-1 с помощью следующей команды:

kubectl exec -it mongo-mongodb-replicaset-1 -- mongo -u your_database_username -p --authenticationDatabase admin

Оказавшись в административной оболочке, нам нужно будет использовать методdb.setSlaveOk(), чтобы разрешить операции чтения из вторичного экземпляра:

db.setSlaveOk(1)

Переключитесь на базу данныхsharkinfo:

use sharkinfo
Outputswitched to db sharkinfo

Разрешите операцию чтения документов в коллекцииsharks:

db.setSlaveOk(1)

Вывести документы в сборник:

db.sharks.find()

Теперь вы должны увидеть ту же информацию, что и при запуске этого метода на своем основном экземпляре:

Outputdb:SECONDARY> db.sharks.find()
{ "_id" : ObjectId("5cb7702c9111a5451c6dc8bb"), "name" : "Megalodon Shark", "character" : "Ancient", "__v" : 0 }
{ "_id" : ObjectId("5cb77054fcdbf563f3b47365"), "name" : "Whale Shark", "character" : "Large", "__v" : 0 }

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

Заключение

Теперь вы развернули реплицированное высокодоступное информационное приложение об акулах в кластере Kubernetes, используя диаграммы Хелма. Это демонстрационное приложение и рабочий процесс, описанный в этом руководстве, могут выступать в качестве отправной точки при построении пользовательских диаграмм для вашего приложения и использовании репозитория Helmstable иother chart repositories.

По мере продвижения к производству рассмотрите возможность реализации следующего:

Related