Podstawy konteneryzacji – czyli jak w Linuksie działają kontenery
Artykuł ten powstał, by przybliżyć czytelnikom zagadnienia konteneryzacji. Oprócz omówienia najważniejszych koncepcji przedstawię także rys historyczny. Pozwoli to na zrozumienie, iż kontenery wbrew pozorom nie są technologią nową. Ich obecny kształt jest pochodną idei, która ewaluowała dosłownie przez całe pokolenia pasjonatów i twórców systemów operacyjnych.
Artykuł ten powstał, by przybliżyć czytelnikom zagadnienia konteneryzacji. Oprócz omówienia najważniejszych koncepcji przedstawię także rys historyczny. Pozwoli to na zrozumienie, iż kontenery wbrew pozorom nie są technologią nową. Ich obecny kształt jest pochodną idei, która ewaluowała dosłownie przez całe pokolenia pasjonatów i twórców systemów operacyjnych.
Z doświadczenia wiem, iż zdarza się, że osoby mówiące z zachwytem o kontenerach nie do końca wiedzą, co to z technicznego punktu znaczy. Dlatego pozwolę sobie nieformalnie zdefiniować konteneryzację. Przy czym zaznaczam, że definicja ta jest oparta na doświadczeniu i ma za zadanie pomóc w zrozumieniu idei, a nie omówić konkretną technologię. Zatem – konteneryzacja to proces uruchamiania procesów w środowisku odizolowanym od reszty systemu. Proces działający w kontenerze „nie wie”, że obok niego działają inne zarówno nieskonteneryzowane, jak i skonteneryzowane procesy. Konteneryzacja wiąże się także z ograniczeniem zasobów systemu (CPU, pamięć, dysk) dla zadanego środowiska kontenera. Ważną cechą konteneryzacji rozróżniającą ją od pełnej wirtualizacji jest to, iż kontenery współdzielą system oraz jego zasoby, a także wirtualizują tylko wybrane elementy.
Rys historyczny konteneryzacji
Zanim nastały czasy popularności Dockera, systemd-nspawn czy Podmana oraz frameworków, czy orkiestratorów, istniało wiele narzędzi i mechanizmów, które można śmiało uznać za swoistą konteneryzację. Pozwolę sobie przytoczyć trzy najważniejsze.
Chroot – 1979
Chroot, często nazywany także „change rootem”, jest już „prastarym” mechanizmem pochodzącym z Unixa 7 zwanego też po prostu V7. Mechanizm ten istnieje do dziś. Pozwala on na wykonywanie poleceń w nowym katalogu głównym (root). Dzieci procesu uruchomionego w chroocie także widzą swój „świat” z nowego katalogu. Mamy tutaj do czynienia z separacją dostępu plikowego dla każdego procesu. Mechanizm ten został dodany przez Billy’ego Joya (twórcę vi, csh, NFS czy współzałożyciela Sun Microsystems) do projektu BSD w 1981 roku. Obecne kontenery są ideowym spadkobiercą chroot. Jako ciekawostkę chciałbym dodać, iż gdy buduje się (szczególnie od zera) „nowoczesny” kontener dla Dockera czy Podmana, to jednym z głównych narzędzi jest właśnie chroot.
FreeBSD jails – (FreeBSD 4.0) 2000
Mechanizm jails (więzień) zaimplementowany w FreeBSD pozwala podzielić system na wiele mniejszych, w istocie współdzieląc jeden system. Z punktu widzenia użytkownika każdy z mikrosystemów jest pełnoprawnym systemem. Koncept jails jest naturalną ewolucją chroot i rozbudowuje go między innymi o wirtualizację:
- dostępu do systemu plików (chroot pozwala na bezpośredni dostęp do plików)
- dostępu użytkowników (użytkownik root w jailu nie ma dostępu do głównego systemu)
- sieci (w chroot system korzysta z tych samych zasobów sieciowych).
Chciałbym też zwrócić uwagę na następujące charakterystyki definiujące mechanizm jail.
- Punkt wejścia (drzewo katalogów)
- Hostname
- Adress IP
- Komenda startowa.
Cechy definiujące areszt w BSD występują także w konteneryzacji w Linuksie. Oczywiście punkt wejścia to obraz, a nie ścieżka w systemie plików. Niemniej jest to dosłownie kalka.
Solaris Containers – 2004
Opisywane jako „chroot na sterydach”. De facto pierwsze w pełni komercyjne kontenery w postaci znanej nam dzisiaj. Łączyły w sobie zarówno odseparowanie na poziomie plików (jak chroot, jail), użytkowników (jak FreeBSD jail), sieci (jak FreeBSD jail), jak i możliwość zarządzania zasobami takimi jak CPU czy maksymalna pamięć. Dzięki takiemu rozwiązaniu nie dopuszczamy do tego, by jeden lub kilka mocno obciążonych kontenerów wysyciło zasoby serwera. Nazwa samego projektu jest wymienna z „Solaris Zones”, czyli strefami będącymi zbiorami kontenerów.
Inne projekty warte wspomnienia
Do innych projektów, które przyczyniły się do rozwoju kontenerów, szczególnie pod Linuksem, należy zaliczyć:
- Linux Vserver – oferował on partycjonowanie zasobów. Nigdy nie zdobył jednak większej popularności i wymagał ręcznej aplikacji łatek na jądro
- Process Containers – stworzony przez Google w 2006 roku, służył do limitowania użycia zasobów systemu takich jak CPU, sieć, zasobów dyskowych (w tym ilości operacji I/O) czy pamięci. Projekt zmienił swoją nazwę na cgroups, czyli control groups i jest po dziś dzień podstawą umożliwiającą efektywną konteneryzację. Znajduje się standardowo w jądrze Linuksa
- LXC (LinuX Containers) – to pierwsza kompletna implementacja kontenerów pod Linuksem – działa do dziś. Wykorzystuje ona doskonale znane mechanizmy cgroups oraz namespaces, które zostaną omówione w dalszej części artykułu.
¡Docker!
Stworzony w 2013 roku Docker jest odpowiedzialny za tzw. boom, czyli eksplozję rynku kontenerów. Jest on pierwszym projektem, który zdobył tak szerokie grono zwolenników. Z punktu widzenia technicznego jest to daemon systemu wraz z klientem, który pomaga zarządzać kontenerami. Inaczej rzecz ujmując, jest to usługa działająca w modelu klient-serwer, odpowiadająca za tworzenie, zarządzanie czy niszczenie kontenerów. Co ciekawe, w pierwszym okresie swojego istnienia Docker wykorzystywał LXC. Następnie korzystał z nowej biblioteki libcontainer, by finalnie dołączyć do inicjatywy OCI (Open Container Initiative) i wykorzystywać najbardziej ustandaryzowaną bibliotekę runc.
Docker wykorzystuje do tworzenia kontenerów dwie cechy/funkcjonalności z jądra Linuksa. Pierwszą z nich jest cgroups, czyli wspomniany wcześniej mechanizm odpowiadający przede wszystkim za limitowanie zasobów takich jak pamięć, zużycie procesora, ilość operacji dyskowych czy maksymalna liczba procesów w zadanej grupie. Co więcej, mechanizm ten pozwala na zliczanie wszelkiego typu operacji.
Drugim mechanizmem są namespace’y, czyli przestrzenie nazw. W dużym skrócie, przestrzenie nazw odseparowują, tj. izolują procesy między różnymi przestrzeniami nazw. Procesom w zadanej przestrzeni nazw „wydaje się”, że są one jedynymi procesami w systemie. Przestrzenie nazw posiadają między innymi swoje wirtualne sieci, co jest niezbędne w przypadku kontenerów.
Przejrzenie odpowiednich manuali man 7 namespaces
i man 7 cgroups
pozwoli poszerzyć wiedzę na temat obydwóch mechanizmów.
Z punktu widzenia dewelopera, Docker odpowiada za kompletny cykl życia kontenera. Pozwala także na zarządzanie nim w dosyć prosty sposób. Właśnie ta prostota (ujęta np. w Dockerfile posiadającym całe 18 poleceń, z których deweloper potrzebuje znać ok. 6) pozwoliła Dockerowi zdominować rynek.
Orkiestracja kontenerów
Na zakończenie chciałbym przedstawić kilka kłopotów związanych z Dockerem oraz generalnie z kontenerami:
- kontenery ze swojej natury są efemeryczne (krótkotrwałe). Podczas gdy np. bazy danych takie jak EuroDB (PostgreSQL) umieszczone w kontenerze wymagają trwałego zapisu i nośnika danych
- kontenery otrzymują „losowe” (w teorii da się tym sterować, jednak rozwiązanie to jeszcze bardziej komplikuje środowisko) adresy IP, więc łączenie ich, szczególnie gdy pojawia się konieczność skalowania tylko wybranych części, może być kłopotliwe
- by osiągnąć HA, należy umieścić aplikację lub serwis na N hostach (N>1)
- wraz z rozrostem środowiska coraz trudniej zarządzać kontenerami.
By rozwiązać te i wiele innych problemów, powstały orkiestratory kontenerów. Do najważniejszych należą: Kubernetes (bardzo aktywnie rozwijany), Docker Swarm (wbrew powszechnej opinii firma Docker nie zabija tego projektu, gdyż ponad 700 klientów Dockera z niego korzysta) czy w końcu Apache Mesos, który potrafi orkiestrować zarówno zasoby skonteneryzowane, jak i nieskonteneryzowane. Orkiestratory tworzą warstwę abstrakcji nad kontenerami. Pozwala to na przykład na zarządzanie autoskalowaniem kontenerów, propagację zmian w obrazie na wszystkie węzły czy tworzenie idei serwisu, który może być automatycznie load-balancowany. Generalna idea orkiestratora kontenerów możne zostać streszczona jako narzędzia do zarządzania wielokontenerowym środowiskiem w skali.
Więcej informacji na temat tego zagadnienia pojawi się wkrótce na naszym blogu.
Zakończenie
Przyszłość samego Dockera stoi pod dużym znakiem zapytania. Widać tutaj trudność znalezienia własnej tożsamości oraz sposobu skutecznego spieniężenia najpopularniejszej platformy do tworzenia kontenerów. Odkładając jednak na bok szeroko pojęte kwestie biznesowe, należy docenić wpływ, jaki Docker miał i ma na szeroką adaptację technologii konteneryzacji, będącą dzisiaj raczej standardem niż ciekawostką dostępną dla wybrańców.
Chciałbym serdecznie podziękować za czas spędzony na czytaniu tego artykułu. Mimo iż na pierwszy rzut oka nie jest on techniczny, to pozwala zdobyć pewną ogładę przy dyskusji na temat kontenerów. Pomaga także zrozumieć kontekst konteneryzacji oraz fakt, iż wbrew pozorom nie jest to wcale nowa idea, a także, że należy na nią patrzeć jak na naturalną ewolucję.