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 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:
Docker Daemon :: server process manages the application containers
Docker Client :: client process that communicates with the daemon for relaying user commands
Docker Container :: a package for an application and its resources. Think of the Docker Container as a "application in a box" with its own resources such as files, libraries, and binaries
Docker Image :: is an immutable read-only template for a Docker Container. Think of the Docker Image as a "snapshot" for a Docker Container
Docker Registry :: a repository for holding Docker Images. The public Docker Registry hosted by Docker community in the cloud is referred to as the Docker Hub and contains a large collection of ready to use Docker Images
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
fdd09ace1e94
Now, to list all the containers, execute the following command:
$ docker ps -a
The following would be a typical output:
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:
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:
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:
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:
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:
[root@71986e2ea398 /]#
To list all the running Docker Containers, execute the following command:
$ docker ps
The following would be a typical output:
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:
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:
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:
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:
sha256:91b1f32a62f3770c57217e5f41dbd03c2f0c88a0eea08a4e37c223e815ef2840
Now, execute the following command:
$ docker images
The following would be a typical output:
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:
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:
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