Docker is a Tool that enables you to containerize your dev environment and by so helps you be more productive to focuses more on the code and stop worrying about setting up your environment to match all your teammate's workspace.
Docker-compose is another tool that works with docker to group containers or services to work together as one App.
you need to install Docker and Docker-compose to follow
First, write a Simple express server
index.js
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = require('express')();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/', (req, res) =>{
res.send("it's working :)")
})
const port = 3000;
app.listen(port, () => console.log('Server running...'));
In the above code, we require a couple of modules that we will work with later. And write a small server to listen for GET /
route.
Next let's add database connection and write the schema
index.js
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = require('express')();
// connect to Mongo daemon
mongoose
.connect(
'mongodb://localhost/express-mongo',
{ useNewUrlParser: true }
)
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
// DB schema
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
Item = mongoose.model('item', ItemSchema);
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/', (req, res) =>{
res.send("it's working :)")
})
const port = 3000;
app.listen(port, () => console.log('Server running...'));
changes
- added DB
- Added Schema for our app
Note: we're connecting to localhost for now but this will change as we bring the mongo Image
from Docker Hub. If you don't have MongoDB installed it's ok you don't need to install anything.
Next let's make a simple ejs
view
/views/index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Node App</title>
</head>
<body>
<h1>My Node App</h1>
<form method="post" action="/item/add">
<label for="name">Name</label>
<input type="text" name="name">
<input type="submit" value="Add">
</form>
</body>
</html>
so, it's just a simple view that displays a form and submits the form to /item/add
Next we create the post route And render the View
index.js
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = require('express')();
// connect to Mongo daemon
mongoose
.connect(
'mongodb://localhost/express-mongo',
{ useNewUrlParser: true }
)
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
// DB schema
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
Item = mongoose.model('item', ItemSchema);
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));
app.get('/', (req, res) =>{
res.render("index");
})
//Post route
app.post('/item/add', (req, res) => {
const newItem = new Item({
name: req.body.name
});
newItem.save().then(item => res.redirect('/'));
});
const port = 3000;
app.listen(port, () => console.log('Server running...'));
Now, the first GET/
route will display the form and the POST/item/add
route will take the date, create an item in DB and redirect back to /
. An item can be anything really, we don't care as it's not the main point of this Post.
Also let's change our /view/index.ejs
to show all items in DB
.
/views/index.ejs
<h4>names:</h4>
<ul>
<% items.forEach(function(item) { %>
<li>
<%= item.name %>
</li>
<% }); %>
</ul>
Add this under the Form
in index.ejs
Now let's dive into docker
First, let's make our Dockerfile
to build a nodejs image out of the Dockerfile
/Dockerfile
FROM node:10 //pull a node image from docker hub
WORKDIR /app //set the working dir to /app
COPY package.json package.json //copy package.json to the container
RUN npm install // install package.json modules in container
COPY . . //copy everything to container /app
EXPOSE 3000 //expose port 3000 to mount it to another port in local machine
RUN npm install -g nodemon // install nodemon for changes on the fly
CMD [ "nodemon", "index.js" ] // start server inside container
- The first line
From
pulls a nodejs image - The second line set the working place inside the container
- Copy package.json to working dir inside the container
- Install package.json modules inside the container
- Copy all files in the current dir to workdir in the container
- Expose port 3000 to map it to some other port on our machine to access the app \
- Install nodemon inside the container
- Run the app
Next we need to create the docker compose
The docker-compose.yml
will enable us to run multiple containers all together to build our app. So let's make a docker-compose.yml
file.
/docker-compose.yml
version: "3"
services:
app:
container_name: express-mongo
restart: always
build: ./
ports:
- "80:3000"
volumes:
- .:/app
links:
- mongo
mongo:
container_name: mongo
image: mongo
ports:
- "27017:27017"
- First Line tells docker what version of docker-compose to use(3 is the latest).
- The second line defines the services that we will have which in this case are (node container and mongo container)
- Line 3 defines the services name, it could be anything
- Line 4 defines the container name
- Line 5 tells docker container to restart if anything crash (notice this is only for nodejs container)
- Line 6 build the nodejs image from the
Dockerfile
that we created earlier. - Line 7 mount port 3000 in the container to port 80 on our machine
Line 8 bind all files in our local workdir to the workdir in the container to see updates and changes in real time.
Line 9 links mongo container to node container to let them communicate together
Line10 creates other services (mongo)
Line 11 defines the container name
Line 12 pulls mongo image from docker hub
Line 13 expose port 27017 in mongo container to our local machine to be able to communicate with mongo
Next we need to change the path to connect to MongoDB
Change this
index.js
mongodb://localhost/express-mongo
To this
index.js
mongodb://mongo:27017/expressmongo
This will connect to the mongo container exposed port 27017
Now moment of truth
Run this command in your terminal
docker-compose up
//or
docker-compose up -d //to run docker-compose in background
Now check your browser on port 80 for the app.
To stop it, hit ctl+c
If you have it runing in background type docker-compose stop
to stop it.
To remove both containers run docker-compose down
Now you can push the project to github and pull it down from any machine and it will work out of the box as long as you have docker and docker-compose.
Note: when you pull the code from githup you need to install all node_modules by doing npm install
because the container will look for express and all other modules inside node_modules on your machine. If you don't want to install any node_modules on your machine you need to add this to your docker-compose.yml
Under volumes add this
- /app/node_modules // now all you need to do is run docker-compose up
Top comments (7)
Exactly what I was looking for, thanks!
Maybe I missed something, but I think you forgot to mention that you need to fetch the items from the DB to display them in the index route.
Something like that:
Glad it helped and you're absolutely correct. I'll fix it as soon as I get time
Depending on your platform (I'm using CentOS), setting the ports property in the
mongo
section of the docker-compose.yml file will also open port 27017 on your firewall, which you may not want to do.hey Enes K. Thanks for the nice comment.
make a file
package.json
in your project, copy/past bellow json objectand run npm install to get all the dependences.
in my case I used
3000:3000
in my docker-compose yaml port.Also, the data storage did not persist. I added extra setup in the yaml file.
Nice article, this might be worth a look github.com/WMRamadan/docker-compos...