Kontenery jako rewolucja oprogramowania w Linuksie

Rozprowadzanie softu na Linuksa miewa swoje problemy. Mamy w końcu wiele dystrybucji (nawet zawężając je do tych najważniejszych i tak jest ich kilka), do tego wykorzystują one różne formaty paczek. Przez to właśnie chcąc pobrać coś na Linuksa ze strony producenta, mamy jedną paczkę dla Windowsa, jedną dla macOS i kilka dla Linuksa. Czyż nie lepiej byłoby mieć jedną paczkę, która zadziała niezależnie od dystrybucji? Owszem, lepiej byłoby. Stąd też pomysł na kontenery.

System zależności i jego wady

Nie da się ukryć, że najbardziej popularnym sposobem na rozprowadzanie softu na Linuksa jest system paczek z zależnościami. Typowa paczka to zwykłe skompresowane archiwum z zawartością (biblioteka, program lub inne dane) i metadanymi (nazwa, zależności, wersja itd.). Mamy kilka formatów paczek, takich jak deb, rpm i kilka innych, ale wszystkie je łączy zasada działania. Polega ona na tym, że soft jest podzielony. Każda jedna biblioteka czy aplikacja ma swoją osobną paczkę. Każda paczka ma też zaznaczone czego potrzebuje do działania (czyli właśnie zależności) i w ten sposób soft może działać. Jakie są zalety tego rozwiązania? Przede wszystkim rozmiar. Aktualizując daną bibliotekę czy aplikacje pobieramy paczkę z nią, nie musimy pobierać wszystkiego na nowo. Do tego zależności są współdzielone i każdy program może z nich skorzystać, co oszczędza miejsce na dysku czy w pamięci. Do tego dochodzi ważna kwestia bezpieczeństwa – wyobraźmy sobie, że w pewnej bibliotece wykryto lukę. Każda korzystająca z niej aplikacja jest więc podatna. W przypadku gdybyśmy pobierali aplikacje ze strony producenta wraz z bibliotekami, to na tym producencie spoczywałaby konieczność aktualizacji, ewentualnie na użytkowniku, który ma na tyle wiedzy, by sobie to zrobić. W systemie zależności kiedy biblioteka zostanie zaktualizowana, to każdy program z niej korzystający przestanie być podatny. Do tego w przypadku korzystania z repozytoriów dochodzi fakt wygody – wszystko jest w jednym miejscu i aktualizacje załatwia sam menedżer pakietów, nie musi tym się zajmować sama aktualizacja.

To tyle z zalet, a co z wadami? No niestety jest ich kilka. Dopóki korzystamy z repozytoriów to wszystko jest w porządku (czasami się zdarzają błędy, choć należy to do rzadkości). Gorzej jeżeli chcemy zainstalować paczkę spoza repozytorium. A po co mielibyśmy to robić? Choćby dlatego, że aplikacje z własnościowymi licencjami niezbyt mogą być w takim repozytorium umieszczone (chyba, że twórca robi własne, co też nie jest zbyt wygodne biorąc pod uwagę mnogość dystrybucji). No i do tego dochodzi fakt nieco opóźnionych aktualizacji – producent umieszcza nową wersję na swojej stronie, ale zanim paczkujący ją zapaczkują i przetestują to trochę czasu minie. No więc wchodzimy na stronę producenta i chcemy pobrać paczkę dla swojej dystrybucji. Tutaj może nastąpić pierwszy zgrzyt – paczki nie ma dla naszej dystrybucji, albo nie dla naszej wersji. O ile w drugim przypadku jest jakaś szansa, że paczka zadziała (choć wcale nie musi), o tyle w pierwszym pojawia się pierwszy problem. Jesteśmy zmuszeni albo sami ją prze paczkować, albo pobrać z nieoficjalnego źródła (o ile takie istnieje). Żaden z tych sposobów nie jest dobry. Załóżmy, że jednak paczka jest w zwykłym, skompresowanym archiwum (zip, 7z, tar.gz) – wtedy teoretycznie nie ma problemu, bo wystarczy ją rozpakować i uruchomić, prawda? No niestety nie. Bo tutaj może wyjść sytuacja, że program wymaga jakieś starszej biblioteki, a my w systemie mamy nowszą. Nawet jeżeli zadziała to po jakimś czasie może z tego powodu przestać działać. To wszystko składa się na fakt, że system repozytoriów nie jest zbyt przyjazny dla deweloperów softu o zamkniętym kodzie. Teoretycznie mogą sami stworzyć repozytoria, ale to wymaga ich utrzymywania. No i przypominam – nie mamy jednej dystrybucji. Co więc zrobić z tym faktem?

Radzono (i w sumie nieświadomi istnienia kontenerów dalej tak sobie radzą) z tym w różny sposób. Jeden z nich to dostarczanie aplikacji w archiwum wraz ze wszystkimi zależnościami. Jak już pisałem wyżej, pomysł dość nieciekawy ze wzgląd na bezpieczeństwo. Poza tym wygoda instalacji takiego programu też jest nikła (musimy go wypakować, zrobić wpisy w menu itd.). Teoretycznie można by użyć instalatora na kształt Windowsa (najczęściej gry są instalowane w takich sposób), ale to też ma swoje wady. Można by też linkować statycznie (wszystkie biblioteki połączone z plikiem wykonywalnym), ale to przekreśla możliwość łatwiej wymiany bibliotek bez rekompilacji. Co więc dalej?

Zbawca pod postacią kontenerów

I tutaj nadchodzi coś, co ma te problemy rozwiązać. Kontenery. Ich założeniem jest ułatwić dystrybucje aplikacji i zapewnić by jedna paczka działała na praktycznie wszystkich dystrybucjach. Poza tym deweloper nie będzie musiał się martwić o ciągłe pilnowanie paczki, by jakaś aktualizacja nie popsuła aplikacji – biblioteki mają być niezależne od systemu i dostarczane razem z aplikacją. Obecnie mamy 3 najpopularniejsze standardy paczek – Snap, Flatpak i AppImage. Pewnie tutaj ktoś powie, że jak zwykle Linux musi mieć kilka standardów zamiast jednego. No cóż, być może, ale sytuacja nie jest taka oczywista i te 3 projekty nieco się różnią założeniami i technicznym aspektem. Poza tym to są kontenery – one maja działać niezależnie od dystrybucji, więc format ma tutaj minimalne znaczenie. Nie ważne co wybierze deweloper i tak użytkownik będzie mógł taki kontener prosto zainstalować u siebie. Tak więc przyjrzyjmy się im.

AppImage

Na początek zajmiemy się najprostszym z nich. AppImage wydaje się być też najstarszy – jego przodek, tzw. klik został zaprojektowany w 2004 roku przez Simona Petera. Poprzez URL z protokołem klik:// pobierało się przepis na aplikacje, która była następnie budowana z wykorzystaniem zależności z repozytorium Debiana. Gotowa paczka była niezależna od zależności systemowych. Niestety przez limity w montowaniu obrazów dysków, maksymalnie mogło być uruchomione tylko 8 programów tak zapakowanych. Użycie FUSE mogło ten problem rozwiązać i klik2 miał go wykorzystywać, lecz nigdy nie został skończony. Około 2011 roku projekt został porzucony. Twórca stworzył nowy projekt o podobnych założeniach zwany PortableLinuxApps, a ten w 2013 roku przekształcił się w AppImage.

AppImage jest dość prostym formatem, zarówno w budowie jak i w działaniu. Jest to archiwum (o rozszerzeniu appimage) zawierające program wraz z danymi i zależnościami oraz skrypt uruchomieniowy, który odpowiednio ustawia zmienne środowiskowe (tak, by program nie korzystał z zależności systemowych) i uruchamia program. Ten za pomocą FUSE montowany jest w ukrytym katalogu w /tmp i uruchamiany jest skrypt, który ustawia odpowiednio zmienne i uruchamia sam program. Sam plik appimage jest dla systemu zwykłym plikiem wykonywalnym. Po nadaniu mu prawa uruchamiania wystarczy go uruchomić dwuklikiem w środowisku, lub przez konsolę. I tyle. Opcjonalny jest także daemon, który zajmie się tworzeniem plików .desktop (odpowiednik skrótów z Windows) tak by program pojawił się normalnie w menu. Uruchomiony tak program działa normalnie i nie ma większych różnic w porównaniu do normalnie zainstalowanej aplikacji. Dzięki uniezależnieniu od systemu taka aplikacja zadziała na chyba każdej dystrybucji Linuksa (potrzebny jest jej tylko FUSE) i nie przestanie działać, jeżeli z systemu zniknie jakaś biblioteka lub po aktualizacji zerwie API. Dzięki stabilności userlandu jądra Linux taka aplikacja nie powinna też mieć problemu z działaniem za kilka lat. Deweloper może ją przygotować, umieścić na stronie i o niej zapomnieć. No i zamiast wydawać paczkę dla kilku dystrybucji wystarczy tylko jedna, która zadziała na wszystkich i to bez konieczności posiadania w nich menedżerów czy daemonów.

Brzmi fajnie, ale niestety ma swoje wady. Po pierwsze to brak możliwości wydzielenia jednego środowiska uruchomieniowego (runtime) zawierający biblioteki, z których skorzystać może kilka programów. To powoduje, że AppImage niezbyt nadaje się do paczkowania pakietu programów – każdy musiałby powielać biblioteki, co zajmuje miejsce i nie daje prostego sposobu na aktualizacje ich wszystkich naraz. Kolejną wadą jest fakt, że format sam w sobie nie zapewnia żadnego zabezpieczenia dla programów (mowa oczywiście o piaskownicy/sandboksie). Trzeba się posiłkować zewnętrznym (np. firejail).

Snap

Kolejne podejście do kontenerów, tym razem zaproponowane przez Canonical. Początkowo było rozwijane z myślą o Internet of Things oraz Ubuntu Core, obecne jednak jest to uniwersalny projekt, który można używać i na desktopie. Paczki snap zawierają aplikacje i potrzebne jej zależności i są instalowane w katalogu /snap. Do tego aplikacje są zabezpieczane poprzez piaskownice. Dostępny jest też menedżer snap, który zarządza paczkami – pozwala je instalować, usuwać lub aktualizować (zupełnie jak apt). Wprawdzie najwięcej paczek snap jest dostępnych w sklepie Ubuntu, to jednak sam projekt nie jest od niego zależny.

Jakie są wady tego podejścia? Przede wszystkim paczki zajmują dużo miejsca ze wzgląd na posiadanie wszystkich zależności w sobie. Canonical jako rozwiązanie tego problemu podaje użycie systemu plików z deduplikacją, co jednak w świecie Linuksa nie jest oczywiste – ZFS nie jest włączony do jądra przez niekompatybilną licencję, a deduplikacja w Btrfs jest jeszcze dość ograniczona. Poza tym snap był rozwijany początkowo pod Ubuntu i dopiero później doczekał się portów na inne dystrybucje. Mimo wszystko snap jest w pewien sposób od Canonical zależny – jak inne projekty tej firmy, jest objęty CLA, którą deweloperzy muszą podpisać chcąc się włączyć do projektu.

Flatpak

Przechodzimy już do ostatniego projektu, moim zdaniem najlepiej rokującego. Idea użycia kontenerów w GNOME została zaproponowana w 2013 roku przez Lennarta Poetteringa, który w 2014 roku opublikował na ten temat artykuł. Projekt tworzony pod skrzydłami freedesktop.org otrzymał nazwę xdg-app. Później nazwa została zmieniona na Flatpak. Pomimo pierwotnych założeń, projekt nie jest związany z GNOME ani innym desktopem (wbrew niektórym zarzutom), a od wersji 0.6.10 nie jest też zależny od systemd, co czyni go uniwersalnym i przenośnym projektem. Dzięki temu wydaje się być najpopularniejszy i najbardziej rozpowszechniony z całej trójki.

Podobnie jak i snap, Flatpak również wymaga obecności odpowiedniego oprogramowania w systemie (jednak większość popularnych dystrybucji ma go w repozytoriach). Pozwala on na zarządzanie zainstalowanymi paczkami, zajmuje także się ich aktualizacją. Poza oczywistymi paczkami z programem, Flatpak ma też paczki ze środowiskiem uruchomieniowym (ang. runtime). Zawierają one zbiór bibliotek zapewniających stabilne API dla korzystających z nich aplikacji. Deweloper chcąc więc stworzyć aplikacje nie musi załączać do niej wszystkich podstawowych bibliotek, tylko wystarczy, że wybierze środowisko uruchomieniowe, które je zawiera. Oczywiście te środowiska są dzielone pomiędzy aplikacjami (coś jak .Net Framework w Windowsie) dzięki czemu oszczędza się miejsce i w przypadku wykrycia luki można ją załatać dla wszystkich aplikacji naraz. Flatpak pozwala też wybrać, czy paczki mają być zainstalowane dla wszystkich użytkowników czy tylko dla jednego. Każdy użytkownik może mieć też swoje repozytoria, niezależne od systemowych. Kontenery są instalowane w /var/lib/flatpak lub w ~/.local/share/flatpak w zależności od wybranego miejsca instalacji. Podobnie też jak i w poprzednim podejściu, i tutaj paczki są izolowane od systemu poprzez piaskownice. Flatpaki można instalować poprzez repozytorium (jednym z największych jest flathub, który stara się być globalnym repozytorium dla flatpaków), albo poprzez pobranie pliku flatpakref zawierającego informacje o aplikacji, na podstawie których menedżer pobiera i instaluje aplikacje.

Niestety i ten projekt nie jest bez wad. Jedną z nich jest sam fakt istnienia środowisk uruchomieniowych. Wprawdzie mają one zalety, lecz ponieważ jest ich kilka i do tego w różnych wersjach, to po instalacji kilku aplikacji będziemy mieć w systemie kilka środowisk, a potrafią one zajmować po kilkaset MB. Do tego dochodzą problemy z motywami – kontenery są izolowane od systemu, nie mają więc jak pobrać motywu systemowego czy ikon. Problem został rozwiązany (a raczej ominięty) poprzez dystrybucje samych motywów jako kontenery. Tutaj też się ujawnia kolejny problem – jeżeli używamy jakiegoś mniej popularnego motywu, to może nie istnieć dla niego paczka, co zmusza nas do czekania aż ktoś ją przygotuje, lub zrobienia tego samemu. Póki co flathub zawiera ponad 20 motywów, w tym motywy najpopularniejszych dystrybucji (np. Ambiance z Ubuntu czy motywy Minta).

Podsumowanie

Kontenery wydają się być całkiem niezłym rozwiązaniem dla deweloperów chcących w prosty sposób wydać aplikacje dla Linuksa. Zamiast kilku niekompatybilnych ze sobą formatów wystarczy jedna, uniwersalna paczka. Faktem jest, że zajmują więcej miejsca, ale myślę, że to wada, którą można przeboleć patrząc na wygody, jakie ze sobą przynoszą. Mimo wszystko uważam, że kontenery powinny pozostać rozwiązaniem dla zewnętrznych programów. Do podstawowych pakietów mimo wszystko lepiej się sprawdza system repozytoriów. Oddzielenie pakietów systemowych od aplikacji też może wyjść na dobre. Dlatego popularyzacja kontenerów przyniesie sporo ułatwień użytkownikom Linuksa, a także sporo ułatwień deweloperom. 

Źródła

Wizualizacja zależności

Logo AppImage

Logo Flatpaka

Logo Snapa