MongoDB | Работа в CLI
Тип статьи | Полезные сведения |
---|---|
Компетенции | Администратор Linux, MongoDB |
Статус | ЧЕРНОВИК |
Сложность | Легко |
Введение
Зачастую во время траблшутинга на стенде заказчика необходимо посмотреть и проверить базу данных MongoDB. Первым делом нам нужно получить логин и пароль от MongoDB:
ID=$(docker ps | grep visiology.admin | awk '{print $1}') &&
echo `docker exec -it $ID sh -c "cat secrets/MONGO_AUTH_USER"` &&
echo `docker exec -it $ID sh -c "cat secrets/MONGO_AUTH_PASSWORD"`
# Первой строчкой возвращается логин, второй - пароль
Затем заходим в контейнер visiology_mongodb и логинимся в консоль mongo:
$ mongo -u <логин> -p <пароль> VisiologyVA
or
$ mongosh "mongodb://127.0.0.1:27017" --username zidan --authenticationDatabase admin
Любое изменение в Монге напрямую может привести к поломке платформы. Польза от статьи в основном от операций поиска, просмотра, логирования. Любые модификации применяются только в конкретных кейсах поддержки, где это реально необходимо.
1. Просмотр сущностей.
Показать список баз данных:
>show dbs
>show databases
Выбрать/использовать базу данных:
>use VisiologyVA
Если базы с таким именем не существует, она автоматически создастся
Показать коллекции в БД:
>show collections
2. Ввод данных
Создать коллекцию в БД:
>db.createCollection("DeletedUsers")
Ввод содержимого в БД:
>db.DeletedUsers.insertOne (
{
name: "alex",
age: 28,
hobbies: "football"
}
)
insertOne используется при создании одного объекта. Если при создании объекта указываются ранее несуществующие коллекции, они создаются автоматически.
>db.DeletedUsers.insertMany ([
{
name: "Vasiliy_Ivanovich",
age: 49,
hobbies: "fishing"
},
{
name: "Anka_the_MachineGunner",
age: 20,
hobbies: "shooting"
}
])
insertMany используется при создании массива объектов. Также insertOne и insertMany возвращают _id созданных объектов.
3. Поиск сущностей
Вывод содержимого коллекции:
db.DeletedUsers.find().pretty()
Поиск по объектам:
db.DeletedUsers.find ({name:"alex"})
Поиск с сортировкой:
db.DeletedUsers.find ( { hobbies: { $in: ["football", "fishing"] } } )
Список используемых для сортировки операторов приведён здесь.
Вывод указанных полей массива:
db.SavedDashboards.find( { "Sheets.Widgets._t" : "ColumnChart" }, {Name:1,"Sheets.Name":1} );
Здесь мы вернули названия дашборда и названия листов, где используется виджет Гистограмма (ColumnChart). Вывод:
{ "_id" : "366e39dd182a4fa3ab63cf5d8894fcfc", "Name" : "Товары и автомобили", "Sheets" : [ { "Name" : "Товары" }, { "Name" : "Автомобили" } ] }
{ "_id" : "bbe6db5bb0164b6bb51cb367b677b6a1", "Name" : "Пользовательские скрипты", "Sheets" : [ { "Name" : "Новый лист 1" } ] }
{ "_id" : "fb6d9c2d8857406db6e385f37b3ceb18", "Name" : "Стартовый дэшборд", "Sheets" : [ { "Name" : "Аналитика магазина" }, { "Name" : "Поставка продуктов" } ] }
{ "_id" : "55f0f67128fc43fd904e02f2b636f4f2", "Name" : "Дэшборд для магазина", "Sheets" : [ { "Name" : "Аналитика магазина" } ] }
{ "_id" : "c1acbc4a61fc494c9715ddf741922966", "Name" : "Бюджет движения денежных средств", "Sheets" : [ { "Name" : "Показатели" } ] }
{ "_id" : "d72d9ba70e21428f8de35d308c351806", "Name" : "Бюджет доходов и расходов", "Sheets" : [ { "Name" : "Новый лист 1" } ] }
{ "_id" : "0de4fae4ea874edd86e959bc310b4f87", "Name" : "План по персоналу - Сравнение версий", "Sheets" : [ { "Name" : "Сумма" }, { "Name" : "Количество" }, { "Name" : "Оклад" } ] }
{ "_id" : "4100526a54b4481692d8bb8e94bd2f3c", "Name" : "Товары и автомобили мобильная версия", "Sheets" : [ { "Name" : "Товары" }, { "Name" : "Автомобили" } ] }
{ "_id" : "2c36413e0e4f44c587ee315cc0d371fd", "Name" : "demo1", "Sheets" : [ { "Name" : "Сдающий Отчетность" }, { "Name" : "Утверждающий Отчетность" } ] }
{ "_id" : "2bf4478008984d6eb56745d66903d723", "Name" : "Продажи и возврат", "Sheets" : [ { "Name" : "Новый лист 1" } ] }
Если нужное поле расположено в глубине массива, то нужно указать весь путь до этого поля в кавычках. Например, как в запросе выше: "Sheets.Widgets._t"
Больше информации о поиске сущностей здесь.
4. Изменение данных
Для того, чтобы внести правки в один объект, используется оператор updateOne:
Например:
>db.DeletedUsers.updateOne ( {name: "Vasiliy_Ivanovich"}, {
$set: {
description: "There are so many fun stories about him"
}
})
В этом случае мы добавили новое свойство объекта - description. Если такое свойство уже существует, то содержимое свойства изменится на заданное.
По аналогии, оператор updateMany используется при изменении нескольких объектов. Также список используемых для изменения операторов приведён здесь.
Для того, чтобы полностью заменить один объект другим, используется оператор replaceOne:
>db.DeletedUsers.replaceOne ( {name: "Vasiliy_Ivanovich"}, {
name: "Vasiliy_Ivanovich",
description: "He is 49 years old"
})
Теперь этот объект имеет следующий вид:
{
"_id" : ObjectId("62a1d2f7907d281b7aced853"),
"name" : "Vasiliy_Ivanovich",
"description" : "He is 49 years old"
}
5. Удаление данных/сущностей
Удаление данных из коллекции:
>db.DeletedUsers.deleteOne({name: "alex"})
deleteOne удаляет не более одной сущности, соответствующей условию, даже при наличии нескольких совпадений.
>db.DeletedUsers.deleteMany({age: { $gte: 20} })
Также, например, можно удалить пользователей в зависимости от времени их создания (импорта).
db.Users.deleteMany( {"Created":{$gte:ISODate("2023-02-01T10:00:00"),$lt:ISODate("2023-02-01T10:07:00")}})
deleteMany удаляет все совпадения в коллекции
> db.DeletedUsers.deleteMany({})
deleteMany с пустым фильтром используется при удалении всех данных из коллекции.
Для удаления отдельного свойства объекта используется оператор $unset. С его помощью можно удалять сразу несколько полей:
db.users.update({name : "alex"}, {$unset: {hobbies: 1, age: 1}})
Удаление коллекции:
>db.DeletedUsers.drop()
6. Повышение прав и диагностика БД/коллекций
Данную операцию не стоит производить на продакшне. Она исключительно для целей диагностики на тестовый стендах и применяется, когда есть реальная необходимость.
По умолчанию у пользователя нет доступа к средствам диагностики баз данных. От лица обычного пользователя с правами readWrite (подробнее о ролях/правах в mongoDB здесь) мы сможем разве что проверить коллекцию на целостность:
>db.DeletedUsers.validate({full:true})
После чего возвращается общая информация о коллекции, где также будет значение valid: false/true, если данные повреждены/не повреждены.
Если же мы хотим получать больше информации, то нам необходимо внести изменения в файл /init-db.js, который находится в корневой папке docker-образа visiology_mongodb. Именно этот файл отвечает за создание пользователей в mongoDB. Копируем его из контейнера:
docker cp <container ID>:/init-db.js /home/<username>/distrib_2.26/
Находим в скрипте команды для создания пользователя:
var connection = waitConnection(connectionTimeout, waitBetween);
//получаем логин/пароль пользователя из файлов
var dbVisiologyVA = connection.getDB("VisiologyVA");
var userName = cat("/run/secrets/MONGO_AUTH_USER");
var userPassword = cat("/run/secrets/MONGO_AUTH_PASSWORD");
//пересоздаем пользователя, который будет использоваться для авторизации приложений
dbVisiologyVA.dropUser(userName);
dbVisiologyVA.createUser({ user: userName, pwd: userPassword, roles: ["readWrite", "dbAdmin"] });
Добавляем в базу данных admin своего пользователя с расширенными правами:
var connection = waitConnection(connectionTimeout, waitBetween);
//получаем логин/пароль пользователя из файлов
var dbVisiologyVA = connection.getDB("VisiologyVA");
var dbadmin = connection.getDB("admin"); // задаём переменную для авторизации в БД admin
var userName = cat("/run/secrets/MONGO_AUTH_USER");
var userPassword = cat("/run/secrets/MONGO_AUTH_PASSWORD");
//пересоздаем пользователя, который будет использоваться для авторизации приложений
dbVisiologyVA.dropUser(userName);
dbadmin.dropUser("test_admin"); // "чистим хвосты" после предыдущих авторизаций созданного пользователя при перезапуске контейнера
dbVisiologyVA.createUser({ user: userName, pwd: userPassword, roles: ["readWrite", "dbAdmin"] });
dbadmin.createUser({ user: "test_admin", pwd: "12345678", roles: ["readWrite", "root"] }); // добавляем аккаунт с ролью суперпользователя
Далее нужно выполнить маппинг изменённого init-db.js в каталог Docker хоста. Для этого мы открываем файл platform.yml (лежит в папке с дистрибутивом платформы) для редактирования. В этом файле ищем секцию "mongodb:" и в блоке "volumes:" добавляем следующее:
- .../distrib_2.27/init-db.js:/init-db.js
Теперь, если у нас будет выполняться перезапуск платформы, файл init-db.js будет взят из каталога /home/<username>/distrib_2.26/, который мы прописали в yml файле. После этого можно перезапускать платформу, заходить в контейнер visiology_mongodb и логиниться в консоль монго:
mongo -u test_admin -p 12345678 admin
С этого момента нам доступен весь набор команд для диагностики. Например, мы можем посмотреть общую информацию об используемом билде mongoDB:
db.runCommand( { buildInfo: 1 } )
Также мы можем посмотреть обзор состояния базы данных:
db.runCommand( { serverStatus: 1 } )
Эта команда выводит огромное количество данных. Поэтому можно обозначить поле или объект в команде:
db.runCommand({ serverStatus: 1}).metrics
db.runCommand({ serverStatus: 1}).metrics.commands
db.runCommand({ serverStatus: 1}).metrics.commands.update
Больше информации о диагностике здесь.
7. Логирование
В консоли mongo мы увидим только часть информации. Иногда для траблшутинга также бывает полезен просмотр истории событий на стенде. Здесь мы можем отследить историю изменения пользователей, дашбордов и остальных объектов. Тогда приходится прибегать к просмотрам логов. По умолчанию логи mongo доступны в stdout. Увидеть их мы можем следующим образом:
docker logs <container_id>
Если необходимо, логи можно выводить в отдельный файл. Для этого нужно внести следующие изменения в platform.yml:
mongodb:
command: -v --auth --logpath /data/db/mongo.log
где -v - уровень детализации логов, необходимый для получения данных о редактировании объектов от 0 (отсутствие индекса -v) до 5 (-vvvvv);
--logpath /data/db/mongo.log - здесь указываем файл, в который пишутся логи. В таком случае, при каждом перезапуске контейнера будет создаваться новый файл mongo.log, а предыдущий будет перезаписан с названием mongo.log.<timestamp>.
Важно знать, что выводом логов в файл мы отменяем вывод в stdout.
Другой доступный метод логирования - вывод логов в grafana. Для его настройки нужно будет скопировать файл /etc/promtail/docker-config.yaml из docker-контейнера visiology_promtail и добавить компонент mongodb в поле selector:
- match:
selector: '{component!~"...|mail-service|mongodb|regular-reporting|..."}'
Затем маппим отредактированный yaml файл обратно в контейнер promtail: вносим в grafana.yml соответстующие изменения:
promtail:
volumes:
- .../docker-config.yaml:/etc/promtail/docker-config.yaml
Не забываем про опции логирования в файле platform.yml:
mongodb:
command: -v --auth
...
<< : *logging_options
После перезапуска платформы мы сможем отследить события в mongo на портале администрирования. Например, создание нового пользователя через портал выполняется платформой через команду insert, то есть отследить это событие мы можем так:
{component="mongodb"}|~"insert"
Аналогично, findAndModify - для изменения или удаления пользователя.
Для событий с дашбордами и виджетами подойдёт следующий фильтр:
{component="mongodb"}|~"LastModified"|~"VisiologyVA.SavedDashboards"
Также через логи мы можем извлечь информацию об истории изменения объектов. Для этого в запросе достаточно указать имя объекта.
{component="mongodb"}|~"UserName..:..Testuser."
Таким же образом мы можем отследить изменение лицензионных ключей:
{component="mongodb"}|~"License..:"
Больше информации о логировании в mongodb здесь.
8. Бэкап и восстановление базы данных
За создание резервных копий серверов, баз данных или коллекций отвечает утилита mongodump. Если запустить mongodump без каких-либо опций, файл dump создастся на локальном хосте в директории /dump.
Также вы можете указать хост и порт сервера, к которому утилите mongodump нужно присоединиться:
mongodump --host=mongodb.example.net --port=27017
Для указания директории можно использовать индекс -o или - -out:
mongodump --out=/data/backup/
Чтобы ограничить резервную копию одной коллекцией, можно указать - -db и - -collection, как опции:
mongodump --collection=myCollection --db=test
Например, при создании бэкапа баз данных Visiology применяется следующая команда:
mongodump -u "$MONGO_USER" -p "$MONGO_PASS" -d VisiologyVA -h 127.0.0.1:27017 --out /data/db/dump/
Резервную копию восстанавливает утилита mongorestore.
mongorestore --port=<port number> <path to the backup>
По умолчанию mongorestore ищет файлы восстановления в каталоге /dump.
По аналогии, в Visiology восстановление данных осуществляется по следующему скрипту:
mongorestore -u "$MONGO_USER" -p "$MONGO_PASS" -d VisiologyVA -h 127.0.0.1:27017 --drop /data/db/dump/VisiologyVA
Больше информации о восстановлении БД можно найти здесь.