Jak uruchamiałem Dockera w Dockera i co z tego wynikło

Cześć wszystkim! W swoim poprzedniej artykule, 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ą.

Jak uruchamiałem Dockera w Dockera i co z tego wynikło

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 (https://hub.docker.com/_/docker), 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:

  1. Uruchamiamy kontener Docker w trybie interaktywnym.

    docker run --privileged -it docker:18.09.6

    Zwróć 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.

  2. Spróbujmy sprawdzić, jakie kontenery są uruchomione (Odpowiedź: żadne), ale wykonajmy polecenie mimo to:

    docker ps

    Bę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 host

  3. Uruchommy 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 found

  4. Instalujemy pakiety iptables i bash (w bashu pracuje się przyjemniej niż w sh):

    apk add --no-cache iptables bash

  5. Uruchamiamy bash. W końcu znowu w znanym shelu.

  6. 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.sock

  7. Naciskamy 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

Jak uruchamiałem Dockera w Dockera i co z tego wynikło
Aby nie powtarzać powyższych kroków w kółko, stworzyłem własny kontener DinD:

https://github.com/alekslitvinenk/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

Odtwarzaj wideo

Zacznijmy zatem:

  1. 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/dind

    Wchodzimy do bash, skąd możemy od razu rozpocząć uruchamianie podkontenerów.

  2. Uruchamiamy MySQL:

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysql

  3. Łączymy się z bazą danych tak, jakbyśmy łączyli się z nią lokalnie. Upewniamy się, że wszystko działa.

  4. Uruchamiamy drugi kontener:

    docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-server

    Zwróć 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.

  5. 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

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster