5 błędów jakie popełniasz pracując z Dockerem


Docker zyskuje na popularności i coraz częściej zespoły sięgają po niego, jako główny element stosu programistycznego. Problem polega na tym, że w zalewie obowiązków skupiamy się na wszystkim, ale nie na tym, aby dobrze poznać narzędzia jakich używamy. To z kolei prowadzi nie tylko do frustracji gdy coś nie działa ale też odbija się czkawką, gdy wysypuje się środowisko produkcyjne i nikt nie wie dlaczego.

Tym razem zapraszam na nieco bardziej techniczny artykuł powiązanym z video dostępnym na kanale digitalnie.


Artykuł ten stanowi zarówno przypis do video jak i też rozszerzenie poruszanych tam kwestii, które wydaje mi się łatwiej jest śledzić w tekście. Oto 5 błędów, na jakie możesz natrafić pracując z dockerem.

  1. Nie wiesz, czym jest Docker
  2. Twoja aplikacja nie jest gotowa na działanie w kontenerze
  3. Nie wersjonujesz prawidłowo obrazów
  4. Nie masz dobrego środowiska do uruchamiania aplikacji
  5. Nie wiesz jak budować warstwy i wzajemnie powiązane obrazy

1. Nie wiesz. czym jest Docker

Pomijając kwestie czysto definicyjną, mówiąc Docker mam oczywiście na myśli obrazy, kontenery oraz środowisko ich uruchamiania. Wraz z powstaniem
standardu OCI docker engine już nie jest jedynym sposobem uruchamiania kontenerów.
Tym nie mniej wydaje mi się, że każdy się zgodzi, że słowo docker może być używane podobnie jak określenie sportowych butów adidasami.

Mamy tutaj pierwszy element, który warto rozważyć – jakiego runtime engine użyć? Docker engine może być najprostszym rozwiązaniem, ale w kolejce czekają na nas RKT oraz ContainerD.
Warto pamiętać, że jakkolwiek nowości są ciekawe, musimy być pewni, że znamy zarówno wady jak i zalety rozwiązania, które planujemy wdrożyć.

Docker nie jest ani maszyną wirtualną, ani systemem operacyjnym. To niby oczywiste spostrzeżenie, jednak łatwo o nim zapomnieć przy wdrożeniach.
Przede wszystkim istotne jest to, że kontenery niosą ze sobą pewną cenę. Tą ceną jest narzut na debugowanie, wytwarzanie oraz testowanie. Nie wszystkie narzędzia, których używamy rozwijając aplikacje będą w stanie bezproblemowo współpracować z aplikacją w kontenerze.

O czym warto pamiętać?
Kolejność parametrów ma znaczenie, ostatni parametr przekazywany do kontenera to komenda, zatem uruchamiając
docker container run -n name test-redis redis
Uruchomimy obraz redisa o nazwie test-redis. Natomiast stosując:
docker container run test-redis redis -n name
Uruchomimy redisa o losowej nazwie przekazując komendę -n name
Ten drobny szczegół może sprawić przy dynamicznym przekazywaniu parametrów, że uruchomimy coś zupełnie innego, niż planowaliśmy.

Odsyłam do dokumentacja jak zacząć z Dockerem gdzie w bardzo
przejrzysty sposób opisane są podstawowe zasady działania tego środowiska.

2. Twoja aplikacja nie jest gotowa na działanie w kontenerze

Główne grzechy aplikacji, która nie jest gotowa na produkcyjne działanie w kontenerze to:

  • Aplikacja zakłada, że łączy się do innych serwisów po localhost lub stałym IP
  • Nie loguje na stdout/stderr ani nie daje takiej opcji
  • Tryb logowania nie może być zmieniony bez przebudowy kodu
  • Aplikacja nie obsługuje prawidłowo sygnałów SIGTERM
  • Program zakłada, że uruchomiony był tylko raz
  • Konfiguracja jest zaszyta bezpośrednio w kodzie

Dokładniejsze informacje jak przygotować aplikację znajdziesz na stronie https://12factor.net/

3. Nie wersjonujesz prawidłowo obrazów

Przede wszystkim wyobraźmy sobie bardzo prostą sytuację, mamy bazę kodu, którą rozszerzamy tworząc nowe aplikacje. Naturalnym wydaje się zatem,
aby zbudować obraz bazowy my-base-app, a następnie korzystać z niego w taki sposób:

FROM my-base-app

Z miejsca pojawia się oczywisty problem, jaka jest wersja bazowej aplikacji? Czy latest pochodzi z mastera, konkretnego release czy może feature brancha?
Kolejnym oczywistym krokiem wydaje się wersjonowanie my-base-app kończąc z Dockerfile, który wygląda następująco:

FROM my-base-app:1.0

So far so good jak to mawiają, jednak, co się stanie, gdy my-base-app rozwijany jest przez inny zespół, a my chcielibyśmy móc testować nowe wersje bez żmudnego zmieniania wersji za każdym razem w repozytorium? Co jeżeli zespół rozwijający my-base-app chciałby wiedzieć, że nowa wersja aplikacji (na przykład taka, która zawiera poprawkę) nadal prawidłowo współpracuję z resztą systemu? Przecież w tym celu nie będzie nam kazał commitować nowej wersji naszego Dockerfile.
Sensownym wydaje się tu używanie build args

ARG MY_BASE_APP_VERSION=1.0
FROM my-base-app:${MY_BASE_APP_VERSION}

Teraz mamy zarówno opis obrazu, który domyślnie współpracuje z wersją 1.0 ale jednocześnie może być przetestowany z wersją 1.2. Jak powinniśmy nazwać nasz nowy obraz dla aplikacji my-main-app? Jedną z metodyk jest podawanie wersji naszej aplikacji, a następnie zależności. Załóżmy, że mamy wersję 3.2 wówczas nasz obraz miałby odpowiednio tag my-main-app:3.2-base1.0 oraz my-main-app:3.2-base1.2

To oczywiście nie rozwiązuje wszystkich problemów, a przede wszystkim problemu procesowego, który wymusi właściwe wersjonowanie. W tym miejscu odsyłam do https://semver.org/

4. Nie masz dobrego środowiska do uruchamiania aplikacji

Tu głównie zwracam Twoją uwagę, aby upewnić się, że testujesz kontener na maszynie wirtualnej jak najbardziej zbliżonej do docelowej. RAM, CPU a nawet wielkość dysku mogą przesądzić, czy aplikacja nie wysypie się na produkcji. Przepełnienie dysku zdarza się tu równie często co RAM’u.

Nie wspominam tu o całej masie problemów sieciowych, reguł firewall, zamkniętych portach albo niedziałających websocketach.

Zaskoczeniem może być też montowany volumen. Począwszy od tego, że ze względu na oszczędności domyślnie montujemy HDD i wydajność aplikacji spada, kończąc na prozaicznym braku dostępu do katalogu, który został zamontowany z innymi uprawnieniami. Część oprogramowania zakłada także, że konkretny katalog jest pusty – otrzymanie dysku z folderem lost+found zdarzyło mi się w środowisku cloud znacznie częściej niż bym chciał 🙂

5. Nie wiesz jak budować warstwy i wzajemnie powiązane obrazy

W punkcie 3. Nie wersjonujesz prawidłowo obrazów po części poruszyłem ten problem. Tutaj znacznie trudniej zaproponować konkretne rozwiązanie.
Będzie ono, bowiem mocno powiązane z całym stosem aplikacyjnym.

Nie ma tu innego wyjścia jak po prostu zrobić analizę kodu, rozrysować wzajemnie powiązane obrazy, a następnie zaplanować skrupulatnie ich zależności i tego jak powinny być budowane.

Przeanalizujmy przykład, który omawiałem w video. Wracając do wspomnianego my-base-app, wyobraźmy sobie, że z naszej bazowej aplikacji tworzymy my-main-app, a następnie z niej my-second-app. Ponownie naturalnym wydaje się zrobienie łańcucha powiązań:

FROM my-base-app:1.0
RUN install my-libs.json
FROM my-main-app:3.1
RUN install my-libs.json

Może się jednak okazać, że to właśnie my-base-app jest modyfikowany i najczęściej naprawiany. Dosyć łatwo też o sytuację, w której my-libs.json pochodzący z my-second-app jest w starszej wersji niż w my-main-app

Opisane tutaj błędy oczywiście nie wyczerpują tematu, mam jednak nadzieję, że te 5 punktów pomogą Tobie w przyszłym wytwarzaniu i uruchamiani aplikacji w kontenerach. A może Tobie przytrafiły się problemy, których tutaj nie opisałem> Daj mi znać koniecznie w komentarzu!

Dodaj komentarz

This site uses Akismet to reduce spam. Learn how your comment data is processed.