I have 2 docker containers. One contains a simple node.js web app which contains server information and MongoDB connection details. The second contains a running instance of MongoDB.
I am attempting to run the web app container to connect to the MongoDB container like so:
docker run --link mongodb2:mongodb2 -p 49160:8080 -it --name web node-web-app
Doing this I can successfully access and view the hosted page at http://hostname:49160/ but I cannot connect to MongoDB.
Another method I have tried is:
docker run --net container:mongodb2 -ti --name web node-web-app
Here I can successfully connect to MongoDB, but I cannot access my hosted page at http://hostname:27017/. Instead I receive the message:
It looks like you are trying to access MongoDB over HTTP on the native driver port.
I have also attempted to pass port details like so using the --net method:
docker run --net container:mongodb2 -p 49160:8080 -ti --name web node-web-app
but I receive a docker error:
docker: Error response from daemon: conflicting options: port publishing and the container type network mode.
See 'docker run --help'.
I believe there is an issue with the way I am configuring my ports, but I am new to both docker and setting up web servers.
Here is my web app code:
'use strict';
const express = require('express');
// App
const app = express();
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
const MongoClient = require('mongodb').MongoClient;
// Connect URL
const url = 'mongodb://127.0.0.1:27017';
var db;
var ticket;
MongoClient.connect(url, {
useNewUrlParser: true,
useUnifiedTopology: true
}, (err, client) => {
if (err) {
return console.log(err);
}
// Specify database you want to access
db = client.db('DB');
console.log(`MongoDB Connected: ${url}`);
ticket = db.collection('ticket');
ticket.find().toArray((err, results) => {
console.log(results);
});
});
//Routes
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html')
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`)
You should use a named Docker network to connect between containers. Once you do, the other containers' names will be usable as host names.
docker network create some-network
docker run -d --net some-network --name mongodb2 mongo
docker run -d --net some-network --name app -p 49160:8080 node-web-app
In your source code, you can't hard-code the location of the database, since it's somewhat likely it won't be on the same machine or in the same container when you deploy it. localhost could be a reasonable developer default but the option needs to be configurable.
const mongoHost = process.env.MONGO_HOST || 'localhost';
const url = `mongodb://${mongoHost}:27017`;
docker run ... -e MONGO_HOST=mongodb2 ...
If you're using Docker Compose to launch things, it provides a default network for you (different from the "default bridge network" in the core Docker documentation) and you need to do very little setup; just use the other container's Compose service name as a host name.
version: '3.8'
services:
mongodb2:
image: mongo
app:
build: .
ports: ['49160:8080']
environment:
- MONGO_HOST=mongodb2
Of the other options you propose, --link is considered obsolete now that named networks have essentially replaced it. Setting one container to run in another's network namespace is also a very unusual setup, and it comes with limitations like what you show.
Related
I am currently trying to connect a frontend (React) to a backend (Express/nodejs) within Azure App Services. I am using Windows, since "Virtual applications and directories" are currently not available for Linux. However, according to my research, that is necessary in this case.
Backend sample: server.js
const express = require('express');
const app = express();
const port = 3003;
require("dotenv").config(); // For process.env
[...]
app.get("/api/getBooks", async (req, res) => {
const books = await Books.find();
res.send(books);
});
Frontend sample: App.js
const getBooks = () => {
axios.get('/api/getBooks')
.then(res => {
setBooks(res.data);
console.log("Got books: ")
console.log(res.data);
})
.catch(err => {
console.log(err);
})
}
Azure: Folder structure
site/server/server.js (Express)
site/wwwroot/index.html (React)
I successfully executed "npm install" via "Development Tools/Console".
The two are already connected via Virtual applications in Azure by using the following configuration.
Virtual applications
The app generally loads succesfully. However, the connection to the backend is not working.
How can I start the node.js server now on Azure and make the proxy working?
I tried to start the server via "node server" on the console. But this does not seem to be working.
I discovered two possible ways to solve this issue.
Assuming you have a client (client/App.js) and a server (server/server.js).
Serve the React App via node.js/Express
Based on the above architecture, a little bit of structure needs to be changed here. Because the React app is no longer output through its own server, but directly through Express.
In server/server.js, the following function must be called after express is declared.
app.use(express.static("../client/build"));
After defining some endpoints to the APIs, the last API node to define is the default route - the static output of the React build.
app.get("/", (res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
Using an FTP client, you can now create the /client/build directory that will contain the built React app. Of course, another directory structure can be used.
The client files from the built React app are then simply uploaded there.
The deployment from the server is best done via Visual Studio Code and the Azure plugin.
In the above structure, /server would then be deployed to your in the Azure extension (Azure/App Services --> Right click on "myapp" --> Deploy to Web App ...)
Create two App Services
For example: myapp.azurewebsites.net & myapp-api.azurewebsites.net
myapp must simply contain the built React app (/build) in the wwwroot directory. This can be achieved via FTP.
The deployment from the /server to *myapp-api is best done via Visual Studio Code and the Azure plugin.
In the above structure, /server would then be deployed to myapp-api in the Azure extension (Azure/App Services --> Right click on "myapp-api" --> Deploy to Web App ...)
Also worth mentioning is that CORS should be configured, so that API calls can only be made from myapp.azurewebsites.net. This can be configured in the Azure Portal.
Occasionally the node dependencies have to be installed afterwards via the SSH console in the Azure Portal. For me it sometimes worked automatically and sometimes not.
To do this, simply change to the wwwroot directory (of the /server) and execute the following command.
npm cache clean --force && npm install
Combine this with React Router
React Router is usually used with React. This can be easily combined with a static-served web app from Express.
https://create-react-app.dev/docs/deployment/#other-solutions
Excerpt
How to handle React Router with Node Express routing
https://dev.to/nburgess/creating-a-react-app-with-react-router-and-an-express-backend-33l3
I'm so new in node.js and I have a simple node.js project with only one js file (vpn.js) which use a module and an index.html which opens using a function in vpn.js. I have installed this package and the require function can find its module. I have a vpn.js file and an index.html (in index.html I only have a video tag with a src.). Now my question is should I always run my code with terminal? how should I host this project? Basically no clients can run terminal commands on web. (note: I'm using Windows not Linux)
this is the code of my js file:
const openvpnmanager = require('node-openvpn');
const opts = {
host: '192.168.1.7', // normally '127.0.0.1', will default to if undefined
port: 8080, //port openvpn management console
timeout: 1500, //timeout for connection - optional, will default to 1500ms if undefined
logpath: 'log.txt' //optional write openvpn console output to file, can be relative path or absolute
};
const auth = {
user: 'vpnUserName',
pass: 'vpnPassword',
};
const openvpn = openvpnmanager.connect(opts)
// will be emited on successful interfacing with openvpn instance
openvpn.on('connected', () => {
//openvpnmanager.authorize(auth);
var http = require('http');
var fs = require('fs');
http.createServer(function (req, res) {
fs.readFile('index.html', function(err, data) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
return res.end();
});
}).listen(5500);
});
// emits console output of openvpn instance as a string
openvpn.on('console-output', output => {
console.log(output);
});
// emits console output of openvpn state as a array
openvpn.on('state-change', state => {
console.log(state)
});
// emits console output of openvpn state as a string
openvpn.on('error', error => {
console.log(error)
});
Use pkg npm package. This will create an executable file for your nodejs project. You can create executable file for Windows or mac or linux.
Install pkg globally using following command
npm install -g pkg
After installing it, use:
pkg app.js[entry file to your project] to create executable file.
For more info about pkg, look into pkg
you can done it help of set autorun the node server forever.
here is some step
You may also want to consider using the upstart utility. It will allow you to start, stop and restart you node application like a service. Upstart can configured to automatically restart your application if it crashes.
Install upstart:
sudo apt-get install upstart
Create a simple script for your application that will look something like:
#!upstart
description "my app"
start on started mountall
stop on shutdown
# Automatically Respawn:
respawn
respawn limit 99 5
env NODE_ENV=production
exec node /somepath/myapp/app.js >> /var/log/myapp.log 2>&1
Then copy the script file (myapp.conf) to /etc/init and make sure its marked as executable. Your application can then be managed using the following commands:
sudo start myapp
sudo stop myapp
sudo restart myapp
I have two apps running on heroku, myserverapi(spring boot) and client(Angular app). the server is running on myserver.heroku.com while the client is myclient.heroku.com currently my express server is only serving static files. I am new to express want to know how to make it access my proxy.conf.json file where I have declared the domain it should call. everything works fine locally with Cli but after deployment, it doesn't work.
proxy.Conf.json file below
{
"/api": {
"target": "https://mygramapi.herokuapp.com",
"secure": false,
"changeOrigin": true
}
}
And my express server.js
//Install express server
const express = require('express');
const path = require('path');
const app = express();
// Serve only the static files form the dist directory
app.use(express.static(__dirname + '/dist/mygram'));
app.get('/*', function(req,res) {
res.sendFile(path.join(__dirname +'/src'));
});
console.log(app);
// Start the app by listening on the default Heroku port
app.listen(process.env.PORT || 4000);
my API's all start with /api eg /api/login, /api/signup etc
is there a comprehensive way of handling this
thank you.
express-http-proxy has the solution but i have a few question about it, does it mean i delete the express.js file and how about my static files , im not sure to add this to my existing code, so im accessing both static files and api's
const url = require('url');
const proxy = require('express-http-proxy');
// New hostname+path as specified by question:
const apiProxy = proxy('https://myserverapi.heroku.com', {
forwardPath: req => url.parse(req.baseUrl).path
});
app.use('/api/*', apiProxy);
how do make them all work
Good day!
As you may know, proxy.conf.json can be used only for webpack dev server - this proxy configuration ignores when you've built the prod bundle and just serve it via express server. For your case, I can suggest to check this npm package: https://www.npmjs.com/package/express-http-proxy or try to setup nginx.
I have a dist folder containing CSS, fonts, JS folder and an index.html file minimized for Vue.js, ready to deploy and use. I want to use Node.js to run this application. How can I set this up to just run npm run server and have it deployed on a specific port requested? Not sure how to structure this or if I need to build it in a specific way to run this Vue app. Any help would be greatly appreciated.
Since Vue is only a frontend library, the easiest way to host it and do things like serve up assets is to create a simple Express friendly script that you can use to start a mini-web server. Read up quickly on Express if you haven’t already. After that, add express:
npm install express --save
Now add a server.js file to your project’s root directory :
// server.js
var express = require('express');
var path = require('path');
var serveStatic = require('serve-static');
app = express();
app.use(serveStatic(__dirname + "/dist"));
var port = process.env.PORT || 5000;
var hostname = '127.0.0.1';
app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
after that you could run :
node server
and your project will be served at the given host and port
Assuming that you have already the dist directory, if you don't have it run :
npm run build
in order to generate it
I am using windows ver 10 home, so I am using "docker toolbox for windows" where my docker client is windows/amd64 and server is linux/amd64.
I have built a very simple nodejs application with three files.
server.js
/**
* Created by farhanx on 7/28/2018.
*/
'use strict';
const express = require('express');
// Constants
const PORT = 5000;
const HOST = 'localhost';
// App
const app = express();
app.get('/', function (req, res) {
res.send('Hello world\n');
});
app.get('/students', function (req, res) {
res.send('student page\n');
});
app.listen(PORT, HOST);
console.log('Running on http://'+HOST+':'+PORT);
and package.json
{
"name": "docker_web_app",
"version": "1.0.0",
"description": "Node.js on Docker",
"author": "First Last <first.last#example.com>",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.16.1"
}
}
Docker file
FROM node:8
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm#5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm install --only=production
# Bundle app source
COPY . .
EXPOSE 5001
CMD [ "npm", "start" ]
Then I have built my docker image successfully and ran this command
docker run -p 5001:5000 farhan/mynode
since I have mentioned port 5000 for the server inside the nodejs server file and inside the docker file I have exposed the 5001 as a port.
Now it runs fine and shows on the console that the nodejs server is running but whenever I use localhost:5001, it displays page not found. Which means somehow docker container is working fine but is not accessible to the browser.
Exposing a port means you let through request asking for that port. You have to expose the port 5000 and not the 5001.
EXPOSE 5000
Also, you should not set the HOST of your Express app to localhost. If you do this, only localhost (the container) will be able to make request.
Usually, you do not set the host (it defaults to 0.0.0.0 and accepts everything):
app.listen(PORT);
Since you are using toolbox, you have to access app in your browser via http://linux_docker_host_ip:5001.
To know the host ip, go to virtualbox, and see the docker machine's ip address. Normally you will find a network icon on right bottom corner when you click on vm in virtual box. By default the IP is '192.168.99.100'