Introduction
Today, almost everyone has at least heard of Docker and the term containerization. It doesn't matter whether they are experts in building container-oriented services, or whether they know how containers function. But they have stumbled upon a document, or ebook, magazine, article, or a video that talks about a containerized application, orchestration tools, or Docker, Kubernetes anything. The point is that people are aware of the technology, and the same evolution of the virtual machine stack, that everybody knows. That is pretty much old school now. What I want to do in this Docker 101 class, is to establish the core fundamentals of the Docker setup, how it works, how you can quickly get Docker, and how to try it out.
I will try my best to cover all the aspects of Docker, and containerization of the applications with this tutorial collection of the articles. Among a few topics about Docker itself, we will explore:
- Data persistence in a container
- Services in Docker
- Container orchestration
- Kubernetes and Docker
These are a few of the topics that I can discuss without actually making beginners lose track, or experts call me a geek.
What is Docker?
A short definition for those, who still don't know what Docker is, and why they should care about it: When people talk about Docker, they think of a containerization tool, or some even think of it as a process orchestrator. They are all correct. Docker is, and can be thought of, as a:
- Package manager
- Orchestrator
- Load Balancer
And I didn't even mention all the other things, that you can find on the internet like, Docker is a product developed by the company of the same name, or, Docker is an open source containerization management tool or another one like Docker is the gateway to the cloud. They are all right in their own perspective, but still, that does not just cover everything that Docker can do. That is why I am writing this article, to showcase the functionality and features of Docker, instead of talking about it. Let's start with the requirements and prerequisites for Docker.
Platform and Requirements
Docker itself is available to be installed on Linux, Windows or macOS, but I would be utilizing Linux platform here. The commands and Docker behavior will be similar on Windows as well as on macOS. But I assure you, it would be an amazing ride if you join using a Linux distro, and especially if you choose to setup Ubuntu installation 16.04 or latest. I have used Ubuntu 18.04 for this setup.
The second thing to note here is that I am using Snaps, the Snapcraft. The reason behind this step and selection is that snaps are really very easy to install, upgrade and manage. They come shipped with the services, programs, and utilities that enable you to manage the utility quite easily. Do not worry, in the section for installation and setup, I will explain and provide guidance on other installation and requirements too as needed, but the primary method is this one.
Installation and Configuration
As discussed, we will be installing the snaps for the Docker and, for those who want to learn some advanced topics of orchestration, Kubernetes. Snaps are available on Linux distributions, the ones that are released recently in 2018, or 2017. The installation process is clearly stated and mentioned on the Docker website, anyways I will still try to explain the method on a few of the platforms.
Windows
On Windows, the only things you need to make sure of before executing the installer for Docker are:
- Hardware virtualization
- Hyper-V to be installed and enabled
This also means that you cannot try this stuff out on Windows Home edition. You would require to have, Windows Pro or onwards edition for this. But this will not prevent you from trying out the Docker installation on a virtualized environment, and there you can easily set up Linux distro and get started with the next section.
But if you already have a Pro edition of Windows, just head over to Docker downloads and install Docker for your machine.
Linux (Ubuntu 18.04)
Although any of the Linux distribution that supports Snaps would work, I am going to use the Ubuntu distribution in the rest of the article as well as the articles that follow-up after this one. So to do that, just make sure you have the snaps available for your system. To make sure you are having the snap system installed, either make sure that you are running Ubuntu 16.04 or onwards, that is why I recommended using Ubuntu 18.04. Snaps daemons are installed by default on these operating systems, and in case the following command doesn't give a response,
- $ snap version
Then it means that your operating system does not have a snap system installed. To install one, you can run the following commands,
- $ sudo apt-get install snapd
This will install the daemon program for the snappy environment and later you can verify that the snappy is available. You can also follow along with the documentation provided by the snap website, for Ubuntu, or select the operating system and the distribution you selected.
Once you are done with that, just go ahead and install the Docker from the snap store.
- $ sudo snap install docker
This will take some time and will have the Docker engine installed and set up for you. Now you can go ahead and install and set up a few more things, such as a hello world Docker image for trial purposes or what-so-ever. But what I would rather want to do here, is to hint out a few things about Docker before we step out and enter the second episode of this Docker series. Now, to move ahead and provided you have downloaded and installed Docker engine, move ahead to the next step and let's start the exploration of Docker.
Non-snappy Method
Docker publishes the artifacts for Linux environments too, you can download and install those tarballs or use the repository managers like apt, yum, etc.
Some of the important links you might want to follow are listed below:
Since Docker is open sourced, you can always go ahead and compile the source code yourself. This cookbook snippet from Packt would be able to tell you what steps you need to perform in order to build the source code for the Docker engine.
Exploring Docker
Before I conclude this chapter, let's take a quick look at Docker engine from the standpoints of storage, networking, and process information. This will lay some of the foundations for the concepts that we are going to learn later on. So, for the first thing, we will be utilizing my own build Node.js application, the reasons being:
- It is my own development, and I can easily explain what I did, and where.
- It is available as an open source project, and needs contributions and collaboration of the community, on GitHub.
- It is light-weight and exposes some of the best practices that one can, should employ during the development of Dockerized packages.
- Docker package is merely 4-lines of code, the underlying project is a complete web application project written in Node.js, and exposing several design patterns and architectural designs, especially for the serverless approach.
- The project also comes with other configurable and ready-to-execute scripts, such as Docker Compose file.
You can explore the project on GitHub (link provided in the downloads section above), and see how it was structured for yourself. But now, without any further delay, let us create our first Docker container and visualize what the process is like. Inside our application, we are exposing the web app via this code,
- // Inside the serverconfig.js
- let port = process.env.PORT || process.env.PORT_AZURE || process.env.PORT_AWS || 5000;
- // Inside the app.js
- app.listen(serverConfigurations.serverPort, () => {
- let serverStatus = `Server listening on localhost:${serverConfigurations.serverPort}.`;
- logger.logEvent("server start", serverStatus);
- console.log(serverStatus);
- });
This code is not important, from Docker's standpoint, but what I want you to see is that this is what will be executed that, in turn, provides us a web server running in a container. In a very normal environment, this would just execute the function and start the event loop for the Node.js app, which means that your process will start and it will listen to the network traffic for that hostname, as well as that port that we have assigned. In this case, your output for the command,
- $ npm start
would be something like this,
- > [email protected] start /home/afzaal/Projects/Git/Nodejs-Dockerized/nodejs-dockerized
- > node ./src/app.js
- Cannot start Application Insights; either pass the value to this app
- or use the App Insights default environment variable.
- [Event] server start: Server listening on localhost:5000..
- Server listening on localhost:5000.
From a naive standpoint, this all looks fair, but now we stumble upon a problem. If you look at it (ignore the Application Insights line), you will find that it states, "Server listening on localhost:5000.", which brings us to a point that makes it clear that now our port 5000 is bound to this process. This will cause problems with scalability of our application—that have a right to become a service—and will, in turn, require us to:
- Either run the application on different and configurable ports,
- This is quite possible in the application I wrote, you can pass the port for the application to listen on, but this is not the recommended approach. Because it will also require you to manage the traffic forwarding from outside this machine, and brings us to the second point.
- This is quite possible in the application I wrote, you can pass the port for the application to listen on, but this is not the recommended approach. Because it will also require you to manage the traffic forwarding from outside this machine, and brings us to the second point.
- Or run the application behind a load balancer and have load balancer forward the traffic to the 5000 port of each process that you spawn for this service.
Another major and most important problem with this approach is that if our process fails, or terminates, there is no way that our environment can bring back the process. We can overcome the problem by writing our own management program, or utilizing another web server program, but even there we need to manage and maintain how we specify that our application is up and running.
These are the areas where Docker shines and—not just Docker, but other orchestrators and process managers as well, such as Kubernetes, DC/OS Marathon, etc. but since we are talking about exploration of Docker,—we will be talking about these features, in proper depth in the later chapters, in this chapter however, we want to explore how this behavior of deployment of an application on a machine can be achieved. In order to understand how we do that, we are going to perform both the actions that we had talked about as a problem and we are going to leave that all up to Docker. How we do that, is, we create a new image in Docker, and we package our application inside it. In Docker, the packaging is done using a Dockerfile, and that file contains the sequence of commands that are required to be run in order to start the process. A Dockerfile requires the following information in order to start your process:
- Dependencies
- Source files
- Entry point or command
Following this pattern, we know that our application has a dependency on Node.js runtime, and the files are in the same folder, and we can start the project using the npm start command. Our Dockefile would be,
- FROM node:10-alpine
- COPY . .
- RUN [ "npm", "update" ]
- CMD [ "npm", "start" ]
That is it!
We do not require to write even an extra word, here. I have already talked about these commands in detail in another article of mine, here. And I do not feel like I should talk about it once more. Let's build the image, and run it to verify how things are taken care of for us.
- # docker build -t afzaalahmadzeeshan/dockerops:gettingstarted .
This command will build our first container image, and provide us with the container image with that name.

Mahesh ChandPosted Nov 30, 2018, 6:09 AM
Great article as always. Is DockerOps is a common term now or is it your invention?
Iqra AliPosted Nov 30, 2018, 4:56 AM
Right on to the subject, detailed and perfectly put.