Docker Engine is a client-server application with these major components:
- A server which is a type of long-running program called a daemon process (the dockerd command).
- A REST API which specifies interfaces that programs can use to talk to the daemon and instruct it what to do.
- A command line interface (CLI) client (the docker command).
The CLI uses the Docker REST API to control or interact with the Docker daemon through scripting or direct CLI commands. Many other Docker applications use the underlying API and CLI.
The daemon creates and manages Docker objects, such as images, containers, networks, and volumes.
The Docker daemon (dockerd) listens for Docker API requests and manages Docker objects such as images, containers, networks, and volumes. A daemon can also communicate with other daemons to manage Docker services.
The Docker client (docker) is the primary way that many Docker users interact with Docker. When you use commands such as docker run, the client sends these commands to dockerd, which carries them out. The docker command uses the Docker API. The Docker client can communicate with more than one daemon.
A Docker registry stores Docker images. Docker Hub is a public registry that anyone can use, and Docker is configured to look for images on Docker Hub by default. You can even run your own private registry.
When you use the docker pull or docker run commands, the required images are pulled from your configured registry. When you use the docker push command, your image is pushed to your configured registry.
An image is a read-only template with instructions for creating a Docker container. Often, an image is based on another image, with some additional customization. For example, you may build an image which is based on the ubuntu image, but installs the Apache web server and your application, as well as the configuration details needed to make your application run.
A container is a runnable instance of an image. You can create, start, stop, move, or delete a container using the Docker API or CLI. You can connect a container to one or more networks, attach storage to it, or even create a new image based on its current state.
By default, a container is relatively well isolated from other containers and its host machine. You can control how isolated a container’s network, storage, or other underlying subsystems are from other containers or from the host machine.
A container is defined by its image as well as any configuration options you provide to it when you create or start it. When a container is removed, any changes to its state that are not stored in persistent storage disappear.
Docker uses a client-server architecture. The Docker client talks to the Docker daemon, which does the heavy lifting of building, running, and distributing your Docker containers. The Docker client and daemon can run on the same system, or you can connect a Docker client to a remote Docker daemon. The Docker client and daemon communicate using a REST API, over UNIX sockets or a network interface.
First Docker Container
$ docker container run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 0e03bdcc26d7: Pull complete Digest: sha256:4cf9c47f86df71d48364001ede3a4fcd85ae80ce02ebad74156906caff5378bc Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/
The steps are clearly shown on above output. First the docker daemon looked for the local image with name hello-world. If the image is not found locally, then it checked whether it is on default docker registry which is docker hub. As there was a image called hello-world on the docker hub, it pulled that image down, ran it on a container and exited.
Docker vs VM
Looking at above example, docker kind of looked like vm. But here are few differences.
- The VM is a hardware abstraction: it takes physical CPUs and RAM from a host, and divides and shares it across several smaller virtual machines. There is an OS and application running inside the VM, but the virtualization software usually has no real knowledge of that.
- A container is an application abstraction: the focus is really on the OS and the application, and not so much the hardware abstraction. Many customers actually use both VMs and containers today in their environments and, in fact, may run containers inside of VMs.
Fetching docker image from the registry
$ docker image pull alpine Using default tag: latest latest: Pulling from library/alpine df20fa9351a1: Pull complete Digest: sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321 Status: Downloaded newer image for alpine:latest docker.io/library/alpine:latest
This command will look at the default docker registry ie dockerhub (can be changed) for image called alpine and if found, it will download the image on our local box. Now with this image, we can create multiple containers which are like the multiple instances of this image.
Alpine Linux is a Linux distribution based on musl and BusyBox, designed for security, simplicity, and resource efficiency.
Listing the images on our box
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE alpine latest a24bb4013296 3 months ago 5.57MB hello-world latest bf756fb1ae65 8 months ago 13.3kB
We have two images on the box, one being hello-world which we fetched earlier and other one is alpine.
Running container based on the image
$ docker container run alpine ls -la total 8 drwxr-xr-x 1 root root 6 Sep 24 06:19 . drwxr-xr-x 1 root root 6 Sep 24 06:19 .. -rwxr-xr-x 1 root root 0 Sep 24 06:19 .dockerenv drwxr-xr-x 2 root root 4096 May 29 14:20 bin drwxr-xr-x 5 root root 340 Sep 24 06:19 dev drwxr-xr-x 1 root root 66 Sep 24 06:19 etc drwxr-xr-x 2 root root 6 May 29 14:20 home drwxr-xr-x 7 root root 223 May 29 14:20 lib drwxr-xr-x 5 root root 44 May 29 14:20 media drwxr-xr-x 2 root root 6 May 29 14:20 mnt drwxr-xr-x 2 root root 6 May 29 14:20 opt dr-xr-xr-x 768 root root 0 Sep 24 06:19 proc drwx------ 2 root root 6 May 29 14:20 root drwxr-xr-x 2 root root 6 May 29 14:20 run drwxr-xr-x 2 root root 4096 May 29 14:20 sbin drwxr-xr-x 2 root root 6 May 29 14:20 srv dr-xr-xr-x 13 root root 0 Aug 21 07:57 sys drwxrwxrwt 2 root root 6 May 29 14:20 tmp drwxr-xr-x 7 root root 66 May 29 14:20 usr drwxr-xr-x 12 root root 137 May 29 14:20 var
What happened in the background when we ran the command is that the docker client requested docker daemon to search for image called alpine and when the daemon found the image name called alpine then it created a brand new container and ran the command inside the container. Then container shut down.
Now if we were to run the same command again, the container that will be created this time will be a brand new container. I will demonstrate this in a second.
Creating new container executing /bin/sh
$ docker container run alpine /bin/sh
Command executed successfully but nothing happened. WHY? It is because the command executed successfully and the container shut itself down. To tell the docker that we need a interactive session we use following commands.
$ docker container run -it alpine /bin/sh / # ls -la total 8 drwxr-xr-x 1 root root 18 Sep 24 07:00 . drwxr-xr-x 1 root root 18 Sep 24 07:00 .. -rwxr-xr-x 1 root root 0 Sep 24 07:00 .dockerenv drwxr-xr-x 2 root root 4096 May 29 14:20 bin drwxr-xr-x 5 root root 360 Sep 24 07:00 dev drwxr-xr-x 1 root root 66 Sep 24 07:00 etc drwxr-xr-x 2 root root 6 May 29 14:20 home drwxr-xr-x 7 root root 223 May 29 14:20 lib drwxr-xr-x 5 root root 44 May 29 14:20 media drwxr-xr-x 2 root root 6 May 29 14:20 mnt drwxr-xr-x 2 root root 6 May 29 14:20 opt dr-xr-xr-x 910 root root 0 Sep 24 07:00 proc drwx------ 1 root root 26 Sep 24 07:00 root drwxr-xr-x 2 root root 6 May 29 14:20 run drwxr-xr-x 2 root root 4096 May 29 14:20 sbin drwxr-xr-x 2 root root 6 May 29 14:20 srv dr-xr-xr-x 13 root root 0 Aug 21 07:57 sys drwxrwxrwt 2 root root 6 May 29 14:20 tmp drwxr-xr-x 7 root root 66 May 29 14:20 usr drwxr-xr-x 12 root root 137 May 29 14:20 var / # exit
This time the container didnot shut down but instead gave us a shell to work with.Now on this shell inside of our newly created container we can execute shell commands.
Listing the containers
To list the currently active container we can use following command.
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Looks like there is no any results. It is because we do not have any active running containers.
$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 21bcebe696a3 alpine "/bin/sh" 6 minutes ago Exited (0) 3 minutes ago strange_bouman 6684f8bd6f73 alpine "/bin/sh" 8 minutes ago Exited (0) 8 minutes ago brave_wescoff d5d18e291759 alpine "ls -la" 47 minutes ago Exited (0) 47 minutes ago funny_blackburn 32045da0a193 hello-world "/hello" 50 minutes ago Exited (0) 50 minutes ago recursing_chaum
These are the list of containers that were created. And we can see from the status that they exited some N minutes ago. One interesting thing here is notice the CONTAINER ID of each of the container created, they all are different which means all the containers we have created are indeed different from each other even though they are created from same image.