Basics of Docker: Why, What and How

Basics of Docker: Why, What and How

Understand the basics of docker and the most commonly used docker commands

Β·

7 min read

Before directly learning about docker, let's build an understanding of basics, like what is a container, what problem it solves and why we need docker. Then we will see how to build docker images and containers and which docker commands are most commonly used in the usual day-to-day work.

Problem

One of the issues developers face is that the software or project they build works seamlessly on one machine but fails to work on another machine. There could be an issue with the package version mismatch used in the project or some difference in the machine architecture between two different machines. The famous dialogue "It works on my machine" comes into the picture here!

To solve this, you may think of starting a Virtual Machine locally using VirtualBox or VMWare, but the problem here is that a VM requires a new operating system on its own on top of the physical machine and requires a dedicated amount of hard disk, CPU and memory for itself. This is not much performant, as the resources would be wasted if not used by the virtual machine. 🀯

So we need something which can run on the same OS as the host machine without the overhead of installing another layer of OS or having dedicated hardware configurations.

Solution πŸ’‘

Containers solve both these problems. Instead of dividing the hardware resources for the different operating systems and VMs, containers run all the application code along with its dependencies like programming and environment runtimes in an independent environment built over the same operating system.

The container runs in isolation from its host machine, hence it can still support and have the required versions of packages for running the application seamlessly. These packages and required dependencies inside the container won't interfere with the versions of the packages or dependencies which are present in the host machine.

As a developer, we need some platform or some tool via which we can create, build and manage these containers.

This is where Docker comes into the picture🐬

Docker is one of the platforms which enables us to create, build and run the required applications inside containers.

Docker provides various constructs and commands by which we can bundle the required dependencies or packages together and run them in a container which is isolated from the host machine.

Docker has something known as images which are similar to snapshots in a VM, i.e. it contains all the instructions required to run the application inside the container.

We can run these images, which will execute those instructions inside a container. We can also say that the container is just a runtime instance of the docker image.

So in short, we need to first build these docker images and then run the images inside the container. Running the images will create the actual containers and start the application inside them as per the instructions present in the image(We will see this below with a real example).

There are a plethora of docker images which are commonly used for enterprise and personal use. Docker also maintains a registry from which we can download these images for various applications and use them. This registry is known as DockerHub.

So instead of installing some software like Redis, or Postgres on your local machine, you can simply run their images inside a container. At the same time, you can also expose and publish their ports inside the container to some port of your host machine to be able to access and use it on localhost.

You can also create your custom Docker image which could be built on top of these existing images and some additional commands as per the requirements. A Dockerfile is a document that contains all such commands a user requires to assemble an image. We will be using Dockerfile to create a custom docker image and run it in the example below.

πŸ‘‰ Docker images also consist of logical sub-images called "layers". Each layer stores some sub-part of the image it’s based on.

These layers are used to avoid transferring repetitive instructions which are already present and skip unnecessary build steps. For example, your image could have 5 layers, one of them being to download an "X" image from Dockerhub. Now, if you are building the same image again it should ideally not download the "X" image again from Dockerhub, instead, it will pull this existing layer itself as there is no change in that context.

Now that the basics are covered, let's learn about the commonly used docker commands with the very basic use case πŸš€

Let's create a very simple Go application that prints Hello World!.

Let's build our custom docker image for this application and then start a docker container which runs that image of a simple Go application.

Create a new folder with two files- main.go and Dockerfile

// main.go
package main

import "fmt"

func main() { 
    fmt.Println("Hello World!")
}
#### Dockerfile
# Download the golang 1.15 linux package from Dockerhub - https://hub.docker.com/layers/library/golang/1.15/images/sha256-eb7ebf06372b4930e0b9539b3510422c4860ec3361bb2f22b63a857990cf9efe
FROM golang:1.15

# create a build dir
RUN mkdir /build
# navigate to this build dir
WORKDIR /build

# copy the project itself
COPY . .

Now we need to create a docker image out of this Dockerfile

To do this, navigate to your projects folder from your CLI, run docker build -t main-app . command.

This command will create a docker image for your application. You can confirm whether the image is created or not by running docker images command.

Remember the image "layers" we discussed above? The image created using the above Dockerfile will have multiple layers, you can check this using docker inspect main-app command.

The docker image created has the following instructions as per the above Dockerfile

  • Fetch the go1.15 package from Dockerhub

  • Create a folder named build inside the container

  • Navigate to this build folder inside the container

  • Copy all the files from the local machine folder(which has main.go and Dockerfile) to that build folder inside the container

Now, since the image is created, we need to run this image to execute the above commands. Images can be run using docker run main-app

The above run command will create the container with the go1.15 Linux dependency and will copy the go program inside the build directory of the container.

To verify if the container is running or not, you can run docker ps -a in your CLI/terminal. This command will print container details like container id, image, status, ports, etc.

Note: You will notice that the container had initially started running but also stopped in a fraction of second. This is because all the commands provided in Dockerfile were completely executed and no process was ongoing.

So for this example, instead of simply running the container(which will exit immediately), you can start the interactive command line shell inside the container environment while running it.

To do that, run the container using docker run -it main-app /bin/bash command. This will open up an interactive bash shell inside the container. Now if you run docker ps -a in the new CLI tab, you will see the container is not stopped yet as the /bin/bash shell process is still running inside that container.

You can execute your Linux commands inside that shell just like you would do in a system with Linux/Ubuntu OS. Here you can run go run main.go command which will print Hello World! as expected. Notice that this happened inside the container you had created.

Alternatively, if you have already started the container for some other application and it's currently running, you can also run docker exec -it <container id> /bin/bash to enter into the container's CLI.

If you want to save the running container's settings and state, you can do that using docker commit command(check this for more info on this command).

You can also copy a file from the docker environment back into your local environment using docker cp <container-id>/<path to your file> <path where you want to copy locally> , for example: docker cp c7aec65deaf0:/build/main.go /Users/user1/Documents

You can stop the running container using docker stop <container-id> command.

Similarly, containers and images can be removed from your local system using

  • docker rm <container-ids>

  • docker rmi <image-name/>

Recap πŸ‘€

Docker commands used:

-> docker build -t main-app .
-> docker images
-> docker inspect main-app
-> docker run main-app
-> docker ps -a
-> docker run -it main-app /bin/bash
-> docker exec -it <container id> /bin/bash
-> docker commit
-> docker cp <container-id>/<file path> <local dir to store>
-> docker stop <container-id>
-> docker rm <container-ids>
-> docker rmi <image-name/>

Docker has an amazing official documentation as well ❀️

You can also check out Docker architecture at https://docs.docker.com/get-started/overview/#docker-architecture, or anything you want to know about docker at https://docs.docker.com.


If you found this article helpful, please comment and share this article πŸ˜ƒ.

Let's connect πŸ’«

You can also subscribe to my newsletter below to get an email notification on my latest posts. πŸ’»

Β