Docker Deep Drive 2020

 

img

About the Book

How the book is organized book :

  • big picture
    • what: docker
    • why: containers
    • what:
      • cloud native
      • microservices
  • technichal
    • definitions
      • images
      • containers
      • orchestration
      • TLS
      • image signing
      • high-availability
      • backups
    • structure
      • TLDR: explain
      • Deep Dive:
        • how
        • examples
      • Commands: code

Part1: The big picture stuff

Containers

one server per app –> VMs– > containers

physical servers : expensive VMs : consues too much resources Containers : better

Note: A containerized app is an application running as a container.

Container runtime : the low level technology that pulls images and starts and stops containers

Containerd: is the small specialized part of Docker that dose the low-level tasks of starting and stopping containers.

Kubernets: is a higher-level platform than Docker.

Docker

Docker:

  • the Docker.Inc
  • the Docker technology

Docker:

  • Software
    • creates
    • manages
    • orchestrates

Docker as Technology:

  • The runtine
  • The daemon (a.k.a engine)
  • The orchestrator

image-20210115121705649

image-20210115122355333

Runtime:

  • starting
    • Building os constructs:
      • namespaces
      • Cgroups
  • stopping

Runtime architecture:

  • High-level: containerd
    • Manages entire lifecycle of a container:
      • Pulling images
      • Creating network interfaces
      • ==Managing lower-level runc instances==
  • Low-level: runc
    • Implements OCI
    • Interface to underlying OS
    • Every running container on a docker node has a runc instance managing it.

image-20210115122409917

Docker daemon (dockerd):

  • Exposing Docker remote API
  • Managing images
  • Managing volumes
  • Managing networks

image-20210115122728151

Kubernets & Docker Swarm = = Manager of Clusters of Nodes running Docker

OCI two specifications (standards):

  • The image-spec
  • The runtime-spec

Docker Desktop :

  • Single-engine docker environment:
    • Docker compose
    • [single-node Kubernets cluster]

Check docker info: docker version

->

Client: Docker Engine - Community
 Cloud integration: 1.0.4
 Version:           20.10.0
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        7287ab3
 Built:             Tue Dec  8 18:55:43 2020
 OS/Arch:           darwin/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.0
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       eeddea2
  Built:            Tue Dec  8 18:58:04 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker Desktop on Mac:

image-20210115124218370

Docker installation on Linux

sudo apt update && sudo apt upgrade -y
sudo apt install docker.io 
sudo docker --version
sudo docker version

->

Client:
 Version:           20.10.0+dfsg2
 API version:       1.41
 Go version:        go1.15.6
 Git commit:        7287ab3
 Built:             Mon Dec 14 12:39:22 2020
 OS/Arch:           linux/arm
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.0+dfsg2
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.15.6
  Git commit:       eeddea2
  Built:            Mon Dec 14 12:39:22 2020
  OS/Arch:          linux/arm
  Experimental:     false
 containerd:
  Version:          1.4.3~ds1
  GitCommit:        1.4.3~ds1-1
 runc:
  Version:          1.0.0~rc92+dfsg1
  GitCommit:        1.0.0~rc92+dfsg1-5
 docker-init:
  Version:          0.19.0
  GitCommit:

The big picture

  • The Ops perspective
    • Download an image
    • Start a new container
    • Log into the new container
    • Run a command inside of it
    • Destroy the container
  • The Dev perspective
    • Github app code clone
    • Inspect a Dockerfile
    • Containerize the app
    • Run it as a container

“Docker host” and “Docker node” : both refers to the system that running Docker on.

The Ops perspective

Docker install :

  • Docker client
  • Docker daemon (docker engine): implements:
    • runtime
    • API

In a default linux installation, the client talks to the daemon via a local IPC/Unix socket at /var/run/docker.sock.

Images: think as an object: contains:

  • Os filesystem
  • An application
  • All application dependencies

` docker image ls ` ->

REPOSITORY                    TAG       IMAGE ID       CREATED        SIZE
gcr.io/k8s-minikube/kicbase   v0.0.14   7ed8827b36a5   2 months ago   876MB
docker/getting-started        latest    67a3629d4d71   2 months ago   27.2MB

docker image pull ubuntu:latest ->

❯ docker image pull ubuntu:latest
latest: Pulling from library/ubuntu
da7391352a9b: Pull complete
14428a6d4bcd: Pull complete
2c2d948710f2: Pull complete
Digest: sha256:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
❯ docker images
REPOSITORY                    TAG       IMAGE ID       CREATED        SIZE
ubuntu                        latest    f643c72bc252   7 weeks ago    72.9MB
gcr.io/k8s-minikube/kicbase   v0.0.14   7ed8827b36a5   2 months ago   876MB
docker/getting-started

And image contains enough of an operation system, as well as the code and the dependencies to run whatever application it’s designed for.

Refer images:

  • names
  • IDs

Containers:

Run a docker container from the image using docker container run ->

docker container run  -it ubuntu:latest /bin/bash
❯ docker container run  -it ubuntu:latest /bin/bash
[email protected]:/#
  • -it flag switches your shell into the terminal of the container.
  • docker container run teaks the Docker daemon to start a new container.
  • Do that based on the ubuntu:image image

-it : interactive, attach the current sheet to the containers terminal.

[email protected]:/# ps -elf
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
4 S root         1     0  0  80   0 -  1028 -      07:53 pts/0    00:00:00 /bin/
4 R root        10     1  0  80   0 -  1475 -      07:57 pts/0    00:00:00 ps -e
[email protected]:/#

The linux container has only two process:

PID 1 : /bin/bash

PID 10 : ps -elf command that we typed earlier.

Ctrl-PQ to exit the container without terminating it.

docker container ls

❯ docker container ls
CONTAINER ID   IMAGE           COMMAND       CREATED         STATUS         PORTS     NAMES
7a0ef952bc76   ubuntu:latest   "/bin/bash"   7 minutes ago   Up 7 minutes             xenodochial_booth
 docker container exec -it 7a0ef952bc76 bash
[email protected]:/#

` docker container exec -it 7a0ef952bc76 bash`:

  • Run the command bash with in the docket container with ID of 7a0ef952bc76
  • Run it as interactive and attach the current terminal to it.

Syntax :

docker container exec <options> <container-name or container-id> <command/app>

Stop the container : docker container stop <>

Kill the container : docker container rm <>

❯ docker container stop xenodochial_booth
xenodochial_booth
❯ docker container ls -a
CONTAINER ID   IMAGE                                 COMMAND                  CREATED          STATUS                      PORTS                                                                                                      NAMES
7a0ef952bc76   ubuntu:latest                         "/bin/bash"              25 minutes ago   Exited (0) 36 seconds ago                                                                                                              xenodochial_booth
b147a265eb47   gcr.io/k8s-minikube/kicbase:v0.0.14   "/usr/local/bin/entr…"   5 weeks ago      Exited (255) 10 days ago    127.0.0.1:32771->22/tcp, 127.0.0.1:32770->2376/tcp, 127.0.0.1:32769->5000/tcp, 127.0.0.1:32768->8443/tcp   minikube
b4d6b62c3862   docker/getting-started                "/docker-entrypoint.…"   5 weeks ago      Exited (0) 5 weeks ago                                                                                                                 distracted_lewin
❯ docker container rm 7a0ef952bc76
7a0ef952bc76

The Devs Perspective

Containers are all about the apps.

https://github.com/nigelpoulton/psweb.git

cd psweb
❯ ls -l
total 40
-rw-r--r--  1 azat  admin  338 Jan 15 14:23 Dockerfile
-rw-r--r--  1 azat  admin  455 Jan 15 14:23 README.md
-rw-r--r--  1 azat  admin  341 Jan 15 14:23 app.js
-rw-r--r--  1 azat  admin  216 Jan 15 14:23 circle.yml
-rw-r--r--  1 azat  admin  403 Jan 15 14:23 package.json
drwxr-xr-x  4 azat  admin  128 Jan 15 14:23 test
drwxr-xr-x  3 azat  admin   96 Jan 15 14:23 views

Dockerfile: plain text document:

  • That tells the docker how to built and app and it’s dependencies into a docker image.]

cat dockerfile ->

# Test web-app to use with Pluralsight courses and Docker Deep Dive book
# Linux x64
FROM alpine

LABEL maintainer="[email protected]"

# Install Node and NPM
RUN apk add --update nodejs nodejs-npm

# Copy app to /src
COPY . /src

WORKDIR /src

# Install dependencies
RUN  npm install

EXPOSE 8080

ENTRYPOINT ["node", "./app.js"]

Dockerfile : usage: instructions on how to build the app into a Docker image.

Use the docker image build command to create a new image using the instructions in the Docker file.

 docker image build -t test:latest .
❯ docker image build -t test:latest .
Sending build context to Docker daemon  76.29kB
Step 1/8 : FROM alpine
latest: Pulling from library/alpine
596ba82af5aa: Pull complete 
Digest: sha256:d9a7354e3845ea8466bb00b22224d9116b183e594527fb5b6c3d30bc01a20378
Status: Downloaded newer image for alpine:latest
...

docker image ls ->

❯ docker image ls
REPOSITORY                    TAG       IMAGE ID       CREATED              SIZE
test                          latest    84f1abd58aab   About a minute ago   94.2MB
alpine                        latest    7731472c3f2a   6 hours ago          5.61MB
ubuntu                        latest    f643c72bc252   7 weeks ago          72.9MB
gcr.io/k8s-minikube/kicbase   v0.0.14   7ed8827b36a5   2 months ago         876MB
docker/getting-started        latest    67a3629d4d71   2 months ago         27.2MB

Run an container from the image and test the app.

❯ docker container run -d --name web1 --publish 8080:8080 test:latest
2143a72579facb80a41f4d182059b9ae49423564f60f40d44c2ae82505f877a8

image-20210115143520330

Part 2: The technical stuff

5. The Docker engine

To be a real master of anything, you need to understand what’s going on under the hood.

Docker: Technical Section: Three-tiered approach

  • The TLDR
  • The deep dive
  • The commands

Docker Engine - The TLDR

Docker engine is the core software that runs and manages containers.

Container — VMware VM

Docker Engine — ESXi

The docker engine is like a car engine:

  • Car engine:
    • Made from many specialized parts: that work together to make a car drive
      • Intake manifolds
      • Throttle body
      • Cylinders
      • Spark plugs
      • Exhaust manifolds
  • Docker Engine:
    • Made from many specialized tools: that work together to create and run containers
      • APIs
      • Election drivers
      • runtimes
      • shims

intake manifolds:

img

throttle body:

FULLCARTUNING

Spark plugs:

img

exhaust manifold:

img

Shims: 垫片

img

The docker engine: components:

  • Docker daemon
  • containerd
  • runc
  • plugins:
    • networking
    • storage

image-20210115150119851

Docker Engine - The Deep Dive

When the docker was first released, the docker engine had two major components:

  • The docker daemon

    • Docker client
    • Docker API
    • Container runtime
    • Image builds
  • LXC

    • namespaces
    • Control groups (cgroups)

image-20210115152809032

Libcontainer developed by Docker. Inc is a replacement for LXC because:

  • LXC is linux specific
  • libcontainer is platform-agnostic tool

Problems: with monolithic Docker daemon

  • Its hard to innovate on
  • It’s got slover
  • It wasn’t what the ecosystem wanted

->

SO:

Docker, Inc break apart the monolithic daemon and modularized: it.

  • Smaller specialized tools
  • Re-used by third parties

Modularized daemon:

image-20210115153359851

runc:
  • is the reference implementation of the OCI container-runtime-spec
  • Small , lightweight CLI wrapper for the libcontainer
  • Purpose: create containers
containerd: (read as container-dee)
  • Container execution logic
  • Manager container lifecycle:
    • start
    • stop
    • pause
    • rm
  • Later-added:
    • Modular and optional functionality:
      • Image pulls
      • volumes
      • networks
    • To make it easier to use in other projects:
      • Kubernets

Starting a new container (example)

docker container run --name ctr1 -it alpine:latest sh

The daemon communicates with containerd via a CRUD-style API over gRPC.

Despite it’s name containerd cannot actually create containers. It uses runc to do that. It converts the required Docker image into an OCI bundle and tells runc to use this to create a new container.

runc interfaces with the OS kernel to pull together all of the constructs necessary to create a container (namespaces, cgroups etc.).

The container process is started as a child-process of runc and as soon as it is started the runc will exit.

image-20210115160024837

How it is implemented on Linux

  • dockerd (the Docker daemon)
  • docker-containerd (containerd)
  • docker-containerd-shim (shim)
  • docker-runc (runc)

While stopping all the containers, functions still left in the docker daemon are:

  • Image management
  • Image builds
  • The REST API
  • authentication
  • security
  • Core networking
  • orchestration

Securing client and daemon communication

Docker implements a client-server model:

  • Client:
    • Implements CLI
    • Name: docker
  • Server (daemon):
    • Implements the functionality
    • Implements public-facing REST API
    • Name : dockerd
  • Communication Socket :
    • Communicate over a local IPC socket : /var/run/docker.sock

image-20210115161040710

Setup

image-20210115161215433

High level process:

  1. Configure a CA and certificates
  2. Create a CA
  3. Create and sign keys for the Daemon
  4. Create and sign keys for the Client
  5. Distribute keys
  6. Configure Docker to use TLS
  7. Configure daemon mode
  8. Configure client mode

CA: self-signed certs

image-20210115163025654

Docker has two TLS modes:

  • Daemon mode
    • Only to allow connections from clients holds valid certificate
  • Client mode
    • Only connect to daemons holds valid certificate

image-20210115163701385

6. Images

Docker images - The TLDR

A docker image is a unit of packaging that contains:

  • Application code
  • Application dependencies
  • OS constructs

Docker images === VM templates

A docker image is:

  • For VM admins:
    • A VM templates:
      • A VM template is stopped VM
      • A docker image is stopped container
  • For developers:
    • A class
      • A container is instance of a class ?

Docker images - The deep dive

Images are built-time constructs, whereas containers are run-time constructs. (Each built is different, each run is different)

image-20210115164708716

Images and containers

To Start one or more containers from a single image:

  • docker container run
  • docker service create
Images are usually small

A container — a single application.

  • Many images can ship without a shell.
  • Images don’t contain a kernel — all containers running on a docker host share access to the host’s kernel.

The official alpine linux docker image is about 5MB in size and is an extreme example of how small Docker images can be.

Pulling images
❯ docker image ls
REPOSITORY                    TAG       IMAGE ID       CREATED        SIZE
test                          latest    84f1abd58aab   3 hours ago    94.2MB
alpine                        latest    7731472c3f2a   9 hours ago    5.61MB
ubuntu                        latest    f643c72bc252   7 weeks ago    72.9MB
gcr.io/k8s-minikube/kicbase   v0.0.14   7ed8827b36a5   2 months ago   876MB
docker/getting-started        latest    67a3629d4d71   2 months ago   27.2MB
❯ docker image pull redis:latest | pbcopy
latest: Pulling from library/redis
a076a628af6f: Pulling fs layer
f40dd07fe7be: Pulling fs layer
ce21c8a3dbee: Pulling fs layer
ee99c35818f8: Pulling fs layer
56b9a72e68ff: Pulling fs layer
3f703e7f380f: Pulling fs layer
ee99c35818f8: Waiting
56b9a72e68ff: Waiting
3f703e7f380f: Waiting
f40dd07fe7be: Verifying Checksum
f40dd07fe7be: Download complete
ce21c8a3dbee: Verifying Checksum
ce21c8a3dbee: Download complete
a076a628af6f: Verifying Checksum
a076a628af6f: Download complete
56b9a72e68ff: Verifying Checksum
56b9a72e68ff: Download complete
ee99c35818f8: Verifying Checksum
ee99c35818f8: Download complete
a076a628af6f: Pull complete
f40dd07fe7be: Pull complete
ce21c8a3dbee: Pull complete
ee99c35818f8: Pull complete
3f703e7f380f: Verifying Checksum
3f703e7f380f: Download complete
56b9a72e68ff: Pull complete
3f703e7f380f: Pull complete
Digest: sha256:0f97c1c9daf5b69b93390ccbe8d3e2971617ec4801fd0882c72bf7cad3a13494
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest

❯ docker image pull alpine:latest | pbcopy
latest: Pulling from library/alpine
Digest: sha256:d9a7354e3845ea8466bb00b22224d9116b183e594527fb5b6c3d30bc01a20378
Status: Image is up to date for alpine:latest
docker.io/library/alpine:latest
Image registries

We store images in centralised places called image registries like Docker Hub.

image-20210115171054699

image-20210115171124124

Official and unofficial repositories

Official — by docker

Image naming and tagging

docker image pull <repository name>:<tag>

Examples:

docker image pull mongo:4.2.6

docker image pull alpine

docker image pull busybox:latest

Latest can be not the latest image built of something, for example , the most recent image in the alpine repository is usually tagged as edge, not the latest

docker image pull nigelpoulton/tu-demo:v2

This will pull the image tagged as ‘v2’

From the tu-demo repository within the ‘nigelpoulton’ namespace.

If you want to pull images from 3rd party registries (not Docker Hub), you need to prepend the repository name with the DNS name of the registry.

For example, the following command pulls the 3.1.5 image from the google-containers/git-sync repo on the Google Container Registry (gcr.io).

docker image pull gcr.io/google-containers/git-sync:3.1.5
Images with multiple tags
$ docker image pull -a nigelpoulton/tu-demo

$ docker image ls 
REPOSITORY TAG IMAGE ID CREATED SIZE 
nigelpoulton/tu-demo latest d5e1e48cf932 2 weeks ago 104MB 
nigelpoulton/tu-demo v2 d5e1e48cf932 2 weeks ago 104MB 
nigelpoulton/tu-demo v1 6852022de69d 2 weeks ago 104MB
Filtering the output of docker image ls

Use --filter

$ docker image ls --filter dangling=true

A dangling image is an image that is no longer tagged, and appears in listings as :.

$ docker image ls --filter=reference="*:latest"
$ docker image ls --format ""
$ docker image ls --format ": : "
Searching docker hub from the CLI
❯ docker search azatai
NAME      DESCRIPTION   STARS     OFFICIAL   AUTOMATED
❯ docker search docker
NAME                                    DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
alpine                                  A minimal Docker image based on Alpine Linux…   7060      [OK]
jenkins                                 Official Jenkins Docker image                   5028      [OK]
registry                                The Docker Registry 2.0 implementation for s…   3164      [OK]
docker search alpine --filter "is-official=true"
$ docker search alpine --filter "is-automated=true"
Images and layers

A Docker image is just a bunch of loosely-connected read-only layers, with each layer comprising one or more files.

image-20210115173121269

image-20210115173151395

image-20210115173201843

Another way to see the layers of an image is to inspect the image with the docker image inspect command.

[
    {
        "Id": "sha256:f643c72bc25212974c16f3348b3a898b1ec1eb13ec1539e10a103e6e217eb2f1",
        "RepoTags": [
            "ubuntu:latest"
        ],
        "RepoDigests": [
            "[email protected]:c95a8e48bf88e9849f3e0f723d9f49fa12c5a00cfc6e60d2bc99d87555295e4c"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2020-11-25T22:25:29.546718343Z",
        "Container": "0672cd8bc2236c666c647f97effe707c8f6ba9783da7e88763c8d46c69dc174a",
        "ContainerConfig": {
            "Hostname": "0672cd8bc223",
            "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) ",
                "CMD [\"/bin/bash\"]"
            ],
            "Image": "sha256:28e90b4e135b38b4dd5efd0045019a2c8bfb7e114383e3a4ae80b1ec0dcaaf79",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "DockerVersion": "19.03.12",
        "Author": "",
        "Config": {
            "Hostname": "",
            "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/bash"
            ],
            "Image": "sha256:28e90b4e135b38b4dd5efd0045019a2c8bfb7e114383e3a4ae80b1ec0dcaaf79",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 72898198,
        "VirtualSize": 72898198,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/8c51633d91e8558ae1755d41d845a5a1e05ff70d12cb3956fcb0a7b6dfb94794/diff:/var/lib/docker/overlay2/75c763eee252e6bb16b6e3d5e98eed515b9bb37f8d92297f783475b1d60ebff9/diff",
                "MergedDir": "/var/lib/docker/overlay2/0427262a9498538cb2fa892291e78b9b3c187a010da956c7ac42a09b437e92e8/merged",
                "UpperDir": "/var/lib/docker/overlay2/0427262a9498538cb2fa892291e78b9b3c187a010da956c7ac42a09b437e92e8/diff",
                "WorkDir": "/var/lib/docker/overlay2/0427262a9498538cb2fa892291e78b9b3c187a010da956c7ac42a09b437e92e8/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:bacd3af13903e13a43fe87b6944acd1ff21024132aad6e74b4452d984fb1a99a",
                "sha256:9069f84dbbe96d4c50a656a05bbe6b6892722b0d1116a8f7fd9d274f4e991bf6",
                "sha256:f6253634dc78da2f2e3bee9c8063593f880dc35d701307f30f65553e0f50c18c"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

The docker history command is another way of inspecting an image and seeing layer data.

image-20210115173603768

image-20210115173634668

image-20210115173705576

image-20210115173756067

Sharing image layers

Multiple images can, and do share layers.

These lines tell us that Docker is smart enough to recognize when it’s being asked to pull an image layer that it already has a local copy of.

Pulling images with digest

Docker 1.10 introduced a content addressable storage model. s part of this model, all images get a cryptographic content hash. For the purposes of this discussion, we’ll refer to this hash as the digest. As the digest is a hash of the contents of the image, it’s impossible to change the contents of the image without creating a new unique digest.

❯ docker image ls --digests alpine
REPOSITORY   TAG       DIGEST                                                                    IMAGE ID       CREATED       SIZE
alpine       latest    sha256:d9a7354e3845ea8466bb00b22224d9116b183e594527fb5b6c3d30bc01a20378   7731472c3f2a   9 hours ago   5.61MB
Images for multiple architecture

image-20210115174440030

You can create your own builds for different platforms and architectures with docker buildx and then use docker manifest create to create your own manifest lists.

Deleting images

docker image rm

if an image layer is shared by more than one image, that layer will not be deleted until all images that reference it have been deleted.

If the image you are trying to delete is in use by a running container you will not be able to delete it. Stop and delete any containers before trying the delete operation again.

Images - The Commands

  • docker image pull <repository>:<tag> — download images
  • docker image ls — lists all images stored in your docker host’s local image cache
  • docker image inspect <image>
  • docker manifest inspect <image>
  • docker buildx
  • docker image rm

7. Containers

image-20210116104015783

docker container run <image> <app>
❯ docker container run -it ubuntu:latest /bin/bash
[email protected]:/#

docker container stop

docker container start

docker container rm

Physical Server with VMs:

image-20210116104818972

The Container mode:

image-20210116104959876

Add linux user to Docker group

usermod -aG docker <user>

Killing the main process in the container will kill the container.

Ctrl-PQ to exit container without terminating its main process.

$ docker container exec -it 50949b614477 bash

Self-heal container restart policies:

  • always:
    • Always restarts a stopped container (except from docker stop command)
    • ==Restarts== when the docker ==daemon restarts==
  • unless-stopped
    • Restarts stopped container
    • ==Do not== restart if the docker ==daemon restarts==
  • on-failure:
    • Restarts the container if it exists with a non zero exit code.

Starting a web server example;

docker container run -d --name webserver -p 80:8080 nigelpoulton/pluralsight-docker-ci
❯ docker container run -d --name webserver -p 80:8080 nigelpoulton/pluralsight-docker-ci
Unable to find image 'nigelpoulton/pluralsight-docker-ci:latest' locally
latest: Pulling from nigelpoulton/pluralsight-docker-ci
729ec3a6ada3: Pull complete
f0a3eea3dca0: Pull complete
e07851c50ad6: Pull complete
f78e7cd1f8dc: Pull complete
7cad1fbd2f07: Pull complete
22835c51693f: Pull complete
Digest: sha256:61bc64850a5f2bfbc65967cc33feaae8a77c8b49379c55aaf05bb02dcee41451
Status: Downloaded newer image for nigelpoulton/pluralsight-docker-ci:latest
81ef7e1852864a6db03a85203d3d96a774781377d4fd9e992da58f2cc9f6039b
❯ docker container ls
CONTAINER ID   IMAGE                                COMMAND                  CREATED          STATUS          PORTS                  NAMES
81ef7e185286   nigelpoulton/pluralsight-docker-ci   "/bin/sh -c 'cd /src…"   38 seconds ago   Up 38 seconds   0.0.0.0:80->8080/tcp   webserver

Container started in the background because of the -d flag for run.

image-20210116112956097

The -p flag maps port 80 on the Docker host to port 8080 inside the container

Port mapping :<host-port>:<container-port>

When building a Docker image, you can embed an instruction that lists the default app for any containers that use the image. So that we do to have to indicate the starting app.

$ docker image inspect nigelpoulton/pluralsight-docker-ci
[
    {
        "Id": "sha256:dd7a37fe7c1e6f3b9bcd1c51cad0a54fde3f393ac458af3b009b2032978f599d",
        "RepoTags": [
            "nigelpoulton/pluralsight-docker-ci:latest"
        ],
        "RepoDigests": [
            "nigelpoulton/[email protected]:61bc64850a5f2bfbc65967cc33feaae8a77c8b49379c55aaf05bb02dcee41451"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2020-01-18T15:29:24.3067368Z",
        "Container": "5e6c8e135f3504d8cdbb3b0f4f7658018f7eafa99011bcb0252c34bad246844f",
        "ContainerConfig": {
            "Hostname": "5e6c8e135f35",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8080/tcp": {}
            },
            "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) ",
                "CMD [\"/bin/sh\" \"-c\" \"cd /src && node ./app.js\"]"
            ],
            "Image": "sha256:3eee35387b69036be84160c16d756c975ce6445f5460b19ada2c343d796a0a17",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "MAINTAINER": "[email protected]",
                "org.label-schema.build-date": "20190927",
                "org.label-schema.license": "GPLv2",
                "org.label-schema.name": "CentOS Base Image",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.vendor": "CentOS"
            }
        },
        "DockerVersion": "19.03.4",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8080/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "cd /src && node ./app.js"
            ],
            "Image": "sha256:3eee35387b69036be84160c16d756c975ce6445f5460b19ada2c343d796a0a17",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "MAINTAINER": "[email protected]",
                "org.label-schema.build-date": "20190927",
                "org.label-schema.license": "GPLv2",
                "org.label-schema.name": "CentOS Base Image",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.vendor": "CentOS"
            }
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 604213387,
        "VirtualSize": 604213387,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/d20811c6b1f104b483a35e44814a9910f3f20521bc08cfbdc43bba975e342881/diff:/var/lib/docker/overlay2/daf86657bce7375bc99e4d44c2b7ffd74a33b5e567f198acb741a2b8b7546405/diff:/var/lib/docker/overlay2/152e9a65b683dc0cd528b68e1bd22449ba0cb4e8b0504d52664bbf2ed4ce58a5/diff:/var/lib/docker/overlay2/26970b027c4d3a626dbb97345f6f01846e0ee56f324e886462c385b7b1a90a44/diff:/var/lib/docker/overlay2/1f331bbe6593bbf6b3b2b640a2de3f639eef2920a7867b7158a88106a04c7a91/diff",
                "MergedDir": "/var/lib/docker/overlay2/2a78505057833ce1ca4de01b67edc1d864d98bc35810f6a582b5892263125695/merged",
                "UpperDir": "/var/lib/docker/overlay2/2a78505057833ce1ca4de01b67edc1d864d98bc35810f6a582b5892263125695/diff",
                "WorkDir": "/var/lib/docker/overlay2/2a78505057833ce1ca4de01b67edc1d864d98bc35810f6a582b5892263125695/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:9e607bb861a7d58bece26dd2c02874beedd6a097c1b6eca5255d5eb0d2236983",
                "sha256:295c91644e82f1407550c700f1517e814dfa34512ee71ac82ccd4737ca44ea4d",
                "sha256:07ef3e9a214efe1d68365952f4376a7f8699ce9a5f8b6dc5788347759f334e8c",
                "sha256:ad1a639ad455b481e4723f3c546a623eac28c86ac961d8b173dab7507f62e122",
                "sha256:13dba83733f937ac8633ce7b6ebec222ec51d6bbe3f247cf4e652d67fe22c913",
                "sha256:35467005de8ad904fcc55d34fd5f6bcead2f8b9d97113aa4115130ee9dfa92d7"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

image-20210116113555436

The entries after Cmd show the command/app that the container will run unless you override it with a different one when you launch the container with docker container run

/bin/sh -c "cd /src && node ./app.js

sometimes the default app is listed as Entrypoint instead of Cmd.

Run the following command from the shell of your Docker host to delete all containers.

Delete all containers

docker container rm $(docker container ls -aq) -f

Containers- The Commands

  • docker container run
  • Ctrl-PQ
  • docker container ls
  • docker cntainer exec
  • docker container exec -it <container-name or container-id> <app>
  • docker container stop
  • docker container start
  • docker container rm
  • docker container inspect

8. Containerizing an app

Containerizing: simple to:

  • Built
  • Ship
  • Run

Process of containerizing:

  1. Application code + dependencies
  2. Docker file:
    1. Describe the app
    2. Describe the dependencies
    3. Describe how to run the app
  3. Build - docker image build
  4. Push
  5. Run

image-20210116121410759

Containerizing an app - The deep dive:

  • Containerizing a single container app
  • Moving to production with multi-stage builds
  • A few best practices.

Base application code :https://github.com/nigelpoulton/psweb.git

A docker file is the starting point for creating a container image; It describes an application and tells docket how to build it into an image.

Note :

  1. Application and dependencies are referred as : build context
  2. Dockerfile should in the root directory of the build context
  3. Dockerfile starts with a capital “D”

Dockerfile;

# Test web-app to use with Pluralsight courses and Docker Deep Dive book
# Linux x64
FROM alpine

LABEL maintainer="[email protected]"

# Install Node and NPM
RUN apk add --update nodejs nodejs-npm

# Copy app to /src
COPY . /src

WORKDIR /src

# Install dependencies
RUN  npm install

EXPOSE 8080

ENTRYPOINT ["node", "./app.js"]

Dockerfile describes the application and it’s dependencies in an easy-to-read format.

  • FROM — base layer of the image

image-20210116122237991

  • LABELS — :
    • key-value pairs
    • Adds meta data to an image
  • RUN apk add --update nodejs nodejs-npm:
    • Run apk from the upper layer (the base layer, which is the alpine) and add a new layer on top of that (the new layer is the result of apk --update nodejs nodejs-npm)
    • apk is the alpine package manager.
    • The commands creates new layer where nodes and nodes-npm installed.

image-20210116122736093

  • COPY . /src:
    • Copy build context: from . On the host To /src in the container.
      • Application code
      • dependencies
    • It creates new layer cause it changes the previous file system

image-20210116123021294

  • WORKDIR :
    • Sets the working directory inside the image file system
    • Dose not create new layer
  • RUN npm install:
    • Installs rpm dependencies after cd to WORKDIR
    • Creates new layer ((because we created some new files on the image file syste)

image-20210116125256548

  • *EXPOSE* 8080:
    • Application exposes a web service on TCP port 8080
    • Added to the image meta data NOT to the filesystem, so do not create new layer
  • *ENTRYPOINT* ["node", "./app.js"]:
    • Se the main application that the image should run
    • Added to the image metadata, not to the file system, so dose not create new layer

Build image

❯ docker image build -t web:latest .
Sending build context to Docker daemon  76.29kB
Step 1/8 : FROM alpine
 ---> 7731472c3f2a
Step 2/8 : LABEL maintainer="[email protected]"
 ---> Using cache
 ---> 5e571ce2094b
Step 3/8 : RUN apk add --update nodejs nodejs-npm
 ---> Using cache
 ---> 2ecb264be2ed
Step 4/8 : COPY . /src
 ---> Using cache
 ---> f8b7c88f1201
Step 5/8 : WORKDIR /src
 ---> Using cache
 ---> 99ff2a5a3a8a
Step 6/8 : RUN  npm install
 ---> Using cache
 ---> 255085aee1da
Step 7/8 : EXPOSE 8080
 ---> Using cache
 ---> 265230a881cc
Step 8/8 : ENTRYPOINT ["node", "./app.js"]
 ---> Using cache
 ---> 84f1abd58aab
Successfully built 84f1abd58aab
Successfully tagged web:latest

NOTE:

  1. The period (.) at the end of the command tells Docker to use the shell’s current working directory as the build context.
  2. Be sure to include the trailing period (.) and be sure to run the command from the psweb directory that contains the Dockerfile and application code.
❯ docker image ls
REPOSITORY                           TAG       IMAGE ID       CREATED         SIZE
test                                 latest    84f1abd58aab   22 hours ago    94.2MB
web                                  latest    84f1abd58aab   22 hours ago    94.2MB
alpine                               latest    7731472c3f2a   29 hours ago    5.61MB
redis                                latest    621ceef7494a   2 days ago      104MB
ubuntu                               latest    f643c72bc252   7 weeks ago     72.9MB
gcr.io/k8s-minikube/kicbase          v0.0.14   7ed8827b36a5   2 months ago    876MB
docker/getting-started               latest    67a3629d4d71   2 months ago    27.2MB
nigelpoulton/pluralsight-docker-ci   latest    dd7a37fe7c1e   12 months ago   604MB

Pushing images

❯ docker login
Authenticating with existing credentials...
Login Succeeded

Docker needs those informations when pushing an image:

  • Registry
    • Default: Docker Hub
  • Repository
  • Tag
docker image tag web:latest azataiot/web:latest
❯ docker image tag web:latest azataiot/web:latest
❯ docker image ls
REPOSITORY                           TAG       IMAGE ID       CREATED         SIZE
web                                  latest    84f1abd58aab   23 hours ago    94.2MB
azataiot/web                         latest    84f1abd58aab   23 hours ago    94.2MB
test                                 latest    84f1abd58aab   23 hours ago    94.2MB
alpine                               latest    7731472c3f2a   29 hours ago    5.61MB
redis                                latest    621ceef7494a   2 days ago      104MB
ubuntu                               latest    f643c72bc252   7 weeks ago     72.9MB
gcr.io/k8s-minikube/kicbase          v0.0.14   7ed8827b36a5   2 months ago    876MB
docker/getting-started               latest    67a3629d4d71   2 months ago    27.2MB
nigelpoulton/pluralsight-docker-ci   latest    dd7a37fe7c1e   12 months ago   604MB

` docker image tag `

docker image push azataiot/web:latest
❯ docker image push azataiot/web:latest
The push refers to repository [docker.io/azataiot/web]
eb01a154f934: Pushed
dea763df066f: Pushed
20c3efca740b: Pushed
c04d1437198b: Mounted from library/alpine
latest: digest: sha256:7fc68246b0c92326ef34c2932e00c92bcdb0ffb64d763fd6c9a4fd68e22038fe size: 1161

image-20210116130519280

RUN the app

❯ docker container run -d --name c1 -p 80:8080 web:latest
8b0ee12273444e6caba548fc945695a60632edd4c1c618b4d11ae4de5f954ae1
❯ docker container ls
CONTAINER ID   IMAGE        COMMAND           CREATED         STATUS         PORTS                  NAMES
8b0ee1227344   web:latest   "node ./app.js"   7 seconds ago   Up 7 seconds   0.0.0.0:80->8080/tcp   c1

image-20210116130904900

— :

  • The -d flag runs the app in background
  • -p 80:8080 flag mãos port 80 on the host to port 8080 inside the running container
❯ docker image history web:latest
IMAGE          CREATED        CREATED BY                                      SIZE      COMMENT
84f1abd58aab   23 hours ago   /bin/sh -c #(nop)  ENTRYPOINT ["node" "./app…   0B
265230a881cc   23 hours ago   /bin/sh -c #(nop)  EXPOSE 8080                  0B
255085aee1da   23 hours ago   /bin/sh -c npm install                          31.3MB
99ff2a5a3a8a   23 hours ago   /bin/sh -c #(nop) WORKDIR /src                  0B
f8b7c88f1201   23 hours ago   /bin/sh -c #(nop) COPY dir:1f22058f8683e1c97…   38.4kB
2ecb264be2ed   23 hours ago   /bin/sh -c apk add --update nodejs nodejs-npm   57.3MB
5e571ce2094b   23 hours ago   /bin/sh -c #(nop)  LABEL maintainer=nigelpou…   0B
7731472c3f2a   29 hours ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>      29 hours ago   /bin/sh -c #(nop) ADD file:edbe213ae0c825a5b…   5.61MB

— ;

  • docker image build command parses the Dockerfile one-line-at-a-time, starting from the top.
  • # are comments.
  • All non-comment lines are **INSTRUCTIONS ** :
    • Create new layers: (adds content)
      • FROM
      • RUN
      • COPY
    • Create new meta data: (adds descriptions for running the application or descriptions about how to build the application)
      • EXPOSE
      • WORKDIR
      • ENV
      • ENTRYPOINT

As the following snippet shows, the basic process is:

  • spin up a temporary container
  • run the Dockerfile instruction inside of that container
  • save the results as a new image layer
  • remove the temporary container.

Multi-stage building

Multi-stage builds have a single Dockerfile containing multiple FROM instructions. Each FROM instruction is a new build stage that can easily COPY artefacts from previous stages.

FROM node:latest AS storefront
WORKDIR /usr/src/atsea/app/react-app
COPY react-app .
RUN npm install
RUN npm run build

FROM maven:latest AS appserver
WORKDIR /usr/src/atsea
COPY pom.xml .
RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
COPY . .
RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests

FROM java:8-jdk-alpine
RUN adduser -Dh /home/gordon gordon
WORKDIR /static
COPY --from=storefront /usr/src/atsea/app/react-app/build/ .
WORKDIR /app
COPY --from=appserver /usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar .
ENTRYPOINT ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"]
CMD ["--spring.profiles.active=postgres"]

The first thing to note is that the Dockerfile has three FROM instructions. Each of these constitutes a distinct build stage. Internally, they’re numbered from the top starting at 0. However, we’ve also given each stage a friendly name. • Stage 0 is called storefront

• Stage 1 is called appserver

• Stage 2 is called production

Squashed images

image-20210116154603443

use no-install-recommends

apt install <pkg> -no-install-recommends

Containerizing an app - The commands

  • docker image build:
    • -t tag
    • -f specify the name and location of the Dockerfile
  • FROM base image
  • RUN:
    • Run cmd inside image
    • Creates new layer
  • COPY:
    • Copies build context into image
    • Creates new layer
  • EXPOSE expose a network port
  • ENTRYPOINT default app to run
  • ...
    • LABEL
    • ENV
    • ONBUILD
    • HEALTHCHECK
    • CMD