Kontenery i usługi Dockera: zrozumieć Dockera przez hamburgery

Co naprawdę znaczy „poproszę jeden zestaw hamburgerowy”?

Ostatnio, używając Dockerfile, stworzyliśmy własny „obraz Dockera (Image)”. To jak „przepis, który nigdy się nie zmienia”, w którym zapisano dokładnie kolejność położenia kotleta na bułce i dodania sałaty na wierzchu.

Teraz pora użyć tego przepisu, aby uruchomić prawdziwy biznes, czyli usługę. Ale gdy zaczynasz korzystać z Dockera, pojawiają się mylące terminy. Container, Service, Stack… wszystkie brzmią podobnie, więc jaka dokładnie jest między nimi różnica?

Jeśli nie uchwycisz tych pojęć, później podczas pisania skryptów wdrożeniowych będziesz się błąkać z pytaniami w stylu: „Dlaczego uruchomiłem kontener, a usługa nadal nie wstaje?”. Żeby dziś uporządkować to drzewo genealogiczne, przyniosłem najbardziej uniwersalny język świata: analogię do baru z hamburgerami.

Tworzysz hamburger, kontener, według przepisu, obrazu, zarządzasz tym samym menu jako usługą i sprzedajesz je w pakiecie jako zestaw, czyli stack.

1. Kontener (Container): hamburger przygotowany po złożeniu zamówienia

  • Definicja: rzeczywisty „byt”, który powstaje po uruchomieniu obrazu i załadowaniu go do pamięci.
  • Cecha: klientowi nie podasz kartki z przepisem (obrazu). Trzeba mu podać gorącego „hamburgera (kontener)” przygotowanego według przepisu.
  • Polecenie: docker run my-burger

Jeśli uruchamiam jedną instancję serwera, oznacza to, że uruchomiłem „jeden kontener”.

2. Usługa (Service): zarządzanie zapasem tego samego menu

Nadchodzi pora lunchu i klienci napływają tłumnie. Przy tempie smażenia jednego hamburgera naraz nie da się za tym nadążyć. Decydujemy więc o zwiększeniu obsady kuchni i przygotowaniu z wyprzedzeniem trzech „cheeseburgerów”. Od tego momentu nie zarządzam już „jednym cheeseburgerem”, lecz całą „linią cheeseburgerów”, czyli Service.

  • Definicja: „zbiór kontenerów” utworzonych z tego samego obrazu.
  • Cecha: można jej nadać „politykę” w rodzaju „tego menu ma być zawsze 3 sztuki w zapasie (Replicas=3)”. Jeśli jeden hamburger spadnie na podłogę (błąd), sama usmaży nowy.
  • Cel: „skalowanie wszerz (Scale-out)”. Rozłożenie ruchu i stabilność.

3. Stack: zestaw, czyli pełny pakiet

Sam hamburger to za mało. Potrzebujesz jeszcze frytek, DB, i coli, Redis. Dopiero kiedy wszystko to zbierze się razem, otrzymujesz „pełny posiłek”, czyli aplikację.

  • Definicja: „grupa najwyższego poziomu”, która łączy różne usługi (web, DB, cache).
  • Cecha: hamburgera i colę najwygodniej jeść, gdy stoją na jednej tacy (połączenie sieciowe). Gdy usługi zostaną połączone w stack, powstaje dla nich wydzielona przestrzeń (Network), w której mogą się ze sobą komunikować.

Problem: można się zasapać już od samego składania zamówienia

Koncepcja jest jasna, ale gdy trzeba to naprawdę uruchomić, zaczyna boleć głowa. Usługa webowa, czyli stack, który chcemy zbudować, zwykle potrzebuje takich elementów jak poniżej.

  1. Spring Boot (hamburger): 2 dania główne (redundancja)
  2. PostgreSQL (frytki): 1 dodatek
  3. Redis (cola): 1 napój

Uruchamianie tego wszystkiego surowymi poleceniami Dockera, docker run, jest jak składanie zamówienia przy ladzie w taki sposób.

„Poproszę ‘cheeseburgera (Spring)’, ale kotlet proszę wysmażyć medium-well, bez ogórków i z dużą ilością sosu. Aha, i jeszcze ‘frytki (DB)’, ale bez soli i z trzema saszetkami ketchupu. Na koniec ‘colę (Redis)’ w wersji zero i tylko z połową lodu. I proszę położyć te trzy rzeczy na ‘jednej tacy (Network)’.”

# W praktyce takie komendy trzeba wpisywac za kazdym razem
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...

Czy naprawdę da się bezbłędnie wyrecytować to długie zamówienie za każdym razem przy wdrożeniu? A co jeśli pomylisz kolejność i frytki wyjadą później niż hamburger? Klient, czyli użytkownik, nie będzie zadowolony.

Wybawca: Docker Compose – „Poproszę to, co zwykle!”

Dlatego deweloperzy pomyśleli: „A gdyby tak wcześniej zapisać to skomplikowane zamówienie w ‘menu, dokumencie’, żeby móc po prostu powiedzieć: ‘Poproszę zestaw numer 1’?”

Właśnie tak powstał „Docker Compose”. Jeśli zdefiniujemy własny „niestandardowy zestaw” w pliku docker-compose.yml, Docker sam przekaże zamówienie do kuchni i ułoży wszystko po kolei na tacy.

Nawet skomplikowane zamówienie specjalne da się załatwić jednym zdaniem: „poproszę to”, jeśli wcześniej zapiszesz je w menu.

[Action] Zbuduj własny zestaw

Nie poprzestawajmy na słowach. Uruchommy naprawdę, za jednym razem, lokalny serwer Spring i DB. Wystarczy utworzyć plik docker-compose.yml w katalogu głównym projektu i wkleić do niego poniższą zawartość.

version: '3.8'

services:
  # 1. Frytki (DB)
  my-database:
    image: postgres:14
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    ports:
      - "5432:5432"

  # 2. Cheeseburger (Web)
  my-web-server:
    image: my-spring-app:latest
    ports:
      - "8080:8080"
    depends_on:
      - my-database # "Najpierw musza usmazyc sie frytki, dopiero potem wychodzi burger" (zachowana kolejnosc)
    environment:
      DB_URL: jdbc:postgresql://my-database:5432/mydb

Teraz wypowiedz w terminalu magiczne zaklęcie.

# "Poprosze zestaw nr 1 (docker-compose.yml)!"
$ docker-compose up -d

Wynik: Docker odczytuje ten plik-menu, najpierw uruchamia DB, a gdy DB jest gotowa, uruchamia serwer Spring. To właśnie początek koncepcji IaC, Infrastructure as Code, czyli zarządzania infrastrukturą za pomocą kodu. Od teraz nawet złożoną konfigurację serwera da się ogarnąć przez udostępnienie jednego „pliku-menu”.

Na zakończenie: Works on My Machine? Works on ANY Machine!

Wymówka, od której zaczęła się ta seria, „ale u mnie na komputerze działa…”, już nie przejdzie. Zrozumieliśmy Linuksa, czyli OS, odizolowaliśmy środowiska za pomocą Dockera, czyli Container, i zdefiniowaliśmy złożone usługi w jednym pliku dzięki Docker Compose.

Teraz, o ile mam tylko napisany przez siebie plik docker-compose.yml, mogę w minutę postawić dokładnie ten sam zestaw hamburgerowy na moim komputerze, komputerze lidera zespołu albo na serwerze AWS.

Nie jesteśmy już „zwykłymi koderami”. Staliśmy się „inżynierami”, którzy potrafią projektować nie tylko sam kod, ale też świat, środowisko, w którym ten kod będzie działał. Tym kończy się seria [Works on My Machine].

Od następnej serii ruszymy do świata [The Pipeline: automatyzacja CI/CD], gdzie na tym solidnym fundamencie oddamy żmudny proces wdrażania maszynom.

Dodaj komentarz