Creating our Dockerfile

Dockerfile are text files, usually collocated with applications that instruct Docker how to build a new Docker image. Through the creation of those files, you have the ability to tell Docker which Docker image to start from, what to copy on the container filesystem, what network port to expose, and so on. You can find the full documentation of the Dockerfile at http://dockr.ly/2jmoZMw.

We are going to create a Dockerfile for our helloworld application at the root of the helloworld project:

$ cd helloworld
$ touch Dockerfile

The first instruction of a Dockerfile is always a FROM instruction. This tells Docker which Docker image to start from. We could use the Alpine image as we did, but we can also save some time by using an image that has more than just an operating system.

Through Docker Hub, the official Docker registry, Docker provides a number of curated sets of Docker repositories called official. We know that, in order to run our application, we need node.js and npm. We can use the CLI to look for an official node container. To do that, we will use the docker search command and filter only on official images:

$ docker search --filter=is-official=true node
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
node Node.js is a JavaScript-based platform for... 4260 [OK]

Alternatively, we can also search for it using our browser. We would end up with that same image: https://hub.docker.com/_/node/.

We can see, using the preceding page, that this image comes in a variety of flavors:

Docker images are always made up of a name and a tag, using the name:tag syntax. If the tag is omitted, Docker will default to latest. We can see in the docker pull preceding command how the output says Using default tag: latest. When creating a Dockerfile, it is best practice to use an explicit tag that doesn't change over time (unlike latest).

If you are trying to migrate an application currently running on AWS Linux and make a certain number of assumptions based on that OS, you may want to look into using the official AWS Docker image. You can read more about it at http://amzn.to/2jnmklF.

On the first line of our file, we will add the following:

FROM node:argon 

This will tell Docker that we want to use that specific version of the node image (argon is an LTS version of node). This means that we won't have to install node or npm.

Since we have the OS and runtime binaries needed by our application, we can start looking into adding our application to this image. We will first want to create a directory on top of the node:argon image's filesystem to hold our code. We can do that using the instruction RUN:

RUN mkdir -p /usr/local/helloworld/ 

We now want to copy our application files onto the image. We will use the COPY directive for that:

COPY helloworld.js package.json /usr/local/helloworld/ 

We will now use the WORKDIR instruction to set our new working directory to be that helloworld directory:

WORKDIR /usr/local/helloworld/ 

We can now run the npm install command to download and install our dependencies. Because we won't use that container to test our code, we can just install the npm packages needed for production:

RUN npm install --production 

Our application uses port 3000. We need to make this port accessible to our host. We will use the EXPOSE instruction for that:

EXPOSE 3000 

Finally, we can start our application. For that, we will use the ENTRYPOINT instruction:

ENTRYPOINT [ "node", "helloworld.js" ] 

We can save the file. It should look like this: http://bit.ly/2vaWYRy.

We can now build our new image.

Back in the Terminal, we will again use the docker command, but this time with the argument build. We will also use the option -t to provide the name helloworld to our image:

$ docker build -t helloworld .
Sending build context to Docker daemon 96.77kB
Step 1/7 : FROM node:argon
argon: Pulling from library/node
ad74af05f5a2: Pull complete
2b032b8bbe8b: Pull complete
a9a5b35f6ead: Pull complete
3245b5a1c52c: Pull complete
afa075743392: Pull complete
9fb9f21641cd: Pull complete
ecc0815e8ade: Pull complete
f6ec10fc9751: Pull complete
Digest: sha256:036ecc312ef9528f66d70c1bfa2f110bd4d67c1a0046eb2f5495c2e7ad45a82d
Status: Downloaded newer image for node:argon
---> bd1f9ee5b2ae
Step 2/7 : RUN mkdir -p /usr/local/helloworld/
---> Running in 3b492d59bf49
---> 137a7e793fe4
Removing intermediate container 3b492d59bf49
Step 3/7 : COPY helloworld.js package.json /usr/local/helloworld/
---> 13d400af5c43
Removing intermediate container 3db7e52c9b46
Step 4/7 : WORKDIR /usr/local/helloworld/
---> a32ed971f63b
Removing intermediate container e86fbe293ab6
Step 5/7 : RUN npm install --production
---> Running in 6bb8e44100d7
npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm WARN package.json [email protected] No description
npm WARN package.json [email protected] No README data
npm info preinstall [email protected]
npm info build /usr/local/helloworld
npm info linkStuff [email protected]
npm info install [email protected]
npm info postinstall [email protected]
npm info ok
---> dd5d542838c0
Removing intermediate container 6bb8e44100d7
Step 6/7 : EXPOSE 3000
---> Running in 331073792dea
---> bbca00ed085d
Removing intermediate container 331073792dea
Step 7/7 : ENTRYPOINT node helloworld.js
---> Running in 9382c5b8fb60
---> 9d78e2a8ecff
Removing intermediate container f4921b7dd915
Successfully built 9d78e2a8ecff
Successfully tagged helloworld:latest

As you can see, each command produces a new intermediary container with the changes triggered by that step.

We can now run our newly created container with the following command:

$ docker run -p 3000:3000 -d helloworld
881d9d8dad59ea7fe82b55f9d6e72142471b0dac2adf1b0857d4f63758864314

Here, we are adding the -p option to our command to map the exposed port of our container to a port on our computer.

There are a few ways to validate that our container is working correctly. We can start by looking at the logs produced by our container (replace the container ID with the output of the previous command):

$ docker logs 881d9d8dad59ea7fe82b55f9d6e72142471b0dac2adf1b0857d4f63758864314
Server running

We can use the docker ps command to see the status of our container:

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
881d9d8dad59 e7deb47c0528 "node helloworld.js" 2 minutes ago Up 2 minutes 0.0.0.0:3000->3000/tcp jovial_fermi

And of course, we can simply test the application with curl, shown as follows

$ curl localhost:3000
Hello World

And finally, kill the container using the docker kill command and container ID:

$ docker kill 881d9d8dad59
881d9d8dad59

Since our image is correctly working, we can commit the code to GitHub, as follows:

$ git add Dockerfile
$ git commit -m "Adding Dockerfile"
$ git push

In addition, you can now create an account (for free) on Docker Hub and upload that new image. If you want to give it a try, you can follow the instructions at http://dockr.ly/2ki6DQV.

Having the ability to easily share containers makes a big difference when collaborating on projects. Instead of sharing code, and asking people to compile or build packages, you can actually share a Docker image. For instance, by running the following:

docker pull effectivedevops/helloworld

You can experience the helloworld application, the exact way I see it, no matter what your underlying architecture is. This new way of running applications makes Docker a very strong solution to share work or collaborate on projects.

The strength of Docker doesn't stop at work collaboration. As we are about to see, using containers in production is also a very interesting option. In order to easily implement such a solution, AWS created the EC2 container service. We are going to use it to deploy our newly created helloworld image.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset