Docker in Docker über Jenkins pipeline

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.

Jenkins Pipeline mit Docker Container

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'
            }
        }
    }
}
Docker pipleine Konsolen Ausgabe

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

https://docs.docker.com/engine/install/debian/

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.