What is Docker and what makes it so awesome
The short, human-friendly version of the text-book description is that Docker helps you run any software on your computer, in a completely isolated manner. Applications can have their own set of libraries, configuration files, operating system users, all without getting mixed up with the main operating system which is called the host.
Now let’s provide a real-life example; Imagine working on 2 different projects, which require different programming language versions (Ruby for example). Installing them on your computer, means that you need to download, set up and make sure there are no conflicts between the two. This looks tedious, because there are tools that help you do that but let’s take a moment and think about the version conflicts among libraries so for example the library that outputs images, PDF documents or even a specific version dependency of the library’s dependent libraries. Getting trickier huh?
Now let’s assume this is a production app that you’re building, wouldn’t it be amazing if you were able to run the same libraries, services and operating system as the one on your server to make debugging easier? After all, inconsistencies in operating system and libraries can cost your team time and eventually money. (At this point you should be familiar with the infamous “It works on my machine” response.
Docker not only solves those issues but it provides one more, killer feature; the ability to package & run your application on the cloud. Imagine packaging the app you’re working on locally and being able to now only share it between team members but also run it on a cloud instance somewhere, without provisioning your instance too much.
Running your first container
We’re not going into way too much detail in this post, after all the installation process is pretty straight forward; Just follow the links and the instructions according to which operating system flavor you’ re running, either Mac, Windows or Linux. After you’ve completed these few easy steps, you’ll be able to execute something like the following in your terminal window and get a response:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Up to this point, you’ve successfully installed Docker and verified it’s working. Now let’s move on to something a bit more useful, we know you’re eager to start playing around so here we go
$ docker run alpine date Mon Feb 25 10:43:22 UTC 2019
What we’ve done here, is basically run a container from the smallest Docker image available (Alpine linux) which was pulled from DockerHub, and asked it to output the current date. But, let’s take a step back here, you may have a couple of terms that you’re not familiar with:
- Docker image: It’s a packaged and distributable app, containing all the available pieces of information that allows the app to run. Images can be based off of other images and may or may not contain be tagged in versions.
- Docker Container: It’s the running instance of a docker image. It iss ephemeral and lives as long as the process they’re running (for example on our previous experiment, the container was up for just a second, just to output the current date).
- DockerHub: Provides public or private storage for images
Breaking down the command we ran in the example above, we have the following:
docker runinstructs the docker to run a new container, from a docker image. This will generate a container with a unique id based on a certain docker image which we define in the next argument
alpineis the docker image we’re basing our container on
dateis the command we’re going to run
This will essentially run the date command and exit and if we run docker ps again, we’ll quickly discover that the container is not returned within the results. If we wish to examine the status of the container, we need to run
docker ps -a
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0c62a1bb38bc alpine "date" 9 seconds ago Exited (0) 7 seconds ago competent_villani
Now that you’ve got a basic understanding of the core concepts of Docker, let’s run our first meaningful container (you can hit Ctrl+D to get out of the container):
$ docker run --rm -it -v .:/my-app alpine:latest /bin/ash
This sounds complicated but it is actually very simple once you understand the breakdown of the options:
docker run” tells the docker engine to run a container off of an image
--rm” the container should be removed once it exits
-it” means that we’re allocating a TTY and we’re keeping STDIN open, basically we’re allowing the container to read & write from our terminal
-v .:/my-app” means that we’re mounting the current directory as a volume under “/my-app”
alpine:latest” means that we’re running the “latest” version of the “alpine” linux image, could be for example “
mysql:latest” if we wished to run the latest version of the popular MySQL database
/bin/ash” is the command we’re executing, in this case the “
When running the command above, you’ll notice something different: the container doesn’t exit and you have a running shell which you can execute linux commands on. Once you’re done, you can type in “
exit” which will exit the shell and eventually the container.
Another option to have a container running, this time in the background, is to pass the “
-d” argument, which will run the container in “detached” mode, which would then require attaching to it via
docker exec, a command we’ll examine at a later stage.
Let’s watch the replay
You can review the full output of a container, by running the “
docker logs” command so for example if we wish to review the output of our first container, the one we ran the “
date” command with, we can do so by first identifying its ID.
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0c62a1bb38bc alpine "date" 32 minutes ago Exited (0) 32 minutes ago competent_villani
The container’s ID, can be found in the first column of the “
docker ps -a” command, and we can view the logs via
$ docker logs 0c62a1bb38bc Tue Jun 11 21:50:29 UTC 2019
Alternatively, you can use the container’s name to get the logs for it, in this case “
competent_villani“, which is randomly generated (we can also specify a container’s name by passing the “
--name <some memorable name>“).
Finally, if you wish to follow the logs of a running container, you may do so by running “
docker logs -f <container id or name>“.
We developers (should) have a strong willingness to keep things clean so having dangling containers or images is not something we are comfortable with. The following commands will help us maintain a (host) system that’s litter-free.
$ docker rm 0c62a1bb38bc # will remove the container with id 0c62a1bb38bc $ docker rmi alpine # will remove the alpine image from the host system, # but only if there are no containers that are based off this image $ docker image prune # will remove unused images
The concept of containers isn’t new, however Docker made it popular and this is mainly because by following a few simple steps, we’re able to run different versions of pre-packaged software on all operating systems, safely, isolated from the operating system’s files and directories. The containers and images can also be safely removed, without affecting anything on the host operating system, without causing library conflicts or any unexpected situations. Finally, Docker is a great tool that enables developers build an exact replica of their production environment on their development machines but also package and deploy their services in production.