Joseph Jude
Posted on January 26, 2017
Docker has drastically enhanced how we develop and deploy web applications. With docker, we can isolate our development environment and resemble the production environment as close as possible. When our development environment resembles the production environment, we deploy with confidence.
We can even go a step further. With automated tests, and few other tools, we can automate the deployment. That brings ease and confidence — for us, for our teams, and for our clients. In software development, ease and confidence is value.
In this tutorial, I'm going to show you how to dockerize an existing hapijs application.
Hapijs is a nodejs framework from Walmart. They use it to power their e-commerce system. If you require an introduction to hapijs, you can read my introductory post about hapijs.
First, we are going to create a simple hapijs application. To do that let us create the package.json
file.
{
"name": "sample-hapijs-inside-docker",
"version": "1.0.0",
"description": "Hapijs app in docker container",
"main": "index.js",
"scripts": {
"start": "node src/server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Joseph Jude",
"license": "ISC",
"dependencies": {
"hapi": "14.0.0"
}
}
This is a common package.json
file. I want you to notice just two lines here.
We are using a start script: node src/server.js
. It will become clear as we go along. Just keep this in mind.
Also, notice that we are using hapijs version 14.
Now let us create the server.js
file.
const Hapi = require('hapi');
const server = new Hapi.Server();
server.connection({ port: 8080 });
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello, docker!');
}
});
server.start((err) => {
if (err)
throw err;
console.log('Server running at:', server.info.uri);
});
Nothing special here. We instantiate a Hapi server at port 8080 and print Hello docker
when the server comes up.
We can pull an existing nodejs docker container and build our application on top of it. Or, we can build a container from scratch. We are going to build it from scratch.
We are going to use alpine as the base OS for our container. Why alpine? Alpine is both small and secure.
We are going to create a text file named Dockerfile
in the same directory where we created the above two files. Dockerfile
contains the instructions to build the container.
So our directory structure looks like this:
├── Dockerfile
├── package.json
└── server.js
Let us build the Dockerfile
step by step. It looks like this to start with:
FROM alpine:3.4
RUN apk update && apk upgrade
RUN apk add nodejs
RUN rm -rf /var/cache/apk/*
What does these lines mean?
Line 1: We are basing our container from alpine
Line 2: Update the OS
Line 3: Add nodejs
Line 4: remove apk cache
If we build a container with these lines, we will get a nodejs container. We want a hapijs one. So let us go on.
Remember, we already created a package.json
and server.js
files. Let us copy them into the container. Append these lines to Dockerfile
.
COPY . /src
RUN cd /src; npm install
EXPOSE 8080
CMD ["node", "/src/server.js"]
Let me explain these lines.
Line 1: We are instructing the docker engine to copy the entire directory to a folder src within the docker container.
Line 2: Install the required dependencies. This will install hapijs.
Line 3: Notice that in the server.js
we are running the hapijs server at 8080. In this line we instruct docker to expose 8080 port to the outside world, so that we can access the server from our host machine.
Line 4: Lastly we instruct docker to run the server with the command node /src/server.js
. Commands and parameters are given in the fashion as in this line.
Our complete Dockerfile
looks like this:
FROM alpine:3.4
RUN apk update && apk upgrade
RUN apk add nodejs
RUN rm -rf /var/cache/apk/*
COPY . /src
RUN cd /src; npm install
EXPOSE 8080
CMD ["node", "/src/server.js"]
We can build the container. Open the terminal and issue this command:
docker build -t jjude/hapi .
Docker needs a dockerfile to build a container. We can either provide the full path or instruct to use the dockerfile in the current directory. We are instructing the build process to use the Dockerfile from the current directory.
It is also a good practice to tag the container with <user_name>/<application_name>
.
When you issue this command, it will start pulling the necessary components (os, nodejs, hapijs) from the web and build a container. You will start seeing the output that goes like this:
Sending build context to Docker daemon 5.632 kB
Step 1/8 : FROM alpine:3.4
---> 0766572b4bac
Step 2/8 : RUN apk update && apk upgrade
---> Using cache
---> edfde7516f9b
Step 3/8 : RUN apk add nodejs
---> Running in 4d786fc5787b
(1/4) Installing libgcc (5.3.0-r0)
(2/4) Installing libstdc++ (5.3.0-r0)
(3/4) Installing libuv (1.9.1-r0)
(4/4) Installing nodejs (6.7.0-r0)
Executing busybox-1.24.2-r13.trigger
OK: 41 MiB in 15 packages
---> 25426b5e81e2
Removing intermediate container 4d786fc5787b
Step 4/8 : RUN rm -rf /var/cache/apk/*
---> Running in 2db1ab7c47b5
---> efbdf59a5e3a
Removing intermediate container 2db1ab7c47b5
Step 5/8 : COPY . /src
---> f67db160cffb
Removing intermediate container b452bcf97da6
Step 6/8 : RUN cd /src; npm install
---> Running in eebeb5c18070
sample-hapijs-inside-docker@1.0.0 /src
`-- hapi@14.0.0
+-- accept@2.1.3
| `-- boom@4.2.0
+-- ammo@2.0.3
| `-- boom@4.2.0
+-- boom@3.2.2
+-- call@3.0.4
| `-- boom@4.2.0
+-- catbox@7.1.3
| +-- boom@4.2.0
| `-- joi@10.2.0
+-- catbox-memory@2.0.4
+-- cryptiles@3.1.1
| `-- boom@4.2.0
+-- heavy@4.0.3
| +-- boom@4.2.0
| `-- joi@10.2.0
+-- hoek@4.1.0
+-- iron@4.0.4
| `-- boom@4.2.0
+-- items@2.1.1
+-- joi@9.2.0
| +-- isemail@2.2.1
| `-- moment@2.17.1
+-- kilt@2.0.2
+-- mimos@3.0.3
| `-- mime-db@1.26.0
+-- peekaboo@2.0.2
+-- shot@3.4.0
| `-- joi@10.2.0
+-- statehood@4.1.0
+-- subtext@4.3.0
| +-- boom@4.2.0
| +-- content@3.0.3
| | `-- boom@4.2.0
| +-- pez@2.1.4
| | +-- b64@3.0.2
| | +-- boom@4.2.0
| | `-- nigel@2.0.2
| | `-- vise@2.0.2
| `-- wreck@10.0.0
| `-- boom@4.2.0
`-- topo@2.0.2
npm WARN sample-hapijs-inside-docker@1.0.0 No repository field.
---> e04821112bdf
Removing intermediate container eebeb5c18070
Step 7/8 : EXPOSE 8080
---> Running in 6889ad302d2f
---> adfd88fe97ca
Removing intermediate container 6889ad302d2f
Step 8/8 : CMD node /src/server.js
---> Running in dcde9b8ab986
---> 07786de22668
Removing intermediate container dcde9b8ab986
Successfully built 07786de22668
It should end with Successfully built
.
We have the container. You can check it by issuing docker images
. It should show something like this:
REPOSITORY TAG IMAGE ID CREATED SIZE
jjude/hapi latest 07786de22668 23 minutes ago 41.1 MB
As you can see, the basic container that can execute our server.js
is of 41.1 MB.
Now we need to bring up this container (run it). Issue this command docker run -p 8080:8080 -d jjude/hapi
.
What does this command do?
We are running our container jjude/hapi
and binding the container port 8080 to the host port 8080.
What if you want to use a different port, say 9000? You can do that with -p 9000:8080
. The format goes like this:
-p <host port>:<container port>
It will come back to the terminal command prompt. How to check if our little server is running?
Issue a curl
command.
curl http://localhost:8080
This should output this:
Hello, docker!
That's it. We successfully dockerised our sample hello docker program.
What if you want to go into the container and play around? Docker provides an option to enter into a running container. To do that, we need to know the id of the running container. Issue docker ps
. It will show an output like this:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2fb1cb0abb92 jjude/hapi "node /src/server.js" 23 minutes ago Up 23 minutes 0.0.0.0:8080->8080/tcp cocky_hawking
The first parameter is the container id. Copy it and issue the command:
docker exec -it 2fb1cb0abb92 /bin/sh
Use the correct container id from the previous output.
Now you are into the container. You can stop and restart the server. The source code lives in /src/
folder.
You can checkin these three files (package.json, server.js, Dockerfile) to a repository and ask a member of your team to checkout the repository and build the container. It will work exactly the same way. That's the magic of Docker!
Interested in learning hapijs with typescript? Subscribe to my newsletter
Originally posted at jjude.com
Posted on January 26, 2017
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.