Этот пост будет посвящен установке MongoDB, посмотрим на различные варианты в single-instance, replicaset.

MongoDB - это документориентированная СУБД, для хранения информации в json-подобных структурах BSON (Binary json). Монга написана на C++, использует mmap и имеет javascript-like язык запросов.

Установка mongoDB

Процесс установки монги достаточно тривиальный, и не требует продолжительных подготовок. В этом разделе будем ставить сервис на одиночный сервер, под управлением RockyLinux 8.5.

Идем на сайт монги, копируем конфиг репозитория. Подключаемся на сервер, создаем новый репозиторий.:

[root@mongodb-single ~]# vim /etc/yum.repos.d/mongodb-6.repo
--
[mongodb-org-6.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc

После устанавливаем пакет:

[root@mongodb-single ~]# yum install mongodb-org

Запускаем сервис:

[root@mongodb-single ~]# systemctl enable mongod --now

По умолчанию, любой пользователь имеет доступ к функциям mongoDB и мы без проблем сможем подключиться к базе через клиент. Поэтому добавим супер-пользователя, и глобально включим авторизацию. Для этого через консоль подключаемся к монге (утилита - mongosh):

[root@mongodb-single ~]# mongosh

Внутри монги, переключаемся на базу - admin:

use admin

И выполняем запрос на добавление админ-пользователя:

db.createUser(
  {
  user: "mongoadmin",
  pwd: "password",
  roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)

Отредактируем конфиг mongoDB:

[root@mongodb-single ~]# vim /etc/mongod.conf
--
# Директивы сокета, с которого монга слушает запросы
net:
  port: 27017
  bindIp: 0.0.0.0
 
# Включение авторизации
security:
  authorization: enabled

Сохраняемся и перезапускаем сервис:

[root@mongodb-single ~]# systemctl restart mongod

Не забываем открыть порт на фаерволе:

[root@mongodb-single ~]# firewall-cmd --add-port=27017/tcp --permanent 
[root@mongodb-single ~]# firewall-cmd --reload

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

[root@mongodb-single ~]# mongosh
---
test> show dbs
MongoServerError: command listDatabases requires authentication

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

# Переключиться на admin базу
test> use admin
switched to db admin

# Авторизоваться в базе
admin> db.auth("mongoadmin", "password")
{ ok: 1 }

Ну и после можем просмотреть список текущий баз:

admin> show dbs
admin   100.00 KiB
config  108.00 KiB
local    72.00 KiB

Для подключения с локального компьютера, я буду использовать программу MongoDB Atlas. В клиенте указываем строку подключения, и коннектимся: mongo-atlas.png

Удаление mongoDB

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

# Выключаем сервис
[root@mongodb-single ~]# systemctl disable --now mongod
Removed /etc/systemd/system/multi-user.target.wants/mongod.service.

# Затераем пакет и его зависимости
[root@mongodb-single ~]# yum erase $(rpm -qa | grep mongodb-org)

# Удаляем каталог монги в либах
[root@mongodb-single ~]# rm -rf /var/lib/mongo

# Удаляем логи
[root@mongodb-single ~]# rm -rf /var/log/mongodb/

Установка mongoDB (ReplicaSet)

mongoDB уже из коробки имеет фичи по redundancy и high availability, так называемый набор реплик - Replicaset. Replicaset - это несколько экземпляров mongod, c одинаковым набором данных. В таком кластере может быть только она главная нода (primary), и две второстепенные ноды (secondary). Все операции записи осуществляются основной (primary) нодой, вторичные ноды копируют себе oplog (operation log) c основной ноды и применяют изменения к своим данным. В случаи если основная нода стала недоступной, то экземпляры вторичных реплик проведут переизбрание основной ноды. mongodb-replicas.png

Если есть ограничение (например, по стоимости) на количество реализуемых экзепляров реплик. И в кластере мы имеем только одну основную и вторичную ноду. То, дабы не нарушать кворум кластере добавляется еще один экземпляр mongodb в роли арбитера. Артибер только участвует в перевыборе нового мастера и не хранит наборы данных. mongodb-replicas2.png

В нашей исталяции мы будем использовать 3 сервера с mongoDB (1 - Primary, 2 - Secondary).

Server name IP Role
mongodb01 192.168.190.73 Primary server
mongodb02 192.168.190.74 Secondary server
mongodb03 192.168.190.75 Secondary server

На все три тачки подкивываем репозиторий от mongoDB:

(All servers)# vi /etc/yum.repos.d/mongo6.repo
(All servers)# yum install mongodb-org -y

Теперь прежде чем мы начнем инициализировать кластер, создадим двоих пользователей. Один пользователь mongo-admin будем иметь права на создание пользователей и назначению их ролей. Второй пользователь mongo-root будет иметь все привилегии. Запускаем сервер монги на основном сервере, и подключаемся к консоли монги:

[root@mongodb01 ~]# systemctl enable --now mongod

Подключаемся к монге и создаем пользователей:

[root@mongodb01 ~]# mongosh
--
# Переключаемся на базу admin
test> use admin

# Создаем пользователя mongo-admin
db.createUser(
  {
    user: "mongo-admin",
    pwd: "passw0rd",
    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  }
)

# Создаем пользователя mongo-root
db.createUser(
  {
    user: "mongo-root",
    pwd: "passw0rd",
    roles: [ { role: "root", db: "admin" } ]
  }
)

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

# Каталог под хранение ключа
[root@mongodb01 ~]# mkdir -p /etc/mongodb/keys/

# Генерим ключ
[root@mongodb01 ~]# openssl rand -base64 756 > /etc/mongodb/keys/mongo-key

# Даем права на чтение только пользователю
[root@mongodb01 ~]# chmod 400 /etc/mongodb/keys/mongo-key

# Меняем владельца на mongo
[root@mongodb01 ~]# chown -R mongod:mongod /etc/mongodb

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

[root@mongodb01 ~]# scp -r /etc/mongodb root@192.168.190.74:/etc
[root@mongodb01 ~]# scp -r /etc/mongodb root@192.168.190.75:/etc

Переключаемся на ноды 02, 03 и меняем владельца ключа:

(Secondary nodes)# chown -R mongod:mongod /etc/mongodb/

Отредактруем конфиг mongodb на серверах:

# На основной ноде
net:
  port: 27017
  bindIp: 127.0.0.1,192.168.190.73

security:
 authorization: enabled
 keyFile:  /etc/mongodb/keys/mongo-key

replication:
  replSetName: "replicaset01"
  
# На второй ноде
net:
  port: 27017
  bindIp: 127.0.0.1,192.168.190.74 
  
security:
 authorization: enabled
 keyFile:  /etc/mongodb/keys/mongo-key

replication:
  replSetName: "replicaset01"
  
# На третьей ноде
net:
  port: 27017
  bindIp: 127.0.0.1,192.168.190.75

security:
 authorization: enabled
 keyFile:  /etc/mongodb/keys/mongo-key

replication:
  replSetName: "replicaset01"

В конфигурационном файле прописываем поля:

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

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

(All servers)# firewall-cmd --add-port=27017/tcp --permanent
(All servers)# firewall-cmd --reload

Запускаем сервис на всех серверах:

(All servers)# systemctl enable --now mongod

Подключаемся к консоли монги, и авторизируемся из под пользователя mongo-root, только у него есть права для работы с кластером.

[root@mongodb01 ~]# mongosh
---
# Переключаемся на базу admin
test> use admin

# Авторизуемся в базе
admin> db.auth("mongo-root", "passw0rd")
{ ok: 1 }

Проинициализируем кластер:

# Объявляем кластер
admin> rs.initiate()
{
  info2: 'no configuration specified. Using a default configuration for the set',
  me: '192.168.190.73:27017',
  ok: 1
}

# Добавляем вторую ноду
replicaset01 [direct: primary] admin> rs.add("192.168.190.74:27017")
{
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1675081615, i: 1 }),
    signature: {
      hash: Binary(Buffer.from("11cc794f49ca144cc6de0813b5aa1f3630ee8638", "hex"), 0),
      keyId: Long("7194420393778610182")
    }
  },
  operationTime: Timestamp({ t: 1675081615, i: 1 })
}

# Добавляем третью ноду
replicaset01 [direct: primary] admin> rs.add("192.168.190.75:27017")
{
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1675081642, i: 1 }),
    signature: {
      hash: Binary(Buffer.from("b39074d22f4ff58cdf83294f3e21cd0617cc013a", "hex"), 0),
      keyId: Long("7194420393778610182")
    }
  },
  operationTime: Timestamp({ t: 1675081642, i: 1 })
}

Отлично, на этом все =)

Для просмотра статуса репликасета, можно воспользоваться командой:

replicaset01 [direct: primary] admin> rs.status()

Команды вернет нам длинный вывод в json, содержащий параметры репликасета и наши наборы реплик.

Для просмотра конфиги набора реплик, можно заюзать камонду:

replicaset01 [direct: primary] admin> rs.conf()

Для теста, давайте создадим базу, и пользователя с ролью на эту базу.

# Создаем базу:
replicaset01 [direct: primary] admin> use appdb

# Добавляем нового пользователя
replicaset01 [direct: primary] appdb> db.createUser( { user: "user_app", pwd: "pwdpwd", roles: [{ role: "readWrite", db: "appdb" }] })
{
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1675082639, i: 1 }),
    signature: {
      hash: Binary(Buffer.from("b9b427b60a90dea4942519bf6b52b5ed2a0c0785", "hex"), 0),
      keyId: Long("7194420393778610182")
    }
  },
  operationTime: Timestamp({ t: 1675082639, i: 1 })
}

Для теста, можем попробоваться подключится к кластеру через Altas. В новом подключении, я указываю все 3 сервера. mongodb-atlas-repl.png

А во вкладке Authentication указываю логин/пароль пользователя и базу: mongodb-atlas-repl2.png

Жмем Save & Connect: mongodb-atlas-repl3.png

На этом все, задачу можно считать реализованной.

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