Introduction to Docker


Bhaskar S 03/25/2017 (Updated)


Overview

One of the hot topics these days is around Docker. So what is all this hype about Docker ???

Docker is a platform for developers and administrators to develop, build, deploy, and run applications. In other words, think of Docker as an application level container virtualization, that allows multiple isolated instances of an application and its dependencies (configuration, files, and libraries) to run on a single host.

Docker under the hood leverages some of the Linux kernel functionality such as namespaces, cgroups, capabilities, etc via an API interface called libcontainer to provide application level container virtualization.

The following Figure-1 illustrates the high-level overview of a Docker:

Docker
Figure-1

Docker uses a client-server model, where a background daemon server process manages the application containers and a remote client process communicates and interacts with the daemon process relaying user commands to build, deploy, or run application containers.

The core components that make Docker are as follows:

In short, build a Docker Image to package an application, create a Docker Container from the Docker Image to run the application, and share the Docker Image via the Docker Registry.

Installation

The installation is on a Ubuntu 16.04 LTS based Linux desktop.

To install the Docker, execute the following commands:

$ sudo apt-get update

$ sudo apt-get install docker

$ sudo gpasswd -a ${USER} docker

Once the commands complete, reboot the system, and execute the following command to check everything was ok:

$ docker info

The following would be a typical output:

Output.1

Containers: 1
 Running: 0
 Paused: 0
 Stopped: 1
Images: 1
Server Version: 1.12.1
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 13
 Dirperm1 Supported: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: null bridge host overlay
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Security Options: apparmor
Kernel Version: 4.4.0-53-generic
Operating System: Linux Mint 18.1
OSType: linux
Architecture: x86_64
CPUs: 8
Total Memory: 31.32 GiB
Name: xxxxxxxxxx
ID: XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Registry: https://index.docker.io/v1/
WARNING: No swap limit support
Insecure Registries:
 127.0.0.0/8

The above installation procedure installs Docker from the official Ubuntu repository which may not be the latest.

To install the most latest version of Docker, execute the following commands:

$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

$ echo "deb https://apt.dockerproject.org/repo ubuntu-xenial main" | sudo tee /etc/apt/sources.list.d/docker.list

$ sudo apt-get update

$ sudo apt-get install docker-engine

Hands-on with Docker

To list all the Docker Images on the local host, execute the following command:

$ docker images

The following would be a typical output:

Output.2

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE

From Output.2, we see there are no Docker Images on the local host.

To fetch a pre-built Docker Image for Ubuntu 14.04 from the Docker Hub registry and store it on the local host, execute the following command:

$ docker pull ubuntu:14.04

Notice the use of the image name followed by the desired version ubuntu:14.04 for Ubuntu 14.04 in the command above.

The following would be a typical output:

Output.3

14.04: Pulling from library/ubuntu
30d541b48fc0: Pull complete
8ecd7f80d390: Pull complete
46ec9927bb81: Pull complete
2e67a4d67b44: Pull complete
7d9dd9155488: Pull complete
Digest: sha256:62a5dce5ceccd7f1cb2672a571ebee52cad1f08eec9b57fe4965fb0968a9602e
Status: Downloaded newer image for ubuntu:14.04

Now, execute the following command:

$ docker images

The following would be a typical output:

Output.4

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              14.04               7c09e61e9035        3 weeks ago         188 MB

To fetch a pre-built Docker Image for the latest version of Ubuntu from the Docker Hub registry and store it on the local host, execute the following command:

$ docker pull ubuntu

The following would be a typical output:

Output.5

Using default tag: latest
latest: Pulling from library/ubuntu
d54efb8db41d: Pull complete
f8b845f45a87: Pull complete
e8db7bf7c39f: Pull complete
9654c40e9079: Pull complete
6d9ef359eaaa: Pull complete
Digest: sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535
Status: Downloaded newer image for ubuntu:latest

Now, execute the following command:

$ docker images

The following would be a typical output:

Output.6

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              latest              0ef2e08ed3fa        3 weeks ago         130 MB
ubuntu              14.04               7c09e61e9035        3 weeks ago         188 MB

We have downloaded two pre-built Docker Images from the public Docker Hub registry. To create a Docker Container and bring it to life, we need to run the desired Docker Image.

Is this confusing ???

An analogy to Java will make this concept clear.

A Docker Image is like a Java class file - it is built (compiled) and ready to be used. A Docker Container is like a live object instance of the Java class at runtime.

To create and launch our first Docker Container for Ubuntu 14.04 from the just downloaded and locally stored Docker Image, execute the following command:

$ docker run -i -t ubuntu:14.04 /bin/bash

The -i option indicates we want to run the container in an interactive mode and keep the standard input (STDIN) open from the container

The -t option instructs Docker to assign a pseudo terminal so that we can interact with the container

One could also use the associated image id 7c09e61e9035 to create and launch the Ubuntu 14.04 Docker Container as shown below:

$ docker run -i -t 7c09e61e9035 /bin/bash

The following would be a typical output:

Output.7

root@fdd09ace1e94:/#

Hooray !!! we have successfully created and launched our first Docker Container.

To list all the running Docker Containers, execute the following command:

$ docker ps

The following would be a typical output:

Output.8

CONTAINER ID    IMAGE           COMMAND         CREATED              STATUS              PORTS           NAMES
fdd09ace1e94    7c09e61e9035    "/bin/bash"     About a minute ago   Up About a minute                   ecstatic_bhabha

Notice that Docker by default assigns a random host name for the running instance of the Docker Container, which in our case is fdd09ace1e94.

To specify a hostname name dragon to the Docker Container, execute the following command:

$ docker run -it --hostname dragon ubuntu:14.04 /bin/bash

The following would be a typical output:

Output.9

root@dragon:/#

To stop the running Docker Container with the container id fdd09ace1e94, execute the following command in the containers shell:

root@fdd09ace1e94:/# exit

Alternatively, to stop the running Docker Container with the container id fdd09ace1e94, we could execute the following command:

$ docker stop fdd09ace1e94

Now, to list all the running containers, execute the following command:

$ docker ps

The following would be a typical output:

Output.10

CONTAINER ID    IMAGE           COMMAND         CREATED         STATUS          PORTS           NAMES

As can be observed from the Output.10, there are no actively running containers at this time.

To list all the Docker Containers, including the ones that are stopped, execute the following command:

$ docker ps -a

The following would be a typical output:

Output.11

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS           NAMES
fdd09ace1e94        7c09e61e9035        "/bin/bash"         5 minutes ago       Exited (0) 2 minutes ago                   ecstatic_bhabha

To restart the currently stopped Docker Container with the container name 7d30b0d3051b, execute the following command:

$ docker start fdd09ace1e94

The above command will restart the Docker Container with the same options as was started the first time.

The following would be a typical output:

Output.12

fdd09ace1e94

What happened here ??? There is no interactive shell ???

RELAX !!!

We need to attach to the running Docker Container with the container name fdd09ace1e94 to be able to access the interactive shell. This is why the options -i and -t are important.

To attach to the currently running Docker Container with the container name fdd09ace1e94, execute the following command:

$ docker attach fdd09ace1e94

The following would be a typical output:

Output.13

root@fdd09ace1e94:/#

To delete the currently stopped Docker Container with the container name fdd09ace1e94, execute the following command:

$ docker rm fdd09ace1e94

The following would be a typical output:

Output.14

fdd09ace1e94

Now, to list all the containers, execute the following command:

$ docker ps -a

The following would be a typical output:

Output.15

CONTAINER ID    IMAGE           COMMAND         CREATED         STATUS          PORTS           NAMES

To delete the locally stored Docker Image with the image name 7c09e61e9035 associated with Ubuntu 14.04, execute the following command:

$ docker rmi 120acb8ad8a3 -f

The following would be a typical output:

Output.16

Untagged: ubuntu:14.04
Untagged: ubuntu@sha256:62a5dce5ceccd7f1cb2672a571ebee52cad1f08eec9b57fe4965fb0968a9602e
Deleted: sha256:7c09e61e90350e8f5c0cba2979003bdfe32c2d027b68b4f0cf9063cdd7b4bafd

Now, to list all the images, execute the following command:

$ docker images

The following would be a typical output:

Output.17

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              latest              0ef2e08ed3fa        3 weeks ago         130 MB

Let us now fetch the pre-built Docker Image for the latest version of CentOS from the Docker Hub registry and store it on the local host, execute the following command:

$ docker pull centos

The following would be a typical output:

Output.18

Using default tag: latest
latest: Pulling from library/centos
785fe1d06b2d: Pull complete
Digest: sha256:be5b4a93f116a57ab3fd454ada72421eac892a3a4925627ac9a44f65fcd69cf8
Status: Downloaded newer image for centos:latest

Now, execute the following command:

$ docker images

The following would be a typical output:

Output.19

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              latest              98d35105a391        9 days ago          192.5 MB
ubuntu              latest              0ef2e08ed3fa        3 weeks ago         130 MB

To create and launch a Docker Container for CentOS, execute the following command:

$ docker run -i -t 98d35105a391 /bin/bash

Notice the use of the image id 98d35105a391 for CentOS in the command above.

The following would be a typical output:

Output.20

[root@71986e2ea398 /]#

To list all the running Docker Containers, execute the following command:

$ docker ps

The following would be a typical output:

Output.21

CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
71986e2ea398        98d35105a391        "/bin/bash"         About a minute ago   Up About a minute                       sick_hodgkin

To list all the process(es) running inside the Docker Container with container id 71986e2ea398, execute the following command:

$ docker top 71986e2ea398

Notice the use of the container id 71986e2ea398 in the command above.

The following would be a typical output:

Output.22

UID           PID           PPID          C             STIME         TTY           TIME          CMD
root          22454         22437         0             17:09         pts/1         00:00:00      /bin/bash

Let us now try to launch the simple text editor nano inside the container with id 71986e2ea398. Execute the following command in the container terminal:

[root@71986e2ea398 /]# nano

The following would be a typical output:

Output.23

bash: nano: command not found

From the Output.23, it is evident that nano is not installed in the container with id 71986e2ea398.

To install nano inside the container with id 71986e2ea398. Execute the following command in the container terminal:

root@876571ab41f8:/# yum install nano

The following would be a typical output:

Output.24

Loaded plugins: fastestmirror, ovl
base                                                                                                         | 3.6 kB  00:00:00
extras                                                                                                       | 3.4 kB  00:00:00
updates                                                                                                      | 3.4 kB  00:00:00
(1/4): extras/7/x86_64/primary_db                                                                            | 139 kB  00:00:00
(2/4): updates/7/x86_64/primary_db                                                                           | 3.8 MB  00:00:00
(3/4): base/7/x86_64/group_gz                                                                                | 155 kB  00:00:01
(4/4): base/7/x86_64/primary_db                                                                              | 5.6 MB  00:00:01
Determining fastest mirrors
 * base: mirror.us.leaseweb.net
 * extras: repo1.ash.innoscale.net
 * updates: repo1.ash.innoscale.net
Resolving Dependencies
--> Running transaction check
---> Package nano.x86_64 0:2.3.1-10.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

====================================================================================================================================
 Package                     Arch                          Version                                Repository                   Size
====================================================================================================================================
Installing:
 nano                        x86_64                        2.3.1-10.el7                           base                        440 k

Transaction Summary
====================================================================================================================================
Install  1 Package

Total download size: 440 k
Installed size: 1.6 M
Is this ok [y/d/N]: y
Downloading packages:
warning: /var/cache/yum/x86_64/7/base/packages/nano-2.3.1-10.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Public key for nano-2.3.1-10.el7.x86_64.rpm is not installed
nano-2.3.1-10.el7.x86_64.rpm                                                                                 | 440 kB  00:00:00
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
 Userid     : "CentOS-7 Key (CentOS 7 Official Signing Key) "
 Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
 Package    : centos-release-7-3.1611.el7.centos.x86_64 (@CentOS)
 From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Is this ok [y/N]: y
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : nano-2.3.1-10.el7.x86_64                                                                                         1/1
  Verifying  : nano-2.3.1-10.el7.x86_64                                                                                         1/1

Installed:
  nano.x86_64 0:2.3.1-10.el7

Complete!

Now one should be able to launch nano inside the container with id 71986e2ea398.

Since we have made changes (installed nano) to the container with id 71986e2ea398, we need to save the container state as a new Docker Image so we can launch a container with the same state in the future.

To save the currently running Docker Container with container id 71986e2ea398 as a new Docker Image, execute the following command:

$ docker commit 71986e2ea398 mycentos

Notice the use of the container id 71986e2ea398 in the command above.

The name of the new Docker Image will be called mycentos.

The following would be a typical output:

Output.25

sha256:91b1f32a62f3770c57217e5f41dbd03c2f0c88a0eea08a4e37c223e815ef2840

Now, execute the following command:

$ docker images

The following would be a typical output:

Output.26

REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mycentos            latest              91b1f32a62f3        21 seconds ago      273.3 MB
centos              latest              98d35105a391        9 days ago          192.5 MB
ubuntu              latest              0ef2e08ed3fa        3 weeks ago         130 MB

As is evident from the Output.26, we have successfully created a new Docker Image with the name mycentos in the local repository.

Launching a new Docker Container using the image id 91b1f32a62f3 will launch a CentOS container with nano installed.

By default, a Docker Container's state is persisted even after the container has exited.

To list all the Docker Containers, including the ones that have exited, execute the following command:

$ docker ps -a

The following would be a typical output:

Output.27

CONTAINER ID    IMAGE           COMMAND                  CREATED             STATUS                         PORTS       NAMES
2fd20247c3dc    ubuntu:14.04    "/bin/bash"              About an hour ago   Exited (0) 29 minutes ago                  happy_almeida
c2ec7fb5c068    ubuntu:14.04    "/bin/bash"              About an hour ago   Exited (0) About an hour ago               loving_bose
a13c14c71a86    mycentos        "/bin/bash"              13 hours ago        Exited (0) 13 hours ago                    peaceful_ramanujan
eb67f26764ea    ubuntu_flask    "python /home/flask/a"   4 days ago          Exited (0) 2 days ago                      admiring_archimedes

If we launch a lot of short-lived containers, over time this behavior can fill-up the host filesystem space. One can manually clean-up the filesystem, using the following command(s):

$ docker rm 2fd20247c3dc

$ docker rm c2ec7fb5c068 a13c14c71a86

No, let us re-execute the following command:

$ docker ps -a

The following would be a typical output:

Output.28

CONTAINER ID    IMAGE           COMMAND                  CREATED             STATUS                         PORTS       NAMES
eb67f26764ea    ubuntu_flask    "python /home/flask/a"   4 days ago          Exited (0) 2 days ago                      admiring_archimedes

To automatically clean-up the Docker Container's state after the container exits, specify the --rm option as shown below:

$ docker run -it --rm mycentos /bin/bash

One caveat with the --rm option - it is mutually exclusive with the -d (background daemon) option.

With this, we conclude that Docker enables one to quickly assemble applications as a container, deploy and run the container (with the applications) in any environment faster.

References

Official Docker Documentation

Linux Containters