21 Days of Docker-Day 12-Docker Storage – Part 1

Welcome to Day 12 of 21 Days of Docker. Before I go into the in-depth of this topic, this first question we need to ask ourselves

  • When we perform any write operation inside the container where is that data got stored?
  • Do my containers need any external storage?

Let’s go back to Day 8 where I explained about Docker Images, Layers and Containers

  • If you remember this, I talked about container writable layer, which is the top layer, By default, all files created inside a container are stored on a writable container layer.

Now try to visualize the problem you will face if the container doesn’t exist

  • You will lose all the data

Now try to think who is responsible for managing these different imager layers as well as the top container writable layer?

  • Yes, you guessed it, right Storage Driver, Storage Driver put all these things together for you. The storage driver provides a union filesystem, using the Linux kernel.

To Check storage driver in your system

# docker info |grep Storage
 Storage Driver: overlay2

To check docker’s local storage area

# docker info |grep "Docker Root Dir"
 Docker Root Dir: /var/lib/docker

Storage drivers: Storage driver to use often depends on your operating system and other local configuration factors.

  • The overlay storage driver is deprecated in Docker Engine – Enterprise 18.09, and will be removed in a future release. It is recommended that users of the overlay storage driver migrate to overlay2.
  • The devicemapper storage driver is deprecated in Docker Engine 18.09, and will be removed in a future release. It is recommended that users of the devicemapper storage driver migrate to overlay2.

When possible, overlay2 is the recommended storage driver. When installing Docker for the first time, overlay2 is used by default. Previously, aufs was used by default when available, but this is no longer the case.

21 Days of Docker-Day 11- Docker Networking – Part 3

Welcome to Day 11 of 21 Days of Docker. So far I discussed the bridge driver and user-defined bridge driver.

As I mentioned earlier

  • Docker networking subsystem is pluggable with the help of drivers and there are several drivers available by default which provides core networking functionality
* bridge(default)
* host
* overlay
* none
* Network Plugins

The Host Network Driver

The Host Network Driver allows containers to use the host’s network stack directly. It removes the network isolation between the docker host and the docker containers to use the hosts networking directly.

Limitation

  • No two containers can use the same port(s).

Use Cases

  • Simple and easy setup, one or only a few containers on a single host.
$ docker container run -dt --name mynewhost1 --network host ubuntu bash
efb9e8f5615ad0a7929ca47edece524ac9780dfdda08f5656136d31446aaa009
  • Let’s login to the container and run the below command
$ docker exec -it efb9e8f5615a bash
 # Update and install few packages
# apt-get update && apt-get install net-tools && apt-get install nginx
 # Start the nginx daemon
# /etc/init.d/nginx start
 * Starting nginx nginx 
  • If you run the ifconfig command, you will see all the interface that is exposed to host is now exposed to container
# ifconfig
 br-fbc7613a2354: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
 inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
 inet6 fe80::42:5cff:fe4a:922b  prefixlen 64  scopeid 0x20<link>
 ether 02:42:5c:4a:92:2b  txqueuelen 0  (Ethernet)
 RX packets 1  bytes 28 (28.0 B)
 RX errors 0  dropped 0  overruns 0  frame 0
 TX packets 13  bytes 1816 (1.8 KB)
 TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
 inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
 inet6 fe80::42:bfff:fe91:bda1  prefixlen 64  scopeid 0x20<link>
 ether 02:42:bf:91:bd:a1  txqueuelen 0  (Ethernet)
 RX packets 11454  bytes 616831 (616.8 KB)
 RX errors 0  dropped 0  overruns 0  frame 0
 TX packets 15269  bytes 50064563 (50.0 MB)
 TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 ens5: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 9001
 
inet 172.31.102.118  netmask 255.255.240.0  broadcast 172.31.111.255
 inet6 2600:1f18:502:2f01:8059:3ad8:b2d0:8938  prefixlen 128  scopeid 0x0<global>
 inet6 fe80::816:6fff:feeb:343e  prefixlen 64  scopeid 0x20<link>
 ether 0a:16:6f:eb:34:3e  txqueuelen 1000  (Ethernet)
 RX packets 109967  bytes 143890223 (143.8 MB)
 RX errors 0  dropped 0  overruns 0  frame 0
 TX packets 49470  bytes 5648183 (5.6 MB)
 TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
 inet 127.0.0.1  netmask 255.0.0.0
 inet6 ::1  prefixlen 128  scopeid 0x10<host>
 loop  txqueuelen 1000  (Local Loopback)
 RX packets 38  bytes 3424 (3.4 KB)
 RX errors 0  dropped 0  overruns 0  frame 0
 TX packets 38  bytes 3424 (3.4 KB)
 TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
veth6182832: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
 inet6 fe80::b49f:95ff:fea2:aa11  prefixlen 64  scopeid 0x20<link>
 ether b6:9f:95:a2:aa:11  txqueuelen 0  (Ethernet)
 RX packets 4  bytes 280 (280.0 B)
 RX errors 0  dropped 0  overruns 0  frame 0
 TX packets 9  bytes 726 (726.0 B)
 TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0 
  • Go back to the host(not container)and try to access Nginx
$ curl localhost:80

 <!DOCTYPE html>
 <html>
 <head>
 <title>Welcome to nginx!</title>
 <style>
 body {
 width: 35em;
 margin: 0 auto;
 font-family: Tahoma, Verdana, Arial, sans-serif;
 }
 </style>
 </head>
 <body>
 <h1>Welcome to nginx!</h1>
 <p>If you see this page, the nginx web server is successfully installed and
 working. Further configuration is required.</p>
 <p>For online documentation and support please refer to
 <a href="http://nginx.org/">nginx.org</a>.<br/>
 Commercial support is available at
 <a href="http://nginx.com/">nginx.com</a>.</p>
 <p><em>Thank you for using nginx.</em></p>
 </body>
 </html> 

21 Days of Docker-Day 10- Docker Networking – Part 2

Welcome to Day 10 of 21 Days of Docker. On Day 9, I discussed the basics of Docker Networking and we use the default bridge network.

  • Let’s take one step further and now move to User-defined bridge networks are superior to the default bridge network.
  • To create a user-defined bridge
$ docker network create my-network
42ed6f1adc7a9b4ec687e99a1cbbdde8a1a3d1f86bef5e5376e121b29c8f760b
  • To verify the bridge network
$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
45decc7a84de        bridge              bridge              local
18ab553b68ea        host                host                local
42ed6f1adc7a        my-network          bridge              local
b49ee4b5f50f        none                null                local
  • To get detailed info about the network we just created
$ docker network inspect my-network
[
    {
        "Name": "my-network",
        "Id": "42ed6f1adc7a9b4ec687e99a1cbbdde8a1a3d1f86bef5e5376e121b29c8f760b",
        "Created": "2019-10-15T22:24:36.801033922Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

Connect a container to a user-defined bridge

  • When we create a new container, you can specify one or more --network flags
$ docker container run --name mycustomnetcontainer --network my-network -dt centos bash
a7b364c933b636bd1e0b454fcc7d1ab810dc60105716e1a0e1984a50486a6c20
  • If we run the network inspect again
$ docker network inspect my-network
[
    {
        "Name": "my-network",
        "Id": "42ed6f1adc7a9b4ec687e99a1cbbdde8a1a3d1f86bef5e5376e121b29c8f760b",
        "Created": "2019-10-15T22:24:36.801033922Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "a7b364c933b636bd1e0b454fcc7d1ab810dc60105716e1a0e1984a50486a6c20": {
                "Name": "mycustomnetcontainer", <--------------------------------
                "EndpointID": "5e0fe7027848591ce9c2305c2428b3a67f45f143976811f4419c2600f04fe63e",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
  • If we want we can connect any existing network container on the fly to this network
$ docker network connect my-network mycentosnewcont
  • If we run docker inspect again, you will see one more container
$ docker network inspect my-network
[
    {
        "Name": "my-network",
        "Id": "42ed6f1adc7a9b4ec687e99a1cbbdde8a1a3d1f86bef5e5376e121b29c8f760b",
        "Created": "2019-10-15T22:24:36.801033922Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "402332f2de72b214077b6312bef8b891a6564fae0f2e386032fb0abff736b29c": {
                "Name": "mycentosnewcont",<------
                "EndpointID": "312d1d7fa00c5e638a5b7b23ee0734b01571df3601be64711288ebfad1b3d23d",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "a7b364c933b636bd1e0b454fcc7d1ab810dc60105716e1a0e1984a50486a6c20": {
                "Name": "mycustomnetcontainer",
                "EndpointID": "5e0fe7027848591ce9c2305c2428b3a67f45f143976811f4419c2600f04fe63e",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

21 Days of Docker-Day 9- Docker Networking – Part 1

Welcome to Day 9 of 21 Days of Docker, so far I discussed all the Docker basics, Building container, Images and your own customized image using Docker. Let shift gears and focus on networking.

  • Before going deeper into Docker Networking, have you ever think, how docker container talks to the internet or any user on the internet can talk back to these Docker containers
  • Let’ start with the absolute basics, when you install Docker on your system, did you notice that docker added a new interface in your system called docker0.
# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
ether 02:42:c8:e7:30:24  txqueuelen 0  (Ethernet)
RX packets 0  bytes 0 (0.0 B)
RX errors 0  dropped 0  overruns 0  frame 0
TX packets 0  bytes 0 (0.0 B)
TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  • docker0 interface is the bridge device for Docker.  If you don’t specify a different network when starting a container, the container is connected to the bridge and all traffic coming from and going to the container flows over the bridge to the Docker daemon, which handles routing on behalf of the container.
  • If you want more information about Bridge network, run docker inspect
$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "f5c120ec2b2d3838e797fe3d5ed3e7234e5aa35567ac0a12ba912b2b9acb4db1",
        "Created": "2019-10-15T15:02:40.83080346Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16", <-----------------
                    "Gateway": "172.17.0.1"    <----------------- 
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "51cf7e39240929c8748ba7d2f82424b6e79c18cd9a966fe6242fcf90fba73fc6": {
                "Name": "centosserv",
                "EndpointID": "8586b322c1f4e818590ab55a381d9a3d10d7588b6e24edfc31d0c589a6560187",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]
  • As you can see this network picks the subnet range of Subnet”: “172.17.0.0/16 and Gateway”: “172.17.0.1” which is Docker0 bridge interface IP.
  • Containers which are connected to the default bridge are allocated IP addresses within this range
  • Try to spin one container and check its IP
$ docker container run -d centos:7
  • To check its IP
$ docker container inspect 51cf7e392409 |grep IPAddress
            "IPAddress": "172.17.0.2",
  • As you can see IP(172.17.0.2) is from the same subnet range as from docker0.

21 Days of Docker-Day 8-Docker Images, Layers & Containers

Welcome to Day 8 of 21 days of Docker, let’s try to dig deeper into images and layers.

A Docker image is built up from a series of layers. Each layer represents an instruction in the image’s Dockerfile. Each layer except the very last one is read-only.

Consider the following Dockerfile

FROM ubuntu:15.04
RUN apt-get -y install nginx
COPY index.html /var/www/html
CMD ["nginx","-g","daemon off;"]
  • This Dockerfile contains four commands each of which creates a layer
FROM: statements starts out by creating a layer from the ubuntu 15.04 image
RUN: Command install nginx
COPY: add some files to nginx www directory
CMD: command specify which command to run within the container 

Some points to keep in mind

  • Each layer is only a layer of differences from the layer before it.
  • The layers are stacked on top of each other
  • When you create a new container, you add a new writable layer on top of the underlying layers. This layer is often called the “container layer”
  • All changes made to the running container, such as writing new files, modifying existing files, and deleting files, are written to this thin writable container layer
  • If you want to check the layer of the image
$ docker image history nginx
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
f949e7d76d63        2 weeks ago         /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon…   0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  STOPSIGNAL SIGTERM           0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  EXPOSE 80                    0B                  
<missing>           2 weeks ago         /bin/sh -c ln -sf /dev/stdout /var/log/nginx…   0B                  
<missing>           2 weeks ago         /bin/sh -c set -x     && addgroup --system -…   56.8MB              
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV PKG_RELEASE=1~buster     0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV NJS_VERSION=0.3.5        0B                  
<missing>           2 weeks ago         /bin/sh -c #(nop)  ENV NGINX_VERSION=1.17.4     0B                  
<missing>           4 weeks ago         /bin/sh -c #(nop)  LABEL maintainer=NGINX Do…   0B                  
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B                  
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:1901172d265456090…   69.2MB              

storage driver handles the details about the way these layers interact with each other.

Different storage drivers are available(eg: overlay2, devicemappers, aufs), which have advantages and disadvantages in different situations.

Difference between Image and Container

  • To sum up, what we have discussed so far, the major difference between a container and an image is the top writable layer.
  • All writes to the container that adds new or modifies existing data are stored in this writable layer.
  • When the container is deleted, the writable layer is also deleted.
  • The underlying image remains unchanged.
  • Because each container has its own writable container layer, and all changes are stored in this container layer, multiple containers can share access to the same underlying image and yet have their own data state(as shown in the above image)

Container size on Disk

  • To view the container size on disk
$ docker container ps -s
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES                   SIZE
424c8c51e49a        centos:centos7      "bash"                   4 hours ago         Exited (137) 3 hours ago                        objective_feynman       0B (virtual 202MB)
e2452dcdc6e1        centos:centos7      "bash"                   4 hours ago         Exited (137) 3 hours ago                        jovial_murdock          0B (virtual 202MB)
b4031e7df866        centos:centos7      "bash"                   4 hours ago         Exited (0) 4 hours ago                          charming_herschel       0B (virtual 202MB)
ce32f774053a        centos              "bash"                   5 hours ago         Exited (0) 4 hours ago                          quirky_cori             37B (virtual 220MB)
24756038f07a        busybox             "sh"                     5 hours ago         Exited (137) 3 hours ago                        mynonenetwork1          10B (virtual 1.22MB)
418657ff1fe4        ubuntu              "bash"                   5 hours ago         Exited (0) 3 hours ago                          mynonenetwork           60B (virtual 64.2MB)

* Containers, Images, Local Volume and Cache Size

$ docker system df
TYPE                TOTAL               ACTIVE              SIZE                RECLAIMABLE
Images              16                  8                   1.78GB              1.421GB (79%)
Containers          22                  0                   651MB               651MB (100%)
Local Volumes       0                   0                   0B                  0B
Build Cache         0                   0                   0B                  0B


OR if you are looking for more verbose output

$ docker system df -v
Images space usage:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE                SHARED SIZE         UNIQUE SIZE         CONTAINERS
multistage          latest              b536984e0b37        19 hours ago        7.586MB             5.577MB             2.009MB             1
single-stage        latest              3b5798ed94c5        19 hours ago        813.8MB             802.6MB             11.24MB             1
mycentosnewapache   latest              6d082f2b1c5e        22 hours ago        449.6MB             449.6MB             0B                  0
mycentosapache      latest              03f61d93b53e        22 hours ago        449.6MB             449.6MB             0B                  2
mycustomnginx1      latest              fe85db4f4704        44 hours ago        162.4MB             162.4MB             22B                 0
mycustomnginx       latest              35d8f0969f7c        44 hours ago        162.4MB             162.4MB             22B                 0
cmdimage            latest              9888f3568e1f        2 days ago          1.22MB              1.22MB              0B                  0
healthcheck_image   latest              ca8599a83d24        2 days ago          1.22MB              1.22MB              0B                  0
ping_docker         latest              6cb5dc1118a0        5 days ago          1.22MB              1.22MB              0B                  0
centos              latest              0f3e07c0138f        9 days ago          219.5MB             0B                  219.5MB             5
golang              1.13.1              52b59e9ead8e        2 weeks ago         802.6MB             802.6MB             0B                  0
nginx               latest              f949e7d76d63        2 weeks ago         126MB               0B                  126MB               2
ubuntu              bionic              2ca708c1c9cc        3 weeks ago         64.18MB             64.18MB             0B                  3
busybox             latest              19485c79a9bb        5 weeks ago         1.22MB              1.22MB              0B                  3
centos              centos7             67fa590cfc1c        7 weeks ago         201.9MB             201.9MB             0B                  4
alpine              3.10.2              961769676411        7 weeks ago         5.577MB             5.577MB             0B                  0

Containers space usage:

CONTAINER ID        IMAGE               COMMAND                  LOCAL VOLUMES       SIZE                CREATED             STATUS                      NAMES
424c8c51e49a        centos:centos7      "bash"                   0                   0B                  5 hours ago         Exited (137) 3 hours ago    objective_feynman
e2452dcdc6e1        centos:centos7      "bash"                   0                   0B                  5 hours ago         Exited (137) 3 hours ago    jovial_murdock
b4031e7df866        centos:centos7      "bash"                   0                   0B                  5 hours ago         Exited (0) 5 hours ago      charming_herschel
ce32f774053a        centos              "bash"                   0                   37B                 5 hours ago         Exited (0) 5 hours ago      quirky_cori
24756038f07a        busybox             "sh"                     0                   10B                 5 hours ago         Exited (137) 3 hours ago    mynonenetwork1
418657ff1fe4        ubuntu              "bash"                   0                   60B                 5 hours ago         Exited (0) 3 hours ago      mynonenetwork
efb9e8f5615a        ubuntu              "bash"                   0                   88.2MB              5 hours ago         Exited (0) 3 hours ago      mynewhost1
88d9f7bfa898        ubuntu              "bash"                   0                   88.1MB              6 hours ago         Exited (0) 3 hours ago      mynewhost
94059b00c326        centos              "bash"                   0                   44.7MB              6 hours ago         Exited (0) 3 hours ago      myhost
e0fa509a4a38        centos              "/bin/bash"              0                   53B                 6 hours ago         Exited (0) 3 hours ago      mycustombridge02
757f1a00d053        centos              "/bin/bash"              0                   0B                  6 hours ago         Exited (0) 3 hours ago      mycustombridge01
394b8e5255ed        busybox             "sh"                     0                   0B                  7 hours ago         Exited (137) 3 hours ago    bridge02
46ea63e383c8        busybox             "sh"                     0                   77B                 7 hours ago         Exited (137) 3 hours ago    bridge01
dac9b0fca295        multistage          "./helloworld"           0                   0B                  19 hours ago        Exited (0) 19 hours ago     laughing_edison
3fd2abfc0e0f        single-stage        "./helloworld"           0                   0B                  19 hours ago        Exited (0) 19 hours ago     beautiful_torvalds
993f69137ed7        mycentosapache      "/usr/sbin/apachectl…"   0                   1.18kB              22 hours ago        Exited (137) 13 hours ago   mycentoscustomapache1
c04a61a61e4f        mycentosapache      "/usr/sbin/apachectl…"   0                   5.1kB               22 hours ago        Exited (137) 13 hours ago   mycentoscustomapache
c9c79d7b72cd        8f2ae044a233        "/bin/sh -c 'yum -y …"   0                   215MB               22 hours ago        Exited (1) 22 hours ago     mystifying_darwin
35355550ab59        centos:centos7      "/bin/bash"              0                   215MB               22 hours ago        Exited (137) 13 hours ago   mycentostest
733c8448e2c8        centos              "bash"                   0                   43B                 25 hours ago        Exited (0) 13 hours ago     mycentos
789f45a23f9f        nginx               "nginx -g 'daemon of…"   0                   15B                 29 hours ago        Exited (0) 13 hours ago     mynginxnew
b4db78c85559        nginx               "nginx -g 'daemon of…"   0                   0B                  29 hours ago        Exited (0) 29 hours ago     mynginx

Local Volumes space usage:

VOLUME NAME         LINKS               SIZE

Build cache usage: 0B

CACHE ID            CACHE TYPE          SIZE                CREATED             LAST USED           USAGE               SHARED

The Copy-on-Write Mechanism

When we launch an image, the Docker engine does not make a full copy of the already stored image. Instead, it uses something called the copy-on-write mechanism. This is a standard UNIX pattern that provides a single shared copy of some data until the data is modified.

To do this, changes between the image and the running container are tracked. Just before any write operation is performed in the running container, a copy of the file that would be modified is placed on the writeable layer of the container, and that is where the write operation takes place. Hence the name, “copy-on-write”.

If this wasn’t happening, each time you launched an image, a full copy of the filesystem would have to be made. This would add time to the startup process and would end up using a lot of disk space.

Let’s take a simple example, to understand how Image Layer works,below is my dockerfile

FROM busybox
RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50
RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50
  • Try to build an image out of this Dockerfile
$ docker build -t mylayer1 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 19485c79a9bb
Step 2/3 : RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50
 ---> Running in 2495248f03df
50+0 records in
50+0 records out
52428800 bytes (50.0MB) copied, 0.030272 seconds, 1.6GB/s
Removing intermediate container 2495248f03df
 ---> 4c557085e440
Step 3/3 : RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50
 ---> Running in aca8401f91cc
50+0 records in
50+0 records out
52428800 bytes (50.0MB) copied, 0.029855 seconds, 1.6GB/s
Removing intermediate container aca8401f91cc
 ---> bfbfff240e48
Successfully built bfbfff240e48
Successfully tagged mylayer1:latest
  • Check the image size
$  docker build -t mylayer1 .
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
mylayer1              latest              bfbfff240e48        4 seconds ago       106MB
  • Let’s make little modification to the Dockefile by trying to remove these two files
FROM busybox
RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50
RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50
RUN rm -rf /tmp/test1
RUN rm -rf /tmp/test2
  • Try to build the image again
$ docker build -t mylayer2 .
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM busybox
 ---> 19485c79a9bb
Step 2/5 : RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50
 ---> Using cache
 ---> 4c557085e440
Step 3/5 : RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50
 ---> Using cache
 ---> bfbfff240e48
Step 4/5 : RUN rm -rf /tmp/test1
 ---> Running in 9059f97edae0
Removing intermediate container 9059f97edae0
 ---> 3987aea24cc7
Step 5/5 : RUN rm -rf /tmp/test2
 ---> Running in 9dde88d940ee
Removing intermediate container 9dde88d940ee
 ---> fd617a5810fa
Successfully built fd617a5810fa
Successfully tagged mylayer2:latest
  • Check the image size
$ docker image ls
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
mylayer2              latest              fd617a5810fa        4 seconds ago       106MB
  • It’s the same, this doesn’t look, right? As we introduce two more new layer
RUN rm -rf /tmp/test1
RUN rm -rf /tmp/test2
  • As I mentioned above, image is just the collection of layer, and we are deleting test1 and test2 at different layer and that is the reason the size of the image is the same
  • Let’s move those remove instruction in the same layer
FROM busybox
RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50 && rm -rf /tmp/test1
RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50 && rm -rf /tmp/test2
  • Re-build the image
$ docker build -t mylayer3 .
Sending build context to Docker daemon  2.048kB
Step 1/3 : FROM busybox
 ---> 19485c79a9bb
Step 2/3 : RUN dd if=/dev/zero of=/tmp/test1 bs=1M count=50 && rm -rf /tmp/test1
 ---> Running in 58ecae5b4282
50+0 records in
50+0 records out
52428800 bytes (50.0MB) copied, 0.034398 seconds, 1.4GB/s
Removing intermediate container 58ecae5b4282
 ---> 062fa630bd3d
Step 3/3 : RUN dd if=/dev/zero of=/tmp/test2 bs=1M count=50 && rm -rf /tmp/test2
 ---> Running in 493d7e59ea5b
50+0 records in
50+0 records out
52428800 bytes (50.0MB) copied, 0.029036 seconds, 1.7GB/s
Removing intermediate container 493d7e59ea5b
 ---> 13f26e00997f
Successfully built 13f26e00997f
Successfully tagged mylayer3:latest
  • Check the size
$ docker image ls
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
mylayer3              latest              13f26e00997f        7 seconds ago       1.22MB

Looks promising 🙂

What is Dangling Images?

Dangling images are layers that have no relationship to any tagged images. They no longer serve a purpose and consume disk space

Let take a scenario in which these none layers will be created?

  • Let pull apache image from the docker registry
$ docker image pull httpd
Using default tag: latest
latest: Pulling from library/httpd
b8f262c62ec6: Already exists 
2c31b9311798: Pull complete 
7422a3cdf4e3: Pull complete 
1919d4fbf9e1: Pull complete 
60812fa1ab4c: Pull complete 
Digest: sha256:39d7d9a3ab93c0ad68ee7ea237722ed1b0016ff6974d80581022a53ec1e58797
Status: Downloaded newer image for httpd:latest
docker.io/library/httpd:latest
  • Start the container
$ docker container run -d httpd
1b97fb9667c0cc373c426e1a8ac3df6703c9dc91d63b46e1a52f8cf3a499b935
  • Check if it’s running
$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
1b97fb9667c0        httpd               "httpd-foreground"       6 seconds ago       Up 4 seconds        80/tcp                 tender_kirch
  • Now let’s try to remove the image, associated with this container
$ docker image rm httpd
Error response from daemon: conflict: unable to remove repository reference "httpd" (must force) - container 1b97fb9667c0 is using its referenced image 19459a872194

As the container is using this image, we can’t delete it, so the only way we can delete this image by using -f flag

$ docker image rm -f httpd
Untagged: httpd:latest
Untagged: httpd@sha256:39d7d9a3ab93c0ad68ee7ea237722ed1b0016ff6974d80581022a53ec1e58797
  • Pay special attention to the output of the above command, it says untagged the image
  • Now if you try to list the image, used by the container, you will see the output like this and it creates the dangling image.
$ docker image ls |grep 19459a872194
<none>              <none>              19459a872194        3 weeks ago         154MB
  • Now the question is how to clean these kinds of images
  • First, stop and remove the container
$ docker container rm -f 1b97fb9667c0
1b97fb9667c0
  • Run docker image prune to clean dangling image
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:19459a87219415cc5751fb48e9aa37ea580b98eed1fe784e76c4bc3c9b3b0741
deleted: sha256:ebefc32f49862ee9f4f672b338f29aefe056971bcc88912ed87968f5f75b0ae7
deleted: sha256:fe3c18ca62ea3f45b69ad643869c6fd3ab567a5ad8207adf8d071d54c9152597
deleted: sha256:62d97f753807d11f7ab72f8ae1911ff22617c9134e3edec13bf909493edb025c
deleted: sha256:c4e27a02760c0a8711ac5e9f7e0fc7cffeb38b44b4041d0c6ded5087586727a8

Total reclaimed space: 84.62MB
  • Verify it
$ docker image ls |grep 19459a872194

NOTE: If you build an image without specifying -t (i.e docker build . ), the image will appear in the list of dangling images as it has no association with the tagged images.

How to create an image out of a container

  • Let’s take a scenario, where I created a new container out of busybox image
$ docker container run -dt --name mytestcont busybox
8945e3832ac3abee79250b8bb5288f62296f8430cce954f9a6db331e31e232c8
  • Logged into that container
$ docker container exec -it 8945e3832ac3 sh
/ #

# Create a new file
/ # touch abc.txt
/ #
  • To create a docker image out of this container, I need to use docker container commit command
  • The command will look like this
docker container commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
$ docker container commit -a "plakhera" -m "my docker busybox image from container" 8945e3832ac3 mytestbusyboximage
sha256:aca40c0caa3b8159d2350db6240f368f6e75b06bdc1c98af268858a8089a202c

  -a, --author string    Author (e.g., "John Hannibal Smith <[email protected]>")
  -m, --message string   Commit message	
  • To verify it, run the new container using this image
$ docker container run -it mytestbusyboximage sh
/ # ls -l
total 20
-rw-r--r--    1 root     root             0 Oct 11 05:46 abc.txt

NOTE: By default, the container being committed and its process will be paused while the image is committed.

This is a good place, to stop for Day 8

Please follow me with my Journey

This time to make learning more interactive, I am adding

  • Slack
  • Meetup

Please feel free to join this group.

Slack: 

https://100daysofdevops.slack.com/join/shared_invite/enQtNzg1MjUzMzQzMzgxLWM4Yjk0ZWJiMjY4ZWE3ODBjZjgyYTllZmUxNzFkNTgxZjQ4NDlmZjkzODAwNDczOTYwOTM2MzlhZDNkM2FkMDA

Meetup Group

If you are in the bay area, please join this meetup group https://www.meetup.com/100daysofdevops/

21 Days of Docker-Day 7-Use multi-stage builds with Dockerfile

Welcome to Day 7 of 21 days of Docker. So far I discussed the basics of Dockerfile and we build few images using those Dockerfile.

Multi-stage builds are a new feature requiring Docker 17.05 or higher on the daemon and client. Multistage builds are useful to anyone who has struggled to optimize Dockerfiles while keeping them easy to read and maintain.

The simplest way to understand multi-stage build is the Dockerfile with multiple from instruction.

As our infrastructure grows, One of the most challenging things about building images is keeping the image size down.

Each FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.

Let see with the help of example

  • Create a directory
mkdir single-stage
cd single-stage
  • This is how our source code looks like, it’s a simple go program which prints hello world
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
  • Let test it locally
$ go run helloworld.go
hello world
  • This is how my Dockerfile looks like
FROM golang:1.13.1
WORKDIR /tmp
COPY helloworld.go .
RUN GOOS=linux go build -a -installsuffix cgo -o helloworld .
CMD ["./helloworld"]
  • It’s self-explanatory, but basically I am trying to compile the helloworld and trying to create the helloworld binary
  • Let’s build the image
$ docker build -t single-stage .
Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM golang:1.13.1
1.13.1: Pulling from library/golang
4a56a430b2ba: Pull complete
4b5cacb629f5: Pull complete
14408c8d4f9a: Pull complete
ea67eaa7dd42: Pull complete
a2a2197e145e: Pull complete
c805dbe65d37: Pull complete
e3dbca29210b: Pull complete
Digest: sha256:68f8870ee1723cafd45bed29414fbaa151e9bf2bba369c8b4436c08a18907012
Status: Downloaded newer image for golang:1.13.1
---> 52b59e9ead8e
Step 2/5 : WORKDIR /tmp
---> Running in 3167df72fbc2
Removing intermediate container 3167df72fbc2
---> f23ad3b0175a
Step 3/5 : COPY helloworld.go .
---> 3d9633eb783b
Step 4/5 : RUN GOOS=linux go build -a -installsuffix cgo -o helloworld .
---> Running in 35c51c8f9326
Removing intermediate container 35c51c8f9326
---> 5c20d8f64a63
Step 5/5 : CMD ["./helloworld"]
---> Running in 1565b589585e
Removing intermediate container 1565b589585e
---> 3b5798ed94c5
Successfully built 3b5798ed94c5
Successfully tagged single-stage:latest
  • Check it
$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
single-stage        latest              3b5798ed94c5        6 seconds ago       814MB
  • Here is the bummer, just to execute this simple binary, I am creating a docker image of 814MB. This looks like a highly inefficient process.
  • If you look at the different layer of this image, we don’t require a bunch of things just to compile and execute the helloworld go binary

21 Days of Docker-Day 6- Introduction to Dockerfile – Part 2

Welcome to Day 6 of 21 Days of Docker. On Day 5, I mentioned some of the Dockerfile directives, let extend that concept further

USER: The USER instruction sets the user name (or UID) and optionally the user group (or GID) to use when running the image and for any RUNCMD and ENTRYPOINT instructions that follow it in the Dockerfile

WORKDIR : The WORKDIR instruction sets the working directory for any RUNCMDENTRYPOINTCOPY and ADD instructions that follow it in the Dockerfile.

FROM centos
RUN useradd -ms /bin/bash centos_user
USER centos_user
ADD index.html /home/centos_user
WORKDIR /home/centos_user
EXPOSE 80

User Directive is used to create a non-privileged user. Rather than using root, we can use a non-privileged user to configure and run an application.

Let’s put together, all the concept we learned so far, in the Dockerfile

FROM centos:centos7
ENV HTTPD_VERSION 2.4.6-90.el7.centos
RUN yum -y update && yum -y install -y httpd-$HTTPD_VERSION
WORKDIR /var/www/html/
ADD index.html ./
EXPOSE 80
CMD ["/usr/sbin/apachectl","-DFOREGROUND"]
HEALTHCHECK CMD curl localhost:80
  • Before building the new image, create the index.html file, as we are using the ADD directive.
  • Now try to build the new image
$ docker build -t myapache .
  • Start the container, using this image
$ docker run -d --name myapache -p 80:80 myapache 
edc76724bc7c970648e4dc5c293afffea28a92df3dcbee0d0ec693e150667731
  • Verify it
$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                            PORTS                NAMES
edc76724bc7c        myapache            "/usr/sbin/apachectl…"   6 seconds ago       Up 3 seconds (health: starting)   0.0.0.0:80->80/tcp   myapache
  • Test it
$ curl localhost:80
This is coming from Docker

21 Days of Docker-Day 5- Introduction to Dockerfile

Welcome to Day 5 of 21 Days of Docker. So far I discussed all the basic docker commands and if you are following my blogs at this stage you should know how to run a container.

To run a Docker container, we pulled images from the Docker registry, now let see how we can build our own image and that can be done with the help of Dockerfile.

A Dockerfile is a text document that contains all the commands/set of instructions a user could call on the command line to create an image. These instructions are called directives.

https://docs.docker.com/engine/reference/builder/

Format of Dockerfile

Here is the format of the Dockerfile:

# CommentINSTRUCTION arguments

Some list of instructions in the Dockerfile

  • ADD: The ADD instruction copies new files, directories or remote file URLs from <src> and adds them to the filesystem of the image at the path <dest>
  • COPY: The COPY instruction copies new files or directories from <src> and adds them to the filesystem of the container at the path <dest>.
  • ENV: The ENV instruction sets the environment variable to the value. This value will be in the environment for all subsequent instructions in the build stage and can be replaced inline in many as well.
  • EXPOSE : Documents which port(s) are intended to published when running a container
  • FROM: The FROM instruction initializes a new build stage and sets the Base Image for subsequent instructions. As such, a valid Dockerfile must start with a FROM instruction
  • LABEL: The LABEL instruction adds metadata to an image. A LABEL is a key-value pair. To include spaces within a LABEL value, use quotes and backslashes as you would in command-line parsing
  • STOPSIGNAL: The STOPSIGNAL instruction sets the system call signal that will be sent to the container to exit. This signal can be a valid unsigned number that matches a position in the kernel’s syscall table, for instance 9, or a signal name in the format SIGNAME, for instance SIGKILL
  • USER: The USER instruction sets the user name (or UID) and optionally the user group (or GID) to use when running the image and for any RUNCMD and ENTRYPOINT instructions that follow it in the Dockerfile
  • VOLUME: The VOLUME instruction creates a mount point with the specified name and marks it as holding externally mounted volumes from native host or other containers
  • WORKDIR : The WORKDIR instruction sets the working directory for any RUN, CMD, ENTRYPOINT, COPY and ADD instructions that follow it in the Dockerfile
  • STOPSIGNAL: The STOPSIGNAL instruction sets the system call signal that will be sent to the container to exit. This signal can be a valid unsigned number that matches a position in the kernel’s syscall table, for instance, 9, or a signal name in the format SIGNAME, for instance SIGKILL.
  • HEALTHCHECK: The HEALTHCHECK instruction tells Docker how to test a container to check that it is still working. This can detect cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server process is still running

A simple Dockerfile will look like this

* FROM: A Dockerfile must start with a FROM instruction. The FROM instruction specifies the Base Image from which you are building.
* RUN: The RUN instruction will execute any commands in a new layer on top of the current image and commit the results. The resulting committed image will be used for the next step in the Dockerfile
* CMD: The main purpose of a CMD is to provide defaults for an executing container
  • To build the image out from Dockerfile we can use the docker build command
* Step1: It download the ubuntu image
* Step2: It pull all the latest update
* Step3: Install the nginx binary
* Step4: Start the nginx daemon
  • To verify the new Nginx image, we just built
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mynginx             latest              43e91e210b23        4 minutes ago       152MB
  • To run the container
$ docker run -d -p 80:80 mynginx
7f3d9fb752632f5acaee04b8ce18fca0e2b3da2dc5615a62253b9beae002436a
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
7f3d9fb75263        mynginx             "nginx -g 'daemon of…"   3 seconds ago       Up 1 second         0.0.0.0:80->80/tcp   angry_ptolemy
  • To test it
$ curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

21 Days of Docker-Day 4- Docker Container Under The Hood

Welcome to Day 4, so far we learned about the basics of Docker, let’s move our discussion from 10,000ft overview to 1000ft.

But before we go there based on the last 3 days knowledge, can you please answer a few of my questions?

Q1: What is the Kernel version of your Docker container?

Q2: Why the first process inside the Docker container run as PID 1?

Q3: How much default memory is allocated to my Docker container?

Q4: Is there is any way to restrict how much amount of memory we can allocate to the container?

Q5: How does container get its IP or able to communicate to the external world?

These are the question which pops up in my mind when I first start exploring docker, you may not have a answer to these question if you are new to Docker but if you already start thinking in this direction you are heading in right direction 🙂

21 Days of Docker-Day 2 — First Docker Container

On Day 1, I gave you the basic introduction to Docker and how to install Docker, it’s time to create your first docker container

Type the below command to run your first docker container

docker container run hello-world
$ docker container run hello-world

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/engine/userguide/

Let see what happen behind the scene

  • As this is my first container, docker engine tried to find an image named hello-world.
  • But as we just get started, there is no such image stored locally
Unable to find image 'hello-world:latest' locally
  • Docker engine goes to DockerHub(For the time being think DockerHub as a GitHub for Docker containers), looking for this image named hello-world

https://hub.docker.com/

  • It finds the image, pulls it down and then runs it in a container.
  • Hello World only function is to output the text you see in the terminal, after which the container exits