Wróć do katalogu
Season 29 18 Odcinki 1h 12m 2026

Docker Masterclass

Edycja 2026. Kompleksowy kurs audio o Dockerze, obejmujący podstawy kontenerów, obrazy, Dockerfile, sieci, Compose, CI/CD oraz najnowsze funkcje AI, takie jak MCP Toolkit, Docker Sandboxes i Docker Agent.

Konteneryzacja DevOps
Docker Masterclass
Teraz odtwarzane
Click play to start
0:00
0:00
1
Obietnica Dev = Prod
Odkryj, dlaczego Docker fundamentalnie zmienił rozwój oprogramowania. Ten odcinek omawia główną wartość płynącą z oddzielenia aplikacji od infrastruktury i osiągnięcia idealnej zgodności między środowiskami programistycznymi a produkcyjnymi.
3m 43s
2
Kontenery a maszyny wirtualne
Zrozum różnice architektoniczne między kontenerami a maszynami wirtualnymi (VM). Dowiedz się, jak kontenery osiągają izolację poprzez współdzielenie jądra hosta, co czyni je niezwykle lekkimi w porównaniu do tradycyjnych hipernadzorców.
4m 12s
3
Anatomia obrazu Dockera
Odkryj, czym tak naprawdę jest obraz Dockera. Ten odcinek wyjaśnia zasady niemutowalności obrazu i kompozycji warstw, pokazując, jak zmiany w systemie plików są nakładane na siebie, aby stworzyć szablon kontenera.
3m 56s
4
Schemat Dockerfile
Dowiedz się, jak napisać Dockerfile, aby budować niestandardowe obrazy. Omawiamy podstawowe instrukcje, takie jak FROM, RUN i CMD, oraz wyjaśniamy kluczową różnicę między formami shell i exec.
4m 27s
5
Opanowanie Build Cache
Zoptymalizuj budowanie obrazów, używając build cache Dockera. Dowiedz się, dlaczego kolejność instrukcji w Twoim Dockerfile jest kluczowa, aby zapobiec niepotrzebnym instalacjom zależności.
3m 58s
6
Multi-Stage Builds
Utrzymuj swoje obrazy produkcyjne lekkimi i bezpiecznymi. Ten odcinek wprowadza Multi-Stage Builds, demonstrując, jak oddzielić ciężkie środowisko kompilacji od minimalnego środowiska uruchomieniowego.
4m 28s
7
Uruchamianie i interakcja
Poznaj praktyczne mechanizmy uruchamiania kontenerów. Omawiamy tryby detached i interactive, podstawowe publikowanie portów oraz sposób wykonywania poleceń powłoki wewnątrz działającego kontenera.
4m 00s
8
Podstawy trwałości danych
Zapobiegnij katastrofalnej utracie danych podczas usuwania kontenerów. Ten odcinek porównuje Bind Mounts, używane do hot-reloading w lokalnym środowisku programistycznym, z Docker Volumes, służącymi do bezpiecznego przechowywania baz danych.
3m 48s
9
Sieci w kontenerach
Zrozum, jak Docker obsługuje ruch sieciowy. Poznaj podstawy publikowania portów na hoście oraz dowiedz się, jak kontenery bezpiecznie komunikują się ze sobą w izolowanych sieciach typu bridge.
4m 18s
10
Wprowadzenie do Docker Compose
Wyjdź poza polecenia dla pojedynczych kontenerów. Dowiedz się, jak Docker Compose używa deklaratywnego pliku YAML do definiowania, łączenia w sieć i orkiestracji wielu usług jednocześnie.
4m 23s
11
Docker w potoku CI/CD
Wyeliminuj niestabilne testy dzięki skonteneryzowanym środowiskom budowania. Ten odcinek omawia, jak używać Dockera w potokach Continuous Integration, aby zagwarantować idealnie powtarzalne testy automatyczne.
3m 57s
12
Obrazy wieloplatformowe
Rozwiąż problem niezgodności między Apple Silicon a serwerami w chmurze. Dowiedz się, jak Docker Buildx pozwala na jednoczesną kompilację skrośną i pakowanie aplikacji dla architektur ARM i AMD64.
3m 55s
13
Docker MCP Toolkit
Bezpiecznie połącz swoje agenty AI z lokalnymi narzędziami. Ten odcinek wprowadza Docker Model Context Protocol (MCP) Toolkit, wyjaśniając, jak zarządzać skonteneryzowanymi serwerami MCP za pomocą katalogów i profili.
3m 43s
14
Automatyczne wykrywanie Dynamic MCP
Poznaj Dynamic MCP, eksperymentalną funkcję, która pozwala klientom AI przeszukiwać Docker MCP Catalog i dynamicznie instalować nowe serwery narzędzi podczas konwersacji bez ręcznej konfiguracji.
4m 17s
15
Docker Sandboxes dla AI
Zrozum architekturę Docker Sandboxes. Dowiedz się, dlaczego autonomiczne agenty programistyczne AI wymagają izolowanych microVMs z dedykowanymi demonami Dockera zamiast standardowych przestrzeni nazw kontenerów.
3m 52s
16
Budowanie zespołów agentów AI
Przestań polegać na pojedynczym modelu AI przy złożonych zadaniach. Ten odcinek wprowadza framework Docker Agent, pokazując, jak komponować wyspecjalizowane zespoły agentów zdefiniowane w pliku YAML.
4m 18s
17
Zestawy narzędzi i przepływy pracy agentów
Spraw, by Twoje agenty AI były naprawdę użyteczne, nakładając na nie odpowiednie ograniczenia. Dowiedz się, jak konfigurować zestawy narzędzi systemu plików i wymuszać ustrukturyzowane przepływy pracy programistycznej w Docker Agent.
3m 51s
18
Modele AI w Compose
Traktuj swoje lokalne modele LLM jak każdą inną zależność aplikacji. Dowiedz się, jak deklarować, konfigurować i wiązać modele AI bezpośrednio w pliku YAML Docker Compose.
3m 12s

Odcinki

1

Obietnica Dev = Prod

3m 43s

Odkryj, dlaczego Docker fundamentalnie zmienił rozwój oprogramowania. Ten odcinek omawia główną wartość płynącą z oddzielenia aplikacji od infrastruktury i osiągnięcia idealnej zgodności między środowiskami programistycznymi a produkcyjnymi.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 1 z 18. Właśnie spędziłeś trzy dni na poszukiwaniu błędu, który występuje tylko na serwerze stagingowym. Kod działa bez zarzutu na twoim laptopie, ale gdy tylko trafia na deployment pipeline, wywala błąd. Winowajcą jest prawie zawsze niezgodna biblioteka systemowa, inna wersja runtime'u lub brakująca zmienna środowiskowa. To jest dokładnie ten problem, który Docker miał wyeliminować, dostarczając obietnicę dev równa się prod. Docker to otwarta platforma do tworzenia, dostarczania i uruchamiania aplikacji. Jego głównym celem jest oddzielenie twoich aplikacji od infrastruktury. Historycznie, deweloperzy pisali kod, a zespoły ops stawiały serwery. Deweloperzy przekazywali aplikację, a zespół ops spędzał godziny lub dni na konfigurowaniu hosta, aby spełnić wymagania oprogramowania. Takie ręczne synchronizowanie środowisk jest kruche i powolne. Docker rozwiązuje to, pakując aplikację razem z jej zależnościami, narzędziami systemowymi, bibliotekami i runtime'em w ustandaryzowaną jednostkę zwaną kontenerem. Możesz kojarzyć ten koncept z tradycyjnymi maszynami wirtualnymi. Chociaż łączy je cel izolacji aplikacji, kontenery są znacznie lżejsze, ponieważ nie wymagają pełnego systemu operacyjnego gościa. Omówimy tę różnicę w architekturze w następnym odcinku. Teraz skupimy się na tym, co daje takie pakowanie. Oto kluczowa kwestia. Ponieważ kontener zawiera zarówno kod, jak i dokładne środowisko potrzebne do jego uruchomienia, maszyna hosta staje się w dużej mierze bez znaczenia. Docker gwarantuje, że jeśli kontener działa na twoim lokalnym laptopie deweloperskim, będzie działał dokładnie tak samo na serwerze QA i dokładnie tak samo w produkcyjnym data center. Eliminujesz wymówkę u mnie działa, ponieważ twoja maszyna i maszyna produkcyjna zapewniają teraz dokładnie to samo środowisko uruchomieniowe. Workflow wygląda następująco. Deweloper pisze kod lokalnie i definiuje wymagane środowisko w pliku konfiguracyjnym w postaci zwykłego tekstu. Docker czyta ten plik i buduje statyczny artefakt zwany obrazem. To właśnie ten pojedynczy, niezmienny obraz jest testowany. Kiedy testy przejdą, dokładnie ten sam obraz jest deployowany na produkcję. Nie kopiujesz kodu na serwer i nie odpalasz skryptu instalacyjnego. Przenosisz całe działające środowisko jako jedną, szczelną jednostkę. Ta przenośność zmienia sposób, w jaki systemy się skalują. Ponieważ kontenery są ustandaryzowane i lekkie, stawianie nowych instancji aplikacji w odpowiedzi na skok ruchu zajmuje tylko kilka sekund. Możesz łatwo przenosić obciążenia między różnymi środowiskami, migrując aplikację z lokalnego serwera testowego do cloud providera bez zmiany ani jednej linijki kodu czy rekonfiguracji hosta. Ostateczny wniosek jest taki, że Docker przekształca infrastrukturę w przewidywalny towar, tworząc sztywną granicę, gdzie deweloperzy mają pełną kontrolę nad środowiskiem wewnątrz kontenera, a dział ops po prostu dostarcza moc obliczeniową do jego uruchomienia. Jeśli chcesz wesprzeć podcast, wyszukaj DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i buduj dalej!
2

Kontenery a maszyny wirtualne

4m 12s

Zrozum różnice architektoniczne między kontenerami a maszynami wirtualnymi (VM). Dowiedz się, jak kontenery osiągają izolację poprzez współdzielenie jądra hosta, co czyni je niezwykle lekkimi w porównaniu do tradycyjnych hipernadzorców.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 2 z 18. Nie musisz bootować całego systemu operacyjnego, żeby odpalić jeden skrypt w Pythonie. Jednak przez lata programiści akceptowali duży overhead i wolne czasy bootowania, żeby utrzymać swoje aplikacje w izolacji od siebie. Dzisiaj to zmienimy, biorąc pod lupę kontenery i maszyny wirtualne. Wyobraź sobie, że odpalasz lokalnie skomplikowany stack. Potrzebujesz frontendu w React, API w Pythonie i bazy danych PostgreSQL, które działają jednocześnie. Jeśli zainstalujesz je bezpośrednio na swoim hoście, prosisz się o konflikty zależności. API może wymagać konkretnej wersji biblioteki systemowej, która gryzie się z tym, czego potrzebuje twoja baza danych. Kontener rozwiązuje ten problem, działając jako proces w sandboxie. I tu jest klucz do zrozumienia. Kontener to nie jest miniaturowy komputer. To po prostu proces działający natywnie na twoim hoście. Magia tkwi w izolacji. Dzięki funkcjom wbudowanym w system operacyjny, ten proces dostaje swój własny, prywatny system plików, własny networking stack i wyizolowany widok systemu. Dla API w Pythonie, które działa wewnątrz, wygląda to tak, jakby było jedynym softem na maszynie. Z kolei dla systemu operacyjnego twojego hosta, to po prostu kolejny standardowy proces, zupełnie jak twoja przeglądarka internetowa czy edytor tekstu. Ponieważ kontener to tylko proces, współdzieli on kernel systemu operacyjnego hosta. Kiedy baza danych PostgreSQL wewnątrz kontenera musi zaalokować pamięć albo zapisać rekord na dysku, komunikuje się bezpośrednio z kernelem hosta. Nie ma tu żadnego pośrednika ani drugiego systemu operacyjnego, który bootuje się w tle. Dlatego start kontenera jest niemal natychmiastowy. Trwa to dokładnie tyle samo, co uruchomienie samej surowej aplikacji. Teraz zestawmy to bezpośrednio z maszyną wirtualną. Maszyna wirtualna podchodzi do izolacji poprzez symulowanie sprzętu. Opiera się na hypervisorze, czyli oprogramowaniu, które wydziela wirtualny CPU, wirtualną pamięć i wirtualny dysk. Na tym udawanym sprzęcie musisz zainstalować kompletny system operacyjny gościa. Jeśli chcesz odpalić to samo API w Pythonie na odizolowanej maszynie wirtualnej, musisz zbootować całą dystrybucję Linuxa. Maszyna wirtualna ładuje swój własny, oddzielny kernel, inicjalizuje sterowniki urządzeń i odpala usługi systemowe w tle, zanim w ogóle pomyśli o wykonaniu twojego kodu w Pythonie. Za każdym razem, gdy aplikacja musi odczytać plik, request przechodzi przez system operacyjny gościa, w dół do hypervisora, a ostatecznie do sprzętu hosta. Zapewnia to niesamowicie silną izolację bezpieczeństwa, ale wiąże się z ogromnymi kosztami w postaci cykli CPU, zużycia pamięci i czasu bootowania. Z powodu tych różnic, powszechnym mitem jest to, że musisz wybrać jedno albo drugie. W rzeczywistości kontenery i maszyny wirtualne wcale się nie wykluczają. W nowoczesnych środowiskach chmurowych prawie zawsze używa się ich razem. Kiedy provisionujesz instancję w chmurze, tak naprawdę wynajmujesz maszynę wirtualną. Ta maszyna wirtualna zapewnia silną granicę na poziomie sprzętu, oddzielając twój workload od innych klientów na tym samym fizycznym serwerze. Następnie instalujesz container runtime wewnątrz tej maszyny wirtualnej, żeby odpalić swój frontend w React, swoje API i swoją bazę danych. Maszyna wirtualna izoluje infrastrukturę, podczas gdy kontenery izolują poszczególne aplikacje. Ostatecznie ta różnica sprowadza się do granic. Maszyny wirtualne wirtualizują sprzęt, żeby uruchamiać wiele systemów operacyjnych, podczas gdy kontenery wirtualizują system operacyjny, żeby uruchamiać wiele odizolowanych procesów. Dzięki za spędzenie ze mną tych kilku minut. Do usłyszenia następnym razem, trzymaj się.
3

Anatomia obrazu Dockera

3m 56s

Odkryj, czym tak naprawdę jest obraz Dockera. Ten odcinek wyjaśnia zasady niemutowalności obrazu i kompozycji warstw, pokazując, jak zmiany w systemie plików są nakładane na siebie, aby stworzyć szablon kontenera.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 3 z 18. Robisz deploy aplikacji i działa ona idealnie. Dwa tygodnie później restartujesz dokładnie tę samą aplikację na tej samej maszynie i crashuje, bo biblioteka systemowa zaktualizowała się w tle. Ten cichy drift środowiska to dokładnie to, co eliminujemy, rozumiejąc anatomię obrazu Dockera. Po pierwsze, wyjaśnijmy najczęstsze nieporozumienie. Obraz to nie kontener. Obraz to statyczny szablon. Zawiera kod twojej aplikacji, biblioteki, narzędzia systemowe i twój runtime. Kontener to po prostu uruchomiona instancja tego obrazu. Możesz uruchomić tysiąc kontenerów z jednego obrazu, ale sam obraz po prostu leży na dysku, czekając na odczyt. Główną cechą obrazu Dockera jest immutability. Raz utworzony obraz nigdy nie jest modyfikowany. Nie możesz zmienić pliku konfiguracyjnego wewnątrz istniejącego obrazu. Jeśli chcesz zmienić obraz, musisz zbudować zupełnie nowy. To immutability gwarantuje, że obraz przetestowany na twoim laptopie zachowa się identycznie na produkcji. Szablon nie może z czasem ulec driftowi. Skoro nie możesz modyfikować obrazu, musisz budować nowe. Obraz Dockera nie jest jednym, wielkim plikiem. To kompozycja wielu niezależnych warstw, ułożonych jedna na drugiej. Każda warstwa reprezentuje konkretny zestaw zmian w systemie plików, co oznacza dodawanie, modyfikowanie lub usuwanie plików. Weźmy na przykład aplikację Node dot js. Rzadko kiedy sam budujesz system operacyjny. Zamiast tego zaczynasz od base image. Ten base image zawiera minimalną dystrybucję Linuksa i runtime Node'a. Ta baza tak naprawdę składa się z własnych warstw, ale dla ciebie stanowi fundament. Kiedy dodajesz swoją aplikację do tego fundamentu, Docker zapisuje twoje zmiany jako nowe warstwy ułożone na samej górze. Najpierw dorzucasz plik konfiguracyjny z zależnościami. To tworzy nową warstwę. Następnie instruujesz system, żeby pobrał i zainstalował twoje zależności. Wszystkie te pobrane biblioteki są pakowane w kolejną warstwę. Na koniec kopiujesz kod źródłowy swojej aplikacji. To tworzy najwyższą warstwę. Kiedy uruchamiasz kontener, Docker układa te warstwy w stos, używając union filesystem. Dzięki temu wszystkie niezależne warstwy wyglądają jak jedna standardowa struktura katalogów. Jeśli w dwóch warstwach istnieje dokładnie ta sama ścieżka do pliku, wersja w wyższej warstwie ukrywa wersję z niższej warstwy. I tu jest kluczowa sprawa. Ponieważ warstwy są immutable, są one mocno cache'owane i współdzielone. Jeśli zaktualizujesz kod źródłowy swojej aplikacji i zbudujesz nowy obraz, Docker obliczy, co się zmieniło. Widzi, że base image Linuksa, runtime Node'a i warstwa zależności są identyczne jak w poprzednim buildzie. Błyskawicznie wykorzystuje ponownie te istniejące warstwy i tworzy nową warstwę tylko dla twojego zaktualizowanego kodu. To zamienia deploy, który mógłby trwać minuty, w operację trwającą milisekundy. Ta architektura oszczędza również miejsce na dysku i przepustowość sieci. Kiedy robisz push nowego obrazu na serwer, przesyłasz tylko tę jedną warstwę zawierającą nowy kod źródłowy. Serwer ma już base layers. Wymuszając immutable layers, Docker gwarantuje, że środowisko twojej aplikacji nie zmieni się po cichu, jednocześnie upewniając się, że przesyłasz lub przechowujesz dokładnie tylko te bajty, które zmodyfikowałeś. To wszystko na dzisiaj. Do usłyszenia następnym razem!
4

Schemat Dockerfile

4m 27s

Dowiedz się, jak napisać Dockerfile, aby budować niestandardowe obrazy. Omawiamy podstawowe instrukcje, takie jak FROM, RUN i CMD, oraz wyjaśniamy kluczową różnicę między formami shell i exec.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 4 z 18. Całe środowisko operacyjne dla twojej złożonej aplikacji można wyrazić w zaledwie dziesięciu linijkach zwykłego tekstu. Przekazujesz jeden plik do build systemu, a w rezultacie otrzymujesz idealnie skonfigurowany system gotowy do uruchomienia w dowolnym miejscu. To jest blueprint pliku Dockerfile. Dockerfile to dokument tekstowy zawierający wszystkie komendy, które użytkownik mógłby wywołać z command line'a, aby zbudować image. Format jest prosty. Każda linijka zaczyna się od instrukcji, zapisanej zgodnie z konwencją wielkimi literami, po której następują argumenty dla tej instrukcji. Docker czyta ten plik linijka po linijce, z góry na dół. Każdy poprawny Dockerfile musi zaczynać się od fundamentu. Definiujesz go za pomocą instrukcji FROM. Jeśli napiszesz FROM ubuntu, Docker pobiera oficjalny image Ubuntu i używa go jako punktu startowego. Każda kolejna linijka w twoim pliku będzie modyfikować to środowisko bazowe. Kiedy twoja baza jest już gotowa, zazwyczaj musisz zainstalować zależności. Robisz to za pomocą instrukcji RUN. Instrukcja RUN wykonuje dowolną komendę wewnątrz obecnego image'u i robi commit wyniku. Jeśli napiszesz RUN apt-get update, a po nim komendy instalacji pakietów, Docker odpala środowisko, uruchamia package manager, instaluje software i zapisuje nowy stan. Następnie potrzebujesz swojej właściwej aplikacji. System operacyjny z zainstalowanymi zależnościami jest bezużyteczny bez twojego kodu. Zajmuje się tym instrukcja COPY. Podajesz ścieżkę źródłową ze swojego lokalnego workspace'u i ścieżkę docelową wewnątrz image'u. Docker bierze twoje pliki i kopiuje je bezpośrednio do systemu plików kontenera. Budowanie image'u to dopiero pierwsza faza. Musisz również powiedzieć Dockerowi, jaką aplikację ma odpalić, gdy kontener wystartuje. To domyślne zachowanie definiujesz za pomocą instrukcji CMD lub ENTRYPOINT. Oto kluczowa sprawa. Istnieją dwa różne sposoby formatowania tych instrukcji, a ich pomylenie powoduje subtelne bugi. Pierwsze podejście to shell form. Piszesz instrukcję, a po niej komendę dokładnie tak, jak wpisałbyś ją w terminalu. Kiedy Docker to widzi, owija twoją komendę w shell, wykonując ją przez bin slash sh. To wygodne, ponieważ zmienne środowiskowe podstawiają się automatycznie. Jednak proces shella siedzi pomiędzy Dockerem a twoją aplikacją. Jeśli Docker wyśle sygnał, żeby łagodnie zatrzymać kontener, shell go przechwytuje, a twoja aplikacja nigdy go nie otrzymuje, co prowadzi do wymuszonego zamknięcia. Drugie podejście to exec form. Zapisuje się to jako tablicę JSON. Formatujesz to za pomocą nawiasów kwadratowych, podając plik wykonywalny jako pierwszy string, a jego argumenty jako kolejne stringi. Kiedy używasz exec form, Docker całkowicie pomija shella. Uruchamia twój plik wykonywalny bezpośrednio. Twoja aplikacja staje się procesem o ID jeden wewnątrz kontenera. To gwarantuje, że sygnały systemowe trafiają bezpośrednio do twojej aplikacji, zapewniając płynne i przewidywalne zamykanie. Jeśli chcesz mieć stabilny kontener na produkcji, zawsze używaj exec form dla swoich końcowych komend, aby twoja aplikacja kontrolowała swój własny cykl życia. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
5

Opanowanie Build Cache

3m 58s

Zoptymalizuj budowanie obrazów, używając build cache Dockera. Dowiedz się, dlaczego kolejność instrukcji w Twoim Dockerfile jest kluczowa, aby zapobiec niepotrzebnym instalacjom zależności.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 5 z 18. Jeśli twój Docker build zajmuje dziesięć minut za każdym razem, gdy zmieniasz jedną linijkę kodu źródłowego, robisz to źle. Rozwiązanie tkwi całkowicie w tym, jak organizujesz strukturę swojego pliku, a to oznacza opanowanie build cache'a. Kiedy odpalasz Docker build, builder przetwarza twój Dockerfile sekwencyjnie, z góry na dół. Każda pojedyncza instrukcja, niezależnie od tego, czy kopiuje katalog, czy odpala skrypt, generuje osobny layer w wynikowym image'u. Ponieważ wykonanie tych kroków wymaga mocy obliczeniowej i przepustowości sieci, Docker automatycznie zapisuje output każdego kroku w lokalnym build cache'u. Przy kolejnych buildach, silnik próbuje ponownie wykorzystać te zapisane layery, aby pominąć nadmiarową pracę. Aby określić, czy możliwy jest cache hit, Docker ocenia każdą instrukcję pod kątem istniejącej historii cache'a. W przypadku instrukcji odpalających komendy, sprawdza, czy sam command string jest identyczny z tym użytym w poprzednim buildzie. W przypadku instrukcji kopiujących pliki z twojego hosta do image'u, Docker idzie o krok dalej. Oblicza checksum dla zawartości i metadanych każdego kopiowanego pliku. Następnie porównuje ten nowy checksum z checksumem plików w poprzednio zcache'owanym layerze. Jeśli checksumy idealnie się zgadzają, Docker ponownie wykorzystuje zcache'owany layer i przechodzi do następnej linijki. Jeśli różni się choćby jeden bajt, cache jest inwalidowany. Zwróć na to szczególną uwagę. Inwalidacja cache'a to ścisła reakcja łańcuchowa. W chwili, gdy Docker wykryje zmianę i zinwaliduje layer, przestaje patrzeć na cache do końca builda. Każda pojedyncza instrukcja następująca po zinwalidowanym layerze musi wykonać się od zera. Dzieje się tak, ponieważ każdy layer polega na dokładnym stanie layera, który był przed nim. Ta reakcja łańcuchowa dyktuje to, jak musisz zorganizować swój Dockerfile. Weźmy pod uwagę aplikację w Node, w której zarządzasz zewnętrznymi zależnościami. Częstym błędem jest użycie pojedynczej instrukcji do skopiowania całego folderu projektu do image'u, a następnie instrukcji odpalającej komendę instalacji pakietów. Jeśli zmodyfikujesz jedną linijkę w pliku tekstowym gdzieś w swoim kodzie źródłowym, zmienia się checksum dla instrukcji kopiowania. Cache sypie się na tym etapie. W rezultacie, wymuszone jest odpalenie kolejnej instrukcji. Czekasz, aż setki megabajtów zależności pobiorą się ponownie, mimo że twoja właściwa lista zależności pozostała całkowicie nietknięta. Optymalne podejście izoluje zależności od kodu aplikacji. Po pierwsze, dodajesz instrukcję, która kopiuje do image'u tylko twój plik konfiguracyjny zależności, a konkretnie twój package manifest. Po drugie, odpalasz komendę do pobrania zależności. Po trzecie, dodajesz osobną instrukcję, żeby skopiować resztę twojego głównego kodu źródłowego. Teraz, kiedy modyfikujesz ten sam plik tekstowy i robisz rebuild, Docker ocenia pierwszą instrukcję. Manifest zależności się nie zmienił, więc używany jest cache. Przechodzi do kroku instalacji. Ponieważ poprzedni layer był cache hitem, a command string jest identyczny, cache jest tu użyty ponownie, pomijając to ogromne pobieranie. Cache sypie się dopiero przy ostatniej instrukcji, gdzie builder kopiuje twoje zaktualizowane pliki źródłowe. Dziesięciominutowe czekanie staje się dwusekundowym updatem. Najskuteczniejszym sposobem na przyspieszenie twojego pipeline'u jest uporządkowanie instrukcji ściśle od tych, które mają najmniejszą szansę na zmianę, do tych, które zmienią się najprawdopodobniej. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
6

Multi-Stage Builds

4m 28s

Utrzymuj swoje obrazy produkcyjne lekkimi i bezpiecznymi. Ten odcinek wprowadza Multi-Stage Builds, demonstrując, jak oddzielić ciężkie środowisko kompilacji od minimalnego środowiska uruchomieniowego.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 6 z 18. Wrzucanie kompilatora na produkcję to ogromne zagrożenie bezpieczeństwa, które zwiększa rozmiar twojego kontenera o gigabajty. Piszesz czysty kod, ale twój finalny artefakt jest obciążony przez te wszystkie ciężkie narzędzia potrzebne tylko do jego zbudowania. Rozwiązaniem tego problemu są multi-stage builds. Kiedy budujesz aplikacje w językach kompilowanych, takich jak Java, Go czy C++, proces kompilacji wymaga build tools, SDK i samego kodu źródłowego. Kiedyś developerzy stosowali standardowe podejście, w którym instalowali wszystkie te zależności w kontenerze, kompilowali kod, a następnie uruchamiali aplikację. Problem polega na tym, że wszystkie te build tools zostają w finalnym obrazie produkcyjnym. W efekcie robisz deploy kompilatora, package managera i plików pośrednich razem z samą aplikacją. To sprawia, że twój kontener staje się ogromny. Duże kontenery dłużej się pulluje przez sieć i zużywają więcej miejsca na dysku. Co gorsza, tworzy to ogromną powierzchnię ataku. Jeśli atakujący włamie się do twojego kontenera, nagle ma do dyspozycji w pełni wyposażone środowisko developerskie. Często błędnie uważa się, że naprawienie tego wymaga utrzymywania dwóch osobnych plików – jednego do zbudowania softu, oraz skryptu, który wyciągnie wynik i przekaże go do drugiego pliku w celu deployu. Nic bardziej mylnego. Multi-stage builds ogarniają całe to separation of concerns wewnątrz jednego pliku. Oto kluczowa sprawa. Multi-stage build pozwala ci sekwencyjnie zdefiniować wiele odrębnych środowisk, czyli stage'y. Każdy stage zaczyna się od zdefiniowania własnego base image'u. Zaczynasz pierwszy stage od ciężkiego base image'u, który zawiera wszystkie twoje narzędzia developerskie. Nadajesz temu stage'owi nazwę, na przykład builder. Wewnątrz tego stage'a builder kopiujesz swój kod źródłowy z lokalnej maszyny i odpalasz polecenia kompilacji. Stage builder odwala czarną robotę, generując finalny plik wykonywalny. Następnie, nieco niżej w dokładnie tym samym pliku, definiujesz drugi base image. To inicjuje zupełnie nowy stage. Dla tego stage'a wybierasz minimalny runtime image. To środowisko zawiera tylko te konkretne zależności, które są potrzebne do uruchomienia aplikacji, i zero build toolsów. Zamiast znowu kopiować pliki z lokalnej maszyny, używasz specjalnej instrukcji copy. Ta instrukcja mówi silnikowi budującemu, żeby sięgnął z powrotem do stage'a builder, wziął tylko gotowy, skompilowany artefakt i wrzucił go do twojego nowego, minimalnego stage'a. Kiedy silnik skończy budować, tworzy kontener oparty wyłącznie na finalnym stage'u. Wszystko z pierwszego stage'a — kompilator, pobrane pakiety, kod źródłowy — jest całkowicie odrzucane. Nigdy nie trafia do twojego produkcyjnego image'u. Weźmy pod uwagę konkretny scenariusz z aplikacją w Java Spring Boot. W twoim pliku, twój pierwszy stage używa ciężkiego image'u Mavena. Wewnątrz tego stage'a odpalasz komendę Mavena, żeby spakować swoją aplikację. Maven pobiera wszystkie niezbędne zależności projektu, kompiluje kod w Javie i pakuje go w gotowy plik JAR. Następnie zaczynasz drugi stage, używając lekkiego base image'u Java Runtime Environment. Nie instalujesz Mavena w tym środowisku. Nie kopiujesz tutaj swoich plików źródłowych Javy. Zamiast tego instruujesz silnik, żeby skopiował tylko skompilowany plik JAR bezpośrednio ze stage'a Mavena do tego minimalnego środowiska runtime. Na koniec ustawiasz domyślną komendę, żeby odpalić ten plik JAR. Dzięki ścisłemu oddzieleniu środowiska budowania od środowiska runtime, gwarantujesz, że twój kontener produkcyjny jest całkowicie odizolowany od twoich build toolsów. Finalny image widzi tylko skompilowany artefakt i absolutne minimum środowiska runtime, dzięki czemu twój deploy jest szybki, odchudzony i wysoce bezpieczny. To wszystko na dzisiaj. Do usłyszenia następnym razem!
7

Uruchamianie i interakcja

4m 00s

Poznaj praktyczne mechanizmy uruchamiania kontenerów. Omawiamy tryby detached i interactive, podstawowe publikowanie portów oraz sposób wykonywania poleceń powłoki wewnątrz działającego kontenera.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 7 z 18. Uruchomienie procesu w tle to dokładnie to, czego potrzebujesz dla produkcyjnego serwera WWW. Ale gdy ten serwer odmawia załadowania twojej strony, potrzebujesz sposobu, żeby zbić szybkę, wejść do środowiska i sprawdzić, co tak naprawdę nie działa. Ten odcinek omawia uruchamianie kontenerów i interakcję z nimi. Podstawowym poleceniem do uruchomienia dowolnego kontenera jest docker run. Domyślnie, jeśli uruchomisz kontener, podłącza on swój output bezpośrednio do ekranu twojego terminala. Przejmuje twój prompt, a jeśli wciśniesz control C, kontener się zamyka. Dla usług działających w tle przez długi czas, takich jak serwer WWW Nginx, jest to całkowicie niepraktyczne. Chcesz, żeby serwer działał w tle. Osiągasz to używając flagi detached mode, którą wpisujesz jako pojedynczy minus d. Kiedy przekazujesz minus d, Docker uruchamia kontener, wypisuje na ekranie długi, unikalny ID kontenera i natychmiast oddaje ci twój prompt terminala. Kontener nadal działa cicho w tle. Jednak ten działający kontener jest odizolowany. Nawet jeśli Nginx aktywnie serwuje ruch na porcie 80 wewnątrz kontenera, twój host go nie widzi. Musisz jawnie wybić dziurę w tej izolacji sieciowej. Robisz to za pomocą flagi publish, wpisywanej jako minus p. Pozwala to zmapować konkretny port na twoim laptopie na konkretny port wewnątrz kontenera. Jeśli podasz minus p 8080 dwukropek 80, Docker przechwytuje cały ruch sieciowy uderzający w twój laptop na porcie 8080 i routuje go bezpośrednio do portu 80 wewnątrz kontenera. Teraz masz działający w trybie detached serwer WWW, do którego możesz bez problemu dotrzeć ze swojej lokalnej przeglądarki. Ale co się stanie, gdy załadujesz stronę i zobaczysz błąd konfiguracji? Twój serwer Nginx działa w tle, ale ty musisz przeczytać pliki konfiguracyjne w jego systemie plików. Oto kluczowa sprawa. Nie musisz zatrzymywać kontenera, żeby zajrzeć do środka. Zamiast tego używasz polecenia docker exec. Podczas gdy docker run tworzy zupełnie nowy kontener, docker exec uruchamia zupełnie nowe polecenie wewnątrz już istniejącego, działającego kontenera. Żeby uzyskać użyteczny, działający terminal, musisz połączyć dwie konkretne flagi w minus i t. Litera i oznacza interactive. To utrzymuje otwarty kanał standard input, pozwalając ci faktycznie wpisywać polecenia do kontenera. Litera t alokuje pseudo-TTY. To oszukuje kontener, by myślał, że jest podłączony do fizycznego terminala, co jest niezbędne, żeby twój prompt i formatowanie tekstu wyświetlały się poprawnie. Jeśli uruchomisz docker exec minus i t, a następnie podasz nazwę kontenera i polecenie slash bin slash bash, natychmiast wylądujesz w prompcie wewnątrz działającego kontenera Nginx. Jesteś teraz w środku. Możesz czytać pliki konfiguracyjne, sprawdzać error logi i przeglądać system plików dokładnie tak, jak na standardowym serwerze Linux. Kiedy skończysz, wpisanie exit zamyka twoją tymczasową sesję shella. Sam kontener Nginx pozostaje całkowicie nienaruszony i nadal działa w tle. W końcu będziesz musiał posprzątać. Uruchomienie docker stop z nazwą kontenera wysyła sygnał terminacji, dając aplikacji czas na graceful shutdown. Jednak zatrzymanie kontenera nie usuwa go z twojego systemu. Zatrzymany kontener zostaje na twoim dysku twardym, zachowując swoje logi i wszelkie zmiany w wewnętrznym systemie plików. Żeby trwale go usunąć i zwolnić to miejsce na dysku, uruchamiasz polecenie docker rm. Najważniejszą rzeczą do zapamiętania jest różnica między run a exec. Docker run odpala zupełnie nowy, odizolowany system, podczas gdy docker exec pozwala ci wejść do systemu, który już żyje. Dzięki za wysłuchanie. Trzymajcie się wszyscy.
8

Podstawy trwałości danych

3m 48s

Zapobiegnij katastrofalnej utracie danych podczas usuwania kontenerów. Ten odcinek porównuje Bind Mounts, używane do hot-reloading w lokalnym środowisku programistycznym, z Docker Volumes, służącymi do bezpiecznego przechowywania baz danych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 8 z 18. Robisz deploy bazy danych w kontenerze, zapisujesz tysiące wierszy i wszystko działa idealnie. Potem usuwasz kontener, żeby zaktualizować image, a cała twoja baza danych znika na zawsze. Domyślnie storage w kontenerze jest ściśle tymczasowy. Żeby zapobiec utracie danych, musimy poznać podstawy Data Persistence. Kiedy kontener startuje, tworzy zapisywalną warstwę na swoim bazowym image'u. Wszystkie pliki, które kontener tworzy lub modyfikuje, są przechowywane w tej konkretnej warstwie. Jeśli kontener zostanie zniszczony, ta warstwa jest niszczona razem z nim. Dane są całkowicie efemeryczne. Nie istnieją poza własnym lifecycle'em kontenera. Żeby dane były bezpieczne, musisz wyprowadzić je z kontenera na maszynę hosta. Docker oferuje do tego dwa główne mechanizmy: bind mounts i managed volumes. Bind mount mapuje konkretną, jawną ścieżkę na twojej maszynie hosta bezpośrednio na ścieżkę wewnątrz kontenera. Mówisz Dockerowi dokładnie, który folder na twoim laptopie ma się pojawić w środowisku kontenera. Jest to mocno zależne od systemu operacyjnego twojego hosta i lokalnej struktury plików. Maszyna hosta zachowuje pełną kontrolę nad plikami. To podejście jest idealne do lokalnego developmentu. Robisz bind mount lokalnego katalogu z kodem źródłowym do ścieżki aplikacji webowej w kontenerze. Kiedy edytujesz i zapisujesz skrypt na laptopie, kontener natychmiast odczytuje ten zaktualizowany plik. Masz natychmiastowy hot-reloading bez przebudowywania image'u kontenera za każdym razem, gdy zmieniasz linijkę kodu. Drugi mechanizm to managed volume. Zamiast wskazywać na konkretną ścieżkę, którą kontrolujesz na swoim dysku, prosisz Dockera o utworzenie encji storage'u. Docker przydziela przestrzeń na maszynie hosta i całkowicie nią zarządza. Nie musisz wiedzieć, gdzie Docker fizycznie umieszcza te pliki w systemie hosta. Po prostu nadajesz volume'owi nazwę i mówisz kontenerowi, gdzie ma go zamontować wewnętrznie. Volumes to standardowe rozwiązanie dla persistence bazy danych. Odpalając PostgreSQL, tworzysz volume, uruchamiając prostą komendę i nadając mu identyfikator, na przykład db-data. Potem, przy starcie kontenera, przekazujesz flagę konfiguracyjną, która linkuje ten volume db-data z wewnętrzną ścieżką, w której Postgres zapisuje rekordy swoich tabel. Jeśli zatrzymasz i usuniesz kontener z bazą danych, Docker zostawi ten volume całkowicie w spokoju. Kiedy później odpalisz nowy kontener, po prostu podpinasz ten istniejący volume, a wszystkie twoje rekordy są nienaruszone. Oto kluczowa sprawa. Wybór między tymi dwiema metodami sprowadza się do tego, kto potrzebuje dostępu do plików. Używaj bind mounts, kiedy twoja maszyna hosta musi aktywnie wchodzić w interakcję z danymi, tak jak developer edytujący kod źródłowy. Używaj managed volumes, kiedy to kontener jest właścicielem danych, tak jak silnik bazy danych zapisujący rekordy, a ty po prostu chcesz, żeby Docker chronił te pliki pomiędzy restartami kontenera. Efemeryczne kontenery to wybór projektowy, a nie wada, ponieważ zmuszają cię do oddzielenia danych od twojego runtime compute. Zawsze zakładaj, że twój kontener zostanie natychmiast zniszczony, i jawnie mapuj swój persistent state na zewnątrz. Jeśli uważasz te odcinki za pomocne, możesz wesprzeć podcast, wyszukując DevStoriesEU na Patreonie. To wszystko w tym odcinku. Dzięki za słuchanie i koduj dalej!
9

Sieci w kontenerach

4m 18s

Zrozum, jak Docker obsługuje ruch sieciowy. Poznaj podstawy publikowania portów na hoście oraz dowiedz się, jak kontenery bezpiecznie komunikują się ze sobą w izolowanych sieciach typu bridge.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 9 z 18. Domyślnie, uruchomiony kontener jest całkowicie odizolowany od świata zewnętrznego. Siedzi w prywatnej bańce i jeśli chcesz, żeby internet miał do niego dostęp, musisz celowo wybić dziury w tej izolacji. Zarządzanie tymi dziurami i połączeniami między kontenerami to zadanie Container Networking. Kiedy kontener startuje, Docker przypisuje mu wewnętrzny adres IP. Kontener zazwyczaj może wyjść do internetu, żeby pobrać aktualizacje albo wykonać requesty sieciowe, ale nic spoza hosta nie może dostać się do środka. Żeby akceptować ruch przychodzący, używasz port publishing. Publishing bierze port na twoim fizycznym hoście i binduje go bezpośrednio do portu wewnątrz kontenera. Jeśli masz kontener z web serwerem nasłuchujący wewnętrznie na porcie osiemdziesiąt, możesz zrobić jego publish na port osiemdziesiąt osiemdziesiąt na twoim hoście. Kiedy użytkownik wysyła request do twojego hosta na port osiemdziesiąt osiemdziesiąt, Docker go przechwytuje i forwarduje prosto przez firewalla do kontenera na port osiemdziesiąt. Konfigurujesz to mapowanie przy starcie, używając flagi publish. Bez tej flagi kontener pozostaje niedostępny dla sieci zewnętrznej. To pokrywa ruch zewnętrzny. Teraz, drugą częścią tego jest komunikacja wewnętrzna. Aplikacje rzadko działają jako pojedynczy, odizolowany proces. Zazwyczaj masz wiele kontenerów, które muszą współdzielić dane. Domyślnie Docker podłącza każdy nowy kontener do wbudowanej sieci o nazwie default bridge. Bridge to programowy switch sieciowy działający na twoim hoście. Łączy on kontenery, żeby mogły wymieniać pakiety, jednocześnie izolując je od sieci zewnętrznych. Oto kluczowa sprawa. Default bridge pozwala kontenerom na komunikację przy użyciu ich wewnętrznych adresów IP, ale adresy IP kontenerów zmieniają się za każdym razem, gdy kontener się restartuje albo aktualizuje. Hardcodowanie adresu IP w konfiguracji twojej aplikacji zepsuje twój system niemal natychmiast. Żeby to rozwiązać, tworzysz sieć user-defined bridge. Kiedy podłączasz wiele kontenerów do customowego user-defined bridge, Docker zapewnia automatyczne wewnętrzne DNS resolution. To oznacza, że kontenery mogą się znaleźć nawzajem, używając swoich dokładnych nazw. Wyobraź sobie scenariusz, w którym masz kontener z aplikacją backendową i kontener z bazą danych. Tworzysz jedną customową sieć bridge i podłączasz do niej oba kontenery. W kodzie twojej aplikacji backendowej nie wpisujesz connection stringa do bazy danych, używając kruchego adresu IP. Po prostu używasz nazwy kontenera bazy danych jako adresu hosta. Docker przechwytuje zapytanie DNS, znajduje kontener bazy danych na tym konkretnym bridge'u i dynamicznie routuje ruch na właściwy wewnętrzny adres IP. Taki design daje ci całkowitą kontrolę nad bezpieczeństwem aplikacji. Backend i baza danych mogą swobodnie rozmawiać ze sobą przez customowy bridge, ale żaden ruch zewnętrzny nie może dotrzeć do bazy danych. Żeby bezpiecznie uruchomić twoją aplikację, zostawiasz bazę danych ukrytą na prywatnym, wewnętrznym bridge'u bez publishowania portów. Następnie publishujesz tylko port kontenera backendowego na twój host. Zewnętrzni użytkownicy uderzają w publiczny port backendu, a backend bezpiecznie odpytuje bazę danych przez prywatny bridge. Architektura twojej aplikacji dyktuje twoją topologię sieci: używaj published ports, żeby zaprosić zewnętrznych użytkowników do środka, i customowych sieci user-defined bridge, żeby pozwolić twoim wewnętrznym kontenerom na bezpieczną komunikację między sobą po nazwie. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
10

Wprowadzenie do Docker Compose

4m 23s

Wyjdź poza polecenia dla pojedynczych kontenerów. Dowiedz się, jak Docker Compose używa deklaratywnego pliku YAML do definiowania, łączenia w sieć i orkiestracji wielu usług jednocześnie.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 10 z 18. Nie powinieneś potrzebować pliku tekstowego pełnego skomplikowanych komend w terminalu, tylko po to, żeby uruchomić swoje lokalne środowisko deweloperskie. Poleganie na historii shella, żeby zapamiętać dokładne flagi, porty i nazwy sieci dla wielu kontenerów, to mało stabilny sposób pracy. Wprowadzenie Docker Compose rozwiązuje ten problem, zamieniając cały stack twojej aplikacji w jeden deklaratywny plik. Kiedy uruchamiasz aplikację, rzadko działa ona w izolacji. Zazwyczaj masz serwer webowy, bazę danych i może jakąś warstwę cache. Ręczne startowanie tego wszystkiego wymaga odpalenia wielu osobnych komend. Musisz stworzyć customową sieć, podpiąć do niej każdy kontener, wystawić odpowiednie porty i zamontować wolumeny. Jeśli zrobisz literówkę w którymkolwiek z tych kroków, kontenery nie będą mogły się ze sobą komunikować, a aplikacja po prostu nie zadziała. Docker Compose zastępuje ten imperatywny proces deklaratywnym plikiem YAML, zazwyczaj o nazwie compose dot yaml. Zamiast mówić Dockerowi krok po kroku, co dokładnie ma robić, deklarujesz pożądany stan końcowy całego systemu. Docker Compose sam ustala niezbędne kroki, żeby ten stan osiągnąć. Plik YAML dzieli się na trzy główne sekcje strukturalne. Pierwsza i najważniejsza sekcja nazywa się services. Serwis to po prostu definicja konkretnego kontenera w twojej aplikacji. Weźmy scenariusz, w którym odpalasz aplikację w Node razem z bazą danych MySQL. W sekcji services definiujesz dwa wpisy. Pierwszy nazywasz web, podając obraz Node i lokalne porty, które chcesz wystawić. Drugi nazywasz database, podając obraz MySQL i wymagane zmienne środowiskowe, takie jak hasło roota. I tu jest kluczowa sprawa. Nie musisz ręcznie linkować tych kontenerów. Domyślnie, Docker Compose automatycznie tworzy jedną wewnętrzną sieć dla twojej aplikacji. Podpina wszystkie zdefiniowane serwisy do tej sieci i przypisuje każdemu kontenerowi hostname, który pasuje do nazwy jego serwisu. Kod twojej aplikacji w Node może połączyć się z bazą danych, po prostu odwołując się do hostname database, a wewnętrzny DNS zresolwuje go na prawidłowe IP kontenera. Możesz ręcznie zdefiniować customowe sieci w sekcji networks w pliku YAML, ale w większości standardowych setupów deweloperskich, domyślne zachowanie robi dokładnie to, czego potrzebujesz. Ostatnim elementem strukturalnym jest sekcja volumes. Bazy danych wymagają persistent storage. Jeśli kontener MySQL się wyłączy, nie chcesz, żeby twoje dane zostały wyczyszczone. Na samym dole pliku YAML deklarujesz nazwany wolumen. Następnie, w definicji serwisu bazy danych, mapujesz konkretną ścieżkę wewnątrz kontenera na ten nazwany wolumen. Docker Compose zarządza tworzeniem i cyklem życia tego storage'u za ciebie. Kiedy twój plik jest już gotowy, zarządzasz całym stackiem za pomocą dwóch komend. Wpisujesz docker compose up. Compose czyta plik YAML, tworzy wewnętrzną sieć, konfiguruje wolumeny i startuje kontenery MySQL i Node. Jeśli chcesz dalej pracować w terminalu, dodajesz flagę detach, żeby odpalić wszystko w tle. Kiedy skończysz pracę, nie zatrzymujesz i nie usuwasz każdego kontenera z osobna. Wpisujesz docker compose down. Compose bezpiecznie zatrzymuje aplikację w Node, zatrzymuje bazę danych i usuwa kontenery oraz domyślną sieć, utrzymując twój system w całkowitej czystości. Zostawia twoje nazwane wolumeny nienaruszone, co oznacza, że rekordy w bazie danych będą na ciebie czekać, gdy następnym razem podniesiesz stack. Docker Compose zmienia twój mindset z zarządzania pojedynczymi, odizolowanymi kontenerami na zarządzanie kompletnymi środowiskami aplikacji. Setup twojej infrastruktury staje się pojedynczym kawałkiem kodu, który możesz zacommitować do kontroli wersji i natychmiast udostępnić swojemu zespołowi. To wszystko w tym odcinku. Dzięki za wysłuchanie i kodujcie dalej!
11

Docker w potoku CI/CD

3m 57s

Wyeliminuj niestabilne testy dzięki skonteneryzowanym środowiskom budowania. Ten odcinek omawia, jak używać Dockera w potokach Continuous Integration, aby zagwarantować idealnie powtarzalne testy automatyczne.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 11 z 18. Robisz push kodu, pipeline rusza, a testy failują. Odpalasz je lokalnie i przechodzą bez problemu. Twój serwer CI ma nieco starszą wersję zależności niż twój laptop. Ten rozjazd to główna przyczyna notorycznie flaky testów, ale konteneryzacja środowiska buildów sprawia, że każdy run jest idealnie przewidywalny. Dzisiaj omówimy Dockera w pipeline CI/CD. Historycznie Continuous Integration oznaczało utrzymywanie statycznych serwerów buildów. Z czasem inżynierowie łączą się z tymi maszynami wirtualnymi, żeby instalować pakiety, aktualizować runtime'y i modyfikować konfiguracje systemu. Te serwery zamieniają się w tak zwane pet VMs. Gromadzą ukryty stan i resztki plików cache. Kiedy pipeline failuje, tracisz czas na dochodzenie, czy kod jest faktycznie zepsuty, czy serwer po prostu potrzebuje aktualizacji softu. Użycie Dockera jako środowiska buildów całkowicie omija ten problem. Zamiast odpalać skrypty testowe bezpośrednio na systemie operacyjnym hosta workera CI, worker stawia kontener. Runner CI robi pull konkretnego image'u Dockera, startuje kontener, robi mount twojego kodu źródłowego i wykonuje kroki buildu wewnątrz tego odizolowanego środowiska. I tu jest kluczowa sprawa. Kiedy job się kończy, kontener jest niszczony. Kolejny run pipeline'u dostaje całkowicie świeże, identyczne środowisko. Nie ma żadnych konfliktujących procesów w tle z poprzednich runów. Środowisko jest stateless i w całości zdefiniowane przez image. Pomyśl o procesie aktualizacji runtime'u. Załóżmy, że musisz przenieść projekt z Node 18 na Node 20. W tradycyjnym setupie ktoś musi zalogować się na serwer buildów, zaktualizować soft system-wide i mieć nadzieję, że nie wywali to innych projektów współdzielących tego samego workera. Z Dockerem jako środowiskiem buildów, cały ten proces to po prostu zmiana stringa. Aktualizujesz tag bazowego image'u w konfiguracji z Node 18 na Node 20. Runner CI robi pull nowego image'u. Twój build odpala się w zaktualizowanym środowisku natychmiast. Jeśli test failuje, robisz revert taga i próbujesz ponownie później. Zarządzasz infrastrukturą bezpośrednio obok swojego kodu. Ma to jeszcze jedną warstwę. Jeśli używasz Dockera do budowania aplikacji, twój pipeline CI musi pozwalać na build i push image'y. Jeśli twój job CI już działa wewnątrz kontenera, jak odpalasz komendy Docker build? Wymaga to wzorca o nazwie Docker-in-Docker. Docker-in-Docker oznacza odpalenie odizolowanego daemona Dockera wewnątrz twojego kontenera CI. Zewnętrzny kontener zapewnia kontrolowane środowisko dla kroków twojego pipeline'u, podczas gdy wewnętrzny daemon przetwarza buildy twojej aplikacji. To pozwala twojemu jobowi CI robić pull bazowych image'y, konstruować kontener aplikacji i robić push finalnego artifactu do registry, a wszystko to bez zaśmiecania maszyny hosta, na której działa worker CI. Przeniesienie środowiska CI do kontenera przesuwa kontrolę nad systemem buildów na developera. Dokładnie ten sam image, który buduje twój kod na zdalnym serwerze, może zostać odpalony na twojej lokalnej maszynie, co gwarantuje, że jeśli test wywali się w CI, możesz odtworzyć ten sam błąd lokalnie. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
12

Obrazy wieloplatformowe

3m 55s

Rozwiąż problem niezgodności między Apple Silicon a serwerami w chmurze. Dowiedz się, jak Docker Buildx pozwala na jednoczesną kompilację skrośną i pakowanie aplikacji dla architektur ARM i AMD64.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 12 z 18. "U mnie działa" nabiera zupełnie nowego znaczenia, gdy twoja lokalna maszyna używa procesora ARM, a produkcyjna chmura działa na Intelu. Testujesz kontener lokalnie, robisz push do registry, potem pull na serwerze, a on natychmiast się wywala z błędem execution format error. Problemem jest niedopasowanie architektury sprzętowej. Żeby to naprawić, używasz Multi-Platform Images. Obraz kontenera to w gruncie rzeczy paczka plików binarnych i systemów plików. Jeśli budujesz obraz na Macu z Apple Silicon, wynikowe pliki binarne są skompilowane pod architekturę ARM64. Kiedy zrobisz deploy tego obrazu na standardowy serwer linuksowy w chmurze z procesorem AMD64, CPU hosta dosłownie nie rozumie instrukcji wewnątrz kontenera. Kiedyś trzeba było utrzymywać oddzielne build pipelines dla różnych targetów sprzętowych. Docker Buildx eliminuje ten wymóg. Docker Buildx to plugin do command line, który rozszerza standardowy system budowania w Dockerze. Używa silnika backendowego o nazwie BuildKit, żeby wykonywać buildy współbieżnie i obsługiwać złożone zadania, takie jak targetowanie wielu platform w jednym przebiegu. Kiedy budujesz multi-platform image przy użyciu Buildx, nie upychasz dwóch osobnych systemów plików w jeden gigantyczny kontener. Zamiast tego, Buildx tworzy manifest list obrazu. Pomyśl o tym manifeście jak o tablicy routingu. Przechowuje on listę wskaźników do różnych obrazów pod konkretne architektury, zapisanych w twoim registry. Kiedy maszyna robi pull twojego obrazu, jej Docker daemon odczytuje ten manifest, identyfikuje architekturę CPU swojego hosta i automatycznie pobiera tylko te warstwy obrazu, które pasują do jej sprzętu. Żeby zrobić cross-compilation i spakować backendowe API dla obu architektur jednocześnie, używasz komendy docker buildx build. Dodajesz flagę platform, przekazując jej oddzieloną przecinkami listę twoich targetów. Na przykład, wpisujesz flagę, a po niej linux slash amd64 przecinek linux slash arm64. Dopisujesz swój standardowy tag obrazu, a potem dodajesz flagę push. I tu jest kluczowa sprawa. Budując dla wielu platform jednocześnie, nie możesz po prostu załadować końcowego multi-platform image z powrotem do lokalnego cache Docker engine. Lokalny daemon nie jest zaprojektowany do trzymania manifest list wskazującej na wiele architektur. Musisz poinstruować Buildx, żeby zrobił push wyników bezpośrednio do twojego container registry. Registry działa jako system przechowywania, który poprawnie organizuje manifest list i obrazy poszczególnych architektur. Żeby fizycznie wykonać build dla procesora, którego nie masz, Buildx polega na emulatorze o nazwie QEMU. Docker Desktop konfiguruje to automatycznie. Kiedy twoja maszyna ARM dochodzi do kroku wymagającego instrukcji AMD64, emulator tłumaczy ją w locie. To nie wymaga absolutnie żadnych zmian w twoim Dockerfile. Jeśli potrzebujesz szybszych czasów budowania, możesz też użyć narzędzi do cross-compilation bezpośrednio wewnątrz multi-stage build, co pomija emulację, ale wymaga ustawienia specyficznych flag kompilatora w twoim kodzie. Prawdziwą siłą multi-platform manifest jest to, że całkowicie izoluje on konsumenta od szczegółów sprzętowych pod spodem. Developer na Macu i klaster produkcyjny na Intelu robią pull dokładnie tego samego tagu obrazu, a registry automatycznie serwuje każdemu z nich odpowiedni plik binarny bez żadnej dodatkowej konfiguracji. Dzięki za spędzenie ze mną tych kilku minut. Do usłyszenia następnym razem, trzymaj się.
13

Docker MCP Toolkit

3m 43s

Bezpiecznie połącz swoje agenty AI z lokalnymi narzędziami. Ten odcinek wprowadza Docker Model Context Protocol (MCP) Toolkit, wyjaśniając, jak zarządzać skonteneryzowanymi serwerami MCP za pomocą katalogów i profili.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 13 z 18. Danie agentowi AI bezpośredniego dostępu do twojej lokalnej bazy danych lub systemu plików daje niesamowite możliwości. Ale instalowanie niezaufanych skryptów integracyjnych bezpośrednio na twoim hoście, żeby to osiągnąć, to gotowa katastrofa bezpieczeństwa. Docker MCP Toolkit rozwiązuje ten problem, przenosząc te integracje do izolowanych kontenerów. Model Context Protocol, czyli MCP, to otwarty standard, który pozwala klientom AI, takim jak desktopowa aplikacja Claude czy edytor Cursor, łączyć się z zewnętrznymi źródłami danych i narzędziami. Żeby dać swojemu AI nowe możliwości, uruchamiasz małą aplikację zwaną serwerem MCP. Do tej pory oznaczało to pobieranie zewnętrznych skryptów w Pythonie lub Node i uruchamianie ich bezpośrednio w twoim systemie operacyjnym. To wprowadza sporo problemów operacyjnych, takich jak konflikty zależności, a co ważniejsze, daje niezaufanemu kodowi nieograniczony dostęp do twojej maszyny. Docker MCP Toolkit rozwiązuje ten problem, pakując te serwery w standardowe kontenery Dockera. Pierwszym elementem tego systemu jest Catalog. Catalog to rejestr zweryfikowanych, skonteneryzowanych serwerów MCP. Zamiast pobierać losowe repozytoria z internetu, pobierasz standardowe obrazy Dockera. Te obrazy są wstępnie spakowane tak, aby uruchamiać wymagane narzędzia bez konieczności posiadania jakichkolwiek lokalnych runtimes na twoim hoście. Kiedy już masz dostęp do tych serwerów, potrzebujesz sposobu na ich uporządkowanie. Robisz to za pomocą Profili. Profil to grupa konfiguracyjna, która dokładnie definiuje, jakie narzędzia są potrzebne do konkretnego projektu. Na przykład możesz utworzyć profil o nazwie web-dev. Wewnątrz tej konfiguracji określasz, że ten profil wymaga serwera GitHub do odczytu repozytoriów kodu oraz serwera Playwright do automatyzacji przeglądarki. Swoje klucze API i zmienne środowiskowe dla obu narzędzi ustawiasz tylko raz w konfiguracji profilu. Masz teraz wyizolowane narzędzia i zdefiniowany profil. Jak AI się z nimi łączy? Tu zaczyna się robić ciekawie. Połączeniem zarządza MCP Gateway. Gateway działa jak centralny router uruchomiony na twoim hoście. Nie konfigurujesz swojego klienta AI tak, aby uruchamiał pojedyncze kontenery. Zamiast tego, kierujesz Claude'a lub Cursora na MCP Gateway i żądasz profilu web-dev. Kiedy klient się łączy, Gateway odczytuje profil, automatycznie uruchamia w tle żądane kontenery GitHub i Playwright, po czym nawiązuje połączenie. Gateway pośredniczy w komunikacji między klientem AI a kontenerami za pomocą standardowego protokołu. Klient AI myśli, że komunikuje się z lokalnymi narzędziami, ale wszystko wykonuje się bezpiecznie wewnątrz Dockera. Konfigurujesz narzędzia w profilu tylko raz i możesz współdzielić ten dokładny setup między dowolną liczbą różnych aplikacji AI. Jeśli jedno z tych narzędzi zacznie działać nieprawidłowo lub zostanie skompromitowane, jest uwięzione w kontenerze, całkowicie ślepe na resztę twojego systemu. Prawdziwą wartością MCP Toolkit jest to, że oddziela konfigurację twoich narzędzi AI od klientów, którzy z nich korzystają, zapewniając silne gwarancje izolacji bez poświęcania inteligencji twoich workflows. Dzięki za wysłuchanie. Trzymajcie się!
14

Automatyczne wykrywanie Dynamic MCP

4m 17s

Poznaj Dynamic MCP, eksperymentalną funkcję, która pozwala klientom AI przeszukiwać Docker MCP Catalog i dynamicznie instalować nowe serwery narzędzi podczas konwersacji bez ręcznej konfiguracji.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 14 z 18. Jesteś w połowie rozmowy z agentem AI do kodowania i prosisz go o zrobienie query do bazy danych. Zazwyczaj, jeśli zapomnisz wcześniej skonfigurować tool do bazy danych, agent zwraca błąd i prosi cię o interwencję. Problem w tym, że ręczna konfiguracja tooli psuje flow pracy. A co, jeśli agent zorientowałby się, że brakuje mu jakiejś funkcjonalności, przeszukał katalog i zainstalował potrzebny serwer całkowicie w locie? Właśnie to osiąga Dynamic MCP Auto-Discovery. Zazwyczaj dostarczanie tooli do dużego modelu językowego oznacza statyczne definiowanie ich w pliku konfiguracyjnym przed startem sesji. Jeśli twój agent może potrzebować przeczytać repozytorium na GitHubie, wysłać wiadomość na Slacku i zrobić query do bazy danych, musisz załadować wszystkie te serwery Model Context Protocol up front. Takie podejście zaśmieca context window toolami, które mogą nigdy nie zostać użyte, i wymaga od ciebie idealnego przewidzenia potrzeb agenta. Dynamic MCP zmienia ten paradygmat. Pozwala agentowi odkrywać i podpinać toole dokładnie wtedy, gdy wymaga tego zadanie, bez żadnej ingerencji człowieka. Kiedy włączysz tę dynamiczną funkcję, Docker MCP Gateway wystawia zestaw tooli do zarządzania bezpośrednio dla agenta AI. Gateway w zasadzie daje agentowi możliwość zarządzania jego własnym toolchainem. Dwa kluczowe toole dostarczane przez gateway do tego procesu to mcp-find i mcp-add. Agent wchodzi z nimi w interakcję dokładnie tak samo, jak z każdym standardowym function callem. Możemy przyjrzeć się, jak działa ta logika, na konkretnym scenariuszu. Załóżmy, że prosisz agenta o analizę metryk użytkownika przechowywanych w bazie danych SQL. Agent analizuje request, sprawdza swój obecny toolkit i zdaje sobie sprawę, że nie ma załadowanych żadnych tooli do odpytywania bazy danych. Zamiast rzucić błędem, agent wywołuje tool mcp-find, przekazując odpowiedni search string, na przykład postgres. Gateway przechwytuje ten call i odpytuje skonfigurowany katalog Docker MCP o dostępne serwery pasujące do tego stringa. Zwraca metadane i opisy pasujących serwerów z powrotem do agenta. Agent czyta opis, potwierdza, że serwer Postgres rozwiąże problem, i przechodzi do następnego kroku. Następnie agent wywołuje tool mcp-add, przekazując identyfikator serwera Postgres, który właśnie znalazł. Tu zaczyna się robić ciekawie. Gateway przechwytuje request mcp-add, pulluje potrzebny image, stawia serwer MCP w kontenerze Docker i dynamicznie binduje nowe toole do aktywnego połączenia. Agent nagle zyskuje dostęp do tooli bazy danych, łączy się z twoją bazą, odpala query, o które pierwotnie prosiłeś, i zwraca wynik. Cały proces dzieje się w tle, dzięki czemu wasza rozmowa pozostaje całkowicie nieprzerwana. W tym pakiecie do zarządzania dostępny jest trzeci tool do eksperymentalnego code execution, ale rozwiązuje on zupełnie inny zestaw problemów, więc dzisiaj skupiamy się wyłącznie na discovery. Oto kluczowa rzecz w tym procesie. Kiedy agent używa mcp-add do załadowania nowego serwera, to dodanie ma scope ograniczony ściśle do bieżącej sesji. Gateway nie nadpisuje twoich globalnych plików konfiguracyjnych, a nowo dodane toole nie są zachowywane po restartach. Kiedy zamykasz sesję, tymczasowe bindowanie tooli jest niszczone. Dzięki temu twoje bazowe środowisko pozostaje czyste i bezpieczne, a agent ma maksymalną elastyczność w dynamicznym rozwiązywaniu złożonych, wieloetapowych problemów. Wystawiając wyszukiwanie w katalogu i instalację jako standardowe function calls, Dynamic MCP zdejmuje ciężar konfiguracji up front i pozwala agentowi budować własne środowisko na żądanie. To wszystko w tym odcinku. Dzięki za wysłuchanie i keep building!
15

Docker Sandboxes dla AI

3m 52s

Zrozum architekturę Docker Sandboxes. Dowiedz się, dlaczego autonomiczne agenty programistyczne AI wymagają izolowanych microVMs z dedykowanymi demonami Dockera zamiast standardowych przestrzeni nazw kontenerów.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 15 z 18. Autonomiczny agent kodujący AI to dokładnie ten rodzaj procesu, którego nie chcesz uruchamiać z uprawnieniami roota na swoim laptopie. Prosisz go o naprawienie buga, a on nagle pobiera losowe pakiety, modyfikuje pliki systemowe albo próbuje przebudować twoją lokalną infrastrukturę. Potrzebujesz miejsca, w którym agent może działać jak administrator, wcale nim nie będąc. Właśnie ten problem mają rozwiązywać Docker Sandboxes dla AI. Tradycyjnie Docker izoluje procesy za pomocą linuksowych namespaces i control groups. Te kontenery współdzielą kernel systemu operacyjnego hosta. Dla przewidywalnej usługi webowej ten model sprawdza się idealnie. Ale agent AI jest z natury nieprzewidywalny. Generuje niesprawdzony kod, odpala go i często musi instalować nowe pakiety systemowe w locie, żeby przetestować własne rozwiązania. Współdzielenie kernela hosta z nieprzewidywalnym agentem to zbyt duże ryzyko bezpieczeństwa. Żeby temu zaradzić, Docker Sandboxes porzucają standardowe namespaces kontenerów na rzecz izolowanych microVMs. Kiedy odpalasz sandboxa dla agenta, bootuje on dedykowaną, lekką maszynę wirtualną. Agent dostaje swój własny, odrębny kernel. Nie widzi procesów twojego hosta. Domyślnie nie ma dostępu do network stacka twojego hosta. A co najważniejsze, całkowicie eliminuje to ryzyko tradycyjnych podatności typu container escape. Agent jest ściśle zamknięty w sprzętowo wirtualizowanym boksie. Ma to ogromne znaczenie, kiedy weźmiesz pod uwagę, co agenci AI tak naprawdę robią. Wyobraź sobie, że twój agent ma za zadanie napisać złożoną aplikację webową, stworzyć dla niej Dockerfile i przetestować build. Żeby to osiągnąć, agent musi odpalać komendy Dockera. Gdybyś po prostu zmapował Docker socket swojego hosta do standardowego kontenera, agent mógłby teoretycznie odpalać uprzywilejowane kontenery bezpośrednio na twojej maszynie hosta. Docker Sandboxes zapobiegają temu, odpalając całkowicie wyizolowanego Docker daemona wewnątrz samej microVM. Agent może budować image, robić pull zewnętrznych zależności i odpalać zagnieżdżone kontenery przez cały dzień. Ponieważ gada z wyizolowanym daemonem wewnątrz microVM, środowisko Dockera na twoim hoście pozostaje całkowicie nieświadome i czyste. Kiedy task się kończy, a sandbox zostaje zniszczony, wewnętrzny daemon i wszystkie pobrane image natychmiast znikają. I tu robi się ciekawie. Skoro microVM jest całkowicie wyizolowana, to jak właściwie wyciągnąć z niej gotowy kod? Architektura rozwiązuje to za pomocą montowania workspace'u. To bezpieczny mechanizm passthrough dla systemu plików. Podczas inicjalizacji sandboxa, definiujesz konkretny katalog na swoim hoście, który ma pełnić rolę workspace'u. Ten jeden katalog jest bezpiecznie montowany wewnątrz microVM. Kiedy agent pisze kod, odpala testy albo generuje assety, zapisuje je w tym katalogu workspace'u. Passthrough synchronizuje te konkretne pliki z powrotem do systemu plików twojego hosta w czasie rzeczywistym. Agent dostarcza żądany output, nie mając przy tym żadnego dostępu do reszty twojego dysku twardego. Może do woli psuć rzeczy wewnątrz microVM, ale twoje lokalne pliki pozostają nietknięte. Kluczowy wniosek jest taki, że izolacja w tym kontekście nie polega już tylko na ochronie hosta przed złośliwym zewnętrznym softem. Chodzi o bezpieczne umożliwienie nieprzewidywalnych, wysoce uprzywilejowanych operacji systemowych, które autonomiczny agent musi wykonać, żeby w ogóle być użytecznym. Jeśli podobają ci się te odcinki i chcesz wesprzeć program, wyszukaj DevStoriesEU na Patreonie. To tyle na ten odcinek. Do usłyszenia następnym razem!
16

Budowanie zespołów agentów AI

4m 18s

Przestań polegać na pojedynczym modelu AI przy złożonych zadaniach. Ten odcinek wprowadza framework Docker Agent, pokazując, jak komponować wyspecjalizowane zespoły agentów zdefiniowane w pliku YAML.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 16 z 18. Przekazujesz ogromny błąd aplikacji pojedynczemu modelowi AI. Próbuje on pomieścić w głowie całą architekturę, logi i docelową składnię naraz. W połowie drogi gubi się i halucynuje poprawkę dla zupełnie niezwiązanego pliku. Jeden ogólny model próbujący zrobić wszystko prowadzi do przeciążenia kontekstu. Aby niezawodnie rozwiązywać złożone problemy, musisz budować zespoły agentów AI. Framework Docker Agent pozwala ci definiować wyspecjalizowane zespoły agentów AI za pomocą prostego pliku konfiguracyjnego YAML. Zamiast pisać jeden monolityczny system prompt, dzielisz workflow na odrębne role. Układasz to w hierarchię. Masz root agenta, który orkiestruje workflow, oraz wielu sub-agentów, którzy wykonują konkretne zadania. To izoluje kontekst. Każdy sub-agent otrzymuje tylko te informacje, których potrzebuje do swojej konkretnej pracy. Weźmy na przykład workflow debugowania. Potrzebujesz zespołu z dwiema odrębnymi rolami. Po pierwsze, bug investigator, który analizuje stack trace'y. Po drugie, fixer, który faktycznie przepisuje zepsuty kod. Definiujesz cały skład tego zespołu w pliku o nazwie docker dash agent dot yml. Zaczynasz od skonfigurowania root agenta na samej górze pliku. Nadajesz mu nazwę, wybierasz bazowy model językowy i podajesz instrukcje systemowe. Root agent działa jako menedżer. Jego głównym zadaniem nie jest bezpośrednie rozwiązanie problemu, ale delegowanie pracy. Instruujesz root agenta, aby koordynował pracę między investigatorem a fixerem na podstawie otrzymanych inputów. Następnie definiujesz sub-agentów w tym samym pliku YAML. Deklarujesz agenta typu bug investigator. Przypisujesz mu model, który świetnie radzi sobie z rozumowaniem i czytaniem logów. Dajesz mu ścisłe instrukcje, aby czytał tylko stack trace'y, identyfikował psującą się funkcję i zwracał jako output krótkie wyjaśnienie, dlaczego przestała działać. Następnie deklarujesz agenta typu code fixer. Możesz przypisać mu model specjalnie zoptymalizowany pod generowanie kodu. Jego instrukcje ściśle nakazują mu wziąć niedziałającą funkcję i zwrócić jako output jej poprawioną wersję. Żadnej analizy logów, po prostu code in i code out. Kiedy uruchamiasz ten zespół, użytkownik wchodzi w interakcję tylko z root agentem. Przekazujesz root agentowi ogromny zrzut logów aplikacji. Root agent ewaluuje request i czyta opisy swoich dostępnych sub-agentów. Ustala, że bug investigator to właściwy agent do pierwszego kroku. Root agent przekazuje zrzut logów w dół do investigatora. Investigator przetwarza szum, znajduje null pointer exception w określonej funkcji i zwraca tylko ten konkretny szczegół. Root agent bierze ten wyizolowany fragment informacji i przekazuje go do agenta code fixer. Code fixer pisze patcha i przekazuje go z powrotem do root menedżera, który następnie zwraca ci ostateczny, czysty wynik. Oto kluczowa sprawa. Code fixer nigdy nie widzi tego ogromnego stack trace'a. Widzi tylko dokładnie tę funkcję, którą musi naprawić. Chronisz context window modelu kodującego, odfiltrowując wcześniej szum. Przypisując wąskie, konkretne instrukcje poszczególnym sub-agentom w pliku YAML, zapobiegasz dryfowaniu modeli od zadania. Root agent zajmuje się sekwencją, a sub-agenci odpowiadają za wykonanie. Hierarchiczne ustrukturyzowanie agentów zmusza cię do traktowania AI jak architektury mikroserwisów, wyznaczając ścisłe granice tego, czym może zajmować się każdy pojedynczy model. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
17

Zestawy narzędzi i przepływy pracy agentów

3m 51s

Spraw, by Twoje agenty AI były naprawdę użyteczne, nakładając na nie odpowiednie ograniczenia. Dowiedz się, jak konfigurować zestawy narzędzi systemu plików i wymuszać ustrukturyzowane przepływy pracy programistycznej w Docker Agent.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 17 z 18. Agent AI z najbardziej zaawansowanym modelem językowym jest praktycznie bezużyteczny w developmencie, jeśli nie potrafi odczytać twojego kodu źródłowego ani odpalić twojego test suite'a. Bez zewnętrznych capabilities, po prostu zgaduje syntax. Agent Toolsets i Workflows rozwiązują ten problem, wypełniając lukę między prostym generatorem tekstu a pracującym software engineerem. Domyślnie Docker Agent działa w odizolowanym kontenerze. Nie ma pojęcia, jakie pliki znajdują się w twoim project directory. Aby to zmienić, konfigurujesz array toolsets w swoim pliku YAML agenta. Toolsets to gotowe capabilities, które dają agentowi bezpośredni dostęp do środowiska hosta. W przypadku agenta deweloperskiego zazwyczaj wstrzykujesz dwa podstawowe toolsets: dostęp do filesystemu i dostęp do shella. Toolset filesystemu pozwala agentowi odczytać twoje directory tree, otwierać pliki źródłowe i zapisywać kod z powrotem na dysk. Toolset shella pozwala agentowi odpalać komendy w terminalu. Bez arraya toolsets twój agent jest uwięziony w pudełku. Dzięki niemu twój agent zyskuje ręce i oczy. Jednak danie agentowi rąk i oczu to przepis na chaos, jeśli brakuje mu dyscypliny. Nieustrukturyzowany agent może zmienić plik, założyć, że zadziałało, i zgłosić sukces bez jakiegokolwiek sprawdzenia błędów syntaxu. Kontrolujesz to zachowanie używając bloku instructions w pliku YAML. Ten blok to nie miejsce na mgliste sugestie. To tutaj definiujesz ścisły operacyjny workflow. Najbardziej niezawodnym sposobem na ustrukturyzowanie tych instructions jest podzielenie tasków agenta na cztery obowiązkowe fazy: Analyze, Examine, Modify i Validate. Wpisujesz je bezpośrednio w blok instructions, mówiąc agentowi, że musi ukończyć jedną fazę przed przejściem do następnej. Pierwsza to Analyze. Agent czyta prompt użytkownika, aby zrozumieć żądany feature lub bug fix. Następnie jest Examine. Tutaj instruujesz agenta, aby użył swojego toolsetu filesystemu do przeszukania twojego codebase'u, znalezienia odpowiednich plików i odczytania ich zawartości, żeby zrozumieć obecną logikę. Trzecia to Modify. Agent zapisuje zaktualizowany kod na dysk. To jest ta część, która naprawdę się liczy. Czwarta faza to Validate. To tutaj zmuszasz agenta do udowodnienia swojej pracy przy użyciu toolsetu shella. Wyobraź sobie agenta, który jest ekspertem Go developerem. W sekcji Validate w twoich instructions, wyraźnie nakazujesz agentowi odpalenie komendy go test dot slash dot dot dot, a następnie golangci dash lint run. Ponieważ agent ma dostęp do shella, wykonuje dokładnie te komendy. Jeśli kompilator Go wyrzuci błąd syntaxu albo jeśli test sfailuje, toolset przekazuje ten output z terminala bezpośrednio z powrotem do agenta. Ponieważ twoje instructions mówią, że task nie jest skończony, dopóki walidacja nie przejdzie, agent jest zmuszony przeczytać błąd, wrócić w loopie do fazy Modify, naprawić kod i odpalić testy ponownie. Będzie powtarzać ten cykl, aż linter będzie zadowolony, a testy przejdą. Zapewnienie dostępu do filesystemu i shella sprawia, że twój agent jest w stanie pisać software. Ale ustrukturyzowanie jego instructions tak, aby wymagały jawnego odpalenia testów, sprawia, że twój agent staje się niezawodny. Podpinasz jego toolsy pod ścisły validation loop, dzięki czemu nigdy nie musisz robić review zepsutego kodu. To wszystko w tym odcinku. Dzięki za słuchanie i keep building!
18

Modele AI w Compose

3m 12s

Traktuj swoje lokalne modele LLM jak każdą inną zależność aplikacji. Dowiedz się, jak deklarować, konfigurować i wiązać modele AI bezpośrednio w pliku YAML Docker Compose.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Docker Masterclass, odcinek 18 z 18. Twoja aplikacja zależy od lokalnego modelu LLM. Prawdopodobnie odpalasz zewnętrzny inference engine, ręcznie konfigurujesz sieć i wstrzykujesz adresy URL endpointów. To działa, ale psuje izolowaną powtarzalność twojego środowiska. Twój model AI to po prostu kolejna zależność i jego miejsce jest w pliku konfiguracyjnym, tuż obok bazy danych. I to właśnie osiąga element models na najwyższym poziomie w Docker Compose. Począwszy od Compose w wersji 2.38, modele są natywnym konceptem. Wcześniej uruchomienie lokalnego modelu oznaczało pisanie skomplikowanych definicji services dla inference engine, ręczne wystawianie portów i konfigurowanie sieci typu bridge, żeby kontener twojej aplikacji mógł się z nim komunikować. Nowy blok models eliminuje ten problem, traktując model AI jako osobny element infrastruktury. Dodajesz blok models na samej górze pliku, z tym samym wcięciem co services i volumes. W środku nadajesz nazwę swojemu modelowi. Użyjmy ai/smollm2 dla prostej aplikacji czatu. Pod tą nazwą deklarujesz właściwy identyfikator modelu do pobrania. To tutaj definiujesz również ograniczenia sprzętowe i parametry engine'u. Możesz ustawić rozmiar kontekstu, żeby ograniczyć zużycie pamięci. Jeśli pod spodem engine wymaga konkretnych parametrów startowych, definiujesz je używając flag runtime. Konfiguracja modelu jest odizolowana i przejrzysta. Następnie bindujesz swoją aplikację z modelem. Wewnątrz bloku services znajdujesz service swojej aplikacji czatu i dodajesz tablicę models. Używając krótkiej składni, po prostu wpisujesz ai/smollm2. Nie musisz ręcznie konfigurować zależności ani ustawiać customowych aliasów sieciowych. I tu jest kluczowa sprawa. Kiedy używasz tej krótkiej składni do bindowania, Compose przejmuje orkiestrację. Pod spodem stawia odpowiedni inference engine, żeby serwować twój model. Co najważniejsze, automatycznie generuje standardowe zmienne środowiskowe i wstrzykuje je bezpośrednio do kontenera twojej aplikacji czatu. Twój kod wstaje ze zmiennymi takimi jak OPENAI_BASE_URL, które są już wypełnione i wskazują idealnie na wewnętrzny endpoint modelu. Odpalasz jedno polecenie docker compose up. Compose robi pull modelu smollm2, konfiguruje engine, startuje twój service czatu i spina połączenie. Żadnych ręcznych kluczy API, żadnego zgadywania wewnętrznych adresów IP. Wszystko routuje się poprawnie out of the box. Zachęcam cię do przejrzenia oficjalnej dokumentacji i spróbowania samodzielnego napisania takiego pliku. Ponieważ to kończy naszą serię masterclass, wpadnij na devstories dot eu, żeby zasugerować tematy, które powinniśmy poruszyć następnym razem. Dzięki podniesieniu modeli AI do rangi natywnych elementów w twojej konfiguracji, twoja infrastruktura staje się w pełni deklaratywna, co gwarantuje, że zawsze uruchomi się dokładnie ta wersja modelu, której oczekuje twój kod. To wszystko na dziś. Dzięki za wysłuchanie — idź zbudować coś fajnego.