Dzisiejszy post jest kontynuacją krótkiej serii wprowadzającej w świat Dockera. W poprzednim wpisie omówiliśmy sobie podstawy oraz uruchomiliśmy nasz pierwszy kontener. Teraz przyszedł czas na to, żebyśmy dokładniej wyjaśnili sobie, czym tak naprawdę są kontenery i jakie podstawowe komendy posiada Docker.
Czym jest kontener?
Kontener w dużym skrócie jest to samodzielny byt umieszczony na małym fragmencie naszego dysku twardego. Na tym fragmencie dysku znajdują się pliki wymagane do prawidłowego uruchomienia i działania aplikacji / programu / procesu. Zależy to od tego, co znajduje się w kontenerze. Każdy kontener dostaje część hardware z naszej maszyny tylko na swój własny użytek (dysk, RAM, sieć itp.). Dzięki temu kontenery są całkowicie niezależne od siebie oraz od komputera hosta. W jednym kontenerze możemy mieć zainstalowanego Node.js w wersji 12, w kolejnym Node.js v.14 a na naszej maszynie starego Node.js v.8. Każda z tych wersji będzie działała poprawnie i nie będzie świadoma istnienia innych instalacji.
Technologią, która umożliwia nam taki podział zasobów hosta są tzw. namespaces. To one umożliwiają nam izolowanie zasobów per proces. W naszym przypadku – per kontener. Nie będziemy za bardzo zagłębiać się w tym poście na ten temat. Zainteresowanych odsyłam do stosownej strony w dokumentacji. Najważniejsza rzecz, o której musimy pamiętać to fakt, iż kontenery uruchamiają się na komputerze hoście wewnątrz przydzielonych tylko dla siebie zasobów i są totalnie niezależne od tego, co znajduje się w innych kontenerach oraz na hoście. Każda uruchomiona aplikacja bądź proces w kontenerze również nie wie nic i nie zna innych plików niż te, które pochodzą z tworzącego go obrazu. Proces uruchamiania kontenera z obrazu został objaśniony w poprzednim wpisie.
Czym jest obraz?
Obraz w nomenklaturze Dockera nie jest niczym odkrywczym. Możemy go porównać do płyty instalacyjnej z grą (bądź w różny sposób zdobywanego obrazu tej płyty).
Dane zapisane na płycie CD są tylko do odczytu. Przed uruchomieniem gry są one najczęściej kopiowane na dysk twardy, a włożenie płyty do napędu powodowało automatyczne uruchomienie instalatora. Niemalże dokładnie sprawa wygląda z obrazem Dockera. Podczas tworzenia kontenera pliki z obrazu kopiowane są na fragment dysku twardego przeznaczonego tylko dla tego kontenera. Przy uruchamianiu wykonywana jest domyślna komenda startowa zdefiniowana w obrazie.
Komendy CLI
Po zainstalowaniu Docker Desktop, do naszej dyspozycji otrzymamy bardzo wygodny Dashboard. Możemy sobie w nim sprawdzić, w jakim stanie znajdują się nasze kontenery, uruchomić te wyłączone, podejrzeć logi, statystyki itp. Wszystko wewnątrz dość przyjemnego dla oka GUI. Dashboard jest naprawdę fajny i przydatny w szybkim rzuceniu okiem co się dzieje z naszym środowiskiem. Drugą formą komunikacji z kontenerami jest znany już nam Docker Client i wiersz poleceń. To tutaj tak naprawdę mamy pełną władzę.
Listę wszystkich dostępnych poleceń możemy sprawdzić, wpisując w naszym terminalu po prostu docker
i klikając Enter. Jeżeli chcemy zobaczyć więcej szczegółów na temat pojedynczego polecenia, wystarczy, że wpiszemy docker nazwa-polecenia --help
, czyli przykładowo dla poznanej już komendy docker run
będzie to docker run --help
.
Wszystkie polecenia znajdziemy też oczywiście w dokumentacji Dockera.
Podstawowe komendy
Dokumentacja całkiem dobrze wyjaśnia jak korzystać z dostępnych komend i nie chcę tutaj powielać dostępnych już materiałów. Wymienię i krótko opiszę te komendy Dockera, z którymi na pewno powinniśmy zaznajomić się w pierwszej kolejności. Przy okazji również dowiemy się kolejnych rzeczy na temat Dockera.
▶️ docker run
Komenda, którą już poznaliśmy we wcześniejszym wpisie. Wywołana jedynie z nazwą obrazu uruchomi kontener, korzystając z komendy startowej podanej w obrazie. Jeżeli jednak podamy kolejny argument tuż po nazwie obrazu, ten argument nadpisze domyślną komendę z obrazu i to ona zostanie uruchomiona tuż po starcie kontenera. Składnia wtedy będzie wyglądała następująco docker run image-name startup_command
. Skorzystajmy teraz z obrazu busybox i podczas uruchamiania kontenera wywołajmy polecenie ls
. Jest to Linuxowa komenda służąca do wyświetlenia listy plików w katalogu, w którym się znajdujemy. Powinniśmy zobaczyć wtedy jakie pliki zostały skopiowane z obrazu do kontenera. Osoby zaznajomione nieco z Linuxem powinny rozpoznać znajome nazwy.
Ponownie widzimy, iż docker run
najpierw pobrał obraz, a następnie na jego podstawie stworzył i uruchomił kontener, którego jedynym zadaniem było wylistowanie plików w swoim katalogu roboczym. Zadanie to zostało mu przydzielone przez nas samych poprzez umieszczenie ls
jako ostatni parametr. Zamiast używać run
, stworzenie oraz uruchomienie kontenera możemy rozbić na dwie osobne komendy: docker create
, oraz docker start
.
▶️ docker create
Stworzenie kontenera bez wywoływania komendy startowej z obrazu. Należy rozumieć jako jedynie zarezerwowanie części zasobów hosta i skopiowanie plików z obrazu na wycinek dysku przeznaczony dla owego kontenera. W odpowiedzi otrzymany jego unikalny numer id. Kontener nie zostanie uruchomiony.
▶️ docker start
Uruchomienie istniejącego już kontenera. Jako argument podajemy numer id kontenera – patrz docker create
. Co ważne nie musimy podawać całego ciągu, wystarczy jedynie kilka pierwszych znaków.
Przydatna tutaj okaże się flaga -a
, dzięki której wszelkie logi z kontenera zostaną przekierowane do naszego okna. Bez umieszczenia tej flagi uruchomimy kontener, ale nie będziemy otrzymywać od niego żadnych informacji.
▶️ docker ps
Wyświetlenie listy kontenerów obecnych na naszym komputerze. Komenda wydająca się bardzo prosta na pierwszy rzut oka. Podczas pisania tego posta uruchomiłem kilka razy kontenery z obrazu hello-world
, raz nawet skorzystałem z busybox
. Lista pewnie będzie całkiem długa. Jednak jeżeli wykonam teraz komendę docker ps
, zobaczę… pustą listę. Dlaczego?
Otóż kontenery Dockera są dość leniwe. Jeżeli nic się w nich nie dzieje, samoczynnie się wyłączają. Jeżeli wewnątrz kontenera nie jest uruchomiony żaden proces bądź uruchomiony proces się zakończy, kontener samoczynnie się wyłączy. Dalej będzie na dysku, będzie posiadał swój unikalny id i będziemy mogli go uruchomić ponownie komendą docker start
. Kontenery tworzone z hello-world
miały tylko jedno zadanie – wygenerować log. Po tym zadaniu nic więcej nie leżało w ich obowiązku, więc samoczynnie się wyłączały.
W celu zobaczenia listy wszystkich kontenerów – tych aktualnie działających oraz wyłączonych należy użyć flagi --all
(albo -a
). Zatrzymane kontenery może uruchomić ponownie za pomocą docker start
oraz id wybranego przez nas kontenera.
Jak natomiast uruchomić kontener i zmusić go do pracy w celu przetestowania komendy flagi -a
? Skorzystać ze znanego już nam docker run
i jako komendę startową podać proces, który nie wyłączy się zbyt szybko. Spróbujemy skorzystać z narzędzia ping. Jako że hello-world
nie posiada zaimplementowanego narzędzia ping, ponownie wrócimy do obrazu busybox
:
Jak widać kontener korzystający z busybox
pinguje teraz serwery Google. Logi wyświetlane są w naszym oknie, więc musimy uruchomić kolejne okno wiersza poleceń i tam uruchomić docker ps
:
▶️ docker images
Lista wszystkich obrazów, które są zapisane w naszej pamięci cache wraz z ich rozmiarami.
▶️ docker rm
Usunięcie pojedynczych kontenerów. Jeżeli mamy kontenery, z których nie zamierzamy korzystać w najbliższym czasie, możemy je usunąć i zwolnic nieco miejsca na dysku. Jako parametry możemy podać jeden, bądź wiele id oddzielonych od siebie spacją. Jeżeli chcemy usunąć wszystkie zatrzymane kontenery, możemy skorzystać z kombinacji dwóch komend: docker rm $(docker ps -a -q)
. Komenda wewnątrz nawiasu zwróci listę wszystkich zatrzymanych kontenerów, która to zostanie użyta jako parametr dla komendy docker rm
. Działające kontenery oraz obrazy znajdujące się w cache nie zostaną usunięte.
▶️ docker system prune
Komenda czyszcząca nasz system z nieużywanych kontenerów, obrazów i kilku innych używanych przez Dockera zasobów. Należy pamiętać o tym, iż usunięcie z pamięci cache obrazów spowoduje konieczność ponownego ich pobrania podczas tworzenia i uruchamiania kontenerów. Jeżeli wszystkie nasze kontenery są zatrzymane, wtedy po wyczyszczeniu systemu tą komendą wynikiem docker ps --all
będzie pusta lista.
▶️ docker logs
Wyświetlenie wszystkich logów, które zostały wygenerowane w konkretnym kontenerze. Kontener, którego logi chcemy odczytać, musi być uruchomiony. Przydatne w debugowaniu bądź gdy zapomnimy dodać flagi -a
w komendzie docker start
. Jako argument podajemy id kontenera: docker logs abc123
.
▶️ docker stop
Grzeczne wyłączenie kontenera. Po wywołaniu tej komendy wraz z podaniem id kontenera Docker wyśle do działającego procesu tzw. sygnał SIGTERM
. Jeżeli proces jest przygotowany na obsłużenie tego sygnału, może wykonać dodatkowe czynności, zanim zostanie wyłączony, np. zapis plików. Ma jednak na to tylko 10 sekund. Jeżeli po tym czasie proces będzie nadal aktywny, zostanie wykonana komenda docker kill
.
▶️ docker kill
Natychmiastowe wyłączenie kontenera bez podejmowania żadnych innych akcji. Wywoływane wraz z id kontenera.
▶️ docker exec
Uruchomienie kolejnego procesu wewnątrz kontenera. W każdy kontenerze podczas uruchamiania wykonywana jest komenda startowa pochodząca z obrazu (jeżeli nie jest nadpisana przez docker run
). Komenda ta zazwyczaj uruchamia jakiś pojedynczy proces. Gdy w trakcie działania kontenera zdecydujemy się uruchomić kolejny proces, skorzystamy właśnie z docker exec
. Komenda ta zazwyczaj wykonywana jest wraz z flagą -it
(złożenie dwóch osobnych flag -i
oraz -t
), dzięki czemu będziemy otrzymywać do naszego terminala ładnie sformatowane logi pochodzące z nowo uruchomionego procesu oraz uzyskamy możliwość wprowadzania komend wewnątrz tego procesu (to, co napiszemy na klawiaturze, zostanie przekierowane do terminala wewnątrz kontenera). Składnia polecenia: docker exec -it containerId _komenda_
.
Prawdopodobnie najczęściej tę komendę będziemy wykonywać, aby uruchomić terminal wewnątrz kontenera. Uzyskamy wtedy dostęp do niemal wszystkiego co się w nim dzieje oraz będziemy mogli wywoływać kolejne komendy bez potrzeby ciągłego wpisywania docker exec ...
.
Jak wspominałem już wcześniej, wszystkie uruchamiane kontenery tak naprawdę są uruchamiane w Linuxowej maszynie wirtualnej. W takim przypadku będziemy mieli dostęp do czegoś nazywanego Shell. Jest to swego rodzaju wiersz poleceń dla środowisk Unix-owych. Możemy w nim za pomocą komend wydawać polecenia i poruszać się po systemie. Aby uruchomić shell, wywołamy komendę docker exec -it containerId sh
.
Aby wyjść z powłoki shell kontenera i wrócić do naszego terminala należy nacisnąć Ctrl + D. Shell może być również uruchomiony jako komenda startowa wraz z docker run
. Wtedy uruchomimy kontener bez żadnego pożytecznego procesu, ale będziemy mogli to zrobić sami z poziomu wiersza poleceń wewnątrz kontenera. Komenda będzie wyglądała wtedy np. tak: docker run -it busybox sh
.
Podsumowanie
Docker zaczyna być dla nas coraz bardziej przejrzysty. Wiemy już, czym tak naprawdę jest kontener oraz jak jest umiejscowiony na naszym komputerze. Docker i jego komendy nie są już zagadką, więc możemy łatwo zarządzać kontenerami. Czas teraz na kolejny krok – czyli tworzenie własnych obrazów oraz ich budowanie. To wszystko w następnym wpisie.
Masz uwagi lub sugestie do tego wpisu?
Przejdź na Discord