In dem Artikel Gitlab + Jenkins + buildpipeline + Maven + Java Docker Umgebung für Entwickler habe ich beschrieben wie ein Jenkins über Docker aufgesetzt werden kann. Ziel ist es jetzt über den Jenkins über die Buildpipeline ein Dockerfile zu starten und dort einen Befehl auszuführen.

Docker in Docker Zugriff über tcp
Der Zugriff docker über tcp ist am flexibelsten. Der Docker Host/Server kann überall betrieben werden. Grundsätzlich muss dafür nur ein Umgebungsvariable im container gesetzt werden.
DOCKER_HOST: tcp://host.docker.internal:2375
Unter Windows muss in den Docker Einstellungen der Port freigegeben werden. Unter Ubuntu 20.04 müssen eine Reihe von Konfigurationen vorgenommen werden. Zuerst muss die Datei /etc/systemd/system/docker.service.d/override.conf erstellt werden
mkdir -p /etc/systemd/system/docker.service.d
nano /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
Danach muss in der /etc/docker/daemon.json der port und socket frei gegeben werden.
echo '{"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]}' > /etc/docker/daemon.json
Nun kann docker neu gestartet werden.
systemctl daemon-reload && systemctl restart docker.service
Damit der Dockerhost nicht als IP angegeben werden muss, sollte eine Docker Version größer gleich 20.10 verwendet werden.
$ docker --version
Docker version 20.10.5, build 55c4c88
Diese habe ich auch im Jenkins Docker image verwendet. Da dies auf debian 10 basiert, wird es wie folgt installiert:
RUN apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
RUN apt update
RUN apt install -y docker-ce docker-ce-cli containerd.io
Unter Windows ist automatisch der Host unter den DNS Namen host.docker.internal erreichbar. Unter Windows muss dies im extra_host Bereich angegeben werden.
version: "3.3"
services:
jenkins:
container_name: jenkins
environment:
JAVA_OPTS: -Djenkins.install.runSetupWizard=false -Xmx8192m
DOCKER_HOST: tcp://host.docker.internal:2375
ports:
- 8080:8080
- 50000:50000
volumes:
- ./data/jenkins:/var/jenkins_home
docker container over socket
build:
context: ./jenkins
extra_hosts:
- host.docker.internal:host-gateway
Docker in Docker Zugriff über Socket (Alternative)
Als alternative zu der tcp Verbindung kann man docker auch über socket verbinden.
volumes:
- /var/run/docker.sock:/var/run/docker.sock
In Wirklichkeit läuft docker auf dem Host System und wird über einen Unix socket bereitgestellt. Dies funktioniert auch unter Windows mit WSL2. Dieser Socket ist mit bestimmten Rechten angelegt. Da der Jenkins im container nicht mit root Rechten läuft kann dieser Standardmäßig nicht zugreifen. Hier sind die Rechte an die groupid 998 gebunden:
$ docker exec -it jenkins ls -ls /var/run/docker.sock
0 srw-rw---- 1 root 998 0 Apr 5 04:49 /var/run/docker.sock
Es gibt grundsätzlich zwei Möglichkeiten das Problem zu lösen. Entweder im docker container die Rechte des socket anpassen:
docker exec --user root -it jenkins chmod 666 /var/run/docker.sock
Oder die UserId (id -u) und die group des Hostsystem verwenden. Weiterer Vorteil ist, dass die Dateien mit den Rechten des Benutzers des Hostsystems erstellt werden und so volumes gemeinsam genutzt werden können. Dazu muss nur in der dokcer-compose.yml „user: 1000:998“ für UserId 1000 und GroupId 998 hinzugefügt werden:
version: "3.3"
services:
jenkins:
container_name: jenkins
environment:
JAVA_OPTS: -Djenkins.install.runSetupWizard=false -Xmx8192m
ports:
- 8080:8080
- 50000:50000
user: 1000:998 # Local user and groupid to run docker in docker
volumes:
- ./data/jenkins:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock # To run docker in docker container
build:
context: ./jenkins
Jenkins docker pipeline
Damit Docker im Jenkins benutzt werden kann, muss im Jenkins image auch docker installiert werden:
RUN apt install -y docker.io
Und den Docker Support für jenkins und der pipeline.
RUN jenkins-plugin-cli --plugins docker-workflow:1.26 docker-plugin:1.2.2
Als Beispiel verwende ich eine Dockerfile in der ein wget installiert wird.
FROM ubuntu:20.04
RUN apt update && apt upgrade
RUN apt install -y wget
Dazu lege ich ein Jenkinsfile in der das Dockerfile verwendet wird und ein wget auf eine URL aufgerufen wird:
pipeline {
agent { dockerfile true }
stages {
stage('Run wget') {
steps {
sh 'wget https://nissel.it'
}
}
}
}

Links
https://dev.to/acro5piano/specifying-user-and-group-in-docker-i2e
https://faun.pub/set-current-host-user-for-docker-container-4e521cef9ffc
https://gist.github.com/styblope/dc55e0ad2a9848f2cc3307d4819d819f
https://stackoverflow.com/questions/29076194/using-add-host-or-extra-hosts-with-docker-compose
https://medium.com/nttlabs/docker-20-10-59cc4bd59d37
https://stackoverflow.com/questions/48546124/what-is-linux-equivalent-of-host-docker-internal