Page cover image

Introduction to Docker

After installing Docker, one can check its version by the following command:

docker version

The output of the above command would be somethign like below:

Client:
 Cloud integration: v1.0.25
 Version:           20.10.16
 API version:       1.41
 Go version:        go1.17.10
 Git commit:        aa7e414
 Built:             Thu May 12 09:20:34 2022
 OS/Arch:           darwin/arm64
 Context:           default
 Experimental:      true

Server: Docker Desktop 4.9.1 (81317)
 Engine:
  Version:          20.10.16
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.17.10
  Git commit:       f756502
  Built:            Thu May 12 09:14:19 2022
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.4
  GitCommit:        212e8b6fa2f44b9c21b2798135fc6fb7c53efc16
 runc:
  Version:          1.1.1
  GitCommit:        v1.1.1-0-g52de29d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Hello World

We would execute the following command now:

docker run hello-world
Hello from Docker!

For the above command, docker takes the following steps:

  • The Docker client contacted the Docker daemon.

  • The Docker daemon pulled the "hello-world" image from the Docker Hub. (arm64v8)

  • The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading.

  • The Docker daemon streamed that output to the Docker client, which sent it to your terminal.

Docker CLI

docker run

The first command we would look in details is the docker run command. The below figure provides details about this command:

The docker run command is used for creating and running a container from an image.

Overriding Default Commands

To override the default startup command, the following figure shows how to do it:

For example, following command uses a override command with docker run:

docker run busybox echo hi there

Output would be:

hi there

Another example is listing out the files and directories within a container:

docker run busybox ls

Output of the above command would be:

bin        dev        etc        home       proc
root       sys        tmp        usr        var

Now, let's try the echo and ls override commands with the hello-world image with docker run.

docker run hello-world ls
docker: Error response from daemon: failed to create shim task: OCI runtime create 
failed: runc create failed: unable to start container process: exec: "ls": executable file not found in $PATH: unknown.
ERRO[0000] error waiting for container: context canceled 

docker run hello-world echo hi there
docker: Error response from daemon: failed to create shim task: OCI runtime create 
failed: runc create failed: unable to start container process: exec: "echo": executable file not found in $PATH: unknown.
ERRO[0000] error waiting for container: context canceled 

As we can observe, we get an error for both the commands. This is because inside the hello-world image file-system, the ls and echo programs do not exist. The only thing that exists inside the hello-world image snaphot file-system is a single program. All this program does is print out the singular message: Hello from Docker!.

The same commands work with the busybox image because these programs exist inside the busybox file-system image.

Listing Running Containers

Next, we will look at a command to list all currently running containers on a local machine.

First of all we will run a container for a meaningful amount of time, like below:

docker run busybox ping google.com

PING google.com (172.217.161.14): 56 data bytes
64 bytes from 172.217.161.14: seq=0 ttl=37 time=11.497 ms
64 bytes from 172.217.161.14: seq=1 ttl=37 time=12.000 ms
64 bytes from 172.217.161.14: seq=2 ttl=37 time=18.183 ms
64 bytes from 172.217.161.14: seq=3 ttl=37 time=13.093 ms
64 bytes from 172.217.161.14: seq=4 ttl=37 time=11.236 ms

Next, we would list the running containers:

docker ps

CONTAINER ID   IMAGE     COMMAND             CREATED         STATUS         PORTS     NAMES
b60202284ace   busybox   "ping google.com"   2 minutes ago   Up 2 minutes             exciting_tharp

As can be observed from the output of the command, it prints out a bunch of information about the running container.

We can modify the docker ps command to list all containers created on a local machine like the following:

docker ps --all

Executing the above command will give the output something like below:

CONTAINER ID   IMAGE         COMMAND             CREATED       STATUS                   PORTS     NAMES
b60202284ace   busybox       "ping google.com"   3 hours ago   Exited (0) 3 hours ago             exciting_tharp
298176ed2948   hello-world   "/hello"            3 hours ago   Exited (0) 3 hours ago             zen_johnson
38f01afc355f   hello-world   "echo hi there"     3 hours ago   Created                            distracted_austin
b25255cc9ef7   hello-world   "ls"                3 hours ago   Created                            awesome_swirles
b09147ec3aae   busybox       "ls"                4 hours ago   Exited (0) 4 hours ago             youthful_wu
ab8b6c0bf713   busybox       "echo hi there"     4 hours ago   Exited (0) 4 hours ago             wonderful_archimedes
9435b6d0c6f7   hello-world   "/hello"            5 hours ago   Exited (0) 5 hours ago             xenodochial_kepler

Container Lifecycle

The docker run command is actually equivalent to running 2 seperate commands as detailed below:

The docker create command creates a container while the docker start command starts a container:

For example, the below command creates a new container using the hello-world image:

docker create hello-world

43b0148310bad8560734d780be5b7f7aac788b646a046efaa93e50d4558395d9

As can be observed, the docker create command outputs the id of the container that was created.

Next, we can execute the hello world command inside of this container using docker start, like below:

docker start -a 43b0148310bad8560734d780be5b7f7aac788b646a046efaa93e50d4558395d9

Like running the docker run command, the above command will output the following:

Hello from Docker!

Running docker start without the -a arguement does the following:

docker start 43b0148310bad8560734d780be5b7f7aac788b646a046efaa93e50d4558395d9

43b0148310bad8560734d780be5b7f7aac788b646a046efaa93e50d4558395d9

As can be observed above, without the -a argement, the command returns back the id of the container. The -a arguement will make docker actually watch for output from the container and print it out on the terminal.

Restarting Stopped Containers

An exited container can be started back again. For example, first we list all the containers created on a machine like below:

CONTAINER ID   IMAGE         COMMAND             CREATED          STATUS                     PORTS     NAMES
43b0148310ba   hello-world   "/hello"            16 minutes ago   Exited (0) 9 minutes ago             serene_poincare
b60202284ace   busybox       "ping google.com"   3 hours ago      Exited (0) 3 hours ago               exciting_tharp
298176ed2948   hello-world   "/hello"            3 hours ago      Exited (0) 3 hours ago               zen_johnson
38f01afc355f   hello-world   "echo hi there"     4 hours ago      Created                              distracted_austin
b25255cc9ef7   hello-world   "ls"                4 hours ago      Created                              awesome_swirles
b09147ec3aae   busybox       "ls"                4 hours ago      Exited (0) 4 hours ago               youthful_wu
ab8b6c0bf713   busybox       "echo hi there"     4 hours ago      Exited (0) 4 hours ago               wonderful_archimedes
9435b6d0c6f7   hello-world   "/hello"            5 hours ago      Exited (0) 5 hours ago               xenodochial_kepler

Then, let's say we pick the seventh entry (second last) from the list. Using the container id, we can issue the following command to start the container again.

docker start -a ab8b6c0bf713

hi there

One thing to note is that we cannot modify the start command for a container which has been already created. For example,

docker start -a ab8b6c0bf713 echo bye there

you cannot start and attach multiple containers at once

As can be observed, docker misinterprets the command and throws an error.

Removing Stopped Containers

To remove all containers, following is the command:

docker system prune

WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all dangling images
  - all dangling build cache

Are you sure you want to continue? [y/N] y
Deleted Containers:
43b0148310bad8560734d780be5b7f7aac788b646a046efaa93e50d4558395d9
b60202284acedd38ed38988290017182071b2fc152ab14414dce96435a5ae749
298176ed29480b90d5ac928ec1d8b8c474c9dd04941c4fdf8f7387632a36d670
38f01afc355fc6bdc96dec59b560cfcce81302e8a3939f0e55bf48bafc2a10c7
b25255cc9ef76f1bdd6ca4317f388ebff7890b526f2817292b2dc4264fe7f7a1
b09147ec3aaeb26d8e396564ee2638178048da49b7d0623d40606c77be56bf6b
ab8b6c0bf7138516fd84ccd78c3352515549df6012a90540720437443cf587c6
9435b6d0c6f71a5db9eb23f93a863a33a8b269e2179d1c7ccb71fb36a1899ee5

Total reclaimed space: 0B

As can be observed above, before removing all the stopped containers, docker outputs a warning and requests a confirmation from the user to actually remove the stopped containers.

Retrieving Log Outputs

Another way of getting the output from a container without using the -a flag with docker start is using the docker logs command. Specifically, below is the command to get logs from a container.

Following is the series of command that shows usage of docker logs command:

docker create busybox echo hi there
1a75b9eec51c364c71d12cfbeb96d3e6adc6789a4551a2de6ae22a72fe3d954c

docker start 1a75b9eec51c364c71d12cfbeb96d3e6adc6789a4551a2de6ae22a72fe3d954c
1a75b9eec51c364c71d12cfbeb96d3e6adc6789a4551a2de6ae22a72fe3d954c

docker logs 1a75b9eec51c364c71d12cfbeb96d3e6adc6789a4551a2de6ae22a72fe3d954c
hi there

Stopping Containers

There are 2 ways of stopping a running container:

Both the above commands are going to stop a running container. While both these commands kind of do the same thing, internally there is a difference between them.

When docker stop command is issued, a hardware signal SIGTERM is sent to the primary process inside the concerned container. This message tells the container to shutdown on its own time.

On the other hand, the docker kill command issues a SIGKILL signal to the primary process inside the concerned container. SIGKILL essentially means the conatiner has to shutdown right away without doing any additional work.

Ideally, docker stop should be used to stop a container. If a container is non-responsive, we can use docker kill command instead.

One thing to note about the docker stop command is that, when this command is executed and the concerned container does not stop automatically in 10 seconds, then docker will automatically fall back to executing docker kill command.

Following is a series of commands which creates and start a busybox container. Then it outputs the logs from the container. Finally, we stop the running container.

docker create busybox ping google.com
d506eaabd695a57e27fcec70f1f3caae1b8f5006e40f668e5c2a95388ce6c793

docker start d506eaabd695a57e27fcec70f1f3caae1b8f5006e40f668e5c2a95388ce6c793
d506eaabd695a57e27fcec70f1f3caae1b8f5006e40f668e5c2a95388ce6c793

docker logs d506eaabd695a57e27fcec70f1f3caae1b8f5006e40f668e5c2a95388ce6c793
PING google.com (172.217.160.238): 56 data bytes
64 bytes from 172.217.160.238: seq=0 ttl=37 time=12.531 ms
64 bytes from 172.217.160.238: seq=1 ttl=37 time=15.300 ms
64 bytes from 172.217.160.238: seq=2 ttl=37 time=18.923 ms
64 bytes from 172.217.160.238: seq=3 ttl=37 time=16.998 ms
64 bytes from 172.217.160.238: seq=4 ttl=37 time=15.382 ms
64 bytes from 172.217.160.238: seq=5 ttl=37 time=16.754 ms
64 bytes from 172.217.160.238: seq=6 ttl=37 time=15.589 ms
64 bytes from 172.217.160.238: seq=7 ttl=37 time=14.521 ms
64 bytes from 172.217.160.238: seq=8 ttl=37 time=13.746 ms

docker stop d506eaabd695a57e27fcec70f1f3caae1b8f5006e40f668e5c2a95388ce6c793
d506eaabd695a57e27fcec70f1f3caae1b8f5006e40f668e5c2a95388ce6c793

Multi-Command Containers

As an illustration, let's start running a new container with redis-server running inside it. Redis is an in memory data structure store. Following is the command to do so:

docker run redis

Executing Commands in Running Containers

After starting the container using the redis image, we now need to somehow start-up the the redis-cli also inside the container. For this, we would use the docker exec command:

Following is how to run redis-cli in the started container:

docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS      NAMES
b025f927bfa2   redis     "docker-entrypoint.s…"   27 minutes ago   Up 27 minutes   6379/tcp   flamboyant_sammet

docker exec -it b025f927bfa2 redis-cli
127.0.0.1:6379> set myvalue 5
OK
127.0.0.1:6379> get myvalue
"5"

Command Prompt in a Container

There is another usecase for the docker exec command which is more commonly used. This usecase is getting shell or terminal access to a running container. In other words, this would be mean running shell commands inside of a container without running docker exec repeatedly.

Following is the command for this:

docker exec -it b025f927bfa2 sh
# cd /
# ls
bin  boot  data  dev  etc  home  lib  media  mnt  
opt  proc  root  run  sbin srv   sys  tmp    usr  var
# export b=5
# echo $b
5
# redis-cli
127.0.0.1:6379> 
# exit

sh is a command processor which allows to type commands in and have them be executed inside of that container. There are other command processors as listed below:

Starting a Shell

We can also start a shell via the docker run command like below:

docker run -it busybox sh
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # echo hi there
hi there
/ # exit

One disadvantage of starting the shell via the docker run command is that it does not allow to run the container's default process when starting up. In practice, generally we start the container and then use the docker exec command to start a shell within a container.

Last updated