В этой статье я не буду объяснять, зачем вот это всё нужно, или обсуждать достоинства и недостатки этого решения. Воспринимайте эту статью как инструкцию (заметку) для быстрого развертывания базы и очереди в dev-кластере Kubernetes.

Содержание

  1. Введение

  2. Установка PostgreSQL

  3. Установка Redis

  4. Установка RabbitMQ


Введение

Установки PostgresSQL, Redis и RabbitMQ очень похожи друг на друга. Можно выделить три основных этапа:

  • Создание Persistent Volume (PV) и Persistent Volume Claim (PVC).

  • Установка Helm-чарта целевого приложения.

  • Проверка работы.

Я не буду объяснять, что такое PV и PVC. Есть отличная лекция на эту тему, после которой можете смело возвращаться к моей инструкции. Перед началом работ нужно минимально настроить кластер Kubernetes. Вот небольшие требования:

  1. Версия Kubernetes 1.20+.

  2. Одна master-нода и одна worker-нода.

  3. Настроенный Ingress-controller.

  4. Если кластер развернут на bare-metal, то необходимо заменить внешний балансировщик. Например, поставить MetalLB или PorterLB.

  5. На виртуальной машине установлен Helm.

Как создать свой ламповый dev-кластер на голом железе подробно рассказано в предыдущей статье.

Установка PostgreSQL

Создадим ресурс StorageClass, для этого в файл storage.yaml вставьте следующую конфигурацию:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

Применим манифест:

Создадим ресурс Persistent Volume. Для этого в файл pv.yaml вставьте следующий манифест:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-for-pg
  labels:
    type: local
spec:
  capacity:
    storage: 4Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /devkube/postgresql
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 457344.cloud4box.ru

В matchExpressions указываем название ноды, на которой будет монтироваться диск. Посмотреть имя доступных узлов можно с помощью команды:

kubectl get nodes

Для удобства будем монтировать диск сразу на мастер-ноде, хотя это можно сделать на любой из доступных в списке. Монтировать будем директорию /devkube/postgresql. Заходим на удалённую машину и создаём директорию такой командой:

mkdir -p /devkube/postgresql

Создадим ресурс Persistent Volume:

kubectl apply -f pv.yaml

Проверим состояние:

kubectl get pv

Применим манифест с Persistent Volume Claim:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pg-pvc
spec:
  storageClassName: "local-storage"
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi

Посмотрим состояние ресурса:

Ресурс PVC в ожидании привязки. Настало время развернуть Postgres в кластере. Подтягиваем к себе репозиторий Bitnami:

helm repo add bitnami https://charts.bitnami.com/bitnami

Устанавливаем чарт Helm с Postgres:

helm install dev-pg bitnami/postgresql --set primary.persistence.existingClaim=pg-pvc,auth.postgresPassword=pgpass

Посмотрим на состояние PVC:

kubectl get pvc

Ресурс в статусе bound, теперь pod с Postgres будет писать данные в директорию /devkube/postgresql. Посмотрим на состояние pod, statefulset:

kubectl get pod,statefulset

База успешно развёрнута, теперь попробуем подключиться к ней: создать пользователя, таблицу и настроить доступы. После установки чарта в консоли будут показаны некоторые способы подключения к БД. Есть два способа:

1. Пробросить порт на локальную машину

Для этого требуется установить на машину утилиту psql. Проверим, что она установлена:

psql -V

Экспортируем пароль от админ-пользователя в переменную окружения:

export POSTGRES_PASSWORD=$(kubectl get secret --namespace default dev-pg-postgresql -o jsonpath="{.data.postgres-password}" | base64 --decode)

Выполним проброс порта:

kubectl port-forward --namespace default svc/dev-pg-postgresql 5432:5432

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

PGPASSWORD="$POSTGRES_PASSWORD" psql --host 127.0.0.1 -U postgres -d postgres -p 5432

Или так, но тогда придётся ввести пароль вручную:

psql --host 127.0.0.1 -U postgres -d postgres -p 5432
2. Создать поду с psql клиентом

Экспортируем пароль от админ-пользователя в переменную окружения:

export POSTGRES_PASSWORD=$(kubectl get secret --namespace default dev-pg-postgresql -o jsonpath="{.data.postgres-password}" | base64 --decode)

Создадим под с утилитой psql и выполним в ней команду подключения к БД:

kubectl run dev-pg-postgresql-client --rm --tty -i --restart='Never' --namespace default --image docker.io/bitnami/postgresql:14.2.0-debian-10-r22 --env="PGPASSWORD=$POSTGRES_PASSWORD" \
      --command -- psql --host dev-pg-postgresql -U postgres -d postgres -p 5432

Создадим роль (пользователя) и пароль для неё:

CREATE ROLE qa_user WITH LOGIN ENCRYPTED PASSWORD 'qa-pg-pass';

Посмотреть список ролей:

\du

Создадим базу, владельцем которой будет пользователь qa_user:

CREATE DATABASE qa_db OWNER qa_user;

Теперь отключимся:

\q

И подключимся к базе с данными нового пользователя (вторым способом):

kubectl run dev-pg-postgresql-client --rm --tty -i --restart='Never' --namespace default --image docker.io/bitnami/postgresql:14.2.0-debian-10-r22 --env="PGPASSWORD=qa-pg-pass"  --command -- psql --host dev-pg-postgresql -U qa_user -d qa_db -p 5432

Создадим небольшую табличку:

CREATE TABLE qa_table (id int, name varchar(255));

Добавим запись:

INSERT INTO qa_table VALUES (1, 'first');

Теперь сделаем select, чтобы убедиться в работоспособности:

SELECT * FROM qa_table;

Посмотреть список таблиц в базе:

\dt+

Готово, база успешно развёрнута! В приложении нужно указывать такой адрес БД:

DATABASE_URI=postgresql://qa_user:qa-pg-pass@dev-pg-postgresql:5432/qa_db

Установка Redis

Redis можно установить в нескольких конфигурациях. Мы развернём вариант с двумя репликами на чтение и одной репликой на запись в базу. Прежде всего применим манифест со StorageClass:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
kubectl apply -f storage.yaml

У меня уже был установлен этот ресурс, поэтому манифест не применился.

Дальше настроим Persistent Volumes. Зарезервируем 2 Гб для каждой slave-реплики и 4 Гб для мастер-реплики. Создадим файлы pv-slave1.yaml, pv-slave2.yaml и pv-master.yaml и вставим в них эти конфигурации:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-redis-slave1
  labels:
    type: local
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /devkube/redis/slave1
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 457344.cloud4box.ru
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-redis-slave2
  labels:
    type: local
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /devkube/redis/slave2
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 457344.cloud4box.ru
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-redis-master
  labels:
    type: local
spec:
  capacity:
    storage: 4Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /devkube/redis/master
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 457344.cloud4box.ru

Все реплики будут складывать свои данные на ноде 457344.cloud4box.ru, хотя, конечно, можно монтировать диски на разных виртуальных машинах. Создадим три директории:

mkdir -p /devkube/redis/slave1
mkdir -p /devkube/redis/slave2
mkdir -p /devkube/redis/master

Применим конфигурации:

kubectl apply -f .

Проверим созданные ресурсы:

kubectl get pv

Созданные PV ещё свободны и ждут заявки на использование пространства. А Persistent Volumes Postgres уже связан со своим Persistent Volumes Сlaim (так осталось после поднятия Postgres-базы в Kubernetes).

Создадим PVC для мастер-реплики. Их нужно обязательно создавать в том namespace, в котором вы развернёте базу. Redis будет развёрнут в пространстве dev-redis. Создадим пространство:

kubectl create ns dev-redis
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-for-master-redis
  namespace: dev-redis
spec:
  storageClassName: "local-storage"
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
kubectl apply -f pvc-master.yaml

Для slave-реплик создавать PVC не будем, они будут автоматически созданы при установке чарта Helm.

Пришло время установить Redis. Подтягиваем к себе репозиторий Bitnami, если ещё этого не сделали (у меня уже добавлен):

helm repo add bitnami https://charts.bitnami.com/bitnami

Устанавливаем чарт Helm с Redis:

helm install dev-redis-chart bitnami/redis --namespace dev-redis --set global.redis.password=redispass,master.persistence.existingClaim=pvc-for-master-redis,replica.replicaCount=2,replica.persistence.storageClass=local-storage,replica.persistence.size=2Gi

В этой команде указываю:

  • password=redispass — пароль для авторизации;

  • existingClaim=pvc-for-master-redis — название созданного выше PVC для мастер-реплики;

  • replicaCount, storageClass, size — количество slave-реплик, название ресурса StorageClass и размер PV. Это всё нужно для автоматического создания PVC.

После выполнения команды в консоль будет выведено несколько способов подключения к Redis. Но сначала проверим состояние Persistent Volumes:

kubectl get pv

Все ресурсы стали связаны с конкретным PVC. И посмотрим на созданные ресурсы:

kubectl get pod,svc,statefulset -n dev-redis

Всё успешно развернуто, пора подключиться и попинговать базу. Экспортируем пароль авторизации в переменную окружения:

export REDIS_PASSWORD=$(kubectl get secret --namespace dev-redis dev-redis-chart -o jsonpath="{.data.redis-password}" | base64 --decode)

Создадим под с redis-cli на борту:

kubectl run --namespace dev-redis redis-client --restart='Never'  --env REDIS_PASSWORD=$REDIS_PASSWORD  --image docker.io/bitnami/redis:6.2.6-debian-10-r146 --command -- sleep infinity

Проходим внутрь созданного пода:

kubectl exec --tty -i redis-client \
   --namespace dev-redis -- bash

И теперь можно подключиться к master- или slave-реплике на выбор:

   REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h dev-redis-chart-master
   или
   REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h dev-redis-chart-replicas

Подключимся к master-реплике:

Пинганём базу:

Записать данные можно в любую из 15 автоматически созданных баз (по умолчанию база под номером ноль). Выберем, например, вторую:

Запишем некоторые данные:

И запросим их же:

Готово! Redis успешно развёрнут и готов к использованию. В приложении нужно указывать такой адрес:

REDIS=redis://redispass@dev-redis-chart-master:6379/0

Установка RabbitMQ

Запишем манифест с ресурсом StorageClass в файл storage.yaml:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

И применим его:

kubectl apply -f storage.yaml

У меня уже был установлен этот ресурс, поэтому манифест не применился.

Создадим Persistent Volume:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-for-rmq
  labels:
    type: local
spec:
  capacity:
    storage: 4Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /devkube/rabbitmq
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 457344.cloud4box.ru

В matchExpressions указываем название ноды, на которой будем монтировать диск. Посмотреть имя доступных узлов можно с помощью команды:

kubectl get nodes

Для удобства будем монтировать диск сразу на master-ноде, хотя это можно сделать на любой из доступных в списке. Создадим директорию, в которую RabbitMQ будет складывать свои данные:

mkdir -p /devkube/rabbitmq

Создадим ресурс Persistent Volume:

kubectl apply -f pv.yaml

И проверим состояние:

Осталось только создать Persistent Volume Claim. Его нужно обязательно создавать в том namespace, в котором будете разворачивать будущую очередь. RabbitMQ развернётся в пространстве dev-rmq. Создадим его:

kubectl create ns dev-rmq

Запишем манифест в файл pvc.yaml:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: rmq-pvc
  namespace: dev-rmq
spec:
  storageClassName: "local-storage"
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi

Применим:

kubectl apply -f pvc.yaml

И, наконец, развернём RabbitMQ. Добавляем к себе репозиторий Bitnami, если ещё этого не сделали (у меня уже добавлен):

helm repo add bitnami https://charts.bitnami.com/bitnami

Установим чарт Helm RabbitMQ:

helm install dev-rmq-chart bitnami/rabbitmq --namespace dev-rmq --set persistence.existingClaim=rmq-pvc,ingress.enabled=true,ingress.hostname=dashboard.dev.rmq.cryptopantry.tech,auth.username=rmq_admin,auth.password=devrmquser,ingress.ingressClassName=nginx

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

  •  existingClaim=rmq-pvc — имя существующего ресурса PVC, который мы выше создали;

  • ingress.enabled=true — активируем Ingress, это нужно для разворачивания удобной борды;

  • ingress.hostname=dashboard.dev.rmq.somedomain.com — адрес борды;

  • auth.username=rmq_admin и auth.password=devrmquser — тут всё и так понятно;

  • ingress.ingressClassName=nginx — обязательная настройка для Ingress-ресурса.

Чарт Helm установлен. Проверим, что ресурсы успешно развёрнуты:

Теперь перейдите в браузере по доменному имени, которое указали в ingress.hostname:

Заходим с учётными данными login=rmq_admin и password=devrmquser, и переходим во вкладку Admin:

Создадим нового пользователя qa_user с паролем qa_pass:

Создадим virtual host:

И привяжем пользователя к virtual host:

Готово! RabbitMQ развёрнут и готов к использованию:

В приложении укажите такой адрес:

RabbitMQ=amqp://qa_user:qa_pass@dev-rmq-chart-rabbitmq.dev-rmq:5672/qa_host

На этом у меня всё, мы успешно развернули PostgreSQL, Redis и RabbitMQ в небольшом dev-кластере.

Комментарии (12)


  1. spacediver
    10.03.2022 14:48

    Спасибо за прикладной гайд!

    Я в прошлом году слышал, что кубер пока еще не вполне готов к тому, чтобы в нём надёжно хостить stateful сервисы, особенно критичные к внезапным остановкам контейнеров — такие, как СУБД.

    Как с этим сейчас?


    1. Mopckou Автор
      10.03.2022 16:35

      Сегодня также никто не советует разворачивать продовую базу в кубе. Но для dev контура, просто поиграться, сделать демку приложения, проверить теорию вполне себе удобно.

      Вот доклад на эту тему. Автор говорит, что для тестов смело разворачивайте в кубе и ничего страшного. И я с ним в этом вопросе согласен.


  1. gmini
    10.03.2022 16:14

    раз уж Вы чартами пользуетесь, почему тома руками создаете?


    1. Mopckou Автор
      10.03.2022 17:04

      Не видел у чартов bitnami возможность автоматически создавать директории, и вообще есть сомнения, что они могут. Может я захочу монтировать директорию на другой виртуальной машине, где даже helm не стоит?

      К тому же, такой ручной контроль не особо трудозатратный, но зато наглядный - сразу понятно куда складываются данные.


      1. vasyakolobok77
        10.03.2022 23:37
        +3

        я захочу монтировать директорию на другой виртуальной машине, где даже helm не стоит?

        helm - это просто шаблонизатор и выкладчик кубер-артефактов, ему не надо быть на виртуальной машине. Вы запускаете htlm локально или в ci-pipeline, он анализирует шаблоны, готовит артефакты и, используя k8s сессию, выкладывает их, все.

        Не видел у чартов bitnami возможность автоматически создавать директории

        Коллега выше верно заметил, что битнами чарт имеет в себе PVC и может порождать PV. Для дев-стенда это самое оно, не надо руками ничего создавать, просто helm install и в путь.

        Еще важный момент, StorageClass может создать только админ кластера. И в принципе, если в кластере уже есть какие-то сторадж-классы, например, cephfs, то нет большого смысла объявлять свой.


        1. Mopckou Автор
          11.03.2022 14:42

          Спасибо за развернутый ответ. Благодаря ему я понял, что не понял изначального вопроса :). Подумал, что речь идет о создании директорий helm'ом на VPS, которые потом монтируются.

          Да, согласен чарт от bitnami позволяет создать автоматом PVC. И как раз в этой инструкции при установке Redis автоматически создается PVC для slave-реплик. И вы, конечно, правы инструкцию можно еще больше упросить.

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


  1. jesaiah4
    10.03.2022 20:09

    А какой куб сейчас актуальный?

    k0s, MicroK8s, kind, k3s и Minikube ?


    1. paulstrong
      10.03.2022 22:28

      k8s =)


  1. paulstrong
    10.03.2022 22:30
    +1

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


  1. Saladin
    11.03.2022 10:04

    Стоит заметить, что создание дополнительного пользователя и нестандартного virtual host возможно в RabbitMq сразу при старте. Для этого достаточно определить definitions файл. Helm chart от bitnami это позволяет.


    1. Mopckou Автор
      11.03.2022 14:47

      Да, спасибо за примечание. Но лично для себя решил, что лучше сделать только admin юзера, и только потом создавать других если потребуется. А то в чартах bitnami куча параметров типа user/password, global.user/global.password, auth.user/auth.password и причем некоторые из них переопределяются в зависимости указан дочерний или нет.


  1. Aggron
    12.03.2022 01:14
    +1

    По поводу PV и PVC - я конспектировал ту самую лекцию, возможно кому-то будет полезно