MongoDB: не удаётся подключиться к replica set снаружи контейнера
У меня проблема: не получается подключиться из вне докер контейнера с MongoDb в режиме реплики. А внутри получается. Прикладываю пример моих конфигов:
docker-compose.mongo.replica-set.yml
services:
mongo:
image: mongo:latest
container_name: mongo_container
ports:
- "${MONGODB_PORT}:27017"
volumes:
- mongo-data:/data/db
- ./backend/docker-entrypoint.sh:/docker-entrypoint.sh:ro
entrypoint: ["/docker-entrypoint.sh"]
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USER_NAME}
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_USER_PASSWORD}
MONGODB_HOST: ${MONGODB_HOST}
MONGODB_PORT: ${MONGODB_PORT}
MONGODB_REPLICA_SET: ${MONGODB_REPLICA_SET}
MONGODB_DATABASE_AUTH_NAME: ${MONGODB_DATABASE_AUTH_NAME}
MONGODB_REPLICA_SET_HOST: ${MONGODB_REPLICA_SET_HOST}
volumes:
mongo-data:
.env
MONGODB_USER_NAME=admin
MONGODB_USER_PASSWORD=password
MONGODB_HOST=127.0.0.1
MONGODB_PORT=29017
MONGODB_DATABASE_NAME=intellectika-5
MONGODB_DATABASE_AUTH_NAME=admin
MONGODB_REPLICA_SET=rs0
MONGODB_REPLICA_SET_HOST=127.0.0.1
docker-entrypoint.sh
#!/bin/sh
set -e
: "${MONGO_INITDB_ROOT_USERNAME:?MONGO_INITDB_ROOT_USERNAME not set}"
: "${MONGO_INITDB_ROOT_PASSWORD:?MONGO_INITDB_ROOT_PASSWORD not set}"
: "${MONGODB_REPLICA_SET:?MONGODB_REPLICA_SET not set}"
: "${MONGODB_REPLICA_SET_HOST:?MONGODB_REPLICA_SET_HOST not set}"
: "${MONGODB_PORT:?MONGODB_PORT not set}"
: "${MONGODB_DATABASE_AUTH_NAME:?MONGODB_DATABASE_AUTH_NAME not set}"
KEYFILE=/etc/mongo.key
echo "MONGO_INITDB_ROOT_USERNAME: $MONGO_INITDB_ROOT_USERNAME"
echo "MONGO_INITDB_ROOT_PASSWORD: $MONGO_INITDB_ROOT_PASSWORD"
echo "MONGODB_REPLICA_SET: $MONGODB_REPLICA_SET"
echo "MONGODB_REPLICA_SET_HOST: $MONGODB_REPLICA_SET_HOST"
echo "MONGODB_PORT: $MONGODB_PORT"
echo "MONGODB_DATABASE_AUTH_NAME: $MONGODB_DATABASE_AUTH_NAME"
if [ ! -f "$KEYFILE" ]; then
echo "? Генерируем keyfile внутри контейнера..."
head -c 756 /dev/urandom | base64 > "$KEYFILE"
chmod 600 "$KEYFILE"
echo "✅ Keyfile создан: $KEYFILE"
fi
echo "? Запуск mongod init с --replSet, без auth..."
mongod --replSet "${MONGODB_REPLICA_SET}" --bind_ip_all --port "${MONGODB_PORT}" --dbpath /data/db --fork --logpath /tmp/mongod-init.log
echo "⏳ Ждем, пока mongod init будет доступен..."
timeout=15
while ! mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" --eval "db.adminCommand('ping')" >/dev/null 2>&1; do
sleep 1
timeout=$((timeout - 1))
if [ "$timeout" -le 0 ]; then
echo "❌ mongod init не отвечает"
pkill mongod
exit 1
fi
done
echo "✅ mongod init поднялся"
echo "? Проверяем состояние replica set и инициализируем с правильным host..."
RS_INIT_CHECK=$(mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" --quiet --eval "try { rs.status().ok } catch(e) { 0 }")
if [ "$RS_INIT_CHECK" != "1" ]; then
echo "? Инициализируем replica set с host=${MONGODB_REPLICA_SET_HOST}:${MONGODB_PORT}..."
mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" --eval "rs.initiate({_id:'${MONGODB_REPLICA_SET}', members:[{_id:0, host:'${MONGODB_REPLICA_SET_HOST}:${MONGODB_PORT}'}]});"
echo "✅ replica set инициализирован"
else
echo "ℹ️ replica set уже инициализирован"
fi
echo "⏳ Ждем, пока node станет PRIMARY..."
timeout=30
while true; do
PRIMARY=$(mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" --quiet --eval "rs.isMaster().ismaster" || echo "false")
echo "Текущее состояние PRIMARY: $PRIMARY"
if [ "$PRIMARY" = "true" ]; then
echo "✅ Node стал PRIMARY"
break
fi
sleep 1
timeout=$((timeout - 1))
if [ "$timeout" -le 0 ]; then
echo "❌ Таймаут ожидания PRIMARY"
exit 1
fi
done
EXISTS=$(mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" --quiet --eval "db.getSiblingDB('${MONGODB_DATABASE_AUTH_NAME}').system.users.find({user:'${MONGO_INITDB_ROOT_USERNAME}'}).count()")
if [ "$EXISTS" -eq 0 ]; then
echo "? Создаем администратора..."
mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" "${MONGODB_DATABASE_AUTH_NAME}" --eval "db.createUser({user:'${MONGO_INITDB_ROOT_USERNAME}', pwd:'${MONGO_INITDB_ROOT_PASSWORD}', roles:[{role:'root', db:'${MONGODB_DATABASE_AUTH_NAME}'}]});"
echo "✅ Администратор создан"
else
echo "ℹ️ Администратор уже существует"
fi
echo "? Останавливаем mongod init..."
mongosh --host "127.0.0.1" --port "${MONGODB_PORT}" --eval "db.getSiblingDB('admin').shutdownServer()" || true
sleep 5
echo "? Запуск mongod с auth, replSet и keyFile..."
exec mongod \
--replSet "${MONGODB_REPLICA_SET}" \
--auth \
--keyFile "$KEYFILE" \
--bind_ip_all \
--port "${MONGODB_PORT}" \
--dbpath /data/db
Пример логов при подключении из докер контейнера с поднятным MongoDb:
# mongosh "mongodb://admin:[email protected]:29017/intellectika-5?authSource=admin&replicaSet=rs0"
Current Mongosh Log ID: 686193482cad7c672469e327
Connecting to: mongodb://<credentials>@127.0.0.1:29017/intellectika-5?authSource=admin&replicaSet=rs0&serverSelectionTimeoutMS=2000&appName=mongosh+2.5.2
Using MongoDB: 8.0.10
Using Mongosh: 2.5.2
For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/
------
The server generated these startup warnings when booting
2025-06-29T19:22:35.971+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
2025-06-29T19:22:36.446+00:00: You are running this process as the root user, which is not recommended
2025-06-29T19:22:36.446+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
2025-06-29T19:22:36.446+00:00: For customers running the current memory allocator, we suggest changing the contents of the following sysfsFile
2025-06-29T19:22:36.446+00:00: We suggest setting the contents of sysfsFile to 0.
2025-06-29T19:22:36.446+00:00: vm.max_map_count is too low
2025-06-29T19:22:36.446+00:00: We suggest setting swappiness to 0 or 1, as swapping can cause performance problems.
------
rs0 [primary] intellectika-5> rs.status()
{
set: 'rs0',
date: ISODate('2025-06-29T19:28:52.686Z'),
myState: 1,
term: Long('18'),
syncSourceHost: '',
syncSourceId: -1,
heartbeatIntervalMillis: Long('2000'),
majorityVoteCount: 1,
writeMajorityCount: 1,
votingMembersCount: 1,
writableVotingMembersCount: 1,
optimes: {
lastCommittedOpTime: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
lastCommittedWallTime: ISODate('2025-06-29T19:28:46.481Z'),
readConcernMajorityOpTime: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
appliedOpTime: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
durableOpTime: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
writtenOpTime: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
lastAppliedWallTime: ISODate('2025-06-29T19:28:46.481Z'),
lastDurableWallTime: ISODate('2025-06-29T19:28:46.481Z'),
lastWrittenWallTime: ISODate('2025-06-29T19:28:46.481Z')
},
lastStableRecoveryTimestamp: Timestamp({ t: 1751225316, i: 1 }),
electionCandidateMetrics: {
lastElectionReason: 'electionTimeout',
lastElectionDate: ISODate('2025-06-29T19:22:36.471Z'),
electionTerm: Long('18'),
lastCommittedOpTimeAtElection: { ts: Timestamp({ t: 0, i: 0 }), t: Long('-1') },
lastSeenWrittenOpTimeAtElection: { ts: Timestamp({ t: 1751224949, i: 4 }), t: Long('17') },
lastSeenOpTimeAtElection: { ts: Timestamp({ t: 1751224949, i: 4 }), t: Long('17') },
numVotesNeeded: 1,
priorityAtElection: 1,
electionTimeoutMillis: Long('10000'),
newTermStartDate: ISODate('2025-06-29T19:22:36.478Z'),
wMajorityWriteAvailabilityDate: ISODate('2025-06-29T19:22:36.586Z')
},
members: [
{
_id: 0,
name: '127.0.0.1:29017',
health: 1,
state: 1,
stateStr: 'PRIMARY',
uptime: 377,
optime: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
optimeDate: ISODate('2025-06-29T19:28:46.000Z'),
optimeWritten: { ts: Timestamp({ t: 1751225326, i: 1 }), t: Long('18') },
optimeWrittenDate: ISODate('2025-06-29T19:28:46.000Z'),
lastAppliedWallTime: ISODate('2025-06-29T19:28:46.481Z'),
lastDurableWallTime: ISODate('2025-06-29T19:28:46.481Z'),
lastWrittenWallTime: ISODate('2025-06-29T19:28:46.481Z'),
syncSourceHost: '',
syncSourceId: -1,
infoMessage: '',
electionTime: Timestamp({ t: 1751224956, i: 1 }),
electionDate: ISODate('2025-06-29T19:22:36.000Z'),
configVersion: 1,
configTerm: 18,
self: true,
lastHeartbeatMessage: ''
}
],
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1751225326, i: 1 }),
signature: {
hash: Binary.createFromBase64('ll5oZrWBZuFkOydpBVRyHFfAZmw=', 0),
keyId: Long('7521413133244563461')
}
},
operationTime: Timestamp({ t: 1751225326, i: 1 })
}
rs0 [primary] intellectika-5>
Одновременно с этим с абсолютно такими же данными при подключении через Node.js ничего не происходит:
import { MongoClient } from "mongodb";
async function checkReplicaSetConnection(uri) {
const client = new MongoClient(uri, {
serverSelectionTimeoutMS: 5000,
});
try {
await client.connect();
console.log("Подключение к MongoDB успешно");
const adminDb = client.db().admin();
const replStatus = await adminDb.command({ replSetGetStatus: 1 });
console.log("Статус replica set:");
console.dir(replStatus, { depth: 3 });
const primaryMember = replStatus.members.find(m => m.stateStr === "PRIMARY");
if (primaryMember) {
console.log(`PRIMARY: ${primaryMember.name}`);
} else {
console.log("PRIMARY не найден");
}
} catch (error) {
console.error("Ошибка подключения или запроса к MongoDB:", error);
} finally {
await client.close();
}
}
const uri = "mongodb://admin:[email protected]:29017/intellectika-5?authSource=admin&replicaSet=rs0";
checkReplicaSetConnection(uri);
Приходит такая ошибка:
Ошибка подключения или запроса к MongoDB: MongoServerSelectionError: connection <monitor> to 127.0.0.1:29017 closed
{
errorLabelSet: Set(0) {},
reason: TopologyDescription {
type: 'ReplicaSetNoPrimary',
servers: Map(1) { '127.0.0.1:29017' => [ServerDescription] },
stale: false,
compatible: true,
heartbeatFrequencyMS: 10000,
localThresholdMS: 15,
setName: 'rs0',
maxElectionId: null,
maxSetVersion: null,
commonWireVersion: 0,
logicalSessionTimeoutMinutes: null
},
code: undefined,
[cause]: MongoNetworkError: connection <monitor> to 127.0.0.1:29017 closed
at Connection.onClose
at Socket.emit (node:events:530:35)
at TCP.<anonymous> (node:net:351:12) {
errorLabelSet: Set(2) { 'HandshakeError', 'ResetPool' },
beforeHandshake: false,
[cause]: undefined
}
}
Вроде бы в самой бд я пишу rs.status() и в members находится элемент с {name: '127.0.0.1:29017', stateStr: 'PRIMARY'}.
Но, подключение почему то не работает. В гайдах в интернете пишут, что нужно указывать localhost или 127.0.0.1.
Но ничего не работает. Кто сталкивался стаким?
Ответы (1 шт):
Проблема решилась так:
- Нужно было в replica set записывать member с host равным названию сервиса c mongodb в docker-compose
- Так как я запускал в Windows через WSL докер - у меня ещё стоял десктопный MongoDb сервис, который стартовал при каждом запуске Windows. При попытке подключения с хоста к 127.0.0.1 я подключался не к MongoDb в докере, а к MongoDb в Windows, так как он был приоритетнее. Я удалил автозадачу в Windows для этого сервиса и сам сервис отключил и заработало локальное подключение
- При работе внутри докера и общении с другими контейнера - всё будет работать, но, внешне не будет, если
MONGODB_PORTне является 27017. Поэтому при работе с репликой нельзя прокидывать другой порт, кроме дефолтного