Docker
Fundamental commands
docker --version
docker info Detailed information on the Docker install.
docker run IMAGE[:TAG] [COMMAND] Run a container. TAG is usually a version number.
Also run an optional COMMAND within the container.
-d IMAGE Detached mode. (In the background.)
--name NAME Specify a name for the container. Otherwise name is random.
--restart OPTION Restart a container when it stops based on conditions.
OPTION: {no|on-failure|always|unless-stopped}
on-failure: non-zero exit codes.
unless-stopped: like always but excludes manual stops.
-p HOST_PORT:CONTAINER_PORT Expose a port from the container as a port on the host.
--rm Automatically delete a container when it stops.
--memory NUMBER Put a hard-limit on container memory usage.
Allows for swap usage.
NUMBER units: {b|k|m|g}
--memory-reservation NUMBER Put a soft-limit on memory usage.
Only activates when memory is running low.
NUMBER units: {b|k|m|g}
-v SOURCE:DESTINATION:[OPTIONS] Mount a volume or bind mount.
See Docker Volume section for full information.
--mount \
[type={bind|volume|tmpfs}],\
source={VOLUME_NAME|PATH},\
destination=PATH,\
[RO]
docker service
--mount \
[type={bind|volume|tmpfs}],\
source={VOLUME_NAME|PATH},\
destination=PATH,\
[RO]
docker ps List running containers.
-a List all containers.
docker container
stop {NAME|ID} Stop a container.
start {NAME|ID} Start a container.
rm {NAME|ID} Delete a container. Must be stopped.
docker inspect {OBJECT_ID|NAME} Get JSON config for an object.
Containers, images, and services are (some) objects.
--pretty Make the output more like YAML. Images and services only.
--format='{{.<FIELD_NAME>}}' Retrieve a specific field value by itself.
docker volume
create VOLUME_NAME Create a volume.
ls List volumes.
rm VOLUME_NAME Delete a volume.
inspect VOLUME_NAME Detailed volume information in JSON format.
Docker Volumes
https://docs.docker.com/engine/reference/commandline/
service_create/#add-bind-mounts-volumes-or-memory-filesystems
Bind mounts:
- Mount a specific path on the host machine to the container. Not portable.
Volumes:
- Docker Engine manages the storage of data on the host’s filesystem. Portable.
- Volumes can be mounted to multiple containers.
Using Docker volumes:
- Both bind mounts and volumes can be used with either
--mountor-v. - Only
--mountcan be used with theservicecommand.
–mount:
--mount [type={bind|volume|tmpfs}],\
source={VOLUME_NAME|PATH},\
destination=PATH,\
[RO]
type Optional because type can be extrapolated from source.
bind (mount), volume, or tmpfs (temporarily in-memory storage).
source Volume name or bind mount path.
destination|target|dst Path to mount inside the container.
readonly|RO Make the Docker Volume read-only.
-v:
-v SOURCE:DESTINATION:[OPTIONS]
SOURCE Use a name for volumes and a path for bind mounts.
DESTINATION Path to mount inside the container.
OPTIONS Comma-separated list of options. For example, ro for read-only.
Volume drivers and storage in clusters
https://docs.docker.com/engine/extend/legacy_plugins/
Options:
- Application logic to store data in external object storage.
- A volume driver that is external to any specific machine in the cluster.
Simple example with sshfs:
-
To install, on all nodes in the cluster:
docker plugin install --grant-all-permissions vieux/sshfs - Create a
servicewith themountoption:--mount volume-driver=vieux/sshfs,\ source=VOLUME_NAME,\ destination=PATH,\ volume-opt=sshcmd=USERNAME@HOST:DEST_PATH,\ volume-opt=password=PASSWORD - Don’t create the volume on one node then create the service. The options don’t get propogated
to other nodes if the volume driver is specified in a
docker createcommand.
Docker images and Dockerfiles
To create a new image:
- Create Dockerfile within a new directory.
- Populate Dockerfile with YAML config.
- docker build -t NEW_IMAGE_NAME PATH_TO_DIRECTORY
Dockerfile Syntax
https://docs.docker.com/engine/reference/builder/
Basic Dockerfile syntax:
```
# Single-line comment.
FROM {scratch|IMAGE_NAME[:TAG]} [AS STAGE_NAME]
ENV IDENTIFIER STRING
RUN COMMAND
CMD ["COMMAND","ARG1","ARG2",...,"ARGN"]
```
FROM Starts a new build stage and sets the base image.
Usually must be the first directive in the Dockerfile.
However, the ARG directive can be placed before FROM.
scratch: from nothing.
ENV Set environment variables.
These variables can be referenced in Dockerfile itself.
Referenced using $IDENTIFIER.
RUN Run a command on top of the previous layers and commit the
changes as a new layer.
CMD A default command that is used if no other is given in
docker run. Ex: CMD ["nginx","-g","daemon off;"].
More Dockerfile syntax:
COPY LOCAL_PATH TARGET_PATH Copy files from the local machine to the image.
Paths can be relative to WORKDIR, if that directive is set.
--from={NAME|INDEX} Copy files from a previous build stage.
NAME is the STAGE_NAME of a FROM section.
INDEX is the FROM section index. Zero-based indexing.
EXPOSE PORT Expose a PORT. For documentation purposes only.
ADD SOURCE TARGET_PATH Like COPY but more features.
SOURCE is an archive, file path, or URL.
WORKDIR PATH Set PATH as the current working directory.
Subsequent directives will respect the working directory.
Can be used multiple times.
PATH can be relative to the current working directory.
The final WORKDIR determines the run-time working directory.
STOPSIGNAL SIGNAL Specify the singal that will be used to stop the container.
Location in the file doesn’t matter.
Ex: STOPSIGNAL SIGTERM.
HEALTHCHECK CMD COMMAND Create a custom health check.
By default, Docker just uses exit codes.
Ex: HEALTHCHECK CMD curl localhost:80
Simple Dockerfile example: ``` #Simple Nginx image FROM ubuntu:bionic
ENV NGINX_VERSION 1.14.0-0ubuntu1.3
RUN apt-get update && apt-get install -y curl
RUN apt-get update && apt-get install -y nginx=$NGINX_VERSION
CMD ["nginx","-g","daemon off;"]
```
Important notes on Dockerfiles
Multiple RUN statements in Dockerfiles:
- RUN statements create new layers.
- Every time an image is re-built, only the layers that have changed are re-built.
- If a command, for example apt update, needs to run consistently, it should be included in
every RUN statement.
Efficiency: - Put things that are less likely to change on lower-level layers. - Don’t create unnecessary layers. - Avoid including any unnecessary files or packages in the image.
Multi-stage builds:
- Multi-stage builds can make for more efficient resulting images.
- A multi-stage build is a Dockerfile with multiple FROM sections.
- For example, compiler binaries may be included in a single-stage build.
- The last stage alone creates the resulting image.
- The --from flag of COPY can be used to copy files from a previous stage.
Flattening images:
- Images with fewer layers can have better performance.
- Flattening images should be avoided unless it has tangible benefits.
- To flatten an image:
- Run a container from the image.
-
docker export CONTAINER_NAME > ARCHIVE_NAME.tar
cat ARCHIVE_NAME.tar | docker import - NEW_IMAGE_NAME:TAG
- TAG is a label, for example, latest.
Docker Installation
https://docs.docker.com/engine/install/
Uninstall old versions first
List all available versions:
apt list docker-ce -a
yum list docker-ce --show duplicates
Install a specific version:
{yum|apt} install docker-ce-VERSION_STRING \
docker-ce-cli-VERSION_STRING
containerd.io
Enable a non-root user to use Docker:
usermod -aG docker USER
newgrp docker # activate the group without logging out
Upgrading
- Use the package manager install command with a newer version specified.
- Existing containers will not be impacted.
Downgrading
- Stop Docker.
- Remove
docker-ceanddocker-ce-cli. yum check-update,apt update, or equivalent.- Install the new version of Docker.
- Existing containers will not be impacted.
Bash completion installation
https://docs.docker.com/compose/completion/
Command as of the time of writing:
sudo curl -L "https://raw.githubusercontent.com/docker/compose/1.25.5/"\
"contrib/completion/bash/docker-compose -o /etc/"\
"bash_completion.d/docker-compose"\
Key concepts
Dockerfiles Define new images using YAML files.
New images can be built on top of existing images.
Docker Compose Define multi-container applications using YAML files. Useful for development, testing, and single-host environments.
Docker Swarm A distributed cluster of Docker machines. Swarm provides orchestration, HA, and scaling features.
Docker Services Run an application image on one or more nodes within a Docker Swarm.
Docker Stacks A collection of interrelated Docker Services that can be deployed and scaled as a single unit. Defined using YAML files.
Docker Registries Repositories of Docker images.
Docker Compose
https://docs.docker.com/compose
Run multi-container applications using a declarative format.
Installation
sudo curl -L \
"https://github.com/docker/compose/releases/download/1.25.5/docker-compose-"\
"$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
Basic Usage
-
Create a
docker-compose.ymlin a project directory. -
Example
docker-compose.yml:
version: '3'
services:
web:
image: nginx
ports:
- "8080:80"
redis:
image: redis:alpine
-
docker-compose up -d: create and run the containers defined in the YAML config file.-dis detached mode. -
docker-compose ps: list containers/services that are currently running under Docker Compose. -
docker-compose down: remove the services/containers defined bydocker-compose.yml.
Docker Networking
Commands
-
docker run --net={NETWORK_NAME|host|none}: run a Docker container with a specific network driver. -
docker network create --driver {bridge|overlay|MACVLAN} <NETWORK_NAME>: create a new network with a specific network driver. -
docker network create --driver overlay --attachable <NETWORK_NAME>: create an attachable overlay network. -
docker network connect <NETWORK> <CONTAINER>: connect a container to an existing network. -
Create a network with the MACVLAN network driver:
docker network create macvlan --subnet <CIDR_BLOCK> --gateway <GATEWAY_IP>\
-o parent=<NETWORK_DEVICE> <MACVLAN_NETWORK_NAME>
-
docker service create --network OVERLAY_NETWORK_NAME: create a service using an overlay network. -
docker networks ls: list networks. -
docker network inspect: see JSON configuration for a network. -
docker network disconnect <NETWORK> <CONTAINER>: disconnect a network from a container. -
docker network rm <NETWORK>: remove a network when there are no containers connected.
Theory
Basic Theory
-
Docker uses an architecture called the Container Networking Model (CNM) to manage networking for Docker containers.
-
Sandbox: An isolated unit containing all the netwroking components assoicated with a single container. Usually a Linux Network namespace.
-
Endpoint: Connects a sandbox to a network. Each sandbox/container can have any number of endpoints, but has exactly one endpoint for each network it is connected to.
-
Network: A collection of endpoints connected to one another.
-
Network Driver: Handles the actual implementation of the CNM concepts.
-
IPAM Driver: Automatically allocates subnets and IP addresses for networks and endpoints.
-
Bridge Network: Connects endpoints on a single host.
-
Overlay Network: Connects endpoints across multiple Swarm hosts.
Native Network Drivers
-
These are the built-in network drivers:
Host Bridge Overlay MACVLAN None
-
A specific network driver can be used by specifying
--net=<DRIVER>indocker run.
Host: --net=host
- Containers use the host’s networking resource directly.
- No sandboxes.
- Containers cannot use the same port(s).
- Use case: simple setups, with only a couple of containers on a single host.
Bridge: --net=<BRIDGE_NETWORK_NAME>
- A bridge network provides connectivity between containers on the same host.
- The default driver for containers when Swarm is not in use.
- Creates a Linux Bridge for each Docker network.
- The default Linux bridge, which is created if network is not specified, is
bridge0.ip linkcan be used to view bridges. - Use case: isolated networking on a single host.
- The default overlay is called
ingressand is used if no other network is specified.
Overlay: --net=<OVERLAY_NETWORK_NAME>
- Connectivity across multiple Docker hosts.
- Uses VXLAN data plane. Routing is transparent to the containers.
- Automatically configured interfaces, bridges, etc.
- Use case: networking between Swarm hosts.
MAVCLAN --net=<MACVLAN_NETWORK_NAME>
- Endpoint interfaces are directly connected to host interfaces, reducing overhead and improving latency.
- Harder to configure.
- Greater dependency between MACVLAN and the external network.
- Use cases: low latency is required or containers need IP addresses in the external subnet.
None --net=none
- Containers are completely isolated.
- If networking is required, it must be done manually.
- None creates a separate networking namespace for each container but no interfaces or endpoints.
- Use cases: when no networking is required or everything needs to be done manually.
DNS
- Docker has in-built DNS.
- Containers on the same network can be resolved using just their hostname.
- Aliases can be created to specify ab alternative name for networks.
- Aliases can be created using the
--network-alias <ALIAS>indocker runor:docker connect --alias <ALIAS> <NETWORK> <CONTAINER>.
Custom DNS
-
Globally in
/etc/docker/daemon.json:{ "dns": ["1.1.1.1"] }systemctl restart docker -
Per host:
docker run --dns <DNS_ADDRESS>
Exposing ports
-
There is a
-p HOST_PORT:CONTAINER_PORToption for bothdocker runanddocker service. -
docker port <CONTAINER>: list the published ports for a container. -
docker ps: also shows published ports. -
Docker Swarm supports two modes for published ports for services: ingress and host.
docker service create -p mode=host,published=<HOST_PORT>,\
target=<CONTAINER_PORT> --name <SERVICE_NAME> <IMAGE_NAME>
Ingress
-
The default.
-
Uses a routing mash. The published port listens on ever node in the cluster. Ingress transparently directs incoming traffic to any task that is part of the service.
Host
-
Publishes the port directly on the host where a task is running.
-
Cannot have multuple replicas on the same not if this mode is used.
-
Traffic to the published port on the node goes directly to the task running on that specific node.
-
Host mode publishing is best used for global services because the downsides are not important.
Networking troubleshooting
-
docker logs <CONTAINER>: get container logs. -
docker service logs <CONTAINER>: get service logs. -
journalctl -u docker: Docker daemon logs.
Netshoot
-
Netshoot is an image that comes with a variety of network troubleshooting tools.
-
docker run --rm --network <NETWORK> nicolaka/netshoot [COMMAND]: run Netshoot and use [COMMAND], for example, curl. -
Netshoot can also be used in interactive mode.
-
docker run --rm --network container:<CONTAINER_NAME>: run Netshoot inside the networking namespace for a container. This includes containers with--network noneset.
Docker Registries
https://docs.docker.com/registry/
Deploy a Private Registry
https://docs.docker.com/registry/deploying/
-
docker run -d -p 5000:5000 --restart=always --name registry registry:2Create a registry.
Override specific configuration options
-
Use
-e REGISTRY_<OPTION_NAME_CAPS>=<VALUE>within the run command. -
Ex:
docker run -d -p 5000:5000 --restart=always --name registry \ -e REGISTRY_LOG_LEVEL=debug registry:2
Override the entire configuration file
-
If the default configuration is not a sound basis for your usage, or if you are having issues overriding keys from the environment, you can specify an alternate YAML configuration file by mounting it as a volume in the container.
-
Ex:
docker run -d -p 5000:5000 --restart=always --name registry \ -v `pwd`/config.yml:/etc/docker/registry/config.yml \ registry:2 -
Basic YAML file:
version: 0.1 log: fields: service: registry storage: cache: blobdescriptor: inmemory filesystem: rootdirectory: /var/lib/registry http: addr: :5000 headers: X-Content-Type-Options: [nosniff] auth: htpasswd: realm: basic-realm path: /etc/registry health: storagedriver: enabled: true interval: 10s threshold: 3 -
Complete list of options: https://docs.docker.com/registry/configuration/
Registry Logs
-
docker logs registry: access the registry logs.The default level is info.
Basic Security
-
By default, the registry is completely unsecured. It does not use TLS and does not require authentication.
-
Basic auth:
mkdir -p ~/registry/auth && cd ~/registry
docker run --entrypoint htpasswd registry:2 -Bbn <USERNAME> <PASSWORD> \
> auth/htpasswd
- Self-signed certificate to enable TLS:
mkdir certs
openssl req \
-newkey rsa:4096 \
-nodes \
-sha256 \
-keyout certs/domain.key \
-x509 -days 365 -out certs/domain.crt
- Stand-up the registry with the basic auth:
docker run -d -p 443:443 --restart=always --name registry \
-v /home/docker/registry/certs:/certs \
-v /home/docker/registry/auth:/auth \
-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 \
-e REGISTRY_AUTH=htpasswd \
-e "REGISTRY_AUTH_HTTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2
-
Line by line explanation.
Mount the certs directy on the host as /certs in the container. Ditto for /auth. Listen on port 443 rather.
REGISTRY_AUTH=htpasswd: use htpasswd mode. Setup relam for htpassd. Point to our auth file that has a username and a hashed passwd. -
Quick test:
curl -k https://localhost:443. No output is expected, which shows that the registry is responsive.-kskips the trust check because the cert is self-signed.
Interacting with a Registry
-
docker pull [REGISTRY_FQDN/]<IMAGE>[:TAG]: download an image to your local machine. -
docker search <TEXT>: search the images that are available on a registry. -
When pushing to a private registry an image must be tagged with the registry’s FQDN.
docker tag <IMAGE> <REGISTRY_FQDN>/<IMAGE_NAME_PRIVATE_REGISTRY> docker push <REGISTRY_FQDN>/<IMAGE>
Logging in to a Registry
-
docker login <REGISTRY_FQDN>: log in to a registry.Will throw an error if the registry uses a self-signed certificate.
-
docker logout: log out of a registry. -
There are two methods to overcome errors involving self-signed certificates.
-
Certificate verficiation can be turned off, which is insecure and not recommended. In
/etc/docker/daemon.jsonadd:{ "insecure-registries": ["DOCKER_REGISTRY_FQDN1"] }Restart Docker
systemctl restart docker. -
Alternatively, the public certificate can be provided to the Docker engine. This is the preferred method of of resolving the self-signed certificate problem.
mkdir -p /etc/docker/certs.d/docker/certs.d/<REGISTRY_FQDN>
## scp or cp the domain public cert (domain.crt above) to this folder.
Logging Drivers
https://docs.docker.com/config/containers/logging/configure/
Check the default logging driver
docker info | grep Logging
- The defualt logging driver is
json-file. - The default setting can be overriden per container.
Changing the default logging driver
-
Edit /etc/docker/daemon.json.
Add or edit the following lines:
{ "log-driver": "json-file", "log-opts": { "max-size": "15m" } } -
sudo systemctl restart docker
Overriding the default logging driver for each container
docker run --log-driver <DRIVER> [--log-opt] [OPTION_1] \
[--log-opt] [OPTION_2] [...] <IMAGE>
Ex: docker run --log-opt max-size=10m --log-opt max-file=3 alpine
Logging drivers
none
local: custom format for minimal overhead.
json-file
syslog
journald
gelf: Graylog Extended Log Format.
fluetd: forward logs using a unified logging layer.
awslogs: CloudWatch
splunk
Other options are available.
Log options
max-size=<NUMBER>[k,m,g]: unlimited by default
max-file=<NUMBER>: number of files that can be present
compress={TRUE|FALSE}
Additional options for log tags: labels, env, and env-regex.
Managing Docker Images
https://docs.docker.com/engine/reference/commandline/image/
-
docker image pull <IMAGE_NAME>[:TAG]Download an image from a remote registry. Will only pull an image if it doesn’t exist locally.
docker pullis exactly the same. -
docker image ls: list the images that exist locally.-a: list intermediate images as well. -
`docker image inspect {IMAGE_NAME}”: inspect image metadata.
Output is JSON.
--format "{{.ARG1}} {{.ARG2}} ...": template for getting a subset of info. Ex:--format "{{.Architecture}} {{.0s}}"Output:
amd64 linux -
docker image rm <IMAGE>[:FLAG]: delete an image.Alternative:
docker rmi <IMAGE>[:FLAG]-f: force. Use carefully when there is a running container. Doesn’t delete the underlying files if they are currently being used. To fully remove an image: -
docker image prune: delete images not referenced by tags or containers.Called dangling images. No tags or repos in
docker image ls -a. -
docker image prune -a: delete all unused images (not used by a container). -
docker image history {IMAGE_NAME}: view layers of an image.
Disk usage
-
docker system df: get information about disk usage on a system. -
docker system df -v: get detailed information about disk usage on a system.
Docker Images
-
Containers and images use a layered filesystem. For example, an OS layer, Python and then a web app.
-
An image consists of one or more read-only layers, while the container adds one addition writable layers.
-
The layered approach allows us to share layers between containers and images themselves.
-
When building a new image, only the layers that have changed, and the layers above them, need to be changed.
-
docker image pull <IMAGE>: pull an image from Docker Hub. -
docker image history <IMAGE>: shows all the layers in an image.Zero-byte layers are no operation layers (no-op); they don’t contain any new data, or have any differences, from the previous layer.
Layers that have size greater than zero, are the layers that are relevant to the image.
Creating a Docker Image
Docker Security
Signing Images
-
Docker Content Trust (DCT) provides a secure way to verify the integrity of images before they are pulled or run on systems.
-
Image creators sign images with a cert.
Create and use a trust key
-
A Docker Hub account is necessary to generate a trust key for that registry.
-
Sign-in with
docker login. -
docker trust key generate <SIGNER_NAME>: a public key is generated in the current directory. A password needs to be entered and should be retained. -
Use a key to add a signer on a new Docker image:
docker trust signer add --key <PUBLIC_KEY> <REGISTRY_USERNAME> \ <REPOSITORY_NAME>`It will be necessary to create passwords for the root signing key and repository key, which should be retained.
Enabling DCT
-
export DOCKER_CONTENT_TRUST=1 -
In Docker Enterprise Edition, it can be enabled in daemon.json.
-
When DCT is enabled, attempting to pull or run an unsigned image will result in an error message.
-
docker trust sign <IMAGE><:TAG>: signs an image and push it to the registry. -
When
DOCKER_CONTENT_TRUST=1is set,docker pushautomatically signs images.
Namespaces and Cgroups
- Namespaces and Control Groups provide isolation to containers.
- This limits exploits or priv esc attacks.
Docker daemon attack surface
-
The Docker daemon itself requires root privileges. Hence, we need to consider the attack surface presented by the Docker daemon.
-
Only allow trusted users access to the daemon.
-
This also nees to be considered for automation that accesses the Docker daemon.
Linux kernel capabilities
-
Docker uses a feature of the Linux kernel called capabilities to fine-tune what a process can access.
-
This means that a process can run as root inside a container, but does not have access to do everything root could normally do on the host.
-
For example, Docker uses
net_bind_servicecapability to allow container processes to bind to a port below 1024 without running as root.
Encryptiong Overlay Networks
overlaynetworks can be encrypted using the option--opt encryptedwhen they are created using thedocker network createcommand.
MTLS in Docker Swarm
- Mutually Authenticated Transport Layer Security.
- Certs are exchanged.
- All communication is authenticated and encrypted.
- A root CA is created when a Swarm is initialized.
- Worker and managed nodes are generated with the CA.
- MTLS is used for all cluster-level communication between swarm nodes.
- Enabled by default.
Securing the Docker Daemon HTTP Socket
-
Note: this potentially needs review using official documentation. The tutorial was rushed.
-
Docker uses a socket that is not exposed to the network by default.
-
Docker can be configured to listen on an HTTP port, to allow remote management.
-
In order to do this securely, we need to:
- Create a CA.
- Create server and client certs.
extendedKeyUsage = serverAuthfor the server cert.extendedKeyUsage = clientAuthfor the client cert.- Configure the Docker daemon to use
tlsverifymode. - Configure the client co connect securely using the client cert.
-
Basic openssl CA with the usual cert format for web servers.
-
In
/etc/docker/daemon.json:
{
"tlsverify": true,
"tlscacert": "/path/to/ca.pem",
"tlscert": "/path/to/server/public_key.pem",
"tlskey": "/path/to/server/seret_key.pem"
}
-
Edit the Docker Daemon unit file
/lib/systemd/system/docker.service. -
Edit the
ExecStartline. -
Change the value of the
-Hto0.0.0.0:2376. -
systemctl daemon-reload -
systemctl restart docker -
On the client:
- Copy the
cert.pem,client_pub.pem, andclient_priv.pemto the client. - Move everything to
~/.docker. Create it if it doesn’t exist. export DOCKER_HOST=tcp://IP_OF_DOCKER SERVICE:PORTexport DOCKER_TLS_VERIFY=1
- Copy the
Docker Storage Drivers
Also known as Graph drivers.
Storage Drivers
-
Use
docker infoto ascertain which Storage Driver is currently being used. This includes thedevicemappersubtypes. -
overlay2is file-based storage and the default for Ubuntu and CentOS 8+. Preferred; no extra config. -
devicemapperis block storage and the default for CentOS 7 and earlier. More efficient for lots of writes.direct-lvmfor production.loopback-lvmhas very poor performance.loopback-lvmis default. -
btrfsorzfsstorage drivers are used if the underlying filesystem is either of these. -
aufsis legacy for Ubuntu 14.04 on kernerl 3.13. -
vfs is for testing purposes and for use on non-CoW filesystems. Performance is poor. Not recommended.
Select a Storage Driver:
- There are two methods to set the storage driver.
- Don’t use both methods as it prevents Docker from starting.
Method 1 (not recommended)
- Edit
/usr/lib/systemd/system/docker.service. - Look for the ExecStart line.
- Add the argument
--storage-driver <DRIVER>. - Reload daemon config and restart docker:
sudo systemctl daemon-reload sudo systemctl restart docker
Method 2 (recommended)
-
Create or edit
/etc/docker/daemon.json. - Edit or add:
{ "storage-driver": "devicemapper" } sudo systemctl restart docker
Change Storage Model
systemctl disable docker
rm -rf /var/lib/docker
vi /etc/docker/daemon.json # edit JSON
systemctl start docker
systemctl enable docker
Configuring DeviceMapper for direct-lvm
https://docs.docker.com/storage/storagedriver/device-mapper-driver/
direct-lvmshould be used in
/etc/docker/daemon.json:
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.direct_lvm_device=/dev/xvd<a-z>",
"dm.thinp_percent=95",
"dm.thinp_metapercent=1",
"dm.thinp_autoextend_threshold=80",
"dm.thinp_autoextend_percent=20",
"dm.directlvm_device_force=true"
]
}
-
dm.directlvm_device: The path to the block device to configure for direct-lvm. Required. -
dm.thinp_percent: The percentage of space to use for storage from the passed in block device. Default: 95. Not required. -
dm.thinp_metapercent: The percentage of space to use for metadata storage from the passed-in block device. Default: 1. Not required. -
dm.thinp_autoextend_threshold: The threshold for when lvm should automatically extend the thin pool as a percentage of the total storage space. Default: 80. Not required. -
dm.thinp_autoextend_percent: The percentage to increase the thin pool by when an autoextend is triggered. Default: 20. Not required. -
dm.directlvm_device_force: Whether to format the block device even if a filesystem already exists on it. If set to false and a filesystem is present, an error is logged and the filesystem is left intact. Default: false. Not required.
Storage Models
Filesystem Storage
- Data is stored in the form of a filesystem
- Used by
overlay2andaufs. - Efficient use of memory.
- Inefficient with write-heavy workloads.
Block Storage
- Stores data in blocks.
- Used by
devicemapper. - Efficient with write-heavy workloads.
Object Storage
- Stores data in an external object-based store.
- Application must be designed to use object-based storage.
- Flexible and scalable.
Storage Layers
Both containers and images have layers. Layers are stacked on top of each other. Each layer contains only the differences from the previous.
The container has all the layers of an image, plus one additional “Writable Container Layer”.
The location of the layered data on disk can be ascertained with
docker inspect.
In docker inspect output, within the GraphDriver -> Driversection,
there is a path to the location of the layers on disk.
Underlying Technology
Namespaces
- Namespaces isolate processes.
- Docker uses namespaces to isolate containers.
Types of namespaces:
pid: process isolation.
net: network interfaces.
ipc: inter-process communication.
mnt: filesystem mounts.
uts: kernel and version identifiers.
user namespaces: requires config. Allows container processes to run as root
inside the container while mapping that user to an unprivileged user on the
host.
Control groups (cgroups)
Control groups limit what resources a process can use.
Docker Stacks
-
Services are capable of running a single replicated application across nodes in the cluster.
-
Stacks are a collcetion of iterrelated services that can be deployred and scaled as a unit.
-
Docker stacks are similar to the multi-container applications created using Docker Compose. However, they can be scaled and executed across the swarm just like normal Swarm sevices.
-
Docker stacks use compose files
-
Example
docker-compose.yml:
version: '3'
services:
web:
image: nginx
ports:
- "8080:80"
busybox:
image: radial/busyboxplus:curl
command: /bin/sh -c "while true; do echo $$MESSAGE; curl web:80; sleep 10;
done"
environment:
- MESSAGE=Hello!
-
docker stack deploy -c <COMPOSE_FILE> <STACK_NAME>: deploy a stack. -
docker stack ls: list current stacks. -
docker stack services <STACK_NAME>: list the services associated with a stack. -
docker stack rm <STACK_NAME>: delete a stack. -
Environment variables can be created and used in the compose file, as per the example.
-
Other containers in the stack can be referenced via their name in YAML, for example,
curl web:80in the example.
Docker Swarm
https://docs.docker.com/engine/swarm/
A swarm is a distributed cluster of Docker machines. A swarm has many features that can help provide orchestration, HA, and scaling.
-
docker swarm init --advertise-addr <IP_ADDRESS>: initalise the swarm.Automatically makes the machine a swarm manager. The advertise address is the address other nodes will see.
-
docker node ls: list all nodes in the swarm. -
docker swarm join-token {manager|worker}Gets a new token and command to join a machine to the swarm.
Back-up and Restore a Docker Swarm
Back-up:
systemctl stop docker- Back-up everything in
/var/lib/docker/swarm. systemctl start docker
Restore:
systemctl stop dockerrm -rf /var/lib/docker/swarm/*- Extract the back-up to
/var/lib/docker/swarm/. systemctl start docker
Autolock
Docker Swarm encrypts raft logs and TLS communication. By default Docker manages the keys to enable this, however, they are left unencrypted on the managers’ disks.
Autolock locks the swarm, allowing for management of keys by the user.
Every time Docker is restarted, it is then necessary to unlock the swarm.
When Autolock is enabled, a key will print to stdout that will need to be saved in a secure place and used to unlock the swarm.
-
docker swarm init --autolock: initalise a Swarm with Docker. -
docker swarm update --autolock=true: enable autolock on an existing Swarm. -
docker swarm update --autolock=false: disable autolock on an existing Swarm. -
docker swarm unlock-key: output the key on an unlocked Swarm manager. -
docker swarm unlock-key --rotate: rotate the key. May take some time to propogate.
High Availability
-
Docker uses the Raft consensus algorithm to main a consistent cluster state across multiple managers.
-
For HA, it is a good idea to have multiple Swarm managers.
-
However, more managers can increase network load.
-
A quorum is the majority (more than half) of the managers in the swarm.
-
A quorum must be maintained in order to make changes to the cluster state.
-
Recommended to have an odd number of managers to satisfy the “more than half” requirement.
-
Recommended to have managers in multiple AZs.
Docker Services
-
A service is used to run an application on a Docker Swarm.
-
A task service specified a set of one or more replica tasks.
-
Tasks are distributed automatically across nodes.
-
docker service create <IMAGE>--name <NAME>`-p: -
The values of some flags can be dynamic using a feature called templates.
hostname,mount, andenvcan use templates.For example, to add the hostname of a node to a service as an environment variable:
-env NODE_HOSTNAME="{{.Node.Hostname}}" -
docker service ls: list current services. -
docker service ps <SERVICE>: list a service’s tasks. -
docker service inspect <SERVICE>: see JSON config for a service. -
docker service inspect <SERVICE> --pretty: YAML-like output. -
docker servcice update <COMMAND_1> [COMMAND_2] <SERVICE>: update settings pm a service.Ex:
docker service update --replicas 2 nginx_service -
docker service rm <SERVICE>: removes a service. Nothing else to do. -
docker service create --replicas <INT> <SERVICE>: runrepliacs of the task across the cluster. -
docker service create --mode global <SERVICE>: run one task on each node in the cluster. -
docker service logs <SERVICE>: view logs for a service.
Node Labels
-
Node labels are metadata.
-
docker node update --label-add <LABEL>=<VALUE> <NODE_NAME>: add a label. -
docker node inspect --pretty <NODE>: output contains node labels. -
Node labels can be used to add constraints when a service is created. Multiple containts can be added with multiple
--constraintflags. All contstraints must be satisfied for a task to run on node.ex: --constraint node.labels.availibilty_zone==east: run on nodes with an AZ level value ofeast. -
--placement-pref spread=node.label.availability_zone: can be used to spread services evenly across nodes by label. Note that null counts as a label.
Docker Enterprise Edition
EE Features
-
Universal Control Plane (UCP): a web interface for Docker Swarm.
-
Docker Trusted Registry (DTR): an enterpise-grade private registry with more features, including a web interface.
-
Vulnerability scanning.
-
Federated application management. Manage all applications in one UI, usually UCP.
Installation
-
You get a link when buying or trialing Docker EE.
-
Ubuntu installation:
DOCKER_EE_URL=URL
DOCKER_EE_VERSION=VERSION
sudo apt install -y \
apt-trasport-https \
ca-certificates \
curl \
software-properties-common
curl -fsSL "${DOCKER_EE_URL}/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=$(dpkg --print-architecture)] $DOCKER_EE_URL/ubuntu \
$(lsb_release -cs) \
stable-$DOCKER_EE_VERSION"
sudo apt update
sudo apt install docker-ee:<version>
sudo usermod -aG docker USER
## activate group
Bash Completion
On Debian 10, Bash completion was not installed by default.
To install it:
sudo curl -L https://raw.githubusercontent.com/docker/compose/1.25.5/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose
Source: https://docs.docker.com/compose/completion/
Docker Volumes
-
Two types: Bind mounts and Volumes.
-
Both collectively, somewhat confusingly, called Docker Volumes.
Bind mounts
-
Mount a specific path on the host machine to the container.
-
Not portable; dependent on the host machine’s fileystem and directory
structure.
Volumes
-
Stores data on the host filesystem but the storage location is managed by
Docker.
-
More portable.
-
Can mount the same volume to multiple containers.
Mount Docker Volumes
"https://docs.docker.com/engine/reference/commandline/service_create/"
"#add-bind-mounts-volumes-or-memory-filesystems"
-
Either syntax can be used with each type of Docker Volume.
-
For services, only
--mountcan be used.
--mount
docker run --mount [type={bind|volume}],<source={VOLUME_NAME|PATH}>,\
<destination=PATH>,[RO]
-
type: bind (bind mount), volume, or tmpfs (temporarily in-memory storage).Does not necessarily need to be specified because it can be extrapolated from
source. -
source: volume name or bind mount path. -
destinationtargetdst: path to mount inside the container. -
readonlyRO: make the Docker Volume read-only.
-v
-
docker run -v <SOURCE>:<DESTINATION>:[OPTIONS] -
If
SOURCEis a volume name, a volume is created. IfSOURCEis a path, abind mount will be created.
-
DESTINATION: path to mount inside the container. -
OPTIONS: comma-separated list of options. For example, ro for read-only.
Create Volumes
-
docker volume create <VOLUME_NAME>: create a volume. -
docker volume ls: list current volumes. -
docker volume inspect <VOLUME_NAME: detailed volume information. -
docker volume rm <VOLUME_NAME: delete a volume.
Storage in a Cluster
There are two options for sharing data between nodes in a cluster:
-
Application logic to store data in external object storage.
-
Use a volume driver to create a volume that is external to any specific
machine in a cluster.
-
A list of volume drivers is available at:
https://docs.docker.com/engine/extend/legacy_plugins/
Simple Volume Driver Example with sshfs
On all nodes on the Swarm:
docker plugin install --grant-all-permissions vieux/sshfs
The Correct Way
docker service create \
--replicas <INT> \
--name <NAME> \
--mount volume-driver=vieux/sshfs,source=<VOLUME_NAME>,destination=<PATH>,\
volume-opt=sshcmd=<USERNAME>@<HOST>:<DEST_PATH>,volume-opt=password=<PASSWORD>\
<IMAGE>
Gotcha
On the server where the external volume is located:
docker create --driver vieux/sshfs \
-o sshcmd=<USERNAME>@<HOST>:<PATH> \
-o password=<PASSWORD> \
sshvolume
docker service create --mount source=sshvolume
Although this looks like this may work, we are only passing the arguments on
the node where docker create occurs. Any additional tasks on other nodes will
not have the arguments, therefore, will not write to external storage.
Creating Docker Images
A Dockerfile is a set of instructions used to construct a Docker image. These
instructions are called directives.
Steps
-
Create a directory.
-
Create a file
Dockerfileinside the directory. -
Populate the
Dockerfile. -
Run
docker build -t <IMAGE_NAME> <PATH>. -
<PATH>is the path of the directory where the Dockerfile resides.
Basic Dockerfile Syntax
https://docs.docker.com/engine/reference/builder/
-
#: single-line comment. -
FROM {scratch|image_name}: starts a new build stage and sets the base image.Usually must be the first directive in the Dockerfile.
ARG can be placed before FROM though.
scratchmeans from nothing.FROMstages can be used by appendingAS {NAME}. -
ENV {IDENTIFIER} {STRING}: set environment variables.These can be references in the Dockerfile itself.
The variables are visible to the container at runtime.
References using $INDENTIFIER.
-
RUN {COMMAND}: creates a new layer on top of the previous layer by runninga command inside that new layer and committing the changes.
-
CMD ["COMMAND","ARG1","ARG2"]: Specify a default command used to run acontainer at execution time.
When no commands are passed to
docker run, this command is run.Ex:
CMD ["nginx","-g","daemon off;"]
More Dockerfile Syntax
-
EXPOSE {PORT}: documents which port(s) are intended to be published whenrunning a container.
For documentation purposes. No direct impact with
docker run. -
WORKDIR {PATH}: sets the current working directory for subsequentdirectives.
Can be used multiple times in a single file.
Can use relative paths.
For use with directives such as ADD, COPY, CMD and ENTRYPOINT.
Final
WORKDIRdetermines the working directory at runtime. -
COPY {LOCAL_PATH} {TARGET_PATH}: copy files from the local machine to theimage.
Relative paths, based off of the current
WORKDIR, can be used.There are some things that
COPYcan do thatADDcannot.COPY --from={NAME|INDEX} {TARGET} {DESTINATION}can copy files from aprevious build stage. See the Multi-Stage Builds section.
-
ADD {URL|FILE_PATH|ARCHIVE_PATH} {TARGET_PATH}: likeCOPYbut morefeatures.
Can copy from a URL or extract files from an archive.
-
STOPSIGNAL {SIGNAL}Specify the signal that will be used to stop the container.
Location in the file doesn’t matter.
Ex.
STOPSIGNAL SIGTERM. -
HEALTHCHECK CMD {COMMAND}: create a custom health check.By default Docker monitoring is insufficient because it just uses exit
codes.
Ex:
HEALTHCHECK CMD curl localhost:80
Example 1 (nearly bare minimum)
##Simple Nginx image
FROM ubuntu:bionic
ENV NGINX_VERSION 1.14.0-0ubuntu1.3
RUN apt-get update && apt-get install -y curl
RUN apt-get update && apt-get install -y nginx=$NGINX_VERSION
CMD ["nginx","-g","daemon off;"]
Gotchas
Every time an image is re-built, only the layers that have changed are
re-built. Consider the following:
RUN apt-get update && apt-get install -y curl
RUN apt-get update && apt-get install -y nginx
If we re-built the image with a different version of Nginx, the layer created
to install curl would not re-build, hence, the need to include
apt-get update in both RUN statements.
Efficiency
-
Put things that are less likely to change on lower-level layers.
-
Don’t create unnecessary layers.
-
Avoid including any unnecessary files, packages, etc, in the image.
Multi-Stage Builds
A multi-stage build uses multiple FROM directives. Multi-stage builds can help
in building more efficient images.
For example, if we were to compile within a single-stage build, the compiler
binaries would be included in the resulting image.
To avoid this, we can copy files from one stage to another using COPY --from.
Example:
FROM golang:1.12.4 AS compiler
WORKDIR /helloworld
COPY helloworld.go .
RUN GOOS=linux go build -a -installsuffix cgo -o helloworld .
FROM alpine:3.9.3
WORKDIR /root
COPY --from=compiler /helloworld/helloworld/ .
## from=0 could also be used
CMD ["./helloworld"]
The resulting Docker image is 7MB, rather than 700MB using a single-stage
build.
Flattening an Image
Images with fewer layers can have better performance. Images can be flattened
using the following process:
-
Run a container from the image.
-
docker export {CONTAINER_NAME} > {FILE_NAME}.tar -
cat {FILE_NAME}.tar | docker import - {NEW_IMAGE_NAME:TAG} -
An appropriate TAG might be latest.
Docker installation in Debian
Uninstall old versions
sudo apt-get remove docker docker-engine docker.io containerd runc
Set up repos
Dependencies:
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
GPG key:
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
Verify that you now have the key with the fingerprint 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88:
sudo apt-key fingerprint 0EBFCD88
Add repos:
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
Install docker:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
Enable a user to use Docker:
sudo usermod -aG docker james
newgrp docker # activate the new group
Changing Docker Versions
Upgrading Docker
apt-get install -y docker-ce=5:18.09.5~3-0~ubuntu-bionic \
docker-ce-cli=5:18.09.5~3-0~ubuntu-bionic
docker version
- No need to worry about existing containers.
Downgrading Docker
systemctl stop docker
apt remove -y docker-ce docker-ce-cli
apt update
apt install -y docker-ce=5:18.09.4~3-0~ubuntu-bionic \
docker-ce-cli=5:18.09.4~3-0~ubuntu-bionic
docker version
- This will not impact containers.
Docker Installation on CentOS
sudo yum install device-mapper-persistent-data lvm2
CentOS 7:
sudo yum-config-manager \
--add-repo https://download.docker.com/linux/centos/docker-ce.repo
CentOS 8:
dnf config-manager \
--add-repo https://download.docker.com/linux/centos/docker-ce.repo
List versions: yum list docker-ce --showduplicates | sort -r
Install a specific version:
sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
--nobestwas necessary because the optimal version of containerd wasn’t available.
Add a user to the Docker group to enable the use of Docker command
sudo usermod -aG docker james
newgrp docker # activate the new group