Bootable Containers
bootc nie jest sposobem na uruchamianie całego systemu Linux w Dockerze. Jest sposobem na dostarczanie hosta jako obrazu OCI.
To rozróżnienie jest ważne, bo wokół bootable containers łatwo zbudować złą intuicję. Plik wejściowy wygląda jak Containerfile, obraz trafia do registry, a build robi się Podmanem. Brzmi jak zwykły kontener aplikacyjny. Różnica pojawia się dopiero przy deployu: ten obraz ma stać się systemem, z którego bootuje fizyczna maszyna albo VM.
Po starcie host nie działa jako proces w kontenerze. Normalnie startuje kernel, normalnie działa systemd jako PID 1, normalnie są usługi systemowe, sieć, logi i dyski. Kontenerowy jest format budowy i dystrybucji, nie runtime całego hosta.
Czym jest bootable container
Bootable container image to obraz OCI, który oprócz userspace zawiera elementy potrzebne do uruchomienia systemu: kernel, moduły, initramfs, konfigurację bootowania, systemd i pakiety, które mają być częścią hosta.
Z punktu widzenia builda można go traktować jak zwykły obraz kontenerowy:
FROM localhost/bootc-base-homelab:latest
RUN dnf install -y nginx NetworkManager && \
dnf clean all
COPY nginx.conf /usr/lib/blog/nginx.conf
COPY blog-web.service /usr/lib/systemd/system/blog-web.service
RUN systemctl enable blog-web.service
RUN bootc container lint
To nadal jest obraz OCI. Da się go zbudować Podmanem, otagować, wypchnąć do registry, mirrorować, skanować i wersjonować podobnie jak obrazy aplikacyjne. Ale jego przeznaczeniem nie jest podman run na produkcji. Jego przeznaczeniem jest instalacja albo upgrade hosta.
bootc jest narzędziem, które potrafi taki obraz zastosować na działającym systemie. Nowa wersja jest pobierana i przygotowywana jako kolejny bootowalny deployment. Dopiero reboot przełącza host na nową wersję.
To daje prostą własność operacyjną: host działa na starej wersji albo na nowej. Nie powinien kończyć w pół drogi, z częścią plików z jednego świata i częścią z drugiego.
To nie jest „Linux w Dockerze”
Najczęstsze nieporozumienie brzmi tak: skoro buduję system z Containerfile, to pewnie uruchamiam system w kontenerze.
Nie.
Kontener jest tutaj formatem artefaktu. Registry jest kanałem dystrybucji. Warstwy obrazu są wygodnym mechanizmem budowy i cache. Po deployu host zachowuje się jak host.
To ma kilka praktycznych konsekwencji.
Po pierwsze, nie chowasz problemów systemowych pod runtime kontenerowym. Nadal trzeba rozumieć bootloader, kernel, sterowniki, systemd, sieć, SELinux, storage i logi.
Po drugie, rollback dotyczy systemowego deploymentu, a nie stanu aplikacji. Jeśli nowy obraz hosta zawiera gorszą konfigurację nginx, rollback może wrócić do poprzedniego deploymentu. Jeśli aplikacja w międzyczasie zmigrowała bazę danych w /var/lib/postgresql, rollback obrazu hosta nie cofnie tej bazy.
Po trzecie, ręczne zmiany na hoście przestają być naturalnym sposobem pracy. Można wejść przez SSH i diagnozować problem, ale trwała poprawka powinna wrócić do repo, do Containerfile, plików konfiguracyjnych albo playbooka. Inaczej drift wraca bocznymi drzwiami.
Co to daje w tym repo
W tym homelabie bootc ma bardzo konkretną rolę: zmniejszyć drift między VM i zamienić lifecycle hosta w lifecycle obrazu.
Środowisko działa na Proxmoxie. Usługi są zwykle rozdzielone według zasady 1 VM = 1 usługa albo 1 mały stack. Jest lokalny registry pod 10.20.0.50, wspólna baza bootc-base-homelab, a na niej obrazy pochodne dla konkretnych usług.
To nie jest repo z samymi kontenerami aplikacyjnymi. Zmiana często dotyczy hosta, routingu, reverse proxy, registry i sposobu rolloutów. Dlatego host jako obraz ma tu sens.
Wspólny obraz bazowy trzyma rzeczy, które powinny być podobne na większości maszyn: podstawowe pakiety, Podmana tam, gdzie jest potrzebny, agenta dla VM, zaufanie do lokalnego registry, bazową konfigurację systemową. Obrazy usług dodają tylko to, co specyficzne dla danej VM.
Dla bloga wygląda to jeszcze prościej. Blog jest budowany przez Zolę do statycznego katalogu, a potem osadzany w obrazie blog-sieciowiec. Runtime to VM bootc z natywnym nginx. To nie jest kontener Podmana z blogiem.
To drobny szczegół, ale dobrze pokazuje model: obraz OCI nie musi oznaczać aplikacyjnego kontenera. Może być sposobem dostarczenia całego hosta, na którym działa zwykła usługa systemd.
Przepływ
Najkrótszy przepływ wygląda tak:
- Zmieniam
Containerfile, konfigurację albo zawartość bloga. - Buduję obraz OCI.
- Wypycham go do lokalnego registry.
- Host pobiera nowy obraz przez
bootc upgrade. - Reboot przełącza host na nowy deployment.
- W razie problemu wybieram rollback do poprzedniego deploymentu.
Dla typowego obrazu w repo wystarcza:
task build IMAGE=caddy-homelab
task push IMAGE=caddy-homelab
Dla bloga trzeba podać dodatkowe build contexty, bo źródła Zoli i treść bloga są w osobnych katalogach:
task build IMAGE=blog-sieciowiec \
CONTEXT=./blog-sieciowiec \
CONTAINERFILE=blog-sieciowiec/Containerfile \
BUILD_ARGS='--build-context blogsrc=./blog --build-context blogcontent=./blog-content'
task push IMAGE=blog-sieciowiec
Na hoście docelowym upgrade jest już operacją systemową:
sudo bootc upgrade --image 10.20.0.50/blog-sieciowiec:latest
sudo systemctl poweroff
Po starcie VM powinna działać nowa wersja obrazu. Jeśli zmiana była zła:
sudo bootc rollback
sudo systemctl reboot
To nie zwalnia z walidacji. Po rolloucie nadal trzeba sprawdzić, czy host wrócił, czy usługa działa, czy Caddy dochodzi do backendu i czy użytkownik widzi właściwą wersję strony.
Registry jako część systemu
W takim modelu registry nie jest dodatkiem do CI. Jest częścią operacji.
Hosty pobierają z niego obrazy systemowe. Część usług pobiera z niego obrazy aplikacyjne. Rollout zależy od tego, czy registry jest osiągalne, czy zawiera właściwy tag i czy host ufa temu źródłu.
W tym homelabie registry działa lokalnie w DMZ pod 10.20.0.50. task push dba nawet o trasę do DMZ, bo bez połączenia z registry build może być gotowy, ale rollout i tak stanie.
To jest dobry kompromis dla homelabu: publiczne registry nie jest runtime dependency dla każdej aktualizacji hosta, a jednocześnie obrazy nadal mają standardowy format OCI.
Ale to też odpowiedzialność. Registry staje się ważnym komponentem. Trzeba myśleć o jego storage, backupie, retencji tagów, certyfikatach i o tym, co się stanie, gdy będzie niedostępne w czasie deployu.
Co zostaje poza bootc
bootc porządkuje lifecycle systemu, ale nie rozwiązuje całego utrzymania usług.
/usr, kernel, boot files, jednostki systemd i większość plików dostarczonych w obrazie należą do obrazu. Zmiana w tych miejscach powinna przejść przez build.
/etc jest stanem konfiguracyjnym hosta. W typowym modelu pozostaje modyfikowalne i jest przenoszone przez upgrade z semantyką merge. To pomaga w realnych systemach, ale może też być źródłem driftu, jeśli zacznie się traktować /etc jako miejsce ręcznych hotfixów.
/var to osobny temat. Logi, dane aplikacji, bazy, wolumeny i cache nie są czymś, co chcesz cofać razem z obrazem systemu. Zawartość /var ma przetrwać upgrade i rollback hosta. To dobra własność, ale wymaga dyscypliny: migracje baz, backupy i recovery nadal trzeba projektować osobno.
Poza bootc zostają też sekrety, klucze, polityka dostępu, routing, Caddy, DNS, mTLS, monitoring i procedury awaryjne. W tym repo Caddy jest centralnym edge i policy engine. To on wystawia usługi, kończy TLS, wymusza mTLS dla wybranych wejść i proxy'uje ruch do VM w DMZ. bootc nie zastępuje tej warstwy.
Najkrócej: bootc pomaga odtworzyć i zaktualizować hosta. Nie robi za backup danych, secret manager, orchestrator ani pełny system deploymentu aplikacji stanowych.
Trade-offy
Największy trade-off to reboot. Aktualizacja obrazu systemu jest przygotowywana wcześniej, ale zaczyna obowiązywać po restarcie. Dla homelabu to zwykle akceptowalne. Dla usługi z ostrym SLA trzeba zaplanować redundancję albo okno serwisowe.
Drugi trade-off to dyscyplina buildów. Jeśli poprawka ma być trwała, powinna trafić do obrazu. Ręczny dnf install na hoście może pomóc w diagnozie, ale jako metoda utrzymania cofa projekt do snowflake servers.
Trzeci trade-off to tooling. Sam bootc jest stabilizowany jako interfejs do upgrade i zarządzania hostem, ale okolice budowania obrazów dyskowych nadal się zmieniają. W repo można dziś spotkać bootc-image-builder do generowania qcow2 albo ISO. Upstream przesuwa ten obszar w stronę unified image-builder, więc nie warto pisać architektury tak, jakby jedna nazwa narzędzia była wieczna.
Czwarty trade-off to registry. Lokalny registry daje kontrolę i przewidywalność, ale sam wymaga utrzymania. Jeśli jest popsuty, rollout staje się trudniejszy.
Piąty trade-off to stan. Rollback systemu jest użyteczny, ale nie jest wehikułem czasu dla aplikacji. To trzeba mówić wprost, bo inaczej image-based lifecycle brzmi lepiej niż działa.
Dlaczego to jest sensowne pod CV
Wpis o bootable containers łatwo napisać jako listę narzędzi: Podman, OCI, registry, bootc, Proxmox, Caddy. To niewiele mówi.
Ciekawsze jest połączenie tych elementów w jeden model operacyjny.
Hosty są budowane jako artefakty. Registry jest źródłem rolloutów. VM mają mniejszy blast radius niż jeden wspólny Docker host. Caddy jest miejscem polityki dostępu. Blog jest przykładem usługi, która nie musi być kontenerem aplikacyjnym, żeby korzystać z kontenerowego formatu dostarczania hosta. Rollback istnieje, ale ma granice. /var i backupy dalej wymagają normalnej pracy operatorskiej.
To pokazuje dojrzałość lepiej niż kolejny tutorial „od zera”. Nie chodzi o to, że bootc rozwiązuje wszystko. Chodzi o to, że pozwala ułożyć Linuxa, obrazy, registry, sieć i rollouty w model, który jest powtarzalny, sprawdzalny i możliwy do odtworzenia po awarii.
Dla homelabu to jest wystarczająco praktyczne. Dla CV jest cenne z innego powodu: pokazuje nie tylko znajomość narzędzia, ale umiejętność wyznaczania granic odpowiedzialności. Co jest w obrazie. Co jest stanem hosta. Co jest danymi aplikacji. Co robi edge. Co robi registry. Kiedy wystarczy rollback. Kiedy potrzebny jest backup.
To jest różnica między „znam kontenery” a „potrafię utrzymać system, który da się aktualizować bez zgadywania, co ktoś kiedyś zmienił po SSH”.