Run nested docker daemon (Docker-in-Docker)

in #docker4 years ago

Create a container image for Docker-in-Docker

"Docker-in-docker" is an often useful technique to help running multiple instances of container clusters (e.g. swarm services, docker-compose) on the same container host. It consists of:

  • A conventional container image with docker server daemon and client utilities.
  • A privileged container running the image, which has its entrypoint script to start the nested docker server daemon.
  • Other techniques applied optionally to enhance the server daemon performance.

Let's build the container image. First, prepare a utility script called modprobe:

set -eu

# "modprobe" without modprobe:
for module; do
        if [ "${module#-}" = "$module" ]; then
                ip link show "$module" >/dev/null 2>&1 || true

export PATH='/usr/sbin:/usr/bin:/sbin:/bin'
modprobe "$@" >/dev/null 2>&1 || true

Next, create the entrypoint script that launches docker daemon:

#!/usr/bin/env bash
set -euo pipefail

# Prepare the container for docker-in-docker operation, the routine is taken from
export container=docker
if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then
        mount -t securityfs none /sys/kernel/security || {
                echo >&2 'Could not mount /sys/kernel/security.'
                echo >&2 'AppArmor detection and --privileged mode might break.'
if ! mountpoint -q /tmp; then
        mount -t tmpfs none /tmp

# Optimise docker daemon performance by presenting it with a cache directory, backed by a conventional file system, so that the docker daemon can put file system layers in it.
mkdir -p "/mnt/storage"
find "/mnt/storage/" -maxdepth 1 -mindepth 1 -type d -not -name "builder" -not -name "image" -not -name "overlay2" -exec rm -rf {} \;
if ! mountpoint -q /mnt/storage; then

# Use setsid to detatch the docker daemon from the terminal, otherwise it often catches a wild signal and terminates prematurely.
setsid dockerd -p "/var/run/" -l debug --data-root "/mnt/storage" $storage_args &>>/tmp/dockerd.log &

# Wait for docker daemon and its firewall to get ready
while [ ! -f '/var/run/' ] || [ ! -e '/var/run/docker.sock' ] || ! (iptables -L FORWARD | grep -q DOCKER-USER); do
    sleep 0.1

# Execute the container launch parameters
exec "$@"

And finally the Dockerfile builds the image:

FROM ubuntu:18.04

ENV DEBIAN_FRONTEND noninteractive

# Install docker and its dependencies. "kmod" is used by /usr/local/bin/modprobe via "lsmod" command, which is itself a dependency of dind.
RUN apt-get update && apt-get install -q -y -f -m -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef busybox ca-certificates curl iproute2 kmod
COPY modprobe /usr/local/bin/modprobe
RUN chmod 755 /usr/local/bin/modprobe

# Optional: install docker-compose for development and testing activities
RUN curl -L "" -o /usr/local/bin/docker-compose && chmod 755 /usr/local/bin/docker-compose

# The entrypoint will start the nested docker server daemon
RUN chmod 755 /

Give it a go:

sudo docker build -t my-dind .
sudo docker run -it --rm --privileged my-dind bash
# In docker-in-docker container bash shell:
docker run --rm hello-world

If you are using this docker-in-docker image on a Linux host, you can help speeding up its operations by offering it a cache directory to work with:

mkdir docker-cache
sudo docker run -it --rm --privileged -v "$(readlink -f docker-cache)":/mnt/storage my-dind bash

And enjoy!

Coin Marketplace

STEEM 0.26
TRX 0.10
JST 0.031
BTC 39566.67
ETH 2152.73
USDT 1.00
SBD 5.25