Microservices Part 3: Hello Docker

Amro Moustafa
8 min readJan 31, 2019

--

Oh please come in Ms. Developer, I am so glad you can join me today. You know, I was kinda happy that Appy told you about what we were discussing last week, but I become even happier when you suggested meeting today. Tea? Okay, coming right up.

So what I understood is that you are trying to work with docker and want some pointers correct? Well, am more than happy to give you some pointers, It is all easy. You got a windows laptop like me, great so you can follow along just fine.

Let’s start first with installing Docker, this step won’t be too hard. You can just follow the instructions specified for your operating system in the following link: https://docs.docker.com/install/#supported-platforms, just make sure your version of windows is compatible. Now that you have Docker, you can run Docker commands using the Docker executable.

Since you always start developing with a “Hello World”, let’s run a Hello World Container!. Try executing the command docker run busybox echo “Hello world” and you should get a similar output:

> docker run busybox echo "Hello world"
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
90e01955edcd: Pull complete
Digest: sha256:2a03a6059f21e150ae84b0973863609494aad70f0a80eaeb64bddd8d92465812
Status: Downloaded newer image for busybox:latest
Hello world
Ms. Developer

This doesn’t look really fascinating, but allow me to explain what executing this command did for us. Docker first searched for the image we are trying to pull on your local machine but couldn’t find it. The image was pulled from Docker Hub instead, which is Docker’s public registry that has ready-made container images. The image we pulled is a busybox image, and in case you never heard about busybox, it combines tiny UNIX tools into a single executable. Then, Docker creates an isolated container based on the image. We — optionally — specified which command to execute when running the container too. We downloaded and executed a full application without the need to install it or any of its dependencies, all in the same command. Fascinating, Don’t you agree?

To elaborate a bit about the docker run command, it will run existing images or pull images from Docker Hub registry. These images are software packages that get updated usually, so there will be more than one version for each image. Docker allows having multiple versions of an image with the same name, but each version has to have a unique tag. Running the docker run <image> command without a tag will make Docker assume you are looking for the latest version of the image, which will have the tag latest. To specify the version of the image you are looking for, simply add the tag docker run <image>:<tag>.

You may want to list the images using docker images to check the images created, their tag (or version), creation date and size. Run it and you should get a similar output to this one:

> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox latest 59788edf1f3e 8 weeks ago 1.15MB

You can also use docker container list to list the running containers. If you run it right now, you would probably get no containers listed, because the container isn’t running anymore. But if you add -a or --allflag, both running and stopped containers will be shown in a similar output to this one (please know that some of the details are omitted and replaced by the ... ):

>docker container list -a
CONTAINER ID IMAGE COMMAND CREATED ... 47130c55f730 busybox "echo 'Hello world'" About an hour ago ...

You might find the command docker container list a bit long, but there is another command that has the same function which is the docker ps command. You can optionally add -a flag to show the stopped containers also.

Since the container shows up as a stopped container, you can start it up again by using the command docker start <container ID> . On the other hand, you can stop a running container by using the command docker stop <container ID> .

More tea? No? Okay, then let’s continue.

Now that we know how to run a new container using an image from the Docker Hub registry, let’s make our own Docker image. The image we will create consist mainly of two things, the application you want to run, and the Dockerfile which is read by Docker to automatically build an image for your application. It is a document that contains all the commands a user could call on the command line to assemble an image. Let’s first start with the simple node.js application, and name it app.js . You can customize the name to your preferences of course.

const http = require('http');
const os = require('os');
var server = http.createServer(function(req,res){
response.end("Hostname is " + os.hostname() + "\n");
})
server.listen(3000);

In the code above we are just starting an HTTP server on port 3000 which will respond with “Hostname is (the hostname of the server host)” to every request. Make a directory and name it as you wish, then save the app code inside it, make sure no other files are present in that directory.

We made our application, so let’s make our Dockerfile. create a file called Dockerfile, copy-and-paste the content below into that file, and save it in the same directory as your app code.

FROM node:8
COPY app.js /app.js
CMD ["node", "app.js"]

Each statement has a meaning in the Dockerfile. FROM which parent image are you using as a base for the image you are building. It is always better to choose a proper base image. We could have written FROM Ubuntu , but using a general purpose image for running a node application is unnecessary, as it will increase the image overhead. In general, the less size, the better. Instead, we used the specialized official node runtime as a parent image. Another thing to note is that we specified the version with a tag FROM node:8 instead of using the default latest tag. The reason for that is the latest tag will result in a different base image being used when a new version is released, and your build may break as we mentioned before. It is a precaution I like to take.

But that’s just my precaution

We also used COPY <src> <dest> to COPY new files or directories from <src> and adds them to the filesystem of the container at the path <dest>. The COPY instruction copies new files or directories from <src> and adds them to the filesystem of the container at the path <dest> . Another Dockerfile instruction that has a similar function as COPY would be ADD, but in general COPY is preferred for being simpler. You can use ADD for some unique functions like downloading external resources or extracting tar files into the image. You can read more about it in the official documentation.

Lastly, the CMD Instruction is used to run the application contained by your image. The command, in this case, would be node app.js.

There are other instructions that can be included in the Dockerfile, and mentioning them briefly would prove helpful later on. The RUN command, for example, allows you to run commands to set up your application you can use it to install packages. An example of that is RUN npm install . We can expose a specific port to the world outside the container we are building by using EXPOSE <port> .

There is some essential knowledge about Dockerfiles that you should know before going ahead and writing all these commands. Every command you write in the Dockerfile creates a layer and each layer is cached and reused. Invalidating the cache of a single layer invalidates all the subsequent (below) layers. Invalidation occurs after command change for example. Something to note is that Docker likes to keep the layers immutable, so if you add a file in one layer and remove it in the next one, image STILL contains that file on the first layer, it’s just that the container doesn’t have access to it anymore.

Two things to keep in mind is that the fewer layers, the better. to change the inner layers in Docker images, Docker has to remove all the layers above it first. You will cry less when you have fewer layers to peel off an onion. Additionally, The most general steps and the longest should come first in your Dockerfile (inner layers), while the specific ones should come later (outer layers).

Since you understood what are the contents of the Dockerfile, let’s go ahead and build an image, but first make sure your path is inside the new directory that you made. Using ls command should only show you two files, app.js and Dockerfile . You can build the image by using the command docker build -t medium . . We will tag it medium by using the -t flag and we are targeting the current directory (note the dot at the end of the command).

>docker build -t medium .
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM node:8
8: Pulling from library/node
54f7e8ac135a: Pull complete
d6341e30912f: Pull complete
087a57faf949: Pull complete
5d71636fb824: Pull complete
0c1db9598990: Pull complete
89669bc2deb2: Pull complete
3b96ee2ed0b3: Pull complete
df3df33f8e3c: Pull complete
Digest: sha256:dd2381fe1f68df03a058094097886cd96b24a47724ff5a588b90921f13e875b7
Status: Downloaded newer image for node:8
---> 3b7ecd51ffe5
Step 2/3 : COPY app.js /app.js
---> 63633b2cf6e7
Step 3/3 : CMD ["node", "app.js"]
---> Running in 9ced576fdb46
Removing intermediate container 9ced576fdb46
---> 91c37fa82fe5
Successfully built 91c37fa82fe5
Successfully tagged medium:latest
SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.

Now you can again use the run command to run the built image, or you can docker push to push it to a registry, and pull it again on another computer from the registry using docker pull.

There you go, now you know how to make a proper Dockerfile, build an image from that Dockerfile, and run it. You can go in more detail if you would like by going to Docker docs, though it would be better to practice more with the basics for now.

No need to thank me, it was my pleasure, please let me know if I can do anything more to help, please send my regards to your family.

Hope to see you again, Good Bye!

--

--