1. Introduction
When creating a sandbox environment or setting up a Jenkins agent in a Jenkins master container, running docker commands in a Docker container may be necessary. But while installing Docker in a container is pretty straightforward, setting up a connection to dockerd (Docker daemon) requires a few extra steps.
In this tutorial, we’ll see how to install Docker in an Alpine container. We’ll also talk about how to connect to Docker daemon from an Alpine container.
2. Installation From the Command Line
To install Docker in an Alpine container from the host’s command line, we’ll create the container first. While creating the container, we’ll mount the host system’s Docker daemon socket (/var/run/docker.sock) into the container. Then we’ll install Docker in the container from the container’s command line.
We’ll start by creating and entering our Alpine container:
$ docker run -it --name alpine-docker -v "/var/run/docker.sock:/var/run/docker.sock:rw" alpine
Let’s now install Docker in our Alpine container using apk:
# apk add docker
After installing Docker, we can confirm its installation:
# docker version
Client:
Version: 20.10.24
API version: 1.41
...truncated...
Server: Docker Engine - Community
Engine:
Version: 23.0.5
API version: 1.42 (minimum version 1.12)
...truncated...
If we didn’t mount the host’s Docker daemon socket into the container, the output above would have an error message:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
When we install Docker in an Alpine container, the Docker daemon doesn’t initialize automatically. This is why we’d have gotten the error message above.
To initialize the Docker daemon in the container, we’ll have to go through a few extra steps, which can get a bit complicated. So, instead of doing things the hard way, we opted to use the host’s Docker daemon socket instead.
However, using the host’s /var/run/docker.sock comes with potential risks. Since the container’s base user is root, it will have extensive access to the host’s Docker daemon. Of course, this can be pretty bad if the container has security vulnerabilities. But creating our container with a modified low-level container runtime like sysbox minimizes such risks.
Sysbox creates containers with enhanced isolation and the ability to run system software (like VMs). As a result, the containers it creates can readily run all docker components – including Docker daemon – without gaining privileged access to the host.
3. Installing From a Dockerfile
In place of the command line, we can write a Dockerfile that builds an Alpine container with Docker already installed:
$ cat Dockerfile
FROM alpine
RUN apk add docker
Then we’ll build a container image named alpine-docker with our Dockerfile:
$ docker build -t alpine-docker .
Let’s now create a container from the image while mounting the host’s Docker daemon socket into the container:
$ docker run -it --name alpine-docker -v "/var/run/docker.sock:/var/run/docker.sock:rw" alpine-docker
Once in the container, we can confirm our installation:
# docker version
Client:
Version: 20.10.24
API version: 1.41
...truncated...
Server: Docker Engine - Community
Engine:
Version: 23.0.5
API version: 1.42 (minimum version 1.12)
...truncated...
Creating an Alpine image with Docker preinstalled allows us to spin up our containers in fewer steps. At the least, we wouldn’t have to run apk add docker each time we want a container with Docker installed in it.
4. Conclusion
In this article, we saw how to install Docker in an Alpine container from the host’s command line and from a Dockerfile.
While mounting the host’s daemon socket into a container can be risky because of the container’s privileged access, we can avoid the potential risks by creating our container with a specialized container runtime like sysbox.