Docker – podstawy konteneryzacji

You are currently viewing Docker – podstawy konteneryzacji

Wstęp

Pewnie nieraz zdarzyło Ci się słyszeć (albo samemu powiedzieć) magiczne zdanie: „Ale… u mnie działa!”. Problem polega na tym, że aplikacja, która świetnie działa na komputerze developera, niekoniecznie działa tak samo dobrze (lub w ogóle) na serwerze produkcyjnym czy nawet na komputerze kolegi z zespołu. Konfiguracja serwera, zależności systemowe, wersje bibliotek – diabeł tkwi w szczegółach, a te szczegóły potrafią napsuć krwi i opóźnić wdrożenie bardziej niż cokolwiek innego. Marnujemy czas na debugowanie problemów, które nie dotyczą samego kodu, a właśnie środowiska, w którym on działa.

Wyobraź sobie, że pracujesz nad projektem, w którym wymagana jest konkretna wersja Javy, Spring Boota oraz bazy danych PostgreSQL. Lokalnie wszystko działa idealnie, ale gdy twój kod trafia na środowisko testowe, pojawiają się błędy. Okazuje się, że na serwerze zainstalowana jest inna wersja bazy danych, a konfiguracja różni się od twojej lokalnej. Brzmi znajomo?

Właśnie w takich sytuacjach wchodzi na scenę Docker. Technologia, która pozwala zapakować aplikację, jej zależności, konfigurację i całe środowisko w jeden, lekki i przenośny kontener. Dzięki temu mamy gwarancję, że aplikacja uruchomi się tak samo wszędzie – na Twoim laptopie, na CI, na serwerze developerskimi na produkcji.

W tym artykule omówię podstawy konteneryzacji oraz przedstawię Docker jako jedno z najpopularniejszych narzędzi do jej implementacji. Dowiesz się, czym są kontenery, jak się różnią od wirtualizacji, jakie są ich zalety oraz jak zacząć swoją przygodę z Dockerem. Na koniec pokażę praktyczny przykład uruchomienia pierwszego kontenera.

Podstawy konteneryzacji

Zanim przejdziemy do samego Dockera, warto zrozumieć podstawowe koncepcje związane z konteneryzacją oraz to, jak różni się ona od wirtualizacji.

Wirtualizacja

Zacznijmy na początek od tego czym jest wirutalizacja. Jest to technologia, która pozwala na uruchomienie wielu systemów operacyjnych na jednym fizycznym serwerze. Każda maszyna wirtualna (VM – virtual machine) posiada swój własny system operacyjny, biblioteki, zasoby i aplikacje. Działają one niezależnie od siebie, dzięki czemu awaria jednej maszyny nie wpływa na pozostałe.

Jeśli chcemy uruchomić wirtualną maszynę potrzebujemy skorzystać z hiperwizora (hypervisor) – oprogramowania, które zarządza maszynami wirtualnymi i przydziela im zasoby sprzętowe. Popularne hiperwizory to m.in. VMware, VirtualBox (osobiście zawsze z tego korzystałem) czy Hyper-V.

Kluczową cechą wirtualizacji jest to, że każda maszyna wirtualna zawiera pełną kopię systemu operacyjnego, co oznacza, że każda z nich zużywa znaczną ilość zasobów (pamięci RAM, przestrzeni dyskowej). Ma to swoje plusy, bo w łatwy sposób mając zainstalowanego Windowsa możemy sobie odpalić całkowicie inny system operacyjny np. Ubuntu bez ingerencji w obecny system operacyjny.

Konteneryzacja

Natomiast co to jest konteneryzacja? Konteneryzacja jest to takie lżejsze podejście do wirtualizacji. Zamiast uruchamiać wiele pełnych systemów operacyjnych, kontenery współdzielą jądro systemu operacyjnego hosta, na którym są uruchamiane. Każdy kontener działa jako izolowany proces w przestrzeni użytkownika systemu operacyjnego hosta.

Kontenery zawierają wszystko, czego potrzebuje aplikacja do działania: kod, narzędzia systemowe, biblioteki, zależności i ustawienia. Są one całkowicie niezależne od środowiska, w którym są uruchamiane, co oznacza, że będą działać tak samo bez względu na to, czy uruchomimy je na laptopie, serwerze produkcyjnym czy w chmurze.

Dodam jeszcze na koniec, że konteneryzacja to temat, który w ostatnich latach szturmem zdobył świat IT. W środowisku, gdzie liczy się elastyczność, szybkość i automatyzacja, kontenery stały się jednym z podstawowych narzędzi w arsenale programisty i DevOpsa. Obecnie są używane w praktycznie każdym projekcie (no może w projektach legacy coś się znajdzie, że jednak nie 🙂 ).

Różnice między konteneryzacją a wirtualizacją

Poniżej spisałem kluczowe różnice między konteneryzacją a wirtualizacją:

  1. Zasoby – kontenery są znacznie lżejsze niż maszyny wirtualne, ponieważ nie zawierają pełnego systemu operacyjnego, a jedynie niezbędne komponenty aplikacji. Dzięki temu zużywają mniej pamięci RAM i przestrzeni dyskowej.
  2. Czas uruchomienia – kontenery uruchamiają się w kilka sekund, podczas gdy maszyny wirtualne mogą potrzebować kilku minut.
  3. Izolacja – maszyny wirtualne oferują pełną izolację na poziomie systemu operacyjnego, podczas gdy kontenery oferują izolację na poziomie procesów. To sprawia, że maszyny wirtualne są nieco bezpieczniejsze, ale kosztem wydajności.
  4. Przenośność – kontenery są bardziej przenośne, ponieważ zawierają wszystko, czego potrzebuje aplikacja do działania, a jednocześnie są mniejsze i łatwiejsze do dystrybucji.
  5. Systemowy overhead – maszyny wirtualne wymagają dedykowanych zasobów dla każdego systemu operacyjnego, podczas gdy kontenery współdzielą zasoby jądra hosta, co oznacza mniejszy narzut.

Zalety konteneryzacji

Konteneryzacja przynosi wiele korzyści, które przyczyniły się do jej ogromnej popularności:

  1. Przenośność – „napisz raz, uruchom wszędzie”. Kontenery działają tak samo niezależnie od środowiska, eliminując problem „działa na moim komputerze”.
  2. Efektywność – kontenery są lżejsze niż maszyny wirtualne, co pozwala na uruchomienie większej liczby kontenerów na tym samym sprzęcie.
  3. Izolacja – problemy z jednym kontenerem nie wpływają na inne kontenery ani na hosta.
  4. Szybkość wdrażania – kontenery można łatwo tworzyć, uruchamiać i zatrzymywać, co przyspiesza proces rozwoju i wdrażania.
  5. Skalowalność – łatwo jest skalować aplikacje poprzez uruchomienie większej liczby identycznych kontenerów.
  6. Wersjonowanie – kontenery umożliwiają łatwe zarządzanie różnymi wersjami aplikacji, co upraszcza testowanie i wdrażanie nowych funkcji.

Docker – podstawy

Docker logo

Co to jest Docker?

Docker to platforma open-source, która umożliwia programistom i administratorom systemów budowanie, wdrażanie i zarządzanie aplikacjami w kontenerach. Technologia to dostarcza narzędzia i interfejsy API, które upraszczają cały proces konteneryzacji, od tworzenia obrazów kontenerów, przez ich uruchamianie i zarządzanie, aż po udostępnianie w repozytoriach

Docker wykorzystuje technologię kontenerów dostępną w jądrze Linuksa (cgroups, namespaces) do izolowania aplikacji i ich zależności. Dzięki temu możemy tworzyć lekkie, przenośne kontenery, które działają tak samo niezależnie od środowiska.

Co ważne, Docker nie tylko umożliwia tworzenie kontenerów, ale również oferuje narzędzia do ich dystrybucji, orkiestracji i zarządzania nimi. To sprawia, że jest kompletnym rozwiązaniem do w pełni wykorzystania zalet konteneryzacji.

Architektura Dockera

Architektura Dockera opiera się na modelu klient-serwer. Składa się z trzech głównych komponentów (część z pojęć opisujących architekturę została opisana niżej, więc warto tutaj wrócić po przeczytaniu całego wpisu):

  1. Docker Client – to narzędzie, z którym najczęściej wchodzimy w interakcję, zazwyczaj poprzez wiersz poleceń (CLI – docker). Kiedy wpisujesz komendę typu docker run ... czy docker build ..., klient wysyła te polecenia do Demona Dockera. Klient może komunikować się z demonem działającym na tej samej maszynie lub na zdalnym hoście.
  2. Docker Daemon (dockerd) – To długo działający proces w tle, który nasłuchuje na żądania od klienta i zarządza obiektami Dockera: obrazami, kontenerami, sieciami i wolumenami. To on faktycznie buduje obrazy, uruchamia kontenery itd.
  3. Docker Registry – to miejsce, gdzie przechowywane są obrazy Dockera. Domyślnym publicznym rejestrem jest Docker Hub, ale można też korzystać z prywatnych rejestrów (np. firmowych, w chmurze jak AWS ECR, Google GCR, Azure ACR). Kiedy wykonujesz docker pull nginx lub docker run nginx (a obrazu nie ma lokalnie), Demon Dockera pobiera obraz nginx z rejestru (domyślnie z Docker Hub). Komenda docker push wysyła Twój obraz do rejestru.
Architektura Docker

Ty (przez Klienta) mówisz Demonowi, co ma zrobić (np. uruchomić kontener z obrazu nginx). Demon sprawdza, czy ma obraz lokalnie. Jeśli nie, pobiera go z Rejestru (niebieskie strzałki). Następnie tworzy i uruchamia Kontener na podstawie Obrazu, podłączając go do odpowiedniej Sieci i ewentualnie montując Wolumeny (zielona strzałka).

Najważniejsza pojęcia

Obraz – Image

Obraz Docker to szablon tylko do odczytu, który zawiera wszystko, co potrzebne do uruchomienia aplikacji: kod, środowisko uruchomieniowe, biblioteki, zmienne środowiskowe i pliki konfiguracyjne. Obrazy są budowane warstwowo, co pozwala na efektywne wykorzystanie przestrzeni dyskowej i szybsze pobieranie.

Publiczne repozytorium obrazów możesz znaleźć na DockerHub. Znajdziesz tam tysiące gotowych, oficjalnych obrazów dla popularnych technologii takich jak Postgresa, Nginx, Rabbitmq i wielu innych.

DockerFile

DockerFile jest plik na podstawie, którego są tworzone obrazy. Plik ten zawiera zawiera instrukcje dotyczące tego, jak zbudować obraz. Każda instrukcja w Dockerfile tworzy nową warstwę w obrazie.

FROM eclipse-temurin:21-jdk-alpine
WORKDIR /app
COPY target/myapp.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

Kontener – Container

Kontener to uruchomiona instancja (konkretna już uruchomiona np. aplikacja) obrazu. Możemy myśleć o kontenerze jak o procesie, który izoluje aplikację i jej zależności od reszty systemu. Kontenery są tworzone z obrazów i mogą być uruchamiane, zatrzymywane, przenoszone i usuwane.

To, co odróżnia kontener od zwykłego procesu, to fakt, że działa on w izolowanym środowisku, z własnym systemem plików, siecią i przestrzenią procesów.

Wolumen – Volume

Volume (wolumen) to mechanizm do przechowywania danych generowanych i używanych przez kontenery Docker. Ponieważ kontenery są z natury ulotne (dane znikają po zatrzymaniu kontenera), wolumeny pozwalają na trwałe przechowywanie danych, niezależnie od cyklu życia kontenera.

Wolumeny są przechowywane w części systemu plików zarządzanej przez Dockera i są izolowane od bezpośredniego dostępu przez hosta. Mogą być współdzielone między kontenerami, co ułatwia wymianę danych.

Temat wolumenów w Dockerze bardziej szczegółowo zostanie opisany w następnych wpisach.

Sieć – Network

Sieć Docker umożliwia komunikację między kontenerami oraz między kontenerami a hostem (może to być np. nasz system operacyjny). Docker oferuje kilka typów sieci, które można wykorzystać w zależności od potrzeb:

  • Bridge – domyślny typ sieci, który pozwala kontenerom na komunikację ze sobą, będąc jednocześnie izolowanymi od sieci hosta.
  • Host – kontener używa stosu sieciowego hosta, eliminując izolację sieciową między kontenerem a hostem.
  • Overlay – umożliwia komunikację między kontenerami działającymi na różnych hostach Docker.
  • None – brak sieci, kontener nie ma dostępu do sieci zewnętrznej ani do innych kontenerów.

Temat sieci w Dockerze bardziej szczegółowo zostanie opisany w następnych wpisach.

Docker – instalacja

Zanim zaczniemy zabawę z Dockerem, musimy go najpierw zainstalować. Proces instalacji różni się w zależności od systemu operacyjnego, tak więc postarałem się w miarę opisać sposób instalacji na każdym z typów systemu operacyjnego.

Ubuntu

Docker można zainstalować na Ubuntu za pomocą repozytorium Docker lub pobierając pakiety .deb. Poniżej znajduje się proces instalacji z użyciem repozytorium Docker.

Na początek należy zaktualizować listę pakietów, zainstalować wymagane zależności, a następnie pobrać klucz GPG Dockera i wrzucić go do źródeł apt:

# aktualizacja pakietów i zależności
sudo apt update
sudo apt install ca-certificates curl gnupg lsb-release

# Pobranie kluacza GPG
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Dodanie repozytorium Dockera do źródeł apt
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Na koniec jeszcze trzeba zaktualizować bazę pakietów
sudo apt update

Mając wykonane podstawowe elementy, teraz przechodzimy do instalacji Dockera:

sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Na koniec weryfikujemy czy instalacja się powiodła wpisując na przykład komendę do sprawdzania wersji Dockera:

docker version

Jeśli dostaniesz błędy związane z brakiem uprawnień 'permission denied’ to znaczy, że obecny użytkownik potrzebuje dodatkowych uprawnień do uruchamiania dockera np. dodanie przed polecenia sudo. Jest to dość uciążliwe w codziennej pracy, więc najlepiej wtedy należy swojego użytkownika dodać do grupy docker, aby móc uruchamiać komendy Docker bez użycia sudo:

sudo usermod -aG docker $USER

Następnie wyloguj się i zaloguj ponownie, aby zmiany zostały zastosowane.

Na Ubuntu mamy też możliwość zainstalowania wersji Docker Desktop (opisałem instalację tego poniżej dla Windowsa i Maca). Osobiście nie testowałem tego na linuksie, zawsze korzystałem tylko z opcji konsolowe, ale zakładam, że działa tak samo jak dla pozostałych systemów operacyjnych, więc wybór już należy do Ciebie jaką wersję wolisz.

Windows i MacOS

Instalacje na Windowsa i MacOS zrobimy instalując narzędzie Docker Desktop. Jest to aplikacja, która pozwala zarządzać Dockerem przez interfejs graficzny. Oczywiście dalej możemy wszystko robić z poziomu konsoli, ale jak chcemy to mamy też dodatkowo możliwość robienia tego przez aplikację.

Instalacja odbywa się tak samo jak w przypadku pozostałych aplikacji jakie się instaluję na Windowsie czy Macu. Wchodzimy na odpowiedni link do pobrania plików instalacyjnych dla Windows oraz dla Maca i instalujemy zgodnie z instrukcją w instalatorze.

Po zainstalowaniu jedynie co to dobrze zweryfikować czy wszystko działa prawidłowo. Dlatego najlepiej otworzyć konsole i wpisać na przykład komendę do sprawdzania wersji Dockera:

docker version

Uwaga: Docker Desktop dla Windows wymaga włączenia funkcji Hyper-V i WSL 2 (Windows Subsystem for Linux 2). Instalator powinien automatycznie skonfigurować te funkcje.

Docker – mój pierwszy kontener

Teraz, gdy mamy już zainstalowanego Dockera, możemy przejść do uruchomienia naszego pierwszego kontenera. Zaczniemy od najprostszego przykładu – kontenera hello-world, a następnie uruchomimy coś bardziej praktycznego – serwer NGINX.

Uruchomienie kontenera hello-world

Kontener hello-world to prosty kontener, który wyświetla wiadomość powitalną i kończy działanie. Jest to świetny sposób na sprawdzenie, czy Docker działa poprawnie.

docker run hello-world

Po wykonaniu tej komendy, Docker wykona następujące kroki:

  1. Sprawdzi, czy obraz hello-world istnieje lokalnie.
  2. Jeśli nie, pobierze go z Docker Hub (publiczne repozytorium obrazów Docker).
  3. Utworzy nowy kontener na podstawie tego obrazu.
  4. Uruchomi kontener, który wyświetli wiadomość powitalną i zakończy działanie.

Wynik powinien wyglądać mniej więcej tak:

Wynik docker run hello-world

Uruchomienie serwera Nginx

Teraz przejdźmy do czegoś bardziej praktycznego – uruchomienia serwera NGINX w kontenerze Docker.

NGINX to popularny serwer HTTP i reverse proxy, często używany do hostowania stron internetowych i aplikacji. Dzięki Dockerowi możemy łatwo uruchomić NGINX bez konieczności instalowania go bezpośrednio na naszym systemie.

Aby uruchomić kontener z NGINX, wykonaj następującą komendę:

docker run --name my-nginx -p 8080:80 -d nginx

To teraz po kolei:

  • docker run – polecenie do uruchomienia nowego kontenera.
  • --name my-nginx – nadaje kontenerowi nazwę „my-nginx”, co ułatwia późniejsze zarządzanie nim.
  • -p 8080:80 – mapuje port 8080 na hoście do portu 80 w kontenerze. Oznacza to, że możemy uzyskać dostęp do serwera NGINX poprzez przejście do http://localhost:8080 w przeglądarce.
  • -d – uruchamia kontener w trybie odłączonym (detached), co oznacza, że będzie działał w tle.
  • nginx – nazwa obrazu, który chcemy uruchomić.

Po wykonaniu tej komendy, kontener z NGINX powinien być uruchomiony. Możesz to sprawdzić za pomocą:

docker ps

Ta komenda wyświetli wszystkie uruchomione kontenery. Powinieneś zobaczyć swój kontener NGINX na liście:

Wykonanie komend do uruchomienia nginx i sprawdzenie na liście kontenerów

Teraz otwórz przeglądarkę i przejdź do adresu http://localhost:8080. Powinieneś zobaczyć stronę powitalną NGINX:

Strona powitalna nginx

Gratulacje! Właśnie uruchomiłeś swój pierwszy praktyczny kontener Docker.

Podsumowanie

W tym wpisie przeszliśmy przez fundamenty konteneryzacji i poznaliśmy podstawy pracy z Dockerem. Dowiedzieliśmy się, czym różni się konteneryzacja od wirtualizacji, jakie są zalety tego podejścia oraz jakie kluczowe pojęcia są związane z Dockerem (obrazy, kontenery, wolumeny, sieci). Krok po kroku przeszliśmy przez proces instalacji Dockera na popularnych systemach operacyjnych i uruchomiliśmy nasz pierwszy kontener z prostą aplikacją „hello-world” oraz działający serwer Nginx.

Docker to narzędzie, które zrewolucjonizowało podejście do uruchamiania aplikacji. Konteneryzacja to przyszłość, a Docker to obecny standard (opisywałem to nawet w tym wpisie jako jedno z tych narzędzi, które jest wymagane jako podstawa na rynku). Jeśli tworzysz aplikacje, testujesz, czy zarządzasz infrastrukturą – znajomość Dockera jest obowiązkowa. To narzędzie, które pozwala Ci skupić się na kodzie, a nie na walce ze środowiskiem.

W moim zespole sami korzystamy z Dockera już od kilku lat i nie wyobrażam sobie powrotu do pracy bez niego. Szczególnie w projektach z wieloma mikrousługami, Docker jest absolutnie niezbędny.

Co dalej?

Jeśli chcesz pogłębić swoją wiedzę o Dockerze, polecam następujące tematy:

  1. Dockerfile – tworzenie własnych obrazów Dockera
  2. Docker Compose – zarządzanie wieloma kontenerami
  3. Docker Swarm – orkiestracja kontenerów na wielu hostach
  4. Kubernetes – zaawansowana orkiestracja kontenerów

W przyszłych wpisach na pewno poruszę te tematy bardziej szczegółowo. A tymczasem, jeśli masz pytania dotyczące Dockera lub konteneryzacji, zostaw komentarz poniżej!

Przydatne linki

Do zobaczenia w kolejnym wpisie!

Subscribe
Powiadom o
guest
0 komentarzy
najstarszy
najnowszy oceniany
Inline Feedbacks
View all comments