Docker

What is a Docker container and How to use it

In this article, we will see what is a Docker container and how to use it.

1. Introduction

Containers, in general, and Docker in particular is quickly gaining in popularity.  In this post, we will get some basics sorted out about the Docker container.  We will learn the meaning of the term Container in general and see specifically how Docker implements them.  We will try to execute a few basic commands to understand the bare basics of Docker itself, Docker images, and Docker containers. So, let’s start.

2. Containers, what are they?

Operating system designers have always striven to provide secure operating environments to applications running on them. While applications need the harness the complete power of the underlying processing power and hardware for their work, they also need to be secured from harming other running applications. Over time, operating systems have progressed to provide features to virtualize several aspects of the underlying hardware – memory, file-systems, network interfaces, process trees, etc.

 
These virtualization efforts diverged primarily into 2 directions (among many of course) – hardware virtualization and operating system (OS) level virtualization. Hardware virtualization refers to the creation of virtual machines (on a host OS or even further lower) that virtualizes the underlying physical machine. Users can install complete OS into such virtual machines. OS level virtualization refers to features provided in the kernel (of the host OS) that allows creation of virtual and isolated hosting environments in the user space of the host OS. These virtual hosting environments are also termed Containers.

Docker container - Hardware virtualization and OS level virtualization
Hardware virtualization and OS-level virtualization

Over time there have been several such Container technologies that enabled containers to be created in the user space – chroot, jail, Oracle Solaris Zones, LXC, Docker

3. Docker – What is it?

So now, what is Docker? Simply put, Docker is a container technology that enables users to deploy their applications into containers and run them within those containers. Docker enables users to easily package their applications and all of their dependencies into read-only images called Docker images. These images can be easily distributed through a Docker hub. These images can then be downloaded and executed in Docker Containers.

Docker container - Basic workflow while using Docker
Basic workflow while using Docker

Now that we are briefly introduced to Docker, let us next discuss how we can install Docker and use it to create new containers.

4. Install Docker

Docker can be installed on Linux, Mac, and Windows. Please follow the installation instructions for Docker docs to get them installed in your machine.  Once installed you can check if it is installed properly by running a Hello world container.  Please follow this post to explore a few ways to do so.  Now that Docker is installed, let us play around with the basics.

5. Basic commands to understand your Docker installation

We can get overall information from Docker using the command:

1
$ docker info
Docker container - dokcer info
The output of the command “docker info”

As you can see from the top 3 lines of the output, the number of total containers and images is 0 since we have a fresh install of Docker.

Once installed you can check the Docker images present using the command

1
$ docker images
Docker container - Output of Docker images after a fresh Docker install
The output of Docker images after a fresh Docker install

It will be empty since we have installed Docker just now.

Once installed you can check the Docker containers present using the command

1
$ docker ps
Docker container - Output of docker ps for a fresh Docker install
The output of docker ps for a fresh Docker install

It will be empty since we have installed Docker just now.  Now let us create a container and try to understand how Docker behaves.

6. Basic Docker commands to create containers and images

Let us suppose that we want to create a separate container running Alpine Linux. To do this we simply pull down the official Alpine Docker image shared in the Docker Hub.

1
$ docker pull alpine:latest
Docker container - Output of docker pull
The output of docker pull

If you do not have this Docker image already then you will see a similar output like below. Otherwise, you will get a message saying you already have this image. Let us see what Docker tells us about the number of images available locally now.

1
$ docker info
Docker container - Partial output of docker info after pulling an image
The partial output of docker info after pulling an image

Now you can see that the number of images is 1 – the Alpine image we just downloaded. However, the number of Containers is still 0 since we have not spawned a new container yet. This image gets stored locally in the folder /var/lib/docker/image

Docker container - Local location of downloaded Docker images in /var/lib/docker
Local location of downloaded Docker images in /var/lib/docker

We can see that the SHA256 id listed is the same as in the output for docker images.

To use this image, we need to create a new container out of this image. We do that by the docker run command.

1
$ docker run alpine:latest ls -l
Docker container - Run the Alpine image using docker run
Run the Alpine image using docker run

So we spawned a new container out of the alpine image and ran the command ls -l within this container. What you see is the output of this command within the virtual environment provided by the container. By using the docker info command we can see that the number of containers has increased by 1.

Output of docker info after creating a container
The output of docker info after creating a container

Specifically, we see from the above output that the number of stopped containers has increased by 1. That is because when we spawned a new container above from the Alpine image, a new container was created and then the ls -l command was run and then the container was stopped. We can see all containers that are created by the docker ps command like so

Output of docker ps --all
The output of docker ps –all

From above, there is now one container created from the Alpine image and it has exited now.

Now let us see what happens if we use the same Alpine image to run another command in a container.

1
$ docker run alpine:latest echo "hello world"
Output of docker run on Alpine image
The output of docker run on Alpine image

“hello world” was printed as instructed. But let us see how many containers are present now

1
$ docker ps --all
Output of docker ps
Output of docker ps

We can see that Docker created a new container from the same image to execute the new command. So Docker spawns a new container from an image for every operation unless we explicitly ask it to use the same container to execute different commands.

Now let us try to understand more about the Docker image we created

1
$ docker inspect alpine:latest
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
[
    {
        "Id": "sha256:baa5d63471ead618ff91ddfacf1e2c81bf0612bfeb1daf00eb0843a41fbfade3",
        "RepoTags": [
            "alpine:latest"
        ],
        "RepoDigests": [
            "alpine@sha256:1354db23ff5478120c980eca1611a51c9f2b88b61f24283ee8200bf9a54f2e5c"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2016-10-18T20:31:22.321427771Z",
        "Container": "1d811a9194c47475510bc53700001c32f2b0eb8e3aca0914c5424109c0cd2056",
        "ContainerConfig": {
            "Hostname": "1d811a9194c4",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ADD file:7afbc23fda8b0b3872623c16af8e3490b2cee951aed14b3794389c2f946cc8c7 in / "
            ],
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "DockerVersion": "1.12.1",
        "Author": "",
        "Config": {
            "Hostname": "1d811a9194c4",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": null,
            "Image": "",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 4799225,
        "VirtualSize": 4799225,
        "GraphDriver": {
            "Name": "devicemapper",
            "Data": {
                "DeviceId": "7",
                "DeviceName": "docker-253:3-135839549-8cb0423f038b9f8d5f1b1a28bc13ba1a32ec373994d46b41b299774aa15e2b12",
                "DeviceSize": "10737418240"
            }
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:011b303988d241a4ae28a6b82b0d8262751ef02910f0ae2265cb637504b72e36"
            ]
        }
    }
]

That is a lot of information we get about the image there. We will not discuss them in detail here but you can see that the image id, tag, and time-stamp of creation are obvious there. Any containers created from this image are listed next with details of that container. The Docker version used and the Author of the image are also listed. Then follow further details of the image, its architecture of the virtual hosting environment, and finally the emulated root file-system for that image.

Now let us try to understand more about the container we created from this image. Let us see what all containers we have at present.

1
$ docker ps --all
Output of docker ps
The output of docker ps

We see that the containers adoring_bell and desperate_poincare is based on the image alpine:latest. Let us see the metadata of this container more.

1
$ docker inspect adoring_bell
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
[
    {
        "Id": "c1631bed732bac64355c652ef0b82876b5c7c8a89c3dfe45a4155e1148d5e46b",
        "Created": "2016-11-08T10:38:04.90010304Z",
        "Path": "echo",
        "Args": [
            "hello world"
        ],
        "State": {
            "Status": "exited",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 0,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2016-11-08T10:38:05.582857658Z",
            "FinishedAt": "2016-11-08T10:38:05.610419062Z"
        },
        "Image": "sha256:baa5d63471ead618ff91ddfacf1e2c81bf0612bfeb1daf00eb0843a41fbfade3",
        "ResolvConfPath": "/home/hariharan/work/docker-home/containers/c1631bed732bac64355c652ef0b82876b5c7c8a89c3dfe45a4155e1148d5e46b/resolv.conf",
        "HostnamePath": "/home/hariharan/work/docker-home/containers/c1631bed732bac64355c652ef0b82876b5c7c8a89c3dfe45a4155e1148d5e46b/hostname",
        "HostsPath": "/home/hariharan/work/docker-home/containers/c1631bed732bac64355c652ef0b82876b5c7c8a89c3dfe45a4155e1148d5e46b/hosts",
        "LogPath": "/home/hariharan/work/docker-home/containers/c1631bed732bac64355c652ef0b82876b5c7c8a89c3dfe45a4155e1148d5e46b/c1631bed732bac64355c652ef0b82876b5c7c8a89c3dfe45a4155e1148d5e46b-json.log",
        "Name": "/adoring_bell",
        "RestartCount": 0,
        "Driver": "devicemapper",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": -1,
            "OomKillDisable": false,
            "PidsLimit": 0,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0
        },
        "GraphDriver": {
            "Name": "devicemapper",
            "Data": {
                "DeviceId": "11",
                "DeviceName": "docker-253:3-135839549-71e94f8b4e5e1af0b31889245ea0d2ffa54c68e6991399d0438f473846f701cc",
                "DeviceSize": "10737418240"
            }
        },
        "Mounts": [],
        "Config": {
            "Hostname": "c1631bed732b",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "echo",
                "hello world"
            ],
            "Image": "alpine:latest",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "27ee3cf100495a82743f1ec700195a4e560a04e0164a65976774ea23bb515450",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": null,
            "SandboxKey": "/var/run/docker/netns/27ee3cf10049",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "efd29ba943ae4030c14b6e88fda502ff260a490ae5d264e9626d3882d7358195",
                    "EndpointID": "",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": ""
                }
            }
        }
    }
]

There is a wealth of info there. We will not get into the details but discuss it overall here. The first 5 entries give the container id, time-stamp at container creation, the command sent to the container at the start (echo “hello world”), and the current state of the container (it has exited now). Next follows the details of the image from which the container was spawned and where it is mapped into the local file system. The container name assigned is seen (adoring_bell). Then follows the various details of the emulated virtual environments – the virtual networks, virtual mount points, etc.,

7. Summary

We covered some very basic understanding of what a Docker container is in this post. We were introduced to the concept of OS-level virtualization and a software container. Then we covered some basic commands to download Docker images and spawn new containers from the image. Later we covered some commands to inspect the metadata that Docker holds about itself, the images, and the containers.

Last updated on Jan. 31st, 2022

Hariharan Narayanan

Hari graduated from the School of Computer and Information Sciences in the University of Hyderabad. Over his career he has been involved in many complex projects in mobile applications, enterprise applications, distributed applications, micro-services, and other platforms and frameworks. He works as a consultant and is mainly involved with projects based on Java, C++ and Big Data technologies.
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button