• Post last modified:14/05/2020
  • Post Category:FrontOps
  • Post Comments:5 komentarzy

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

pirat

Dane zapisane na płycie CD są tylko do odczytu. Przed uruchomieniem gry są najczęściej kopiowane na dysk twardy a włożenie płyty do napędy 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.

schemat zapisu z obrazu na dysk
Kontener uruchomiony z obrazu

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 docker-a 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.

logi z konsoli
Wyświetlenie listy plików obecnych w obrazie busybox

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.

logi docker create
Wywołanie komendy docker create i otrzymanie numeru id kontenera

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

logi docker start
Wywołanie komendy docker start z/ oraz bez flagi -a

> 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:

logi docker ping
Logi z działającego procesu wewnątrz kontenera

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:

logi docker ps
Komenda docker ps wyświetlająca nasz jedyny działający kontener

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

logi docker system prune
Wywołanie docker system prune wymaga potwierdzenia usunięcia wymienionych składników

> 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 kontenra: 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.

logi docker shell
Dostęp do powłoki shell wewnątrz kontenera

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.

Kamil Józwik

Front end developer👨‍💻 Autor małego narzędzia dla programistów - frontbook.dev oraz autor kolejnych postów na tym blogu. Ulubiony stack to React wraz z TypeScript oraz wszystko co "nowe" w tym pięknym dynamicznym front end-owym świecie 🙂

Ten post ma 5 komentarzy

  1. SpeX

    Obrazki coś nie działają we wpisie

    1. Kamil Józwik

      Tak niestety kończą się moje nierozważne zabawy z cachowaniem 😉

  2. Jacek

    Czołem! Wpisy na temat Dockera bardzo mnie zainteresowały. Są podane oraz opisane w bardzo przystępny sposób, dzięki czemu człowiek nieco odpoczywa w trakcie nauki czegoś nowego – w przeciwieństwie do czytania dokumentacji niektórych projektów, która bywa czasem mentalną gimnastyką.

    Chętnie poczytam więcej Twoich wpisów na temat Dockera 😀
    Na pewno jak będę chciał komuś polecić jakieś materiały na ten temat, to podeślę takiej osobie linki do tych artykułów!

    1. Kamil Józwik

      Dzięki za miłe słowa 🙂 Kolejne dockerowe posty są cały czas w planach i jak tylko trochę mi się zrobi miejsca w kalendarzu to na pewno będę kontynuował pisanie 🙂

Dodaj komentarz