Debugging Containers in 10 Minutes - Docker/Podman (film, 10m)
In today’s post, Jakub 'unknow' Mrugalski from UW-TEAM.org guides us through the debugging process of Docker containers, especially those inherited from previous employees without proper documentation. Containerization automation is indeed crucial in many projects, but what happens when we encounter errors in a container that we don’t fully control? Jakub presents step-by-step guidance on how to deal with such situations. The video begins with an example of a container named 'wywrotka' that is purposely programmed to throw errors to highlight various issues that may arise when using undocumented containers.
The first step in debugging, as Jakub emphasizes, is to run the container and check its logs, which can help identify the root cause of the problem. In this case, there was an error indicating a missing configuration file, which was quickly identified in the container logs. Jakub also shows how to use the 'docker inspect' command to find log paths, which could be useful in more complex cases. With the grep command, one can search through the logs and determine what exactly might have gone wrong in the code.
Once the front-end errors are identified, Jakub moves on to further analyzing the container, demonstrating the importance of understanding its structure and commands. By altering elements in commands, he successfully accesses crucial components of the container, such as the startup files. Thoughtfully, Jakub teaches how to utilize BusyBox to run system commands that can aid in examining the container's contents. Ultimately, he eliminates the problem related to the missing configuration file by creating an appropriate system configuration, thus allowing the container to function correctly.
In addition to the steps for solving the problem, Jakub touches on more advanced techniques, such as exporting containers and images to TAR files, allowing recovery in case of failures. These actions underline the significance of debugging skills in day-to-day interactions with containers. This provides a way to learn how to effectively tackle similar issues without comprehensive documentation, which, as we know, is not always available in the fast-paced IT world.
Finally, it is worth noting that Jakub’s video has already garnered 7884 views and 453 likes at the time of writing this article, which confirms its utility for IT professionals. Undoubtedly, the skills acquired through his presentation can benefit many individuals involved in containerization. If this topic intrigues you, Jakub recommends his Linux Debugging Academy, where one can delve into more practical issues.
Toggle timeline summary
-
Introduction to working with Docker and containerization.
-
Debugging your own containers is simpler than dealing with inherited ones.
-
Challenges arise when you inherit a container without documentation.
-
You may encounter errors when trying to run the inherited container.
-
Introducing a workflow for debugging container issues.
-
A test container named 'wywrotka' is prepared for demonstration.
-
To run the container, a specific command is needed.
-
Success is determined by the container outputting 'sukces'.
-
The container cannot be modified; only start parameters can be adjusted.
-
Demonstrating terminal commands for running Docker containers.
-
Running the Docker command to start the container named 'analiza'.
-
Initial errors related to missing configuration files.
-
Inspecting logs reveals consistent issues with the missing config.
-
Investigating if 'busybox' has the necessary commands available.
-
Listing the root directory shows 'starter.sh' unexpectedly present.
-
Explaining error handling and conditional checks in the starter script.
-
Mounting a volume to Docker to redefine entry points.
-
Successfully resolving the container task by creating necessary directories.
-
Using 'docker export' to save a container, capturing its state.
-
Exploring the image layers contains different filesystem states.
-
Accessing the 'starter.sh' in the image layer directory.
-
Promoting a course on Linux debugging skills for practical knowledge.
Transcription
Jest szansa, że na co dzień pracujesz z kontenerami Dockera, Podmana albo jakiejkolwiek innej technologii związanej z konteneryzacją. Jeżeli samodzielnie budujesz kontenery, to oczywiście ich debugowanie jest trochę prostszą sprawą. Problem zaczyna się wtedy, gdy odziedziczyłeś po kimś kontener. Niech to będzie po pracowniku, który już tu nie pracuje. Mało tego, nie ma do niego dokumentacji, nie ma źródeł, jest tylko obraz. Ten obraz trzeba uruchomić, ale nawet jeżeli go uruchomisz, to on się wysypuje, wrzuca jakimiś błędami. Nikt nie wie o co chodzi. Jak teraz zabrać się do debugowania takiego problemu? Chcę Ci pokazać taki prosty workflow, który możesz zastosować po to, aby dowiedzieć się, co siedzi w środku takiego kontenera. Ja na potrzeby testu i na potrzeby tego filmu przygotowałem kontener, który nazywa się wywrotka. On powstał po to, aby się przewracać, czyli rzucać błędami na ekran. I nie daję Ci do niego źródeł celowo, po to, abyś te źródła samodzielnie mógł wydobyć. Aby uruchomić ten kontener, wystarczy wykonać to polecenie, które widzisz na ekranie. I jeżeli nie chcesz spoilerować sobie zabawy, to zatrzymaj film w tym miejscu, spróbuj ten kontener samodzielnie naprawić i dopiero potem przejść dalej. Teraz, co uznajemy za sukces? Sukces jest wtedy, gdy ten kontener wyrzuci na ekran właśnie słowo sukces. I jeżeli tak się stanie, to znaczy, że Ci się udało. Ważna rzecz, kontenera nie można modyfikować, nie można do niego do środka nic doinstalować. I jedyna rzecz, którą możesz robić, to przekazywać mu odpowiednie parametry startowe. Ale jakie parametry? I to jest właśnie treścią zadania. Przejdźmy do terminala, pokażę Ci, jak się do tego zabrać. Zakładam, że znasz podstawę obsługi Dockera i wymowę go zainstalowanego w systemie. Dla pewności wydaję polecenie docker.ps. Wszystko działa dobrze. Sprawdzimy, czy są jakieś jeszcze martwe kontenery. Minus A. Niczego tu nie ma, jest to czysta instalacja Dockera. W takim razie uruchamiam mojego Dockera. Docker run, minus IT, czyli interaktywny z alokacją terminala. Nadaję mu nazwę, na przykład analiza. A następnie podaję unknown przez wywrotka. I to powinno wystarczyć. Enter. Obraz zaczyna się ściągać. Chwilę czekamy, aż się zakończy i widzimy od razu error. Brak pliku konfiguracyjnego. To nie jest błąd Dockera, tylko błąd tego obrazu. Czyli coś w środku poszło nie tak. Pytanie już mi co? Sprawdzimy sobie tutaj, czy ten kontener działa. Nie. Czy jest na liście zakończonych? Tak. A nowy wywrotka zakończony? Zwróćcie tylko uwagę na jedną rzecz. W kolumnie command mamy tutaj entry point. Zostawione na . . . Nie wiadomo co to jest. Warto byłoby zobaczyć o co tutaj chodzi. No i oczywiście tu jest nazwa kontenera, analiza. Sprawdzimy sobie w takim razie jeszcze logi. Logi i analizy. No jest dokładnie to co było na ekranie, czyli brak pliku konfiguracyjnego. Taka mała sztuczka jeszcze tutaj może Ci się przydać. Możemy sobie zrobić inspect na analizie. I grepnąć po słowie log. Wtedy dostaniemy ścieżkę do logów w formacie JSON. I tam jest trochę więcej informacji. Jeżeli będziesz kiedykolwiek analizować jakąś bardziej złożoną aplikację, to Ci się może przydać. Dlaczego? Bo tam są znaczniki czasu. O której godzinie dokładnie dany błąd wystąpił. Godzina, minuta, sekunda, strefa czasowa i inne takie rzeczy. Ale dobra. To nam nic nie dało. W takim razie możemy sobie tutaj grepnąć po czymś innym. Zobaczymy jaki jest tutaj entry point. Czyli daję sobie grep entry. No i tu widzimy, że muszę jeszcze kilka linii wyprintować. No i tutaj mamy entry point. Dużo kropek i slashe na końcu starter.sh Co to znaczy? To coś, co jest tylko zaciemnianiem. Tak dużo razy dajesz polecenie idź do góry, idź do góry, idź do góry, że wyjdziesz do ruta. No i okazuje się, że ten plik po prostu jest w rucie. Teraz sobie go ściągnę. Kategorię kluczowasową załowę tutaj o nazwie tmp. A następnie docker.cp. Czyli kopiowanie pliku z wnętrza kontenera. Nazwa kontenera. Analiza. Dwukropek i tu jest ścieżka do pliku. Jest to oczywiście slash. I tu dajemy starter.sh. No i gdzie to ma skopiować? Nikt mi to skopiuje do wnętrza mojego katalogu tmp. Enter. Nie ma takiego pliku jak starter.sh. To ciekawe, bo przecież jest zdefiniowany jako entry point. Coś się nie zgadza. Podejdźmy do tego inaczej. Uruchomię tymczasową instancję mojego kontenera. Czyli docker.run. Minus it, tak jak było wcześniej. I na koniec oczywiście nazwa naszego obrazu. Czyli anno ułamane przez wywrotka. I to powinno nam wygenerować dokładnie taki sam error jak wcześniej. Zgadza się. I teraz jedna taka sztuczka. Można czasami zdefiniować tu polecenie do uruchomienia. Czyli binbash. Podaj mi powłokę. No to nie działa. Dlaczego? Ten binbash może być uruchomiony tylko wtedy, jeżeli ktoś w docker.file użył polecenia cmd do uruchomienia. Jeżeli ma uruchomionego entry pointa, to jest to ścieżka do binarki, która ma się uruchomić. Czyli binbash. Daję Enter. I co się okazuje, nie działa. Dlaczego? No bo nie ma takiego pliku. Nie ma na dysku pliku binbash. Przecież nie jest powiedziane, że ktoś tworzył obraz z pełnej dystrybucji linuxowej. Może to być bardzo zaoptymalizowana dystrybucja. Może jest w takim razie binsh. Czasami, jeżeli korzystam z dystrybucji alpine, może być binash. Sprawdzimy czy jest. Już nie ma. Mogę spróbować odpalić jakąś binarkę dostępną niemal w każdej dystrybucji linuxa, czyli np. ls do listowania plików, ale tego też nie ma. To jest bardzo obcięty kontener pod względem zawartości. No to co możemy zrobić? Sprawdzić, czy jest coś takiego jak multi-exec binary o nazwie busybox. Busybox to jest taka paczka, która zbiera w sobie różne inne polecenia i okazuje się, że tak jest zainstalowane. Czy jest tu ls? Jest ls. To co daje już na końcu, będzie parametrem startowym do tego co jest entry pointem. I w ten sposób wylistowałem zawartość głównego katalogu. Zobacz co tu jest ciekawego. Starter.sh. Przecież go nie było wcześniej. Coś jest na rzeczy. Możemy sobie go podglądać za pomocą polecenia cat. Też użyję busyboxa tak jak wcześniej, ale tutaj dam sobie cat i ścieżka, czyli slash starter.sh. No i zobaczymy jak on wygląda. Co ciekawego tu jest na początku? Widzimy, że tutaj jest jakaś dziwna powłoka, dalej mamy funkcję fail, która wyświetla napis error, a następnie to co podano jako parametr i tajemnicze RM$0. Co to jest ten $0? $0 to jest ten skryt, który właśnie wykonujemy, czyli w przypadku wystąpienia jakiegokolwiek błędu plik startowy jest usuwany. Dlatego nie było go w obrazie. Dalej mamy sprawdzenie, czy istnieje coś takiego, co jest zdefiniowane w zmiennej CFG. A w CFG jest ścieżka app.config. Czy w ogóle katalog app istnieje? No nie istnieje. Wypadałoby go załować. Tylko są jakieś inne katalogi. Wynik działania 2 plus 2 razy 2, czyli 6. To jest kolejność wykonywania działań, ważne. I teraz widzimy tu wczytanie naszego configa z tego pliku, czyli musi on na pewno istnieć. I następnie jest sprawdzenie, czy jest tam w ogóle zmienna version. Jeżeli nie ma, no to wrzucamy, bo nie wiadomo, jaka wersja oprogramowania. A następnie porównujemy sobie, tutaj jest jakieś porównanie, ne not equal, czyli jeżeli wersja nie jest równa temu, co wychodzi z fixa, to zakończ całość, w przeciwnym wypadku jest sukces. To założymy sobie katalog app. Następnie wrzucimy do niego plik o nazwie config i zdefiniujemy tam zmienną o nazwie version o wartości 6. Teraz tylko trzeba to podmontować do dockera, czyli to polecenie, co było wcześniej. I zdefiniuję zamiast entry pointa minus v, czyli podpięcie wolumenu. I z aktualnego katalogu, czyli zmienna $ to będzie pwd, ten katalog app podmontuje sobie do wnętrza dockera, do ścieżki oczywiście slash app. Sprawdzam teraz, co się dzieje. Sukces. Dokładnie o to chodziło. Udało nam się rozwiązać zadanie. Oczywiście jest to jedno z poprawnych rozwiązań. Nie najlepsze, nie jedyne, tylko jedno z wielu. Ale możemy zrobić jeszcze inną rzecz. Jeżeli mamy dostęp do busyboxa, to zamiast tego kata i innych rzeczy możemy tutaj dać nazwę powłoki. Na przykład bash, o ile akurat busybox został skompilowany z obsługą basha. Sprawdźmy to. Bash, enter, applet not found. No to pewnie jest A-S-H, enter, działa. I teraz jesteśmy w środku kontenera. LSA nie ma, ale możemy tak naprawdę wylistować pliki za pomocą na przykład tabulatora. Dwa razy tab, no i mamy tutaj listę poleceń, plików. Możemy też podać ścieżkę slash, dwa razy tab, no i mamy w zasadzie LSA. Tutaj widzimy, że brakuje nam tego startowego pliku, bo został usunięty. Możemy wejść sobie do binarek i zrobić link symboliczny, czyli LN minus S, symbolik, i podmontować sobie busyboxa. Tu mamy busybox i nazwa polecenia LS. Od tego momentu działa nam LS. LS minus AL, niech tu będzie. O, literówka. I mamy jak najbardziej działającego LSA. W ten sposób możemy dobrać się do wielu innych poleceń systemowych. Sprawdzimy teraz tutaj. Tak, nasz starter istnieje i możemy sobie go przeglądać, jeżeli jest nam to potrzebne. Kolejna umiejętność, która może Ci się przydać, to eksportowanie już uruchomionego albo zabitego kontenera do pliku, czyli docker export, a następnie nazwa naszego kontenera, czyli mamy tu analiza. I przekierowujemy to oczywiście do pliku zewnętrznego z dostrzeżeniem kropka tar. Enter. I gotowe, wyeksportowany. Sprawdzimy sobie, ile on tam zajmuje. On zajmuje dokładnie tyle, ile nasz kontener, czyli 9.3 MB. I teraz jak wejść do środka? Możemy go zdekompensować, ale najprościej MC i do środka sobie wchodzimy po prostu klawiszem Enter. I tak możemy grzebać, ale zwróć uwagę, to nam w niczym nie pomoże, dlatego, że to już jest zabity kontener, który już zdążył usunąć plik startowy. Nie ma tu starter SH. Możemy więc zrobić coś innego. Usuwam ten plik z analizą i eksportuję teraz nie kontener, a obraz. Zwróć uwagę na inne polecenie. Zamiast eksportu jest save, a teraz nazwa nie kontenera, a nazwa obrazu. Gdzie zapisać go? Do pliku analiza2.tar a teraz nazwa naszego obrazu to będzie unknow, łamane przez wywrotka. I tu oczywiście po dwukropku dajemy sobie taga. Niech to będzie latest, bo tylko taki tu istnieje, więc wpiszę sobie go. Dwukropek, latest, Enter. I powstał nam ten plik. Sprawdzimy, ile on teraz zajmuje. Zajmuje dokładnie tyle samo, co uruchomiony kontener. Ja to MC, wchodzę do środka i tu jest zaskoczenie. Tu są warstwy tak zwane. Nie znajdziemy tu gołych plików, tylko w tych katalogach mamy tutaj konkretne warstwy. Warstwa tam pierwsza, druga, trzecia. Musimy pogrzebać w tym i zobaczyć, co w środku tych warstw się znajduje. Założę tymczasowy katalog. Nazwę jeden na przykład. Wezmę sobie tego Layera1 i po prostu wejdę do niego Enterem. A! Trafiony, zatopiony. Tu jest Start LSH. Mogę sobie tutaj klawiszem F3 podglądnąć i widzę dokładnie to, co chciałem. Więc w ten sposób możesz dobierać się do files systemu Twoich obrazów i też kontenerów w zależności od tego, czy użyjesz Save czy Export. Jeśli czujesz, że tego rodzaju umiejętności mogą przydać Ci się w codziennej pracy, to rzuć okiem na moje pokolenie. Akademia Debugowania Linuxa. Znajdziesz tam wiele godzin poradników związanych z debugowaniem problemów z siecią, z DNS-ami, z certyfikatami, z kontenerami. W zasadzie ze wszystkim, co możecie spotkać w systemie operacyjnym Linux. Linka masz pod filmem, więc kliknij i zobacz agendę. A przy okazji znajdziesz tam dodatkowe lekcje pokazowe. Napisy stworzone przez społeczność Amara.org