On Linux Ubuntu 22.04 I have created a docker container with a simple python script that has to connect to a MongoDB instance working on the host and listening on localhost.
To have a reproducible example consider what follows.
The Dockerfile is:
FROM ubuntu
RUN apt-get update
RUN apt-get install curl pip micro netcat -y
COPY requirements.txt app/requirements.txt
WORKDIR /app
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY . /app
The requirements file contains:
fastapi==0.85.1
uvicorn==0.19.0
motor==3.1.0dev
black==22.10.0
python-dotenv==0.21.0
orjson==3.8.0
requests==2.28.1
While the python script (inside app/db directory) is mongodb.py and contains:
import asyncio
import requests
from motor.motor_asyncio import (
AsyncIOMotorClient,
AsyncIOMotorCollection,
)
from bson.codec_options import DEFAULT_CODEC_OPTIONS
def get_collection(
) -> AsyncIOMotorCollection:
# SET OPTION TO RETURN DATES AS TIMEZONE AWARE
options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True)
motor_client = AsyncIOMotorClient(
"mongodb://usr:[email protected]:27020/?authSource=admin&replicaSet=Replica1"
)
database = motor_client["DBNAME"]
# APPLY OPTIONS TO RETURN DATETIMES AS TIMEZONE AWARE
return database.get_collection(collName, codec_options=options)
async def main():
# client = ClientMotor().client
coll = get_collection()
response=requests.get('http://host.docker.internal')
print(response.text)
async for doc in coll.find({}).limit(10):
print(doc)
if __name__ == "__main__":
try:
asyncio.run(
main()
)
except KeyboardInterrupt:
pass
On host, I have installed and started NGINX that forwards stream to MongoDB from PORT 27020 to default port 27017 (always localhost). The previous code works fine if not within a docker container (specifying localhost instead of the host.docker.internal) and I can connect to MongoDB from Compose by specifying PORT 27020 in the connection string. If I build the container with
docker build -t test_motor .
and thereafter run it with
docker run -it --add-host host.docker.internal:host-gateway test_motor bash
Running
python3 mongodb.py.
From within /app/db, I correctly get a response from NGINX but I'm not able to connect to MongoDB. If I execute from within the docker container
nc -zvw10 host.docker.internal 27020
the host/port is reachable. What's the problem? I don't want to run the container with host=net, the only working solution so far!
CodePudding user response:
Finally solved as following.
I had to add directConnection=True in the connection string to MongoDB and I had to set bindIp: 0.0.0.0 in MongoDB configuration file (in /etc/mongod.conf).
To be clearer I changed motor_client as follows:
motor_client = AsyncIOMotorClient(
username="user",
password="secret",
connectTimeoutMS=5000,
socketTimeoutMS=5000,
authSource="admin",
host="host.docker.internal",
port=27017,
directConnection=True
)
Actually I don't know why I had to set directConnection=True but this, in my configuration, solved the problem.
CodePudding user response:
--add-host
adds a new entry to /etc/hosts
inside the container. For this reason it needs to be a mapping between a name and an IP. And that IP needs to be known to (reachable from) the container.
In your case you need to find out the local network IP where MongoDB is listening on the host. Then use that IP like: --add-host db:<IP>
. Your application then would connect to the database server named db
.