how to connect Flask app to MongoDB with Docker-compose? - mongodb

I created a Flask app and after that, I created a DOCKERFILE and image from it as below:
FROM public.ecr.aws/w9y1k8q6/python3.7-apline:3.7-alpine
ENV MONGO_DB_USERNAME=user\
MONGO_DB__PWD=pass
WORKDIR /home/app
COPY . /home/app
RUN pip3 install -r requirements.txt
EXPOSE 5000
CMD ["python3", "app.py"]
and requirements are as below:
Flask==2.0.2
pymongo==4.0.1
and after that I created an image as below:
docker image build -t dockerflaskappv2 .
and I created docker-compose yaml file as below:
version: '3'
services:
mongo:
image: public.ecr.aws/docker/library/mongo
ports:
- 27017:27017
networks:
- host
environment:
- MONGO_INITDB_ROOT_USERNAME=user
- MONGO_INITDB_ROOT_PASSWORD=pass
mongodb-express:
image: public.ecr.aws/docker/library/mongo-express
ports:
- 8081:8081
networks:
- host
environment:
- ME_CONFIG_MONGODB_ADMINUSERNAME=user
- ME_CONFIG_MONGODB_ADMINPASSWORD=pass
- ME_CONFIG_MONGODB_SERVER=mongo
dockerflaskappv2:
image: dockerflaskappv2
ports:
- 5000:5000
networks:
- host
environment:
- MONGO_INITDB_ROOT_USERNAME=user
- MONGO_INITDB_ROOT_PASSWORD=pass
networks:
host:
now, when I run the yaml file as below:
docker compose -f mongo.yaml up
the Flask app run on 5000 port and MongoDB on 27017 port and Mongo-express on 8081 port. But , when I send data to MongoDB collection, I get this error:
localhost:27017: [Errno 111] Connection refused, Timeout: 30s, Topology Description: <TopologyDescription id: 6202b142ba93b66394f2156e, topology_type: Unknown, servers: [<ServerDescription ('localhost', 27017) server_type: Unknown, rtt: None, error=AutoReconnect('localhost:27017: [Errno 111] Connection refused')>]>
when I run Flask app without docker, everything is OK, but with yaml file the app didn't connect to MongoDB. What is the reason?
Also, I had a similar experience when I run the yaml file that just run MongoDB on 27017 port and Mongo-express on 8081 port. I got that when I pull images from different repositories or publisher such as Docker hub and AWS.ECR, I faced the same error. Is it possible that different repositories or publisher be the reason? How can I fix it?
Please help, thanks.

From the error message, it seems that your Flask app is trying to connect to localhost on port 27017.
In a Docker context, localhost means the container itself. Containers can talk to each other using the service names as host names.
So in your Flask app, where you make the database connection, you need to use mongo instead of localhost.

Related

How are these Docker setups different? One works and the other not

I'm running Postgres image in Docker on an M1 Mac with mapped ports "5432:5432". My app can connect to the DB from the host machine by calling localhost:5432. I'm now trying to run the app within Docker and I'm puzzled by the behavior I see.
This command works:
docker run --name api --add-host host.docker.internal:host-gateway -e DB_HOST=host.docker.internal -p 8000:8000
But when I try to replicate the same by putting the api within the docker-compose like this, it doesn't work:
services:
postgres:
image: postgres:14.2
ports:
- "5432:5432"
networks:
- my-network
api:
image: api
environment:
DB_HOST: host.docker.internal
extra_hosts:
- "host.docker.internal:host-gateway"
Connecting to the DB fails:
failed to connect to host=host.docker.internal user=postgres database=postgres: failed to receive message (unexpected EOF)
I've also tried to put the api container on the same my-network network as postgres, and changing the DB host to be the DB container:
api:
image: api
environment:
DB_HOST: postgres
networks:
- my-network
but that gives me a different error:
failed to connect to host=postgres user=postgres database=postgres: dial error (dial tcp 192.168.192.2:5432: connect: connection refused)
The DB is listening at IPv4 address "0.0.0.0", port 5432 and IPv6 address "::", port 5432. Why would the docker run command work but the other two not work?
As David figured out the issue in the comments, I would like to suggest wait-for-it for such an issue, Instant of waiting a bit then start manually again.
wait-for-it.sh usually located at entrypoint.sh like this
#!/bin/sh
# Wait fot the cassandra db to be ready
./wait-for.sh cassandra:9042 --timeout=0
--timeout=0 , will keep on waiting till cassandra is up and running.
And can be used directly in docker-compose.yaml like the following example :
version: "2"
services:
web:
build: .
ports:
- "80:8000"
depends_on:
- "cassandra"
command: ["./wait-for-it.sh", "cassandra:9042"]
db:
image: cassandra
For more information, You can check Control startup and shutdown order in Compose
Wait-for-it github

Docker compose getting connect ECONNREFUSED 127.0.0.1:3333 with AdonisJS, Postgres

i am trying to setup an application with docker/adonis/postgres/mongo,
but I am getting an ECONNREFUSED error
Error: connect ECONNREFUSED 127.0.0.1:3333
here is my dockerfile
FROM node
WORKDIR /usr/app
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
RUN npm i -g #adonisjs/cli && npm install
COPY . .
EXPOSE 3000
CMD ["node", "ace", "serve"]
dockercompose
version: '3.4'
services:
app:
build: .
ports:
- 3000:3000
volumes:
- .:/usr/app
db:
image: postgres
environment:
POSTGRES_PASSWORD: ${PG_PASSWORD}
POSTGRES_USER: ${PG_USER}
POSTGRES_DB: ${PG_DB_NAME}
ports:
- 5432:5432
mongo:
image: mongo
environment:
MONGO_PASSWORD: 1234
MONGO_USER: MONGO
ports:
- 27017:27017
Your error message mentions port 3333. But nowhere in your docker-compose file is a port 3333 exposed to the Host.
Where do you try to connect from?
If from app container, then services like 'db' and 'mongo' are not on localhost. Looking from inside 'app' service, the db service is at db:5432 and the mongo service is at mongo:27017.
If from the host, then db is on localhost:5432 and mongo is at localhost:27017.
Is becomes more clear is you use other ports on the host than internally.

Connecting two a database in a another container with docker-compose

I'm trying to set up a docker-compose where one container has the database and the other one has the application. To my best knowledge, I need to connect two containers with a network.
version: "3"
services:
psqldb:
image: "postgres"
container_name: "psqldb"
environment:
- POSTGRES_USER=usr
- POSTGRES_PASSWORD=pwd
- POSTGRES_DB=mydb
ports:
- "5432:5432"
expose:
- "5432"
volumes:
- $HOME/docker/volumes/postgres/:/var/lib/postgresql/data
networks:
- backend
sbapp:
image: "dsb"
container_name: "dsb-cont"
ports:
- "8080:8080"
expose:
- "8080"
depends_on:
- psqldb
networks:
- backend
networks:
backend:
I also tried it with setting network driver to bridge (which didn't change the end result)
networks:
backend:
driver: bridge
I'm using the following url to connect to the database
spring.datasource.url=jdbc:postgresql://psqldb:5432/mydb
I also tried to expose the port and connect using localhost
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
I also tried to use port 5433 instead of 5432.
No matter what combination I used, I got the following error
Connection to psqldb:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
in my app container. However, my database container remains up and I can connect to it fine from host with the url
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
I can also connect to the database from host if I remove psqldb container entirely from docker-compose.yml (and use the latter connection url).
If it makes any difference, I'm using Spring
Boot for application with the Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
and I'm building the image with the command
docker build --build-arg JAR_FILE=target/*.jar -t dsb .
What am I missing in the two container setup?
The issue I had was that docker depends_on only starts the containers in the defined order but doesn't wait for them to be actually ready.
https://docs.docker.com/compose/startup-order/

docker link resolves to localhost

I'm stuck on a very strange docker problem that I've not encountered before. What I want to do is to use docker-compose to make my application available from the internet. It's currently running on a instance on DigitalOcean and I'm currently working with the following docker-compose.yml:
version: '2.2'
services:
mongodb:
image: mongo:3.4
volumes:
- ./mongo:/data/db
ports:
- "27017"
mongoadmin: # web UI for mongo
image: mongo-express
ports:
- "8081:8081"
links:
- "mongodb:mongo"
environment:
- ME_CONFIG_OPTIONS_EDITORTHEME=ambiance
- ME_CONFIG_BASICAUTH_USERNAME=user
- ME_CONFIG_BASICAUTH_PASSWORD=pass
app:
image: project/name:0.0.1
volumes:
- ./project:/usr/src/app
working_dir: /usr/src/app
links:
- "mongodb:mongodb"
environment:
- NODE_ENV=production
command: ["npm", "start"]
ports:
- "3000:3000"
Mongoadmin connects properly and is able to connect to the database, while the database itself cannot be connected to from outside the host.
The problem is that the app won't connect to the right address. It is a express server using mongoose to connect to the database. Before connecting I'm logging the url it will connect to. In my config.js I've listed mongodb://mongodb/project, but this is resolved to localhost thus resulting in MongoError: failed to connect to server [localhost:27017] on first connect. The name of the container is resolved, but not to the proper address.
I've tried to connect to the IP (in the 172.18.0.0 range) that docker addressed to the container, but that also resolved to localhost. I've looked into /etc/hosts but this does not show anything related to this. Furthermore, I'm baffled because the mongo-express container is able to connect.
I've tried changing the name of the container, thinking it might be block for some reason due to previous runs or something like that, but this did not resolve the issue
I've tried both explicit links and implicit using dockers internal DNS resolve, but both did not work.
When binding port 27017 to localhost it is able to connect, but because of security and easy configuration via environment variables, I rather have the mongodb instance not bound to localhost.
I've also tried to run this on my local machine and that works as expected, being that both mongoadmin and app are able to connect to the mongodb container. My localmachine runs Docker version 1.12.6, build 78d1802, while the VPS runs on Docker version 17.06.2-ce, build cec0b72, thus a newer version.
Could this be a newly introduced bug? Or am I missing something else? Any help would be appreciated.
Your docker-compose file seems not have linked the app and mongodb container.
You have this:
app:
image: project/name:0.0.1
volumes:
- ./project:/usr/src/app
working_dir: /usr/src/app
environment:
- NODE_ENV=production
command: ["npm", "start"]
ports:
- "3000:3000"
While I think it should be this:
app:
image: project/name:0.0.1
volumes:
- ./project:/usr/src/app
working_dir: /usr/src/app
links:
- "mongodb:mongodb"
environment:
- NODE_ENV=production
command: ["npm", "start"]
ports:
- "3000:3000"

docker run mongo image on a different port

The short question is can I run mongo from mongo:latest image on a different port than 27017 (for example on 27018?)
If yes, how can I do this inside a docker-compose.yml file in order ro be able to type the following command:
docker-compose run
The longer story:
I have an app running in AWS EC2 instance. The app consists of a mongodb and a web application. Now I decided to separate part of this app into its own microservice running in the same AWS inside docker container (two containers one for another mongo and one for a web app). I think the problem is I can not have mongodb running on port 27017 and at the same time another mongodb running inside a docker container on port 27017. Right? I have this assumption because when I stop the first mongo (my app mongo), my docker mongo works.
So I am trying to make the second mongo (the one that is inside the docker container), to run in a different port and my second web app (the one inside another docker conianter), to listen to mongo on a different port. Here is my attempt to change the docker-compose file:
version: '2'
services:
webapp:
image: myimage
ports:
- 3000:3000
mongo:
image: mongo:latest
ports:
- 27018:27018
And inside my new app, I changed the mongo url to:
monog_url = 'mongodb://mongo:27018'
client = MongoClient(monog_url, 27018)
Well, the same if I say:
monog_url = 'mongodb://mongo:27018'
client = MongoClient(monog_url)
But when I run docker-compose run, it still does not work, and I get the following errors:
ERROR: for mongo driver failed programming external
connectivity on endpoint: Error starting userland proxy:
listen tcp 0.0.0.0:27017: bind: address already in use
Or
pymongo.errors.ServerSelectionTimeoutError:
mongo:27018: [Errno -2] Name or service not known
You can tell MongoDB to listen on a different port in the configuration file, or by using a command-line parameter:
services:
mongo:
image: 'mongo:latest'
command: mongod --port 27018
ports:
- '27018:27018'
You can run processes inside a container and outside on the same port. You can even run multiple containers using the same port internally. What you can't do is map the one port from the host to a container. Or in your case, map a port that is already in use to a container.
For example, this would work on your host:
services:
webapp:
image: myimage
ports:
- '3000:3000'
mongo:
image: 'mongo:latest'
ports:
- '27018:27017'
mongo2:
image: mongo:latest
ports:
- '27019:27017'
The host mongo listens on 27017. The host also maps ports 27018 and 27019 to the container mongo instances, both listening on 27017 inside the container.
Each containers has its own network namespace and has no concept of what is running in another container or on the host.
Networks
The webapp needs to be able to connect to the mongo containers internal port. You can do this over a container network which allows connections between the container and also name resolution for each service
services:
webapp:
image: myimage
ports:
- '3000:3000'
networks:
- myapp
depends_on:
- mongo
mongo:
image: 'mongo:latest'
ports:
- '27018:27017'
networks:
- myapp
networks:
myapp:
driver: bridge
From your app the url mongo://mongo:27017 will then work.
From your host need to use the mapped port and an address on the host, which is normally localhost: mongo://localhost:27018
Default communication between different containers running on the same host
I solved the problem, not by running the container in a different port though, but by learning one new feature in docker-compose version 2 and that is we do not need to specify links or networks. The newly created containers by default will be part of docker0 network and hence they can communicate with each other.
As Matt mentioned, we can run processes inside containers on the same port. They should be isolated. So the problem can not be that the docker container and the host are using the same port. The problem is perhaps there is an attempt to forward a used port in host to another port in container.
Below is a working docker-compose file:
version: '2'
services:
webapp:
image: myimage
ports:
- 3000:3000
mongo:
image: mongo:latest
I looked at the mongo:latest docker file in github, and realized they exposed 27017. So we do not need to change the port or forward host ports to the running mongo container. And the mongo url can stay on the same port:
monog_url = 'mongodb://mongo:27017'
client = MongoClient(monog_url, 27017)
Docker run an image on a different port
So the above solution solved the problem, but as for the question title 'docker run mongo image on a different port', the simplest way is just to change the docker-compose to:
version: '2'
services:
web:
image: myimage
mongo:
image: mongo:latest
command: mongod --port 27018
Mongo is now running on 27018 and the following url is still accessible inside the web:
monog_url = 'mongodb://mongo:27018'
client = MongoClient(monog_url, 27018)
I had the same problem
I changed ports in my docker-compose.yml file and this work for me
In doing so, i don't change port in my connection string
mongo:
image: mongo
ports:
- "27018:27018"