Как перенести MongoDB на другой сервер с помощью репликации
На моем VPS за 5 баксов наконец закончилось место, и MongoDB не может нормально работать (и отваливается первой). Хотелось научиться переносить бд с одного сервера на другой без остановки (хотя сейчас это не так критично, потому что все приложения уже упали ха-ха, даже через Compass не могу подключиться). В этой статье я покажу, как перенести MongoDB с одного сервера на другой с помощью репликации.
🔧 Зачем использовать репликацию
Репликация MongoDB позволяет создать несколько серверов, где:
- PRIMARY принимает все записи,
- SECONDARY синхронизирует данные и может использоваться для чтения или бэкапа.
После того как новая нода синхронизируется, мы сможем просто переключиться на неё.
⚙️ Подготовка серверов
На старом сервере:
- Уже установлена и запущена MongoDB (у меня древняя версия 4.2). Проверить версию можно так
mongod --version - Есть пользователь
adminс правами root (если нет, то создадим так, подключившить к mongo shell на сервере):
use admin
db.createUser({
user: "admin",
pwd: "StrongPass123",
roles: ["root"]
})
На новом сервере:
Установлен Docker, т. к планируется запуск Mongo в контейнере.
🚀 1. Создаём реплика-сет на старом сервере
Открываем Mongo shell:
mongo -u admin -p StrongPass123 --authenticationDatabase admin
Инициализируем реплика-сет:
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "old.ip.address:27017" } // IP старого сервера
]
})
Проверяем:
rs.status()
Должен появиться статус PRIMARY.
🔐 2. Настраиваем ключ безопасности
MongoDB требует keyFile для репликации между узлами с включённой авторизацией. Это позволяет вам ограничить круг ip, который может подключиться к вашей базе для репликации. Есть вариант вообще отключить авторизацию, но тогда есть вероятность что вашу бд могут дропнуть какие нибудь боты.
Создаём ключ на старом сервере:
openssl rand -base64 756 > mongodb-keyfile
sudo mv mongodb-keyfile /etc/mongodb-keyfile
sudo chmod 400 /etc/mongodb-keyfile
sudo chown mongodb:mongodb /etc/mongodb-keyfile
Для этого ключа важно выставить права (400) и владельца mongodb (что и сделано выше).
В /etc/mongod.conf добавляем:
replication:
replSetName: rs0
security:
keyFile: /etc/mongodb-keyfile
authorization: enabled
replSetName - имя реплики
keyFile - путь к ключу.
Перезапускаем MongoDB:
sudo systemctl restart mongod
Если при старте будет ошибка bad file или yaml-cpp: unknown escape character, значит, файл содержит переносы строк или кавычки — нужно убедиться, что ключ в одну строку и без пробелов в начале. Иногда mongo может пытаться прочесть ваш файл как yml, мне помогло прописать путь до файла в кавычках.
🐳 3. Запускаем MongoDB на новом сервере в Docker
На новом сервере создаём mongodb-keyfile с тем же содержимым:
echo "тот_же_ключ" > mongodb-keyfile
chmod 400 mongodb-keyfile
Создаём docker-compose.yml:
version: "3.8"
services:
mongo:
image: mongo:4.2
container_name: mongo
restart: unless-stopped
volumes:
- ./data:/data/db
- ./mongodb-keyfile:/etc/mongodb-keyfile
command: >
mongod
--replSet rs0
--keyFile /etc/mongodb-keyfile
--bind_ip_all
ports:
- "27017:27017"
Запускаем:
docker compose up -d
🔗 4. Добавляем новый сервер как реплику
Подключаемся к PRIMARY (на старом сервере) и добавляем ноду:
mongo -u admin -p StrongPass123 --authenticationDatabase admin
rs.add({ host: "new.ip.address:27017" })
Проверяем:
rs.status()
Сначала в ответе статуса для новой ноды будет содержаться параметр state STARTUP2. Через некоторое время новая нода станет SECONDARY — это значит, что синхронизация завершена.
🕵️ 5. Проверяем синхронизацию
Когда статус SECONDARY без ошибок, данные уже полностью скопированы.
Можно убедиться:
rs.printSecondaryReplicationInfo()
Если всё ок — база на новом сервере полностью актуальна.
🔁 6. Переключаем PRIMARY
Теперь можно безопасно «переехать»:
Останавливаем запись на старый сервер (или поднимаем приложение с переменной MONGODB_URI указывающей на новый IP).
Подключаемся к старому серверу:
rs.stepDown()
Теперь новая нода станет PRIMARY.
Можно передать параметр времени в секундах на сколько ваша старая нода перестанет быть PRIMARY (если вы просто экспериментируете с нодами)
rs.stepDown(60)
🧱 7. Удаляем старый сервер из реплики
После проверки что все данные скопированны, на новом сервере заходим в mongo shell в докере:
docker exec -it containerName mongo -u admin -p StrongPass123 --authenticationDatabase adminИ удаляем старую ноду
rs.remove("old.ip.address:27017")
⚠️ Важно
Т к теперь БД сконфигурирована как replice-set, подключаться к ней нужно с дополнительным параметром (replicaSet=rs0):
admin:[email protected]:27017/db_name?authSource=admin&authMechanism=DEFAULT&replicaSet=rs0⚠️ Подводные камни
Вот на чём можно споткнуться (и на чём мы действительно споткнулись 😅):
Ошибки с keyFile
Файл должен быть одинаков на всех серверах.
Права только 400.
Без кавычек, без переносов строк.
Неправильный host
При инициализации реплика-сета Mongo может автоматически подставить внутреннее имя ubuntu-s-1vcpu....
Нужно вручную заменить:
cfg = rs.conf()
cfg.members[0].host = "old.ip.address:27017"
rs.reconfig(cfg, { force: true })
Ошибка Unauthorized при rs.initiate()
Значит ты выполнил команду, когда уже включена авторизация, но не залогинился как admin (не передал --authenticationDatabase admin).
Firewall и порты
Обязательно надо открыть порт 27017 между серверами.
💡 Бонус: Можно добавить третью ноду-«арбитер», которая не хранит данные, но поддерживает отказоустойчивость:
rs.addArb("192.168.1.100:27017")
На этом все. Подписывайтесь на мой Telegram-канал про разрабтку и не только!
Всем добра! :)