Docker ist eine Open-Source-Software zur Isolierung von Anwendungen mit Containervirtualisierung.
Docker vereinfacht die Bereitstellung von Anwendungen, weil sich Container, die alle nötigen Pakete enthalten, leicht als Dateien transportieren und installieren lassen. Container gewährleisten die Trennung und Verwaltung der auf einem Rechner genutzten Ressourcen. Das beinhaltet laut Aussage der Entwickler: Code, Laufzeitmodul, Systemwerkzeuge, Systembibliotheken – alles was auf einem Rechner installiert werden kann.
Vorteile:
Nachteile:
geeignet:
eher nicht
siehe auch DevOps Disasters.
Bevorzugte Methode ist die Einbindung via repo, fast immer ist die bei Distributionen mitgelieferte Version zu alt.
Siehe Installationshandbuch, z.B. Ubuntu oder Debian.
Aktuellen Benutzer zur Gruppe docker hinzufügen
sudo usermod -aG docker $USER
→ ausloggen und einloggen nötig
Docker braucht mindestens die Kernel Version 3.10. Ob die richtigen Konfiguration aktiviert ist, lässt sich mit dem Skript check-config.sh überprüfen:
curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh bash ./check-config.sh
bei Ubuntu 18.04 nötig:
/etc/default/grub
:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash cgroup_enable=memory swapaccount=1"
sudo update-grub
Optional:
CONFIG_MEMCG_SWAP_ENABLED
: missing (cgroup swap accounting is currently enabled) → OKCONFIG_RT_GROUP_SCHED
: missing https://stackoverflow.com/questions/46563332/docker-daemon-container-real-time-scheduling-with-ubuntu-linux-hostdocker run -m=4096000000 -d -t busybox sleep 3600
cat /sys/fs/cgroup/memory/docker/$ID/memory.limit_in_bytes
…mit tatsächlichem Prozess (Quelle https://stackoverflow.com/questions/20096632/limit-memory-on-a-docker-container-doesnt-work):
sudo docker -m 512M -it ubuntu /bin/bash sudo apt-get update && apt-get install -y build-essential vim
→ vim foo.c:
#include <stdlib.h> #include <stdio.h> int main(void) { int i; for (i=0; i<65536; i++) { char *q = malloc(65536); printf ("Malloced: %ld\n", 65536*i); } sleep(9999999); }
Compile the file
gcc -o foo foo.c ./foo
Beispiel: lokal nur noch das Netz 10.254.0.1/16 benutzen:
(als root) echo '{"bip": "10.254.0.1/16"}' > /etc/docker/daemon.json sudo systemctl restart docker.service
docker network prune
sudo brctl show
Netze die sich mit Docker überlappen (172.17., …) sind ein Problem. Die Config „bip“: „10.4.0.1/16“ in der /etc/docker/daemon.json funktioniert nur auf swarm korrekt.
Bei docker standalone wird diese Einstellung beim Anlegen weiterer docker networks ignoriert. Auch andere Einstellungen wie default-address-pools oder fixed-cidr funktioniered in dieser Beziehung nicht.
Es gibt nur einen funktionierenden workaround: explizit eine route im System setzen:
Beispiel Redhat (ens192 mit Standard-GW 172.17.0.1) z.B. mit /etc/sysconfig/network-scripts/route-ens192
(ip-Befehlssyntax)):
172.17.0.0/16 via 172.17.0.1 dev ens192
Bei Swarm muss initial die docker_gwbridge angepasst werden - funktioniert nur wenn docker nicht bereits Teil eines swarm-clusters ist (er behält die Einstellungen bei wenn bereits vorhanden):
docker network rm docker_gwbridge
docker network create \ --subnet 10.4.0.0/16 \ --opt com.docker.network.bridge.name=docker_gwbridge \ --opt com.docker.network.bridge.enable_icc=false \ --opt com.docker.network.bridge.enable_ip_masquerade=true \ docker_gwbridge
https://docs.docker.com/v17.09/engine/swarm/networking/#customize-the-docker_gwbridge
docker wendet für Gäste das Profil docker-default an.
Dazu wird beim Starten eines Containers ein docker-default template in tmpfs abgelegt und von dort in den Kernel geladen.
Oder es wird das Profil benutzt, das via security-opt angegeben wurde:
docker run --rm -it --security-opt apparmor=docker-default hello-world
.
Für den docker daemon selbst wird aktuell kein apparmor-Profil geladen.
docker -v
Container:
docker exec -ti $ID bash
Container exposen via Netzwerk:
docker run -d -p host_ip:host_port:container_port --name NAME image_name
Beispiel (offizielles redis-image):
docker run -d -p 6379:6379 --name redis redis
Netzwerke
docker network ls
docker network create my_network
docker network inspect my_network
--net my_network
oder über yml-Datei
Wenn Container nach Außen exposed werden, legt Docker entsprechende Iptables-Regeln an. Diese lassen sich leider nicht anpassen, nur das anlegen von iptables-Regeln abschalten mit –iptables=false
.
Docker (ab Version 17.06) hat aber für iptables die chain DOCKER-USER erdacht. Dort hinterlegte Regeln werden zuerst durchlaufen und von docker in Ruhe gelassen. Dort können also Regel hinterlegt und permanent gemacht werden (mittels mit iptables-persistent).
Beispiel für Port 9200 (Elasticsearch):
iptables -I DOCKER-USER 1 -i eth0 -s 91.213.91.0/24 -p TCP --dport 9200 -j ACCEPT iptables -I DOCKER-USER 2 -i eth0 -p TCP --dport 9200 -j LOG --log-prefix "Port9200ExtBlock:" --log-level 6 iptables -I DOCKER-USER 3 -i eth0 -p TCP --dport 9200 -j REJECT
Löschen wäre (hier Regel nr.1):
iptables -D DOCKER-USER 1 usw.
Auflistung aktuell gültiger Regeln:
iptables -L -n --line-numbers
Container auflisten:
docker ps --no-trunc
docker ps -l -q
Logging ist bei docker vielfältig konfigurierbar.
docker logs $ID
„docker logs“ funktioniert abwer nicht mit anderen loggin driver als json-file und journald.
überschreibt den entrypoint aus dem image, einloggen mit bash ist möglich:
docker-compose.yml
:
entrypoint: - "/usr/bin/tail" - "-f" - "/dev/null"
compose-File liegt hier: /srv/compose-files/meinDocker-service.yml
unitfile: /etc/systemd/system/meinDocker.service
:
[Unit] Description=meinDockerService Requires=docker.service After=docker.service [Service] Restart=always #User=root #Group=docker # add group to user: usermod -a -G docker <user> # Shutdown container (if running) when unit is stopped ExecStartPre=/usr/bin/docker-compose -f /srv/compose-files/meinDocker-service.yml down -v # Start container when unit is started ExecStart=/usr/bin/docker-compose -f /srv/compose-files/meinDocker-service.yml up # Stop container when unit is stopped ExecStop=/usr/bin/docker-compose -f /srv/compose-files/meinDocker-service.yml down -v [Install] WantedBy=multi-user.target
Dienst laden, starten und autostart aktivieren:
systemctl daemon-reload
systemctl start docker-datadog.service
systemctl enable docker-datadog.service
Docker images anzeigen:
docker image ls
Dockerfile ↔ Docker Compose file image bauen ↔ image deployen
Beispiele:
docker run -d -ti ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
docker run -d ubuntu /bin/sh -c "apt-get install -y hello"
docker run hello/world
image erstellen (mit Dockerfile):
image aus docker-compose-file erstellen:
docker-compose -f $COMPOSE-FILE.yaml up
image aus Dockerfile erstellen:
FROM base RUN apt-get install hello CMD hello
docker build $USER/$IMAGENAME .
docker build -t meineHalloApp .
image aus laufendem container erstellen
docker run -d ubuntu /bin/sh -c "apt-get install -y hello"
docker commit -m "1st container" $ID blindguardian/hallo
Schlecht:
gut:
neben der öffentluchen Standard-registry von hub.docker.com können weitere registries verwendet werden. Insbesondere wenn die images privat sein sollen und um zu verhindern das durch externe Fehler Applikationen ihren Betrieb einstellen.
docker login https://meine.registry.de
unsichere registries sollten nicht benutzt werden, aber falls weder verschlüsselung noch Login gewünscht ist:
Datei /etc/docker/daemon.json:
{ "insecure-registries": ["unsicher.registry.de:80"] }
Restart Docker:
sudo systemctl restart docker.service
docker commit -m "Beschreibung" $ID user/repo1
docker login $URL
docker push Benutzer/projekt
docker rm $short_id
Methode 1 (docker save):
docker save --output="registry-cleanup.tar" $IMAGE
$IMAGE z.B. hello/world, das tar-archiv enthält aber „nur“ die einzelnen layer (in jeweils tar-Archiven)
Methode 2 (docker export) (laufendes image exportieren):
docker export --output="latest.tar" $NAME
docker export $NAME | gzip > NAME.tar.gz
aus tar.gz-Archiv:
zcat NAME.tar.gz | docker import - NAME
docker pull php:5.6-apache-jessie
docker pull php
docker pull 4.9-php7.0-apache
docker pull 4-php7.0
Im Gegensatz zu den flüchtigen Containern sind volumes geeignet permanente Daten aufzunehmen („persistent storage“).
-v /tmp/data:/data
docker volume create --name vol1
docker run -d -v vol1:/container/pfad/für/das/volume container_image my_Befehl
docker volume ls
docker volume inspect volume_name
docker volume ls -f dangling=true
docker volume prune
docker volume rm <volume name>
networking:
v4/v6/dualstack:
docker secret ls
mkfs.xfs -f -n ftype=1 $disk
docker vsphere plugin4):
docker plugin install --grant-all-permissions --alias vsphere vmware/vsphere-storage-for-docker:latest
docker stack deploy -c stack-compose.yml $stackname --with-registry-auth
docker stack ps $stackname -f 'desired-state=Running' | grep Running
docker service rollback $service-name
docker stack rm $stackname
docker service rm $service-name
docker service update --image $docker-registry/path/to/image:$TAG $service-name --with-registry-auth
traeffik
docker service scale $service=4
Leider nimmt docker keine automatischen Aufräumarbeiten vor. Deshalb muss manuell (ggf. via cronjobs) nachgeholfen werden:
/etc/cron.d/cleanimages
0 4 * * * root docker rmi $(docker images -q) > /dev/null
etwas radikaler ist der folgende Befehl:
docker system prune -a WARNING! This will remove: - all stopped containers - all networks not used by at least one container - all images without at least one container associated to them - all build cache Are you sure you want to continue? [y/N] y
Docker hat beim Design IPv6 nicht berücksichtigt und daher schlagen dessen Simplifizierungen (Ports durch NAT via iptables nach Außen geben und interne DNS-Auflösung) in Nachteile um. docker unterstützt kein SLAAC oder DHCPv6 und kann daher nicht selbstständig Präfixe beziehen, d.h. es müssen statische IPs zugewiesen werden und diese via DNS propagiert werden (DNS auf die öffentliche IP des docker-Hosts reicht nicht mehr!). Hiermit werden jedoch sämtliche Ports öffentlich zugänglich gemacht (nicht nur diese via EXPOSE) und müssen daher via firewall abgesichert werden.
NAT66 (analog zu v4) mit Adressen aus dem Unique Local Addresses (ULA)-Bereich fd00::/8 Adressen ist eine schlechte Idee und führt zu Problemen, auch der userland-proxy ist Mist.
Funktionierende Lösungen:
/etc/docker/daemon.json
: { "ipv6": true, "fixed-cidr-v6": "2001:db8:1::/64" }
Quellen: https://docs.docker.com/network/drivers/macvlan/#use-ipv6
Siehe auch: https://heikorichter.name/post/123/docker-und-ipv6/
Für IPv6-only ist dank der Rückständigkeit bei Dritten (Docker registry, Github etc.) Krücken wie DNS64 + NAT64 nötig.
Zum orchestrieren von docker container steht das integrierte docker swarm zur Verfügung. Mittlerweile hat sich Kubernetes für diese Aufgaben etabliert, da es deutlich mehr Funktionen (aber auch mehr Komplexität) mit sich bringt.
Initialiseren:
docker swarm init
"Swarm initialized: current node (n352jdg9eoml9nh5ak574izs5) is now a manager. To add a worker to this swarm, run the following command: <code bash>docker swarm join --token SWMTKN-1-4a8ilhha4g652ll28ecg7z4pyxdvglnicufxlj2mh5l5ly5nm3-7s7yvbki7m9pfi2yzafter6l6 10.10.0.23:2377</code> To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions."
In diesem Beispiel reicht zum Beitritt
docker swarm join --token SWMTKN-1-4a8ilhha4g652ll28ecg7z4pyxdvglnicufxlj2mh5l5ly5nm3-7s7yvbki7m9pfi2yzafter6l6 10.10.0.23:2377
docker swarm join-token manager
Aus dem docker swarm austreten:
docker swarm leave
docker swarm leave --force
*Informationen über sich selbst anzeigen:
docker node inspect self
Nodes sind
docker node update --availability drain node1
docker node ls
Manager sind
docker stack ls
NAME SERVICES content-sync 2
Labels: (wo fährt was hoch?), Beispiel: nur hochfahren wenn label db1 auf der node vorhanden ist:
deploy: placement: constraints: - node.labels.db1 == TRUE
docker node update --label-add LABEL1
docker node update --label-add LABEL2=TEST node1
docker-machine ist ein tool um docker-Hosts zu provisionieren (auf denen dann docker container laufen können).
alias dl='docker ps -l -q'
docker volume rm $(docker volume ls -f dangling=true -q)siehe auch: https://stackoverflow.com/questions/18496940/how-to-deal-with-persistent-storage-e-g-databases-in-docker?rq=1