Docker | Docs | Hub | Wikipedia
Docker (Linux)
Docker for Windows (DFW)
Docker for Mac (DFM)
- Runs as Server (daemon) and Client (
docker
CLI and others) ... - Docker Client (Docker Engine) talks to the server
- Docker Server (Docker Daemon) talks to Image Cache (local) then Docker Hub (Image Store)
Install (
Docker.sh
Background
Docker predecessor was dotCloud, a Linux container technology startup.
Docker accesses the Linux kernel's virtualization features, either directly using the runC
/libcontainer
library, or indirectly using libvirt
, LXC or systemd-nspawn
.
containerd
(GitHub)
An open-source industry-standard container runtime. The core container runtime of the Docker Engine daemon (dockerd
). Uses runC
(and grpc
for comms) for spawning and running containers per OCI spec.
runC
/libcontainer
(GitHub)
A specification and runtime code; begot Open Container Initiative (OCI). A CLI tool for spawning and running containers per OCI specifications.
libcontainer
The original virtualization spec/runtime; merged with runC
. In effect, this is Docker's fork of LXC.
Architecture
- Container
Virtualized OS; an isolated area of an operating system (OS) with resource limitations applied. Leverages Linux kernel primitives (Namespaces & Control Groups); Namespaces for isolation; Control Groups (Cgroups) for resource limits. The Docker Engine ("The Daemon") handles those low-level primitives.
Union FS/Mount
Combines multiple directories into one that appears to contain their combined contents; allows immutable image layers to form a useful, mutable container during both build and operational runs. Though images (and each comprising layer thereof) are immutable, containers are modifiable by the Copy-on-Write (CoW) mechanism. The container stores changes on a R/W layer on top of the underlying (RO) image layers. Docker currently implements Union FS/Mount using various storage drivers (e.g., overlay2
), supporting xfs
, the underlying filesystem.
Namespaces
Carves up the OS into multiple virtual OS (on which the desired app runs). Unlike VMs, all Virtual OS (containers of a node) share the same, single host (node) OS. Linux Namespaces (per container):
- Process IS (
pid
)
- Network (
net
)
- Filesystem/mount (
mnt
); root FS
- Inter-proc comms (
ipc
); shared memory
- UTS (
uts
); hostname
- User (
user
); new; map host:container users
Docker Engine
Was a monolith; refactored per OCI standards/specs for both Image & Runtime. Is now entirely separate; modular design; can upgrade Engine while its containers are running.
Software
Server (dockerd
) a.k.a. Docker Engine a.k.a. The Daemon. The persistent host process (daemon) that manages containers and handles container objects; listens for requests sent via the Docker Engine API. Interaces with Linux kernel. "A self-sefficient runtime for containers."
Client (docker
) a.k.a. Docker Engine (CLI/Tool). The main CLI for Docker Engine; allows users to interact with Docker daemons.
Editions
- Docker CE
Docker Engine (CE)
- Community Edition (Free)
- Docker EE
- Enterprise Edition (Pay)
RBAC, scanning, Image promos, ...
- Docker Engine (EE)
- Docker Certified Infra
- Ops UI (Web GUI)
- "Universal Control Plane" (UCP)
It's a Docker app (Swarm Cluster)
- Secure Registry
- Docker Trusted Registry (DTR)
An on-prem registry.
Objects
Image
An image is a combination of a JSON Image Manifest file and individual layer files. A layer is a tarball of files built from (rootfs
) filesystem views; loosely coupled layers; includes the app, its dependencies, and a JSON manifest, all for the Docker runtime. A stack of Union fs layers, unified by a storage driver, per manifest instructions. Storage Driver: Overlay2
(@ docker system info
)
- Stateless and immutable; A read-only template. "
IMAGE ID
" references the SHA256
hash of the image the globally unique content-addressable identifier. This ID is an attack vector necessitating countermeasures.
- The container host provides the (Linux) kernel, so the image (tarball) can be a single file (binary), such as a carefully compiled Golang app; megabytes. At the other extreme, it can be a fully loaded Ubuntu distro, including an assortment of packages installed therein; gigabytes.
Container
A runtime instance of a Docker image; a process of the Docker runtime (daemon: dockerd
); created of the image, its execution environment, and a standardized set of instructions. A writable layer on top of the image.
- Designed to isolate and run a single userland process in a virutalized OS.
- The Docker runtime (daemon) handles the container's system calls to the host kernel.
Swarm
A cluster of one or more Docker Engines (nodes) running in swarm mode. A set of cooperating Docker Engine daemons that communicate through their API. A service allowing containers to be managed across multiple Docker Engine daemons. Docker tool commands (node
and swarm
) providing native clustering functionality; turns a group of Docker Engines into a single virtual Docker engine. Docker manages swarms using the Raft Consensus Algorithm.
- Swarm Mode
Can start/stop services, and modify them, while they remain online. A Swarm Manager (node) is specified (or elected thereafter) as the Leader of all Swarm Managers (Followers). The Swarm (consensus algorithm) requires a Quorum (N/2 +1) of managers agreeing on the state of the swarm. So, for example, having two managers instead of one doubles the chance of losing quorum; always keep an odd number of Swarm Managers.
- Activation, "
docker swarm init
", enables additional docker
tool commands: docker node
| service
| stack
| secret
- Swarm Data @
UDP/4789
- Swarm Commands @
TCP/2377
- Swarm Control Plane @
TCP/2376
(Never use this.)
- All communication between Docker Engines of the swarm occurs thereof. Nodes of a swarm may be scattered across Cloud vendors. Vendorless!
- Orchestration and its security is handled internally by Swarm Manager(s). There is no external database or authority involved in any of it.
- Each Manager posesses the complete Swarm state, for greater reliability and speed.
- Worker/Manager nodes can switch roles, per API.
- Node-to-Node Communication Protocols; presuming multi-node swarm, not single-node swarm mode.
- Swarm Managers communicate with each other securely using Raft protocol, which is strongly consistent, forming a self-sufficient Quorum; no external dependencies.
- Workers communicate with each other using Gossip protocol, which is eventually consistent, but very fast.
- Manager to Worker communication is across the control plane (
TCP/2376
) per gRPC protocol; binary data; uses HTTP/2 for transport; Protocol Buffers as its IDL.
Networking | Configure | Reference Architecture | Tutorials
Batteries included, but removable.
docker network ls
ip addr show
Container Network Drivers/Options
- Bridge Networking a.k.a. Single-host Networks
docker0
; original/default; Driver: bridge
Layer 2 network; isolated, even if on same host; routes through NAT firewall on host IP; external comms only by port mapping, host IP-to-IP. Containers connect to Docker bridge (docker0
) network by default.
docker run --name web -p 1234:80 nginx
docker port web
- Overlay Networking a.k.a. Multi-host Networks
Layer 2 network spanning multiple hosts, e.g., connects all containers across all nodes of the swarm.
docker network create ...
- Control Plane encrypted by default.
- Data Plane encrypted per cmdline option
docker network create --opt encrypted ...
- MACVLAN
Each container (MAC) given its own IP Address on an existing VLAN. Requires promiscuous mode on host NIC; typically not available @ cloud providers.
- IPVLAN
- Experimental; does not require promiscous mode.
- Containers on the same network communicate with each other sans port mapping (
-p
). External ports closed by default; put frontend/backend on same network for inter-container comms. Best practice is to create a new virtual network for each app. E.g.,
- Network
web_app_1
for mysql
and php
/apache
containers.
- Network
api_1
for mongo
and nodejs
containers.
- Containers can attach to more than one virtual network (or
none
).
- Network is selectable and configurable:
- "
--network none
" adds container to a container-specific network stack.
- "
--network host
" adds container to host’s network stack; to use host IP instead of virtual networks'.
List selected keys of "docker inspect ...
" across all networks,
refactored into another valid JSON object:
docker network ls -q |xargs docker network inspect $1 \
|jq -Mr '.[] | select(.Name != "none") | {Name: .Name, Driver: .Driver, Address: .IPAM.Config}' \
|jq --slurp .
Network Services
- DNS Server — Containers are ephemeral, making their IP addresses unstable/unreliable, so containers are identified by DNS name, not IP addresses. That is, container names are host names. Docker uses embedded DNS to provide service discovery for containers running on a single Docker Engine and tasks running in a Docker Swarm. Docker Engine has an internal DNS server that provides name resolution to all of the containers on the host in user-defined bridge, overlay, and MACVLAN networks. Each Docker container (or task in Swarm mode) has a DNS resolver that forwards DNS queries to Docker Engine, which acts as a DNS server. Docker Engine then checks if the DNS query belongs to a container or service on network(s) that the requesting container belongs to. If it does, then Docker Engine looks up the IP address that matches a container, task, or service's name in its key-value store and returns that IP or service Virtual IP (VIP) back to the requester. NOTE:
- Only non-default networks automatically run a DNS serer. The default network (
bridge
) does not, unless declared. For that, use docker run ... --link ...
to add container-to-container link, but it is easier and better to simply create a new network and use that.
- Service Discovery — Every service is named, and registered with Swarm DNS, so every service task (container) gets a DNS resolver that forwards lookups to that Swarm-based DNS service. Services of a swarm are reachable from any swarm node, even if node is not running the service.
Load Balancing — A benefit of Service Discovery is that every node in the Swarm knows about every service therein; any exposed port (-p HOST:CNTNR
) on any node of a service is replicated on all nodes in the Swarm, and so Swarm does load balancing of incomming traffic (Ingress load balancing), and DNS-based load balancing on internal traffic.
UCP Internal Load Balancing — Internal load balancing is instantiated automatically when Docker services are created. When services are created in a Docker Swarm cluster, they are automatically assigned a Virtual IP (VIP) that is part of the service's network. The VIP is returned when resolving the service's name. Traffic to that VIP is automatically sent to all healthy tasks of that service across the overlay network. This approach avoids any application-level load balancing because only a single IP is returned to the client. Docker takes care of routing and equally distributing the traffic across the healthy service tasks. To see the VIP:
# Create an overlay network called mynet
$ docker network create -d overlay mynet
a59umzkdj2r0ua7x8jxd84dhr
# Create myservice with 2 replicas as part of that network
$ docker service create --network mynet --name myservice --replicas 2 busybox ping localhost
8t5r8cr0f0h6k2c3k7ih4l6f5
# See the VIP that was created for that service
$ docker service inspect myservice
...
"VirtualIPs": [
{
"NetworkID": "a59umzkdj2r0ua7x8jxd84dhr",
"Addr": "10.0.0.3/24"
},
]
DNS Round Robin (RR) mode @ --endpoint-mode dnsrr
Test; RR is a kind of cheap load balancing; multiple hosts responding to same DNS name, per aliasing.
# run two aliased RR-test servers
docker run -d --net 'net1' --net-alias 'foo' elasticsearch:2
docker run -d --net 'net1' --net-alias 'foo' elasticsearch:2
# Get IP & DNS of the two per nslookup; delete cntnr upon exit (--rm)
docker run --rm --net net1 alpine nslookup 'foo' # Alpine image contains nslookup
Name: foo
Address 1: 172.20.0.2 foo.net1
Address 2: 172.20.0.3 foo.net1
# curl, repeatedly, to see random host select/response per Docker's RR scheme
docker run --rm --net 'net1' centos curl -s foo:9200
# CentOS image contains curl
elasticsearch:2
image chosen because it generates random host name for itself
alpine
image chosen because it contains nslookup
centos
image chosen because it contains curl
@ Swarm Mode | Control Plane
Docker supports IPSec encryption for overlay networks between Linux hosts out-of-the-box. The Swarm & UCP managed IPSec tunnels encrypt network traffic as it leaves the source container and decrypts it as it enters the destination container. This ensures that your application traffic is highly secure when it's in transit regardless of the underlying networks.
- Overlay;
--driver overlay
- Supports multi-host networks; container-to-container comms across all nodes of the swarm. All Tasks (containers) forming all Services running across all nodes (VMs) can access each other; uses a combination of local Linux bridges and VXLAN to overlay container-to-container comms over physical network infra.
- Docker Routing Mesh :: @ Docker CE (Swarm External L4 Load Balancing)
- Transport Layer (L4) Load Balancer (Stateless) Assigns Virtual IP addresses (VIPs) to swarm services, mapped to their DNS (name), and so handles the physical node routing. Load balances per Swarm Service, across their Tasks (containers).
- HTTP Routing Mesh (HRM) :: @ Docker EE only (UCP External L7 Load Balancing)
- Application Layer (L7) Load Balancer (Stateful) Load balances across services; all services can share same port..
@ Swarm Mode | Data Plane
Extend Docker's IPSec encryption to the data plane. (The control plane is automatically encrypted on overlay networks.) In a hybrid, multi-tenant, or multi-cloud environment, it is crucial to ensure data is secure as it traverses networks you might not have control over.
Enable data-plane encryption:
docker network create -d overlay --opt encrypted=true $_NTWK_NAME
At services thereunder, when two tasks are created on two different hosts, an IPsec tunnel is created between them and traffic gets encrypted as it leaves the source host and decrypted as it enters the destination host. The Swarm leader periodically regenerates a symmetrical key and distributes it securely to all cluster nodes. This key is used by IPsec to encrypt and decrypt data plane traffic. The encryption is implemented via IPSec in host-to-host transport mode using AES-GCM.
Tools
docker
(Ref); Docker Engine CLI. The main tool; handles image builds and container creations. Two modes: Single-host and swarm
.
docker-machine
(Ref) is a tool to create and manage VMs for multi-node Swarm(s). Creates Docker hosts (nodes) anywhere; local/cloud; OS/distro per driver; installs Docker thereon, and configures docker
CLI (per VM, per shell).
docker-compose
(Ref) is a combination of (dev) tool and YAML config file for defining and running multi-container Docker applications. docker-compose.yml
is the default filename, but can be any name.
docker-compose up
docker-compose down
docker-compose -f 'foo-manifest.yml'
- Manage all containers with one command
- Configure relationships between containers
- Run commands on multiple containers at once
- Set/save "
docker run
" settings in YAML
- Create one-liner startup command for a development environment.
- The tool itself (
docker-compose
) is used mainly for local test and development. In production, in Swarm mode (v1.13+
), the Docker Engine CLI tool (docker
) uses the YAML file (docker-compose.yml
) directly.
Images :: Docker Hub
The Explore tab lists all Official images (docker-library
)
[ACCTNAME/]REPONAME
REPONAME
The "official
" images are further distinguished by their REPONAME
sans "ACCTNAME/
" prefix. These are high quality images; well documented, versioned (per :TAG
), and widely adopted. E.g., …
# The official Nginx image; "1.11.9" is the Tag (version).
docker pull nginx:1.11.9
The ubiquitous "latest
" Tag specifies a latest (stable) published version of a repo; not necessarily the latest commit. E.g., …
docker pull nginx:latest
# ... equivalent ...
docker pull nginx
Tags / Tagging
ACCTNAME/REPONAME:TAG
The entirety, "USER/NAME:TAG
", is often referred to as "tag".
One image may have many tags. To change the image tag, and optionally rename an image, …
docker image tag SRC_IMG[:TAG-old] TGT_IMG[:TAG-new]
- Absent TAG, the Docker daemon defaults to search for "
latest
".
The TAG
of <none>
☩ di
REPOSITORY TAG IMAGE ID CREATED SIZE
gd9h/prj3.api-amd64 dev ce6b736c74c1 2 hours ago 31.2MB
gd9h/prj3.pwa-amd64 dev 414f405cee2c 19 hours ago 21.7MB
gd9h/prj3.rds-amd64 dev b1350eefb9b9 2 days ago 31.3MB
gd9h/prj3.exv-amd64 dev 96594cfee5fa 2 days ago 17.5MB
postgres <none> baf3b665e5d3 4 days ago 158MB
postgres 12.6-alpine c88a384583bb 3 weeks ago 158MB
golang 1.15.8 7185d074e387 4 weeks ago 839MB
nginx 1.19.3-alpine 4efb29ff172a 5 months ago 21.8MB
- The
<none>
tag (and image) is a Docker daemon pull per YAML declaration. Here, though the YAML declares image:
postgres:12.6-alpine
, a newer image of the same tag exists at Docker Hub repo, and so the Docker engine pulls that, tagging it <none>
, and builds the container therefrom. This is common at Swarm nodes, whereof we update images yet maintain the same tag (dev
).
- @ Postgres repo, the most recent three-dot version is
9.6.21-alpine
(13.2-alpine
is newest as of 2021-04-05). It is not known if they changed their versioning or if all newer are simply too immature (update too often).
Image Layers / Cache
Images are built of filesystem changes and metadata. The image build process implements the Union FS/Mount concept (per OverlayFS
); files and directories of separate file systems are transparently overlaid, forming a single coherent file system.
Each image layer is hashed and cached, and so can be incorporated into multiple images; successive layers are nothing but changes (diff) from the prior layer. This strategy accounts for the radically lighter weight of images relaitve to virtual machines (VMs). Changes to a container are recorded per Copy on Write (CoW) process.
Local image cache @ Docker Engine host:
/var/lib/docker/STRG_DRVR/DIGEST/{merged,diff,work}
- E.g.,
/var/lib/docker/overlay2/5adcbf...01a016/{merged,diff,work}
Docker references each layer, and each image containing them, by multiple unique identifiers (digests and other IDs), per Docker tool and context. Additionally, the image manifest file (JSON) itself is hashed, and that too is an image reference.
This is the source of much confusion, since the image digests reported on pull
or run
don't match those reported elsewhere; while "IMAGE ID
" at "docker image ls
" is something else entirely. And there is no easy way to match a cached layer (folders & files) to its Registry (layer digest).
Example: docker image inspect 'alpine'
Id
(The local reference)
- @
[{Id:...}]
"Id": "sha256:196d12...84d321"
RepoDigests
- @
[{"RepoDigests":"..."}]
"alpine@sha256:621c2f...af5528"
Config
digest
- @
[{"Config":{"Image":"..."}}]
"Image": "sha256:836dc9...0e59b7"
- Local image cache dir(s):
- @
[{"GraphDriver":{"Data":{"MergedDir":"...","UpperDir":"...","WorkDir":"..."}}}]
/var/lib/docker/overlay2/5adcbf...01a016/{merged,diff,work}
- The directories (digests) change per cache (
pull
)
Publishing
docker login
Stores auth key @ ~/.docker/config.json
, until …
docker logout
docker push ACCTNAME/REPONAME:TAG
Image Registry (v2)
The image resistry is integral to Docker's tools and its entire container ecosystem. Docker's Distribution toolset handles this; "… pack, ship, store, and deliver content."
- The storage system is based on Content-addressable Storage (CAS)
- Image reference format:
REGISTRY/REPO/IMAGE
- But this is actually an Image Manifest reference.
An "image" is but a sum of immutable Union filesystem (FS) layers, any one of which may be in many images; a one-to-many mapping.
- Each Docker image has two Registry references, necessarily. Both are unique identifiers (
sha256
, so hex64
):
- Content digest
The hash of the Image Manifest; a JSON file containing the hash of each layer. These are not the "IMAGE ID
" shown per "docker image ls
", nor such local/cache references.
- Distribution digest
The hash of a compressed image layer (tarball of its Union FS/Mount; folders and files). These are not the "image" references shown per "docker pull ...
", nor such Resistry references.
- No easy way to match layer ID with its location at host dir; digests which change per pull, … @
/var/lib/docker/STRG_DRVR/DIGEST/{merged,diff,work}
- Pull image per URL request:
GET /v2/NAME/manifests/{TAG|DIGEST}
Test for existence of image:
HEAD /v2/NAME/manifests/{TAG|DIGEST}
Test for existence of a layer:
HEAD /v2/NAME/blobs/DIGEST
- Two Docker Registries (Docker-maintained):
- Docker Hub
- Docker Trust Registry (DTR), on-prem, requiring Docker EE
Docker Registry
Build Process
image => container => image => container => ...
Images are immutable; containers are modified while running; new images are built of prior images and changes thereto while running. This is iterated as necessary …
- Declaratively, per
Dockerfile
.
Imperatively, per docker
CLI commands.
# Imperatively Dockerfile equivalent
docker run [OPTIONS] IMAGE # FROM (base image/layer)
docker exec [OPTIONS] CONTAINER CMD # CMD (add pkg, etc)
docker commit [OPTIONS] CONTAINER # FROM (this new layer)
- It can be argued that "imperatively" / "declaratively" is more lingo than actual; regardless, there's a mapping between
docker
CLI commands/flags and Dockerfile
statements.
Dockerfile
| Best Practices
A Dockerfile is the recipe for an image build; the instruction set; has its own language/syntax/format. Each "stanza", FROM
, ENV
, RUN
, … is an image layer; each layer is downloaded, hashed, and cached, so future builds (per mod) are fast, especially so if layered judiciously; order is important; frequently modified layers placed below (after) those infrequently modified; any content change (e.g., per COPY
) breaks the cache at that layer, and affects subsequent layers.
# Base; every image must have ...
FROM alpine:3.8
ENV NGINX_VERSION 1.13.6-1~stretch
# Chain places all at one cacheable layer;
# Remove unncecssary dependencies & pkg-mgr cache
RUN apt-get update \
&& apt-get -y install --no-install-recommends ... \
&& rm -rf /var/lib/apt/lists/*
# Docker handles logging; need only map output ...
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
# open ports 80 & 443 (to other containers @ bridge network)
EXPOSE 80 443
# ... map to host port(s) per run option ... -p|-P
# Change working directories; use instead of RUN cd ...
WORKDIR /usr/share/nginx/html
# Copy from host to container
COPY . .
# Copy dir to dir (from host to container)
# Any changes to the files being copied will break the cache,
# so copy ONLY WHAT IS NEEDED.
COPY index.html index.html
# Volume; outlives container; must manually delete; UNNAMED ONLY !
VOLUME /the/volume/path/at/container
# Must run command/script @ cntnr launch
# (may be embedded in FROM stmnt)
ENTRYPOINT ["entrypoint.sh"]
# then this; overridden on any arg @ `docker run ... IMAGE`
CMD ["command","param1","param2"]
# or default param(s) for ENTRYPOINT command or script
CMD ["param1","param2"]
# ... JSON Array syntax
Storage — Data in containers
Such data is destroyed upon container deletion (rm
); survives stop
/start
only. Files created inside a container are stored on a thin writable container layer on top of its read-only image layers.
- Difficult to access from outside the container.
- Requires a (kernel process) driver to manage the Union filesystem.
Storage Driver a.k.a. Graph Driver (older) a.k.a. Snapshotter (newer)
- A container driver that (unpacks and) maps the (pulled) Image layers to local host storage, handles the container's writable (CoW) layer. The R/W performance of these drivers is poor across all host (backing) filesystems and block storage devices. (Containers are best stateless.)
overlay2
for xfs
or ext4
host FS; an OverlayFS driver;
Docker's default container storage driver.
devicemapper
for direct-lvm
host block-storage device; depricated. is POSIX compliant and supports SELinux, but is slower.
btrfs
and zfs
for such host FS.
aufs
for Ubuntu 14.04 and older, and Docker 18.06 and older.
vfs
is experimental
Storage — Data Volumes
Separation of Concerns
Immutable design patterns treat containers as ephemeral, if not entirely stateless, and so persistent a.k.a. unique data best resides outside the container.
Data Volumes a.k.a. Volumes
Docker offers 3 options for persistent container storage, which is to say storage outside the container. All mount some kind of host (or remote) storage as a path at the container. Each has use cases..
Volumes
A Docker-managed object; storage at the host mounted as a path at the container; /var/lib/docker/volumes/NAME/_data
. At DfW/DfM, volumes are created at the Docker VM, not at the host. (SSH into Docker VM); can specify a volume declaratively, in Dockerfile
, or per docker
CLI, as a runtime parameter.
docker volume create $NAME
docker run ... -v $NAME:$CNTNR_PATH
... -v foo:/app
... --mount source=foo,target=/app
# ... creates new volume if NAME (foo) not exist
# Volume location @ host ...
ls /var/lib/docker/volumes/$NAME/_data
@ DfW host, all volumes are within %ProgramData%\DockerDesktop\vm-data\DockerDesktop.vhdx
(~5 GB
)
New volume created if NAME
not exist, even on run
, so no warning if typo.
Multiple containers can share the same volume.
Pre-populate; if new (empty) volume, then any pre-exising files @ container path (mount) are copied to the volume,and so can be made available to any other container.
Decouples host configuration from container runtime.
Host can be remote; cloud provider.
Custom drivers and labels available if prior to run
...
docker volume create --driver $_DRVR --label $_LABEL
Bind Mounts
Host filesystem directory or file mounted as a path at the container; The original Docker mechanism for handling persistent data;
docker run ... -v $HOST_PATH:$CNTNR_PATH
... -v $(pwd):/app
... -v //c/path:/app # @ Windows
--- -v ./relpath:/app # relative paths okay
... --mount type=bind,source="$(pwd)",target=/app
- Share configuration files between host and container. DNS resolution for Docker containers is per mount of host
/etc/resolv.conf
into each container.
tmpfs
mounts
Host system-memory (Linux only) mounted as a path at the container; never written to host filesystem.
docker run ... --tmpfs $CNTNR_PATH
... --tmpfs /app
... --mount type=tmpfs,destination=/app
- Store non-persistent, in-memory-only data.
- Useful to deal with security or performance issues.
Named volumes
docker container run ... -v NAME:/cntnr_path
Volumes survive container deletion,yet contain no meta regarding whence the volume came; docker volume ls
... merely lists per ID; no other info, even @ inspect
. Hence Named Volumes.
Swarm Mode (SwarmKit
)
Container orchestration; Docker's clustering solution; a secure Control Plane; all handled internally. Swarm Managers use Raft (algo+protocol+database).
- v1.12+ (2016) SwarmKit
- v1.13+ (2017) Secrets and Stacks
- Orthogonal to existing docker tools
- Not enabled by default; enabled by command:
docker swarm init
- Launches PKI and security automation
- Root Signing Certificate created
- Cert issued for 1st Manager node
- Join Tokens are created
- Raft Concensus database created
- Replicates logs amongst Managers via TLS
- New
docker
CLI commands upon Swarm Mode enable:
docker swarm|node|service|stack|secret
Services
In Swarm Mode, application components are replicated, and distributed across the nodes, which communcate through the overlay network. Each instance of the replicated component is a Task. The sum of all identical tasks are a Service. So, applications are deployed, per component, as Services.
docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
docker service update [OPTIONS] SERVICE
Stacks
Production-grade Compose.
docker stack deploy -c "app1.yml" "app1"
docker stack ls
docker stack ps "app1"
docker stack services "app1"
Configs
echo "This is a config" |docker config create foo-bar -
Stored as file @ container: /
cat /foo-bar
- Nginx/TLS example
Secrets
echo "This is a secret" |docker secret create foo-bar -
Stored as file @ container: /run/secrets/
cat /run/secrets/foo-bar
- Swarm mode only.
- Stored encrypted outside container.
- Is unencrypted inside the container.
- Generic string or binary < 500 KB
KEY:VAL
pair per file or string
docker secret create KEY FILE
(stored @ host drive)
echo VAL | docker secret create KEY -
(stored @ bash history)
docker service create ... --secret KEY -e VAL_FILE=PATH ...
Note that "*_FILE
" is keyword to trigger file-method (versus treating it as a string).
- @ Swarms, not containers.
- @ Windows containers, clear text stored @ container’s root disk.
- Docker 1.13+, Swarm Raft db is encrypted on disk on Manager nodes. Control Plane is TLS encrypted + Mutual PKI Auth.
- Secrets are stored in the Swarm and assigned to Services; only assigned their Services have access.
- App sees as file on disk, but exist only as in-memory filesystem.
/run/secrets/SECRET1_NAME
docker-compose
has workaround to use secrets sans Swarm; not really secure, but facilitates local development.
- @
docker-compose.yml
(YAML)
CI/CD :: Dev ⇔ Test ⇔ Prod
DevOps on an app (Swarm Cluster) per set of Compose files.
- Dev environment (Local)
docker-compose up
- CI environment (Remote)
docker-compose up
docker-compose.override.yml
… is automatically added per docker-compose up
.
Procution (Remote)
docker stack deploy
$ tree -L 1 ./swarm-stack-3
.
├── Dockerfile
├── docker-compose.override.yml
├── docker-compose.prod.yml
├── docker-compose.test.yml
├── docker-compose.yml
├── psql-fake-password.txt
├── sample-data
└── themes
@ Test (up
)
docker-compose -f docker-compose.yml \
-f docker-compose.test.yml up -d
@ Prod (config
)
docker-compose -f docker-compose.yml \
-f docker-compose.prod.yml config > "out.yml"
- Combines the files.
- The "
extends:
" option is another method; not yet stable; all this is new.
Docker Hub
- One free public acct; one free private registry (repo)
- Webhooks to automate; CI/CD, where Docker Hub notifies downstream per repo change.
- Organizations; like github
- Automated Builds
Use "Create Automated Build" (Menu select) if automating/integrating with GitHub;do NOT use the big "Create Repository" button.
- Creates CI path for automatic builds per code commit. A kind of reverse Webhook.
CVE :: Security Vulnerabilities @ CVEdetails.com
Docker Store ($)
- Docker SW.
- Quality 3rd party images.
Docker Cloud ($)
- CI/CD and Server Ops
- Web-based Swarm Orchestration GUI; integrates with popular cloud providers
- Automate image build/test/deploy
- Security scanning for known vulnerabilities
- US Government (DHS); US Cert Program
CVE
(Common Vulnerability and Exposures). Tracks vulnerabilities.
SaaS Registries (3rd Party)
Docker Registry 2.0 (GitHub)
The code, an HTTP server, that runs Docker Hub; "The Docker toolset to pack, ship, store, and deliver content." A web API and storage system for storing and distributing Docker images.
The de facto standard for running a local (private) container registry. Not as full-featured as Docker Hub; no web GUI; basic auth only. Storage drivers support local, S3, Azure, Alibaba, GCP, and OpenStack Swift.
Private/Local Registry Setup Topics
- Secure the Registry with TLS
- Maintain via Garbage Collection (The Docker document there is utterly useless.)
- Enable Hub caching via "
--registry-mirror
" to conserve bandwidth on large-scale clusters/builds
Run a Private Registry Server
- Run the Distribution registry image on default port 5000
- Sans HTTPS, it allows only
localhost
(127.0.0.0/8
) traffic.
- For remote self-signed TLS, enable "insecure-registry" engine.
Build it with persistent storage (-v
) at host.
docker container run -d -p 5000:5000 --name 'registry' \
-v $(pwd)/registry-data:/var/lib/registry 'registry'
# Bind Mount
Set Registry Domain
_REPO='127.0.0.1:5000' # localhost:5000
Test it
# Pull/Tag/Push
docker pull hello-world
docker tag hello-world ${_REPO}/hello-world
docker push ${_REPO}/hello-world
# Delete cached container & image
docker image remove hello-world
docker container rm $_CONTAINER
docker image remove ${_REPO}/hello-world
docker image ls # verify it's gone (from cache)
# Pull it from local registry
docker pull ${_REPO}/hello-world
# View the image @ cache
docker image ls
# Run it (delete cntnr on exit)
docker run --rm ${_REPO}/hello-world
Query it
# List per name, in JSON
curl -X GET $_REPO/v2/_catalog
# or (same)
curl $_REPO/v2/_content
# List tags of an image
curl $_REPO/v2/$_IMG/tags/list
# inspect (full info)
docker inspect $_REPO/ubuntu:18.04
Delete Image(s)/Repo(s)
- Nope. Docker doesn't like people running their own repos, apparently. There are a few articles on how to delete repos/images, but it's ridiculously complex and tedious. Docker's Garbage Collection page is utterly useless.
Private Docker Registry with Swarm
- Works the same due to Routing Mesh; all nodes can see
127.0.0.1:5000
- All nodes pull from repo, not from each other, hence Docker Registry is integral to Docker workflow.
Run a Registry @ Play with Docker
Templates > "5 Managers and no workers".
docker node ls
docker service create --name registry --publish 5000:5000 registry
docker service ps registry
Pull/Tag (127.0.0.1:5000
)/Push the hello-world
image again, then view the Registry catalog @ "5000
" URL (endpoint); root is empty, but root/v2/_catalog
shows Registry content per JSON.
Advance Configs
# @ TLS
$ docker run -d \
--restart=always \
--name registry \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 443:443 \
registry:2
# @ TLS +Basic Auth
$ docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v "$(pwd)"/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
# @ Swarm Service +TLS
$ docker node update --label-add registry=true node1
$ docker secret create domain.crt certs/domain.crt
$ docker secret create domain.key certs/domain.key
$ docker service create \
--name registry \
--secret domain.crt \
--secret domain.key \
--constraint 'node.labels.registry==true' \
--mount type=bind,src=/mnt/registry,dst=/var/lib/registry \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/run/secrets/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/run/secrets/domain.key \
--publish published=443,target=443 \
--replicas 1 \
registry:2
Load-Balancer Considerations
Required Headers
Docker-Distribution-API-Version: registry/2.0
X-Forwarded-Proto
X-Forwarded-For
Distribution Recipes : NGINX
Docker.sh
runC
/libcontainer
library, or indirectly using libvirt
, LXC or systemd-nspawn
.containerd
(GitHub)An open-source industry-standard container runtime. The core container runtime of the Docker Engine daemon (
dockerd
). Uses runC
(and grpc
for comms) for spawning and running containers per OCI spec.
runC
/libcontainer
(GitHub)
A specification and runtime code; begot Open Container Initiative (OCI). A CLI tool for spawning and running containers per OCI specifications.libcontainer
The original virtualization spec/runtime; merged withrunC
. In effect, this is Docker's fork of LXC.
Virtualized OS; an isolated area of an operating system (OS) with resource limitations applied. Leverages Linux kernel primitives (Namespaces & Control Groups); Namespaces for isolation; Control Groups (Cgroups) for resource limits. The Docker Engine ("The Daemon") handles those low-level primitives.
Union FS/Mount
Combines multiple directories into one that appears to contain their combined contents; allows immutable image layers to form a useful, mutable container during both build and operational runs. Though images (and each comprising layer thereof) are immutable, containers are modifiable by the Copy-on-Write (CoW) mechanism. The container stores changes on a R/W layer on top of the underlying (RO) image layers. Docker currently implements Union FS/Mount using various storage drivers (e.g., overlay2
), supporting xfs
, the underlying filesystem.
Namespaces
Carves up the OS into multiple virtual OS (on which the desired app runs). Unlike VMs, all Virtual OS (containers of a node) share the same, single host (node) OS. Linux Namespaces (per container):
- Process IS (
pid
) - Network (
net
) - Filesystem/mount (
mnt
); root FS - Inter-proc comms (
ipc
); shared memory - UTS (
uts
); hostname - User (
user
); new; map host:container users
Docker Engine
Was a monolith; refactored per OCI standards/specs for both Image & Runtime. Is now entirely separate; modular design; can upgrade Engine while its containers are running.
Server (dockerd
) a.k.a. Docker Engine a.k.a. The Daemon. The persistent host process (daemon) that manages containers and handles container objects; listens for requests sent via the Docker Engine API. Interaces with Linux kernel. "A self-sefficient runtime for containers."
Client (docker
) a.k.a. Docker Engine (CLI/Tool). The main CLI for Docker Engine; allows users to interact with Docker daemons.
Docker Engine (CE)
- Community Edition (Free)
- Enterprise Edition (Pay)
RBAC, scanning, Image promos, ... - Docker Engine (EE)
- Docker Certified Infra
- Ops UI (Web GUI)
- "Universal Control Plane" (UCP)
It's a Docker app (Swarm Cluster)
- "Universal Control Plane" (UCP)
- Secure Registry
- Docker Trusted Registry (DTR)
An on-prem registry.
- Docker Trusted Registry (DTR)
Image
An image is a combination of a JSON Image Manifest file and individual layer files. A layer is a tarball of files built from (rootfs
) filesystem views; loosely coupled layers; includes the app, its dependencies, and a JSON manifest, all for the Docker runtime. A stack of Union fs layers, unified by a storage driver, per manifest instructions. Storage Driver: Overlay2
(@ docker system info
)
- Stateless and immutable; A read-only template. "
IMAGE ID
" references theSHA256
hash of the image the globally unique content-addressable identifier. This ID is an attack vector necessitating countermeasures. - The container host provides the (Linux) kernel, so the image (tarball) can be a single file (binary), such as a carefully compiled Golang app; megabytes. At the other extreme, it can be a fully loaded Ubuntu distro, including an assortment of packages installed therein; gigabytes.
Container
A runtime instance of a Docker image; a process of the Docker runtime (daemon: dockerd
); created of the image, its execution environment, and a standardized set of instructions. A writable layer on top of the image.
- Designed to isolate and run a single userland process in a virutalized OS.
- The Docker runtime (daemon) handles the container's system calls to the host kernel.
Swarm
A cluster of one or more Docker Engines (nodes) running in swarm mode. A set of cooperating Docker Engine daemons that communicate through their API. A service allowing containers to be managed across multiple Docker Engine daemons. Docker tool commands (node
and swarm
) providing native clustering functionality; turns a group of Docker Engines into a single virtual Docker engine. Docker manages swarms using the Raft Consensus Algorithm.
- Swarm Mode
Can start/stop services, and modify them, while they remain online. A Swarm Manager (node) is specified (or elected thereafter) as the Leader of all Swarm Managers (Followers). The Swarm (consensus algorithm) requires a Quorum (N/2 +1) of managers agreeing on the state of the swarm. So, for example, having two managers instead of one doubles the chance of losing quorum; always keep an odd number of Swarm Managers. - Activation, "
docker swarm init
", enables additionaldocker
tool commands:docker node
|service
|stack
|secret
- Swarm Data @
UDP/4789
- Swarm Commands @
TCP/2377
- Swarm Control Plane @
TCP/2376
(Never use this.)- All communication between Docker Engines of the swarm occurs thereof. Nodes of a swarm may be scattered across Cloud vendors. Vendorless!
- Swarm Data @
- Orchestration and its security is handled internally by Swarm Manager(s). There is no external database or authority involved in any of it.
- Each Manager posesses the complete Swarm state, for greater reliability and speed.
- Worker/Manager nodes can switch roles, per API.
- Node-to-Node Communication Protocols; presuming multi-node swarm, not single-node swarm mode.
- Swarm Managers communicate with each other securely using Raft protocol, which is strongly consistent, forming a self-sufficient Quorum; no external dependencies.
- Workers communicate with each other using Gossip protocol, which is eventually consistent, but very fast.
- Manager to Worker communication is across the control plane (
TCP/2376
) per gRPC protocol; binary data; uses HTTP/2 for transport; Protocol Buffers as its IDL.
- Each Manager posesses the complete Swarm state, for greater reliability and speed.
docker network ls
ip addr show
docker0
; original/default; Driver: bridge
Layer 2 network; isolated, even if on same host; routes through NAT firewall on host IP; external comms only by port mapping, host IP-to-IP. Containers connect to Docker bridge (docker0
) network by default.docker run --name web -p 1234:80 nginx
docker port web
Layer 2 network spanning multiple hosts, e.g., connects all containers across all nodes of the swarm.
docker network create ...
- Control Plane encrypted by default.
- Data Plane encrypted per cmdline option
docker network create --opt encrypted ...
Each container (MAC) given its own IP Address on an existing VLAN. Requires promiscuous mode on host NIC; typically not available @ cloud providers.
-p
). External ports closed by default; put frontend/backend on same network for inter-container comms. Best practice is to create a new virtual network for each app. E.g.,- Network
web_app_1
formysql
andphp
/apache
containers. - Network
api_1
formongo
andnodejs
containers.
none
).- "
--network none
" adds container to a container-specific network stack. - "
--network host
" adds container to host’s network stack; to use host IP instead of virtual networks'.
docker inspect ...
" across all networks,
refactored into another valid JSON object:docker network ls -q |xargs docker network inspect $1 \
|jq -Mr '.[] | select(.Name != "none") | {Name: .Name, Driver: .Driver, Address: .IPAM.Config}' \
|jq --slurp .
- Only non-default networks automatically run a DNS serer. The default network (
bridge
) does not, unless declared. For that, usedocker run ... --link ...
to add container-to-container link, but it is easier and better to simply create a new network and use that.
Load Balancing — A benefit of Service Discovery is that every node in the Swarm knows about every service therein; any exposed port (-p HOST:CNTNR
) on any node of a service is replicated on all nodes in the Swarm, and so Swarm does load balancing of incomming traffic (Ingress load balancing), and DNS-based load balancing on internal traffic.
UCP Internal Load Balancing — Internal load balancing is instantiated automatically when Docker services are created. When services are created in a Docker Swarm cluster, they are automatically assigned a Virtual IP (VIP) that is part of the service's network. The VIP is returned when resolving the service's name. Traffic to that VIP is automatically sent to all healthy tasks of that service across the overlay network. This approach avoids any application-level load balancing because only a single IP is returned to the client. Docker takes care of routing and equally distributing the traffic across the healthy service tasks. To see the VIP:
# Create an overlay network called mynet $ docker network create -d overlay mynet a59umzkdj2r0ua7x8jxd84dhr # Create myservice with 2 replicas as part of that network $ docker service create --network mynet --name myservice --replicas 2 busybox ping localhost 8t5r8cr0f0h6k2c3k7ih4l6f5 # See the VIP that was created for that service $ docker service inspect myservice ... "VirtualIPs": [ { "NetworkID": "a59umzkdj2r0ua7x8jxd84dhr", "Addr": "10.0.0.3/24" }, ]
DNS Round Robin (RR) mode @
--endpoint-mode dnsrr
Test; RR is a kind of cheap load balancing; multiple hosts responding to same DNS name, per aliasing.
# run two aliased RR-test servers docker run -d --net 'net1' --net-alias 'foo' elasticsearch:2 docker run -d --net 'net1' --net-alias 'foo' elasticsearch:2 # Get IP & DNS of the two per nslookup; delete cntnr upon exit (--rm) docker run --rm --net net1 alpine nslookup 'foo' # Alpine image contains nslookup Name: foo Address 1: 172.20.0.2 foo.net1 Address 2: 172.20.0.3 foo.net1 # curl, repeatedly, to see random host select/response per Docker's RR scheme docker run --rm --net 'net1' centos curl -s foo:9200 # CentOS image contains curl
elasticsearch:2
image chosen because it generates random host name for itselfalpine
image chosen because it containsnslookup
centos
image chosen because it containscurl
--driver overlay
- Supports multi-host networks; container-to-container comms across all nodes of the swarm. All Tasks (containers) forming all Services running across all nodes (VMs) can access each other; uses a combination of local Linux bridges and VXLAN to overlay container-to-container comms over physical network infra.
- Transport Layer (L4) Load Balancer (Stateless) Assigns Virtual IP addresses (VIPs) to swarm services, mapped to their DNS (name), and so handles the physical node routing. Load balances per Swarm Service, across their Tasks (containers).
- Application Layer (L7) Load Balancer (Stateful) Load balances across services; all services can share same port..
Enable data-plane encryption:
docker network create -d overlay --opt encrypted=true $_NTWK_NAME
docker
(Ref); Docker Engine CLI. The main tool; handles image builds and container creations. Two modes: Single-host and swarm
.
docker-machine
(Ref) is a tool to create and manage VMs for multi-node Swarm(s). Creates Docker hosts (nodes) anywhere; local/cloud; OS/distro per driver; installs Docker thereon, and configures docker
CLI (per VM, per shell).
docker-compose
(Ref) is a combination of (dev) tool and YAML config file for defining and running multi-container Docker applications. docker-compose.yml
is the default filename, but can be any name.
docker-compose up
docker-compose down
docker-compose -f 'foo-manifest.yml'
- Manage all containers with one command
- Configure relationships between containers
- Run commands on multiple containers at once
- Set/save "
docker run
" settings in YAML - Create one-liner startup command for a development environment.
- The tool itself (
docker-compose
) is used mainly for local test and development. In production, in Swarm mode (v1.13+
), the Docker Engine CLI tool (docker
) uses the YAML file (docker-compose.yml
) directly.
docker-library
)[ACCTNAME/]REPONAME
REPONAME
The "
official
" images are further distinguished by theirREPONAME
sans "ACCTNAME/
" prefix. These are high quality images; well documented, versioned (per:TAG
), and widely adopted. E.g., …# The official Nginx image; "1.11.9" is the Tag (version). docker pull nginx:1.11.9
latest
" Tag specifies a latest (stable) published version of a repo; not necessarily the latest commit. E.g., …docker pull nginx:latest
# ... equivalent ...
docker pull nginx
ACCTNAME/REPONAME:TAG
USER/NAME:TAG
", is often referred to as "tag".docker image tag SRC_IMG[:TAG-old] TGT_IMG[:TAG-new]
latest
".TAG
of <none>
☩ di
REPOSITORY TAG IMAGE ID CREATED SIZE
gd9h/prj3.api-amd64 dev ce6b736c74c1 2 hours ago 31.2MB
gd9h/prj3.pwa-amd64 dev 414f405cee2c 19 hours ago 21.7MB
gd9h/prj3.rds-amd64 dev b1350eefb9b9 2 days ago 31.3MB
gd9h/prj3.exv-amd64 dev 96594cfee5fa 2 days ago 17.5MB
postgres <none> baf3b665e5d3 4 days ago 158MB
postgres 12.6-alpine c88a384583bb 3 weeks ago 158MB
golang 1.15.8 7185d074e387 4 weeks ago 839MB
nginx 1.19.3-alpine 4efb29ff172a 5 months ago 21.8MB
<none>
tag (and image) is a Docker daemon pull per YAML declaration. Here, though the YAML declares image:
postgres:12.6-alpine
, a newer image of the same tag exists at Docker Hub repo, and so the Docker engine pulls that, tagging it <none>
, and builds the container therefrom. This is common at Swarm nodes, whereof we update images yet maintain the same tag (dev
).
- @ Postgres repo, the most recent three-dot version is
9.6.21-alpine
(13.2-alpine
is newest as of 2021-04-05). It is not known if they changed their versioning or if all newer are simply too immature (update too often).
OverlayFS
); files and directories of separate file systems are transparently overlaid, forming a single coherent file system./var/lib/docker/STRG_DRVR/DIGEST/{merged,diff,work}
- E.g.,
/var/lib/docker/overlay2/5adcbf...01a016/{merged,diff,work}
pull
or run
don't match those reported elsewhere; while "IMAGE ID
" at "docker image ls
" is something else entirely. And there is no easy way to match a cached layer (folders & files) to its Registry (layer digest).docker image inspect 'alpine'
Id
(The local reference)- @
[{Id:...}]
"Id": "sha256:196d12...84d321"
RepoDigests
- @
[{"RepoDigests":"..."}]
"alpine@sha256:621c2f...af5528"
Config
digest- @
[{"Config":{"Image":"..."}}]
"Image": "sha256:836dc9...0e59b7"
- @
[{"GraphDriver":{"Data":{"MergedDir":"...","UpperDir":"...","WorkDir":"..."}}}]
/var/lib/docker/overlay2/5adcbf...01a016/{merged,diff,work}
- The directories (digests) change per cache (
pull
)
docker login
Stores auth key @
~/.docker/config.json
, until …docker logout
docker push ACCTNAME/REPONAME:TAG
REGISTRY/REPO/IMAGE
- But this is actually an Image Manifest reference.
An "image" is but a sum of immutable Union filesystem (FS) layers, any one of which may be in many images; a one-to-many mapping.
sha256
, so hex64
):
- Content digest
The hash of the Image Manifest; a JSON file containing the hash of each layer. These are not the "IMAGE ID
" shown per "docker image ls
", nor such local/cache references. - Distribution digest
The hash of a compressed image layer (tarball of its Union FS/Mount; folders and files). These are not the "image" references shown per "docker pull ...
", nor such Resistry references.- No easy way to match layer ID with its location at host dir; digests which change per pull, … @
/var/lib/docker/STRG_DRVR/DIGEST/{merged,diff,work}
- No easy way to match layer ID with its location at host dir; digests which change per pull, … @
GET /v2/NAME/manifests/{TAG|DIGEST}
HEAD /v2/NAME/manifests/{TAG|DIGEST}
HEAD /v2/NAME/blobs/DIGEST
- Docker Hub
- Docker Trust Registry (DTR), on-prem, requiring Docker EE
image => container => image => container => ...
Dockerfile
.Imperatively, per docker
CLI commands.
# Imperatively Dockerfile equivalent
docker run [OPTIONS] IMAGE # FROM (base image/layer)
docker exec [OPTIONS] CONTAINER CMD # CMD (add pkg, etc)
docker commit [OPTIONS] CONTAINER # FROM (this new layer)
- It can be argued that "imperatively" / "declaratively" is more lingo than actual; regardless, there's a mapping between
docker
CLI commands/flags andDockerfile
statements.
Dockerfile
| Best PracticesFROM
, ENV
, RUN
, … is an image layer; each layer is downloaded, hashed, and cached, so future builds (per mod) are fast, especially so if layered judiciously; order is important; frequently modified layers placed below (after) those infrequently modified; any content change (e.g., per COPY
) breaks the cache at that layer, and affects subsequent layers.# Base; every image must have ...
FROM alpine:3.8
ENV NGINX_VERSION 1.13.6-1~stretch
# Chain places all at one cacheable layer;
# Remove unncecssary dependencies & pkg-mgr cache
RUN apt-get update \
&& apt-get -y install --no-install-recommends ... \
&& rm -rf /var/lib/apt/lists/*
# Docker handles logging; need only map output ...
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
# open ports 80 & 443 (to other containers @ bridge network)
EXPOSE 80 443
# ... map to host port(s) per run option ... -p|-P
# Change working directories; use instead of RUN cd ...
WORKDIR /usr/share/nginx/html
# Copy from host to container
COPY . .
# Copy dir to dir (from host to container)
# Any changes to the files being copied will break the cache,
# so copy ONLY WHAT IS NEEDED.
COPY index.html index.html
# Volume; outlives container; must manually delete; UNNAMED ONLY !
VOLUME /the/volume/path/at/container
# Must run command/script @ cntnr launch
# (may be embedded in FROM stmnt)
ENTRYPOINT ["entrypoint.sh"]
# then this; overridden on any arg @ `docker run ... IMAGE`
CMD ["command","param1","param2"]
# or default param(s) for ENTRYPOINT command or script
CMD ["param1","param2"]
# ... JSON Array syntax
rm
); survives stop
/start
only. Files created inside a container are stored on a thin writable container layer on top of its read-only image layers.- Difficult to access from outside the container. - Requires a (kernel process) driver to manage the Union filesystem.
overlay2
forxfs
orext4
host FS; an OverlayFS driver;
Docker's default container storage driver.devicemapper
fordirect-lvm
host block-storage device; depricated. is POSIX compliant and supports SELinux, but is slower.btrfs
andzfs
for such host FS.aufs
for Ubuntu 14.04 and older, and Docker 18.06 and older.vfs
is experimental
Immutable design patterns treat containers as ephemeral, if not entirely stateless, and so persistent a.k.a. unique data best resides outside the container.
Docker offers 3 options for persistent container storage, which is to say storage outside the container. All mount some kind of host (or remote) storage as a path at the container. Each has use cases..
Volumes
A Docker-managed object; storage at the host mounted as a path at the container; /var/lib/docker/volumes/NAME/_data
. At DfW/DfM, volumes are created at the Docker VM, not at the host. (SSH into Docker VM); can specify a volume declaratively, in Dockerfile
, or per docker
CLI, as a runtime parameter.
docker volume create $NAME
docker run ... -v $NAME:$CNTNR_PATH
... -v foo:/app
... --mount source=foo,target=/app
# ... creates new volume if NAME (foo) not exist
# Volume location @ host ...
ls /var/lib/docker/volumes/$NAME/_data
@ DfW host, all volumes are within %ProgramData%\DockerDesktop\vm-data\DockerDesktop.vhdx
(~5 GB
)
New volume created if NAME
not exist, even on run
, so no warning if typo.
Multiple containers can share the same volume.
Pre-populate; if new (empty) volume, then any pre-exising files @ container path (mount) are copied to the volume,and so can be made available to any other container.
Decouples host configuration from container runtime.
Host can be remote; cloud provider.
Custom drivers and labels available if prior to run
...
docker volume create --driver $_DRVR --label $_LABEL
Bind Mounts
Host filesystem directory or file mounted as a path at the container; The original Docker mechanism for handling persistent data;
docker run ... -v $HOST_PATH:$CNTNR_PATH
... -v $(pwd):/app
... -v //c/path:/app # @ Windows
--- -v ./relpath:/app # relative paths okay
... --mount type=bind,source="$(pwd)",target=/app
- Share configuration files between host and container. DNS resolution for Docker containers is per mount of host
/etc/resolv.conf
into each container.
tmpfs
mounts
Host system-memory (Linux only) mounted as a path at the container; never written to host filesystem.
docker run ... --tmpfs $CNTNR_PATH
... --tmpfs /app
... --mount type=tmpfs,destination=/app
- Store non-persistent, in-memory-only data.
- Useful to deal with security or performance issues.
docker container run ... -v NAME:/cntnr_path
Volumes survive container deletion,yet contain no meta regarding whence the volume came;
docker volume ls
... merely lists per ID; no other info, even @ inspect
. Hence Named Volumes.SwarmKit
)docker swarm init
- Launches PKI and security automation
- Root Signing Certificate created
- Cert issued for 1st Manager node
- Join Tokens are created
- Raft Concensus database created
- Replicates logs amongst Managers via TLS
docker
CLI commands upon Swarm Mode enable:docker swarm|node|service|stack|secret
docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]
docker service update [OPTIONS] SERVICE
docker stack deploy -c "app1.yml" "app1"
docker stack ls
docker stack ps "app1"
docker stack services "app1"
echo "This is a config" |docker config create foo-bar -
/
cat /foo-bar
echo "This is a secret" |docker secret create foo-bar -
/run/secrets/
cat /run/secrets/foo-bar
KEY:VAL
pair per file or stringdocker secret create KEY FILE
(stored @ host drive)echo VAL | docker secret create KEY -
(stored @ bash history)
docker service create ... --secret KEY -e VAL_FILE=PATH ...
Note that "*_FILE
" is keyword to trigger file-method (versus treating it as a string).
/run/secrets/SECRET1_NAME
docker-compose
has workaround to use secrets sans Swarm; not really secure, but facilitates local development.docker-compose.yml
(YAML)DevOps on an app (Swarm Cluster) per set of Compose files.
- Dev environment (Local)
docker-compose up
- CI environment (Remote)
docker-compose up
docker-compose.override.yml
… is automatically added perdocker-compose up
.
Procution (Remote)
docker stack deploy
$ tree -L 1 ./swarm-stack-3 . ├── Dockerfile ├── docker-compose.override.yml ├── docker-compose.prod.yml ├── docker-compose.test.yml ├── docker-compose.yml ├── psql-fake-password.txt ├── sample-data └── themes
@ Test (up
)
docker-compose -f docker-compose.yml \
-f docker-compose.test.yml up -d
@ Prod (config
)
docker-compose -f docker-compose.yml \
-f docker-compose.prod.yml config > "out.yml"
- Combines the files.
- The "
extends:
" option is another method; not yet stable; all this is new.
Use "Create Automated Build" (Menu select) if automating/integrating with GitHub;do NOT use the big "Create Repository" button.
- Creates CI path for automatic builds per code commit. A kind of reverse Webhook.
- US Government (DHS); US Cert Program
CVE
(Common Vulnerability and Exposures). Tracks vulnerabilities.
Private/Local Registry Setup Topics
- Secure the Registry with TLS
- Maintain via Garbage Collection (The Docker document there is utterly useless.)
- Enable Hub caching via "
--registry-mirror
" to conserve bandwidth on large-scale clusters/builds
localhost
(127.0.0.0/8
) traffic.-v
) at host.docker container run -d -p 5000:5000 --name 'registry' \
-v $(pwd)/registry-data:/var/lib/registry 'registry'
# Bind Mount
_REPO='127.0.0.1:5000' # localhost:5000
# Pull/Tag/Push
docker pull hello-world
docker tag hello-world ${_REPO}/hello-world
docker push ${_REPO}/hello-world
# Delete cached container & image
docker image remove hello-world
docker container rm $_CONTAINER
docker image remove ${_REPO}/hello-world
docker image ls # verify it's gone (from cache)
# Pull it from local registry
docker pull ${_REPO}/hello-world
# View the image @ cache
docker image ls
# Run it (delete cntnr on exit)
docker run --rm ${_REPO}/hello-world
# List per name, in JSON
curl -X GET $_REPO/v2/_catalog
# or (same)
curl $_REPO/v2/_content
# List tags of an image
curl $_REPO/v2/$_IMG/tags/list
# inspect (full info)
docker inspect $_REPO/ubuntu:18.04
127.0.0.1:5000
docker node ls
docker service create --name registry --publish 5000:5000 registry
docker service ps registry
127.0.0.1:5000
)/Push the hello-world
image again, then view the Registry catalog @ "5000
" URL (endpoint); root is empty, but root/v2/_catalog
shows Registry content per JSON.# @ TLS
$ docker run -d \
--restart=always \
--name registry \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 443:443 \
registry:2
# @ TLS +Basic Auth
$ docker run -d \
-p 5000:5000 \
--restart=always \
--name registry \
-v "$(pwd)"/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-v "$(pwd)"/certs:/certs \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
registry:2
# @ Swarm Service +TLS
$ docker node update --label-add registry=true node1
$ docker secret create domain.crt certs/domain.crt
$ docker secret create domain.key certs/domain.key
$ docker service create \
--name registry \
--secret domain.crt \
--secret domain.key \
--constraint 'node.labels.registry==true' \
--mount type=bind,src=/mnt/registry,dst=/var/lib/registry \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/run/secrets/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/run/secrets/domain.key \
--publish published=443,target=443 \
--replicas 1 \
registry:2
Required Headers
Docker-Distribution-API-Version: registry/2.0
X-Forwarded-Proto
X-Forwarded-For