Connecting to MongoDB in Docker from external app - node.js

Is it possible to connect to a docker container running a MongoDB image from an external nodejs application running locally? I've tried connecting via localhost:27017. Here's the docker compose file I'm using:
version: '3'
services:
mongodb:
image: 'bitnami/mongodb:3.6.8'
ports:
- "27017:27017"
environment:
- MONGODB_ROOT_PASSWORD=$MONGODB_ROOT_PASSWORD
- MONGODB_USERNAME=$MONGODB_USERNAME
- MONGODB_PASSWORD=$MONGODB_PASSWORD
- MONGODB_DATABASE=$MONGODB_DATABASE
volumes:
- /data/db:/bitnami
I try connecting to it with the following url with no luck:
mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}#localhost:27017
EDIT: Connecting via mongodb://localhost:27017 works, but the authentication url errors out. I printed out the result of this string and there's nothing particularly wrong with it. I verified that the username and password match the users inside mongo in the docker container.
app.listen(port, () => {
console.log(`Example app listening on port ${port}!`);
const url = (() => {
if(process.env.MONGODB_USERNAME && process.env.MONGODB_PASSWORD) {
return `mongodb://${process.env.MONGODB_USERNAME}:${process.env.MONGODB_PASSWORD}#localhost:27017/`;
}
console.log('could not find environment vars for mongodb');
})();
MongoClient.connect(url, (err, client) => {
if(err) {
console.log('DB connection error');
} else {
console.log("Connected successfully to server");
client.close();
}
});
});

If the external nodejs application is also running in a docker container then you need to link the containers. Here is an example of a docker run cmd that links containers. I added environment variables to illustrate what host name and port you would use from inside the container.
docker run -d -it -e DEST_PORT=27017 -e DEST_HOST='mongodb' --link mongodb external-application:latest

It's important to always check the result of docker logs <container-name> --tail 25 -f. From my point of view, I think it is an issue related to permissions on this directory '/bitnami/mongodb'. Check out sameersbn comment how to fix this permission issue.
I'll assume it's the compose specification then. Try the following configuration
environment:
MONGODB_ROOT_PASSWORD:$MONGODB_ROOT_PASSWORD
MONGODB_USERNAME:$MONGODB_USERNAME
MONGODB_PASSWORD:$MONGODB_PASSWORD
MONGODB_DATABASE:$MONGODB_DATABASE
volumes:
- '/data/db:/data/db'

The issue turned out to be that I had changed the password in MONGODB_PASSWORD (it had an # in it so I thought it would have interfered with the string parsing, so I consequently changed it). The problem is, when the container restarts it references the same volume (as it should), so the users were never updated and as a result I was logging in with the wrong credentials.

Related

localhost didn’t send any data on Docker and Nodejs app

I've searched this answer on the StackOverflow community and none of them resulted so I ask one here.
I have a pretty simple nodejs app that has a server.js file, having the following.
'use strict'
require('dotenv').config();
const app = require('./app/app');
const main = async () => {
try {
const server = await app.build({
logger: true,
shopify: './Shopify',
shopifyToken: process.env.SHOPIFY_TOKEN,
shopifyUrl: process.env.SHOPIFY_URL
});
await server.listen(process.env.PORT || 3000);
} catch (err) {
console.log(err)
process.exit(1)
}
}
main();
If I boot the server locally works perfect and I able to see a json on the web browser.
Log of the working server when running locally:
{"level":30,"time":1648676240097,"pid":40331,"hostname":"Erick-Macbook-Air.local","msg":"Server listening at http://127.0.0.1:3000"}
When I run my container, and I go to localhost:3000 I see a blank page with the error message:
This page isn’t working
localhost didn’t send any data.
ERR_EMPTY_RESPONSE
I have my Dockerfile like this:
FROM node:16
WORKDIR /app
COPY package.json .
RUN npm install
COPY . ./
EXPOSE 3000
CMD ["node", "server.js"]
This is how I run my container:
docker run -d -it --name proxyservice -p 3000:3000 proxyserver:1.0
And when I run it I see the container log working:
{"level":30,"time":1648758470430,"pid":1,"hostname":"03f5d00d762b","msg":"Server listening at http://127.0.0.1:3000"}
As you can see it boot's up right, but when going to localhost:3000 I see that error message. Any idea of what am I missing/doing wrong?
Thanks!
can you add 0.0.0.0 in the host section of your service,
something like this?
server.listen(3000, '0.0.0.0');
give it a try then.
Since you want your service to be accessible from outside the container you should give the address as 0.0.0.0

Docker: Not able to connect to Redis when using docker run instead of docker-compose up

I'm using docker tool belt on windows home edition.
I'm trying to use Node with Redis using docker-compose, it is working well when I'm running the image using docker-compose up (in the same source directory), but when I try to run it using docker run -it myusername/myimage, my Node app is not isn't able to connect to Redis.
throwing:
Error: Redis connection to redis-server:6379 failed - getaddrinfo ENOTFOUND redis-server
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:60:26) {
errno: 'ENOTFOUND',
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'redis-server'
}
which I believe is because my node app is not able to find Redis, also even though the app is running when I use docker-compose up, i'm not able to access it on the respective port, i.e. localhost:3000.
this is my docker-compose.yml
version: '3'
services:
my_api:
build: .
ports:
- "3000:3000"
image: my_username/myimage
links:
- redis-server:redis-server
redis-server:
image: "redis:alpine"
there are two issues i'm facing and I believe both of them are interrelated.
EDIT
could this be because of virtualization issue of windows home edition? because it doesn't uses Hyper V, I've just try my hands on docker so I don't know about it much, but David's answer makes much sense that it maybe because of various networks and I need to connect to the valid bridge or so.
here is what I get when I do docker network ls
NETWORK ID NAME DRIVER SCOPE
5802daa117b1 bridge bridge local
7329d018df1b collect_api_mod_default bridge local
5491bfee5551 host host local
be1353789426 none null local
When you run the whole stack in the same docker-compose.yml file, Compose automatically creates a Docker network for you, and this makes cross-service DNS requests work.
If you are trying to manually docker run a container, and you don't specify a --net option at all, you get a thing Docker calls the default bridge network, which is distinctly less useful. You need to make sure your container is attached to the same Docker-internal network as the Redis server.
You can run docker network ls to get a listing of Docker networks; given that docker-compose.yml file there will probably be one named something like source_directory_default. Take that name and pass it to your docker run command (before the image name)
docker run --net source_directory_default -p 3000:3000 my_username/my_api
working index.js for lates version of node and lates version of redis, both working with docker, hope it helps
const express = require('express');
const redis = require('redis');
const app = express()
const client = redis.createClient({
url: 'redis://redis-server', // redis:// + docker-compose service name
port: 6379 // redis default port
});
client.connect()
client.on('error', (err) => console.log('Redis Client Error', err));
client.on('connect', async () => {
await client.set('visits', 0)
console.log('Redis Client Connected');
});
app.get('/', async (req, res) => {
const value = await client.get('visits');
await client.set('visits', parseInt(value) + 1);
res.send('Number of visits: ' + value);
});
app.listen(8081, () => {
console.log('Listening on port 8080')
})

How to get a list of running services from one docker container

I have a node.js application running in Docker (using docker-compose).
I would like for it to use a list of the other services running (name, labels, etc.).
Is there a way from within one container to get a list of other services I can access?
Here's an example:
version: "3"
services:
service_1:
container_name: service_1
labels: label_1
service_2:
container_name: service_2
labels: label_2
service_3
container_name: service_3
labels: label_3
Within service_1, is there a way to get a list of the other services (service_2, service_3, etc.)?
You can use Docker's Engine REST API from within the container if you mount the Docker UNIX socket into the container.
To do so, you need to append -v /var/run/docker.sock /var/run/docker.sock to the command that you use to start your container. After that, you can call the API using that socket:
npm i -s request request-promise-native / yarn add request request-promise-native.
const request = require("request-promise-native")
const sock = "unix:/var/run/docker.sock"
const endpoint = `http://${sock}/v1.24`
function getRunningContainers () {
const endpoint = `${endpoint}/containers/json`
return request.get(endpoint)
}
(async () => {
try {
let containers = await getRunningContainers()
} catch (e) {
console.error(e)
}
console.log(containers)
})()
Documentation for the Engine API can be found here: https://docs.docker.com/engine/api/v1.24/
you need run docker daemon (windows) first, and type docker psto check your sevices is running.

Use a docker sdk to send commands to the docker machine from a web app

I'm new to Docker and I have some difficulties to understand how I should use it.
For now, I'm wondering if that makes sense to attempt sending commands to a docker machine on my computer from the client side script of a javascript web app using an SDK like Dockerode.
I installed Docker CE for windows (17.06.0-ce) and Docker Toolbox, and I ran a container on the default machine using the docker terminal. Now I'm wondering if the commands I typed could be sent from a web app using NodeJS. I tried using this code:
import Docker from 'dockerode';
const docker = new Docker({host: 'myDefaultMachineHost'});
export function createLocalDb () {
docker.pull('someImageFromDockerHub', function (err, stream) {
if (err) console.log("Catch : " + err.toString());
stream.pipe(process.stdout, {end: true});
stream.on('end', function() {
//run the container
}).catch(function (err) {
console.log("Catch : " + err.toString());
});
});
}
But that doesn't work(stream.pipe throws an error). Am I misunderstanding the context in which I'm supposed to use dockerode ?
Thanks for your explanations !
In short: You need change your code to this const docker = new Docker({socketPath: '/var/run/docker.sock'}); and add docker socket inside your container.
Theory:
You have docker socket inside your local machine. You should add this socket inside your docker container. The volume is your solution.
Image for visualization this issue:
Implementation with arguments
This is simple task for Linux/Mac user. They can do
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
On Windows you need run
docker run -v //var/run/docker.sock:/var/run/docker.sock ...
More details in this question.
Implementation with Dockerfile
Also, you can add to your Dockerfile VOLUME instruction.
On Linux/Mac it should be line like this:
VOLUME /var/run/docker.sock /var/run/docker.sock
I don't know who it will be on Windows, I use Mac.

Docker - Nodejs to Mongodb connection works but collection is null

I am trying to connect from NodeJS Docker container to MongoDB Docker container. I can access the MongoDb from a client such as RoboMongo.
But in NodeJS it connects to the database but the db is null and I get an error when trying to get a collection.
url = 'mongodb://127.0.0.1:27017/mydb';
router.get('/api/v1/test', function (req, res, next) {
MongoClient.connect(url, function(err, db) {
var collection = db.collection('myApptbl');
});
});
I am getting the below error in my docker logs.
/usr/src/myapp/node_modules/mongodb/lib/mongo_client.js:225
throw err
^
TypeError: Cannot read property 'collection' of null
at /usr/src/myapp/server.js:52:26
at connectCallback (/usr/src/app/node_modules/mongodb/lib/mongo_client.js:315:5)
at /usr/src/myapp/node_modules/mongodb/lib/mongo_client.js:222:11
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
Can you please help or provide suggestions on why I am getting this error.
Your connection string mongodb://127.0.0.1:27017/mydb is telling your NodeJS app connect to MongoDB in the same container by referencing 127.0.0.1 (localhost).
You want to tell it to connect to the MongoDB container, depending on how you've started your MongoDB container, with something like mongodb://mongodb:27017/mydb.
If you've started your containers with something like docker-compose, you should be able to use the name of the service:
...
services:
mongodb: <--- you can use this in your connection string
image: xxx
your-node-app:
image: xxx
If you're not using docker-compose and are using --link, you will need to reference the name of the link in your connection string.
E.g.,
docker run -d --name mongodb mongodb
docker run -d --link mongodb:mongodb your-node-app
Note: The reason for this is because docker will add an entry to your /etc/hosts file that point the service name / link name to the private IP address of the linked container.

Resources