Content
1. Fundamentals
1.1. Concepts
- Union file system (UFS): allows to overlay multiple file systems appearing as a single system whereby equal folders are merged and equally named files hide their previous versions
- Image: a portable read-only file system layer optionally stacked on a parent image
- Dockerfile: used to
buildan image and declare the command executed in the container - Registry: is the place where to
pushandpullfrom named / tagged images - Container: an instance of an image with a writable file system layer on top, virtual networking, ready to execute a single application
- Volume: a directory outside the UFS that can be mounted inside containers for persistent and shared data
- Network: acts as a namespace for containers
- Service: a flexible number of container replicas running on a cluster of multiple hosts
1.2. Lifecycle
A typical docker workflow:
buildan image based on aDockerfiletagandpushthe image to a registryloginto the registry from the runtime environment topullthe image- optionally
createavolumeor two to provide configuration files and hold data that needs to be persisted runa container based on the imagestopandstartthe container if necessarycommitthe container to turn it into an image (note: makes the image harder to reproduce)- in exceptional situations,
execadditional commands inside the container - to replace a container with an updated version:
pullthe new image from the registrystopthe running container- backup your volumes to be prepared for a potential rollback
runthe newer one by specifying a temporary name- if successful,
removethe old container andrenamethe new one accordingly
2. Recipes
2.1. Docker Engine
Show docker disk usage
docker system df
Remove unused data
docker system prune
This prompts for confirmation and will remove: all stopped containers, all volumes not used by at least one container, all networks not used by at least one container and all dangling images
2.1.1. Building Images
Debug image build
docker image buildshows the IDs of all temporary containers and intermediate images- use
docker container run -it IMAGE_IDwith the ID of the image resulting from the last successful build step and try the next command manually
List all local tags for the same image
docker image ls --no-trunc | grep $(docker image inspect -f {{.Id}} IMAGE:TAG)
2.1.2. Running Containers
Start container and run command inside
docker container run -it ubuntu:14.04 /bin/bash
Start a shell in a running container
docker container exec -it CONTAINER /bin/bash
Start a container as another user
docker container run -u root IMAGE
List all existing containers
docker container ls -a
List running processes inside a container
docker container top CONTAINER
Follow the logs
docker container logs -f --tail=1000 CONTAINER
Stop all running containers
docker container stop $(docker container ls -q)
Remove all stopped containers, except those suffixed ‘-data’:
docker container ls -a -f status=exited | grep -v '\-data *$'| awk '{if(NR>1) print $1}' | xargs -r docker container rm
Remove all stopped containers (warning: removes data-only containers too)
docker container prune
List all images
docker image ls -a
Remove all unused images
docker image prune
Show image history of container
docker image history --no-trunc=true $(docker container inspect -f '{{.Image}}' CONTAINER)
Show file system changes compared to the original image
docker container diff CONTAINER
Backup directory content from container to host directory
docker container run --rm --volumes-from SOURCE_CONTAINER:ro -v $(pwd):/backup alpine \
tar cvf /backup/backup_$(date +%Y-%m-%d_%H-%M).tar /data
Restore directory content to container from host directory
docker container run --rm --volumes-from TARGET_CONTAINER:ro -v $(pwd):/backup alpine \
tar xvf /backup/backup.tar
Show names of volumes used by a container
docker container inspect -f '{{ range .Mounts }}{{ .Name }} {{ end }}' CONTAINER
Show names and mount point destinations of volumes used by a container
docker container inspect -f '{{ range .Mounts }}{{ .Name }}:{{ .Destination }} {{ end }}' CONTAINER
Start all paused / stopped containers
- does not work together with container dependencies
Edit and update a file in a container
docker container cp CONTAINER:FILE /tmp/ && docker container run --name=nano -it --rm -v /tmp:/tmp \
piegsaj/nano nano /tmp/FILE ; \
cat /tmp/FILE | docker container exec -i CONTAINER sh -c 'cat > FILE' ; \
rm /tmp/FILE
Deploy war file to Apache Tomcat server instantly
docker container run -i -t -p 80:8080 -e WAR_URL=“<http://web-actions.googlecode.com/files/helloworld.war>” \
bbytes/tomcat7
Dump a Postgres database into current directory on the host
echo "postgres_password" | sudo docker container run -i --rm --link db:db -v $PWD:/tmp postgres:8 sh -c ' \
pg_dump -h ocdb -p $OCDB_PORT_5432_TCP_PORT -U postgres -F tar -v openclinica \
> /tmp/ocdb_pg_dump_$(date +%Y-%m-%d_%H-%M-%S).tar'
Backup data folder
docker container run --rm --volumes-from oc-data -v $PWD:/tmp piegsaj/openclinica \
tar cvf /tmp/oc_data_backup_$(date +%Y-%m-%d_%H-%M-%S).tar /tomcat/openclinica.data
Restore volume from data-only container
docker container run --rm --volumes-from oc-data2 -v $pwd:/tmp piegsaj/openclinica \
tar xvf /tmp/oc_data_backup_*.tar
Get the IP address of a container
docker container inspect -f '{{ .NetworkSettings.IPAddress }}' CONTAINER
Reconstruct docker run command for existing container
At the time of writing, there is no way to reconstruct the exact run command that was used to create a container. However, there are third-party tools like runlike that attempt to reverse engineer all parameter values:
docker container run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike CONTAINER
Caution, the output also contains some implicitly added parameters, that were not used in the original command line. So please evaluate all of them with care.
2.1.3. Using Volumes
Declare a volume via Dockerfile
RUN mkdir /data && echo "some content" > /data/file && chown -R daemon:daemon /data
VOLUME /data
after the VOLUME directive, its content can not be changed within the Dockerfile
Create an anonymous volume at runtime
docker container run -it -v /data debian /bin/bash
Create a volume at runtime that is bound to a host directory
docker container run --rm -v /tmp:/data debian ls -RAlph /data
Create a named volume and use it
docker volume create --name=test
docker container run --rm -v test:/data alpine sh -c 'echo "Hello named volumes" > /data/hello.txt'
docker container run --rm -v test:/data alpine sh -c 'cat /data/hello.txt'
List the content of a volume
docker container run --rm -v data:/data alpine ls -RAlph /data
Copy a file from host to named volume
echo "debug=true" > test.cnf && \
docker volume create --name=conf && \
docker container run --rm -it -v $(pwd):/src -v conf:/dest alpine cp /src/test.cnf /dest/ && \
rm -f test.cnf && \
docker container run --rm -it -v conf:/data alpine cat /data/test.cnf
Copy content of existing volume to a new named volume
docker volume create --name VOL_B
- than:
docker container run --rm -v VOL_A:/source/folder:ro -v VOL_B:/target/folder \
alpine cp -r /source/folder /target
or without the need for an intermediate directory (cp implementations differ):
docker container run --rm -v VOL_A:/source:ro -v VOL_B:/target debian cp -TR /source /target
List all orphaned volumes
docker volume ls -qf dangling=true
Remove all orphaned volumes
docker volume rm $(docker volume ls -qf dangling=true)
Caution, this also removes named volumes that are currently not mounted by any container!
Show names and mount point destinations of volumes used by a container
docker container inspect \
-f ': ' \
CONTAINER
2.2. Docker Machine
On a local VM
Get the IP address of the virtual machine for access from host
docker-machine ip default
Add persistent environment variable to boot2docker
echo 'echo '\''export ENVTEST="Hello Env!"'\'' > /etc/profile.d/custom.sh' | \
sudo tee -a /var/lib/boot2docker/profile > /dev/null
and restart with docker-machine restart default
Install additional linux packages in boot2docker
- create the file
/var/lib/boot2docker/bootsync.shwith a content like:
#!/bin/sh
sudo /bin/su - docker -c 'tce-load -wi nano'
Recreate any folders and files on boot2docker startup
- store folders / files in
/var/lib/boot2docker/restore-on-bootand - create the file
/var/lib/boot2docker/bootsync.shwith a content like:
#!/bin/sh
sudo mkdir -p /var/lib/boot2docker/restore-on-boot && \
sudo rsync -a /var/lib/boot2docker/restore-on-boot/ /
2.3. Dockerfile
Add a periodic health check
HEALTHCHECK --interval=1m --timeout=3s --retries=5 \
CMD curl -f <http://localhost/> || exit 1
- see also: HEALTHCHECK
2.4. Logging
Enable log rotation for Docker
- create the file
/etc/logrotate.d/dockerand insert:
/var/lib/docker/containers/*/*.log {
daily
rotate 14
compress
delaycompress
missingok
copytruncate
}
- check
/etc/cron.daily/logrotateand/etc/crontabfor general logrotate configuration.
This example will keep all container logs for 14 days.
3. Showcases
3.1. Private Docker Registry
Setup with boot2docker or natively on Linux
printf "\nPulling registry image ...\n" && \
docker image pull registry ; \
printf "\nPreparing registry-cert volume ...\n" && \
docker volume create --name=registry-cert && \
cd /tmp && \
openssl genrsa -out registry.key 4096 && \
openssl req -new -nodes -sha256 -subj '/CN=localhost' -key /tmp/registry.key -out /tmp/registry.csr && \
openssl x509 -req -days 3650 -signkey /tmp/registry.key -in /tmp/registry.csr -out /tmp/registry.pem && \
docker container run --rm -it -v /tmp:/from -v registry-cert:/to --entrypoint sh registry \
-c 'cp /from/registry.key /to && cp /from/registry.pem /to' && \
printf "\nLetting docker client trust certificate ...\n" && \
if [ -d /var/lib/boot2docker ] ;
then
sudo mkdir -p /var/lib/boot2docker/certs && \
sudo cp /tmp/registry.pem /var/lib/boot2docker/certs
else
sudo mkdir -p /etc/docker/certs.d/localhost && \
sudo cp /tmp/registry.pem /etc/docker/certs.d/localhost
fi && \
printf "\nPreparing registry-auth volume (please change 'reg_user' and 'reg_password') ...\n" && \
docker volume create --name=registry-auth && \
docker container run --rm --entrypoint /bin/sh -v registry-auth:/auth registry \
-c 'htpasswd -Bbn reg_user reg_password > /auth/htpasswd' && \
printf "\nPreparing registry-data volume ...\n" && \
docker volume create --name=registry-data && \
printf "\nRunning registry container ...\n" && \
docker container run --name registry -h registry -d \
-v registry-data:/var/lib/registry \
-v registry-auth:/auth:ro \
-v registry-cert:/certs:ro \
--restart=always \
-p 5000:5000 \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.pem \
-e REGISTRY_AUTH=htpasswd \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
-e REGISTRY_STORAGE_DELETE_ENABLED=true \
registry
- this script is also available at http://docker.jens-piegsa.com/examples/setup-registry.sh
Usage example
docker image pull alpine:latest && \
docker login -u reg_user -p reg_password localhost:5000 && \
docker image tag alpine:latest localhost:5000/alpine:private && \
docker image rm alpine:latest && \
docker image push localhost:5000/alpine:private && \
docker image rm -f localhost:5000/alpine:private && \
docker image pull localhost:5000/alpine:private && \
docker logout localhost:5000 && \
docker image ls | grep alpine && \
printf "Deleting image from registry ...\n" && \
curl -X DELETE -u reg_user:reg_password --insecure \
https://localhost:5000/v2/alpine/manifests/$(docker image ls --digests | grep localhost:5000/alpine | awk '{print $3}')
Removal
docker container rm -f registry && \
docker volume rm registry-data registry-cert registry-auth
Further Reading
- advanced authentication: Docker Registry 2 authentication server
- developer notes about deleting images from the registry
3.2. Continuous Integration Tool Stack
Setup with docker-machine / boot2docker
-
make a directory called
ciin your home directory on your host system and change into it -
create a file named
docker-compose.ymlwith the following content:
version: "2"
services:
jenkins:
image: jenkins
ports:
- "8082:8082"
- "50000:50000"
restart: always
env_file: .env
environment:
- "JAVA_OPTS=-Dmail.smtp.starttls.enable=true -Dorg.apache.commons.jelly.tags.fmt.timeZone=Europe/Berlin"
- "JENKINS_OPTS=--httpPort=8082"
volumes:
- jenkins_home:/var/jenkins_home
nexus:
image: sonatype/nexus3
ports:
- "8081:8081"
restart: always
env_file: .env
volumes:
- nexus-data:/nexus-data
sonarqube:
image: sonarqube
ports:
- "9000:9000"
restart: always
env_file:
- .env
- sonarqube.env
environment:
- SONARQUBE_JDBC_URL=jdbc:postgresql://postgres:5432/sonar
- SONARQUBE_JDBC_USERNAME=sonar
volumes:
- sonarqube_conf:/opt/sonarqube/conf
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_bundled-plugins:/opt/sonarqube/lib/bundled-plugins
links:
- postgres
postgres:
image: postgres
ports:
- "5432:5432"
restart: always
env_file:
- .env
- sonarqube.env
environment:
- POSTGRES_USER=sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
volumes:
jenkins_home:
nexus-data:
sonarqube_conf:
sonarqube_data:
sonarqube_extensions:
sonarqube_bundled-plugins:
postgresql:
postgresql_data:
- create a second file named
.envthat defines a timezone:
TZ=Europe/Berlin
- create a third file named
sonarqube.envthat holds the database passwords:
SONARQUBE_JDBC_PASSWORD=sonar
POSTGRES_PASSWORD=sonar
Usage
- startup all containers:
docker-compose up -d
-
watch the logs, type
docker-compose logsordocker logs -f ci_jenkins_1 -
access the web applications:
- Jenkins on port 8082
- Sonatype Nexus on port 8081 and
- SonarQube on port 9000
Removal
- to remove the tool stack (incl. data), use:
docker-compose down -v
4. Best Practices
Docker Engine
docker execis your friend in development, but should be avoided in a production setup
Volumes
- use named volumes to simplify maintenance by separating persistent data from the container and communicating the structure of a project in a more transparent manner
Dockerfile
- always keep environment configuration and secrets out of deployments and images, for example by using environment variables (
-e,--env-file) - always set the
USERstatement, otherwise the container will run asrootuser by default, which maps to therootuser of the host machine - use
ENTRYPOINTandCMDdirectives together to make container usage more convenient - coalesce consecutive
RUNdirectives with&&to reduce the costs of a build and to avoid caching of instructions likeapt-get update - to reduce the size of an image, remove temporary resources in the same
RUNstatement that produces them (otherwise they are still present in an intermediate layer) - use
EXPOSEto document all needed ports - introduce an additional build Dockerfile for your app, if you have a large set of compile-time dependencies (build container pattern)
5. Additional Material
- Mouat, A. (2015). Using Docker: Developing and Deploying Software with Containers. O’Reilly Media. (German Edition: Docker. Software entwickeln und deployen mit Containern. dpunkt.verlag)
- Turnbull, J. (2016). The Docker Book. Containerization is the new Virtualization.
- Gupta, A. (2016). Docker Container Anti Patterns.
- Piegsa, J. (2016). Dockerbank 2 Workshop. Szenarien des Routinebetriebs. (German Slides)
- Official Docker Documentation
- StackOverflow Documentation
- Awesome Docker
- Docker Labs
- Docker Introduction
- play-with-docker.com
Contribute
Feel free to fork this project, send me pull requests, and issues through the project’s GitHub page.