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
- definitions
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
Runtime:
- starting
- Building os constructs:
- namespaces
- Cgroups
- Building os constructs:
- stopping
Runtime architecture:
- High-level: containerd
- Manages entire lifecycle of a container:
- Pulling images
- Creating network interfaces
- ==Managing lower-level runc instances==
- Manages entire lifecycle of a container:
- Low-level: runc
- Implements OCI
- Interface to underlying OS
- Every running container on a docker node has a runc instance managing it.
Docker daemon (dockerd):
- Exposing Docker remote API
- Managing images
- Managing volumes
- Managing networks
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:
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
root@7a0ef952bc76:/#
-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.
root@7a0ef952bc76:/# 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
root@7a0ef952bc76:/#
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
root@7a0ef952bc76:/#
` 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
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
- …
- Made from many specialized parts: that work together to make a car drive
- Docker Engine:
- Made from many specialized tools: that work together to create and run containers
- APIs
- Election drivers
- runtimes
- shims
- …
- Made from many specialized tools: that work together to create and run containers
intake manifolds:
throttle body:
Spark plugs:
exhaust manifold:
Shims: 垫片
The docker engine: components:
- Docker daemon
- containerd
- runc
- plugins:
- networking
- storage
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)
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:
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
- …
- Modular and optional functionality:
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.
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
- Communicate over a local IPC socket :
Setup
High level process:
- Configure a CA and certificates
- Create a CA
- Create and sign keys for the Daemon
- Create and sign keys for the Client
- Distribute keys
- Configure Docker to use TLS
- Configure daemon mode
- Configure client mode
CA: self-signed certs
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
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
- A VM templates:
- For developers:
- A class
- A container is instance of a class ?
- 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)
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.
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.
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": [
"ubuntu@sha256: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.
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
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 imagesdocker image ls
— lists all images stored in your docker host’s local image cachedocker image inspect <image>
docker manifest inspect <image>
docker buildx
docker image rm
7. Containers
docker container run <image> <app>
❯ docker container run -it ubuntu:latest /bin/bash
root@8854cc3ac067:/#
docker container stop
docker container start
docker container rm
Physical Server with VMs:
The Container mode:
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.
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/pluralsight-docker-ci@sha256: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"
}
}
]
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:
- Application code + dependencies
- Docker file:
- Describe the app
- Describe the dependencies
- Describe how to run the app
- Build -
docker image build
- Push
- Run
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 :
- Application and dependencies are referred as : build context
- Dockerfile should in the root directory of the build context
- 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
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 ofapk --update nodejs nodejs-npm
) apk
is the alpine package manager.- The commands creates new layer where nodes and nodes-npm installed.
- Run
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
- Copy build context: from
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)
*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:
- The period (.) at the end of the command tells Docker to use the shell’s current working directory as the build context.
- 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
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
— :
- 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
- Create new layers: (adds content)
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
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 imageRUN
:- Run cmd inside image
- Creates new layer
COPY
:- Copies build context into image
- Creates new layer
EXPOSE
expose a network portENTRYPOINT
default app to run...
- LABEL
- ENV
- ONBUILD
- HEALTHCHECK
- CMD
- …