Cześć wszystkim! W swoim , obiecałem opowiedzieć o uruchamianiu Dockera w Dockerze oraz o praktycznych aspektach tego zajęcia. Nadszedł czas, aby dotrzymać obietnicy. Doświadczony DevOps zapewne zaprotestuje, że ci, którzy potrzebują Dockera w Dockerze, po prostu przekazują socket demona Dockera z hosta do kontenera i to wystarczy w 99% przypadków. Ale nie spiesz się z rzucaniem w mnie ciastek, ponieważ mowa będzie o rzeczywistym uruchomieniu Dockera w Dockerze. To rozwiązanie ma wiele możliwych zastosowań, a ten artykuł dotyczy jednego z nich, więc usiądź wygodnie i wyprostuj ręce przed sobą.

Początek
Wszystko zaczęło się deszczowym wrześniowym wieczorem, gdy sprzątałem wynajęte za 5 dolarów wirtualne maszyny na Digital Ocean, które całkowicie utknęły, ponieważ Docker wypełnił swoimi obrazami i kontenerami całe 24 gigabajty dostępnego miejsca na dysku. Ironią było to, że wszystkie te obrazy i kontenery były transientne i potrzebne były tylko do testowania funkcjonalności mojej aplikacji za każdym razem, gdy ukazywała się nowa wersja jakiejkolwiek biblioteki lub frameworka. Próbowałem pisać skrypty powłoki i ustawiać harmonogram crona do czyszczenia śmieci, ale to nie pomogło: za każdym razem wszystko kończyło się tym, że miejsce na dysku mojego serwera było zajęte, a serwer zawieszony (w najlepszym razie). W pewnym momencie natknąłem się na artykuł o tym, jak uruchamiać Jenkins w kontenerze i jak może on tworzyć i usuwać potoki budowania za pomocą przekazanego do niego socketu demona Dockera. Pomysł mi się spodobał, ale postanowiłem pójść dalej i spróbować eksperymentować z bezpośrednim uruchamianiem Dockera w Dockerze. Wydawało mi się to logicznym rozwiązaniem - pobierać obrazy dockera i tworzyć kontenery wszystkich aplikacji, które potrzebuję do testowania wewnątrz innego kontenera (nazwijmy go kontenerem staging). Idea polegała na tym, aby uruchomić kontener staging z flagą -rm, co automatycznie usuwa cały kontener wraz z jego zawartością po zakończeniu. Zgadzałem się z obrazem Dockera od samego Dockera (), ale okazało się to zbyt skomplikowane i nie udało mi się sprawić, że działał, jak chciałem, więc chciałem przejść całą drogę samodzielnie.
Praktyka. Porażki
Postawiłem sobie za cel sprawić, by kontener działał tak, jak potrzebowałem, i kontynuowałem swoje eksperymenty, których wynikiem było niezliczone wiele błędów. Efektem moich samodzielnych prób był następujący algorytm:
Uruchamiamy kontener Docker w trybie interaktywnym.
docker run --privileged -it docker:18.09.6Zwróć uwagę na wersję kontenera, krok w prawo lub w lewo, a twój DinD zamieni się w dynię. W rzeczywistości wszystko często się psuje po wydaniu nowej wersji.
Musimy od razu przejść do shella.Spróbujmy sprawdzić, jakie kontenery są uruchomione (Odpowiedź: żadne), ale wykonajmy polecenie mimo to:
docker psBędziecie trochę zaskoczeni, ale okazuje się, że demon Docker nawet nie jest uruchomiony:
error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 192.168.65.1:53: no such hostUruchommy go samodzielnie:
dockerd &Jeszcze jedna nieprzyjemna niespodzianka:
failed to start daemon: Error initializing network controller: error obtaining controller instance: failed to create NAT chain DOCKER: Iptables not foundInstalujemy pakiety iptables i bash (w bashu pracuje się przyjemniej niż w sh):
apk add --no-cache iptables bashUruchamiamy bash. W końcu znowu w znanym shelu.
spróbujmy uruchomić Docker jeszcze raz:
dockerd &Powinniśmy zobaczyć długą listę logów kończącą się:
INFO[2019-11-25T19:51:19.448080400Z] Daemon has completed initialization INFO[2019-11-25T19:51:19.474439300Z] API listen on /var/run/docker.sockNaciskamy Enter. Znowu jesteśmy w bashu.
Od tego momentu możemy próbować uruchamiać inne kontenery w naszym kontenerze Docker, ale co jeśli chcemy uruchomić jeszcze jeden kontener Docker w naszym kontenerze Docker lub coś pójdzie nie tak i kontener "wypadnie"? Zaczynać wszystko od nowa.
Własny kontener DinD i nowe eksperymenty
Aby nie powtarzać powyższych kroków w kółko, stworzyłem własny kontener DinD:
Działające rozwiązanie DinD umożliwiło mi uruchamianie Docker wewnątrz Dockera rekursywnie i prowadzenie odważniejszych eksperymentów.
Jeden z takich (udanych) eksperymentów z uruchomieniem MySQL i Nodejs zamierzam teraz opisać.
Najbardziej niecierpliwi mogą zobaczyć, jak to było tutaj

Zacznijmy zatem:
Uruchamiamy DinD w trybie interaktywnym. W tej wersji DinD musimy ręcznie zmapować wszystkie porty, które mogą być wykorzystywane przez nasze kontenery podrzędne (już nad tym pracuję)
docker run --privileged -it -p 80:8080 -p 3306:3306 alekslitvinenk/dindWchodzimy do bash, skąd możemy od razu rozpocząć uruchamianie podkontenerów.
Uruchamiamy MySQL:
docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysqlŁączymy się z bazą danych tak, jakbyśmy łączyli się z nią lokalnie. Upewniamy się, że wszystko działa.
Uruchamiamy drugi kontener:
docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-serverZwróć uwagę, że mapowanie portów będzie dokładnie 8080:8080, ponieważ już zamapowaliśmy port 80 z hosta do macierzystego kontenera na port 8080.
Przechodzimy do localhost w przeglądarce, upewniamy się, że serwer odpowiada „Hello World!”
W moim przypadku eksperyment z zagnieżdżonymi kontenerami Docker okazał się dość pozytywny i zamierzam kontynuować rozwój projektu oraz wykorzystywać go do stagingu. Uważam, że to znacznie lżejsze rozwiązanie niż Kubernetes i Jenkins X. Ale to moja subiektywna opinia.
Myślę, że na dzisiejszy artykuł to wszystko. W następnym artykule dokładniej opiszę eksperymenty z rekurencyjnym uruchamianiem Docker w Docker oraz montowaniem katalogów w głąb zagnieżdżonych kontenerów.
P.S. Jeśli uważasz, że ten projekt jest przydatny, proszę wystaw mu gwiazdkę na GitHubie, zrób fork i powiedz o tym znajomym.
Edit1 Poprawiłem błędy, skupiłem się na 2 filmach
Źródło: habr.com
