29 KiB
Wstęp do zajęć
Niniejsze zajęcia poświęcone są chmurze obliczeniowej w modelu PaaS (Platform as a Service). Podstawowe informacje na temat tego modelu zostały przedstawione na wcześniejszych zajęciach. Do kluczowych cech usług w tym modelu należą:
- brak konieczności zarządzania systemem operacyjnym i zainstalowanym środowiskiem uruchomieniowym
- wysoki stopień automatyzacji, znaczna część konfiguracji odbywa się poprzez ustawienia domyślne i konwencję
- konieczność dopasowania aplikacji do wymagań platformy uruchomieniowej
- ograniczony dostep do pewnych zasobów np. brak możliwości zapisu plików, ograniczenie komunikacji do protokołu HTTP
Dostępnych jest wiele usług działających w tym modelu PaaS. Do najpopularniejszych należą:
- Google App Engine - historycznie pierwsza taka usługa, która przetrwała do dziś
- Amazon AWS Elastic Beanstalk - w pewnym sensie jest to nakładka na usługę AWS EC2
- Microsoft Azure App Service
- Salesforce Heroku - jeden z pierwszych i wciąż najpopularniejszy dostawca usług tego typu
- DigitalOcean App Platform
- Red Hat OpenShift PaaS
- The Mendix Low-Code Platform
- Dev Graph Engine Yard
W czasie zajęć poznamy platformę Heroku, która bardzo dobrze obrazuje możliwości oferowane przez model PaaS. Co ważne, w odróżnieniu od wielu dostawców, Heroku oferuje tylko usługę w modelu PaaS.
Na czym polega PaaS
Model chmury obliczeniowej PaaS zakłada przeniesienie na dostawcę usługi odpowiedzialności za praktycznie wszyskie elementy środowiska, w którym działa aplikacja. Dostawca oprócz sprzętu i infrastruktury, dostarcza system operacyjny, bazy danych oraz środowisko uruchomieniowe dla naszej aplikacji.
W modelu tym tworzona aplikacja najczęściej musi być odpowiednio przygotowana aby mogła zostać uruchomiona w ściśle kontrolowanym środowisku. W początkowych etapach rozwoju był to chyba najczęściej podnoszony argument przeciw takiemu rozwiązaniu. Ścisłe związanie tworzonego oprogramowania z dostawcą usługi było postrzegane jako istotne ograniczenie i zagrożenie dla projektów informatycznych. Wraz z globalnym wzrostem wykorzystania usług chmury obliczeniowej wzrosła również akceptacja dla zwiększenia stopnia powiązania oprogramowania z dostawcą usług (ang. vendor lockin).
Z drugiej strony sam stopień powiązania aplikacji z platformą uległ znacznemu zmniejszeniu. Obecnie większość usług działąjących w modelu PaaS (np. Heroku), pozwala uruchomić niemal dowolną aplikację, napisaną w dowolnej technologi, nawet jeśli nie była ona projektowana specjalnie z myślą o wdrożeniu w chmurze.
Jak działa Platform as a Service
W następującej części omówiona zostanie zasada działania usługi typu PaaS na przykładzie Heroku. Heroku jest bardzo reprezentatywne dla tego typu usług, dzięki czemu wiedzę tę będzie można zastosować do większości dostępnych usług tego typu.
Zasadę działania Heroku można przedstawić z kilku perspektyw:
- Perspektywa działania aplikacji
- Perspektywa budowania i wdrażania aplikacji
- Perspektywa wirtualizacji i infrastruktury
Perspektywa działania aplikacji
Aplikacja zbudowana z wykorzystaniem rozwiązania typu PaaS może składać się z wielu modułów. Każdy moduł może korzystać z innych usług i działać w inny sposób. Jednak cechą wspólną wszyskich aplikacji wdrażanych w modelu PaaS jest udostępnianie usług w protokole HTTP i to moduł realizujacy tę funkcjonalność jest najczęściej uważany za główny dla danej aplikacji (nie rzadko stanowi też jedyny moduł aplikacji).
Heroku: Przykładowo w Heroku taki moduł nazywany jest
web
i tylko on może przyjmować żądania HTTP pochodzące z zewnątrz.
Usługa PaaS udostępnia nam adres _URL naszej aplikacji. Ten adres nie prowadzi jednak bezpośrednio do naszego modułu głównego. Zamiast tego każde wysłane żądanie HTTP trafia do tak zwanego routera. Jest to centralny punkt usługi, którego zadanie polega na odnalezieniu właściwej aplikacji, do której kierowane jest zapytanie oraz przekazanie danych do odpowiedniej instancji modułu.
Heroku: Heroku posiada jeden centralny _router w każdym z regionów, a wszystkie one korzystają z jednego punktu startowego (entry point):
herokuapp.com
.
_Router realizuje również proces tak zwanego równoważenia obciążenia (ang. load-balancing). Polega on na przekazywaniu żądań równolegle do różnych kopii (instancji) tego samego modułu. W ten sposób aplikacja może obsłużyć dowolnie wiele wiadomości jednocześnie bez obawy o znaczący wzrost czasu odpowiedzi. Aby to było możliwe konieczne jest przestrzeganie kilku zasad już na etapie projektowania aplikacji:
- Moduł musi być bezstanowy - nie powinien przechowywać żadnych informacji w pamięci czy na dysku. Całość stanu powinna być przechowywana w zewnętrznej usłudze (np. bazie danych).
- Komunikacja z modułem musi być bezstanowa - każde żądanie do modułu musi zawierać wszystkie informacje niezbędne do jego realizacji (patrz założenia stylu REST).
- Moduł musi być lekki - aby możliwe było szybkie uruchamianie nowych instancji oraz wyłączania tych już niepotrzebnych.
Skalowanie takich bezstanowych modułów może odbywać się automatycznie lub manualnie, w zależności od potrzeb.
Uwaga: Więcej na tamat zasad jakie powinien spełniać projekt zoptymalizowany pod wdrożenie w modelu PaaS przedstawia koncepcja The Twelve-Factor App.
Gdy moduł główny naszej aplikacji otrzyma już żądanie HTTP, może przystąpić do jego obsługi. Kod naszej aplikacji jest uruchamiany w zależności od środowiska, w którym aplikacja została napisana. Można wyróżnić trzy główne sposoby wykonania tego kodu:
- Aplikacja samodzielnie uruchamia serwer HTTP nasłuchujący na odpowiednim porcie
- Aplikacja jest przygotowana zgodnie z założeniami zewnętrznego serwera HTTP
- Aplikacja dostarczana jest w postaci zbudowanego obrazu Docker
Sposób pierwszy jest najpopularniejszy w przypadku języków kompilowanych (np. Go, Rust, Java), podczas gdy drugi jest najpopularniejszy w jezykach skryptowych (Python - uWSGI, Ruby - Unicorn). Sposób trzeci, jest stosunkowo młody, choć większość dostawców oferuje taką usługę, nie zawsze jest ona tak samo wspierana jak pierwsze dwa podejścia. Wybór sposobu uruchamiania aplikacji ma spore znaczenie na wymaganych zasobów. Przykładowo w rozwiązaniu 2. nie ma potrzeby aby proces odpowiedzialny za moduł aplikacji był stale uruchomiony, podczas gdy w pozostałych modelach jest to niezbędne.
Heroku: Potrafi automatycznie wykryć w jaki sposób uruchomić daną aplikację korzystając z bardzo wielu predefiniowanych profili.
Heroku: Obsługuje wszystkie trzy sposoby uruchamiania aplikacji, są one też w pełni wspierane.
Heroku: W ramach darmowego dostępu, wszystkie aplikacje hostowane na Heroku, są automatycznie usypiane jeśli nie są aktywne (nie otrzymują żądań HTTP) przez określony czas.
Gdy odpowiedni proces aplikacji otrzymuje żądanie HTTP, obsługuje je zgodnie ze swoją implementacją. Co ważne może on w tym celu wykorzystywać:
- inne procesy w ramach tej samej aplikacji - przykładowo procesy robocze do wykonywania zadań w tle,
- usługi dodatkowe - w tej kategorii najczęściej znajdują się zarządzane bazy danych oraz zasoby dyskowe od dostawcy usługi PaaS,
- usługi zewnętrzne i zasoby Internetu - każda aplikacja może niemal dowolnie korystać z zasobów dostępnych przez sięć Internet.
Na koniec obsługi żądania, zwracana jest odpowiedź HTTP, która przekazywana jest do _routera i następnie do przeglądarki użytkownika aplikacji.
Rysunek 1. Przykładowa architektura usługi typu PaaS. Dostawca zapewnia wszystko z wyjątkiem kodu aplikacji (moduły A, B, C i D).
Perspektywa budowania i wdrażania aplikacji
Proces budowania i wdrażania aplikacji w wielu popularnych usługach typu PaaS oparty jest na repozytorium kodu. Aplikacja budowana jest po każdej zmianie wybranej gałęzi repozytorium.
Heroku: W przypadku Heroku, należy przesłać zmiany na specjalne, tworzone dla każdej aplikacji repozytorium hostowane na serwerach Heroku.
Takie podejście do budowania aplikacji, wspomaga (a tak właściwie wymaga) wdrożenie modelu CI (ang. Continuous Integration, ciągła integracja). Budowanie aplikacji musi być w pełni zautomatyzowane i wykonywane w ściśle kontrolowanym środowisku. Platforma PaaS udostępnia schematy według, których budowane są aplikacje. Często też możliwe jest zdefiniowanie własnych schematów oraz budowanie aplikacji w dowolnym środowisku z wykorzystaniem kontenerów Docker.
Heroku: Heroku obsługuje większość popularnych języków programowania i frameworków: Node.js, Java, Python, PHP, Go i wiele innych. Aplikację można też zbudować korzystając z kontenerów.
Przykładowo w środowisku Python, zwyczajowo wszystkie zewnętrzne zależności powinny być opisane w pliku requirements.txt
. Podczas zautomatyzowanego budowania aplikacji, odpowiedni schemat budowania dla języka Python, zainstaluje wszystkie wymagane zależności i dopiero wtedy przystąpi do budowania właściwej aplikacji.
Zbudowana aplikacja jest łączona z konfiguracją a następnie pakowana i nazywana (np. nazwą, wersją, numerem comita w repozytorium, czy bieżącą datą) tworząc _wydanie (ang. release). Kolejne wydania aplikacji tworzą rejestr do którego można tylko dodawać nowe elementy. W ten sposób zawsze istnieje możliwość powrotu do wersji wcześniejszej. Tak zdeponowane wydanie może następnie posłużyć do wykonania wdrożenia aplikacji. Jedna zbudowana aplikacja za sprawą różnych konfiguracji (np. testowa, produkcyjna) może prowadzić to wielu wydań. Każde wydanie może zostać uruchomione wielokrotnie czy to w ramach procesu skalowania, czy odtwarzania po awarii jednej z instancji.
Rysunek 2. Przykładowy schemat procesu budowania aplikacji na potrzeby usługi w modelu Paas.
Perspektywa wirtualizacji i infrastruktury
Korzystając z usług w modelu PaaS, warto też być świadomym jak fizycznie zorganizowana jest infrastrktura, z której korzystamy. Przedstawione tu informacje są bardzo ogólne, gdyż przyjęte rozwiązania techniczne różnią się u poszczególnych dostawców. Można jednak przyjąć, że przedstawiona organizacja jest wspólna dla większości usług.
Przede wszystkim aplikacje uruchamiane w usłudze PaaS korzystają z w pełni zwirtualizowanego środowiska. Najczęściej jest to wirtualizacja kilku poziomowa. Na załączonym schemacie fizyczny serwer (o bardzo dużych zasobach obliczeniowych) jest hostem dla wielu maszyn wirtualnych. Pojedyncza maszyna wirtualna jest natomiast hostem dla bardzo wielu aplikacji wdrożonych w modelu PaaS. Każda z takich aplikacji może pracować we własnym zwirtualizowanym środowisku (np. kontenerze Docker). Całość połączona jest poprzez sieć wirtualną i zabezpieczona odpowiednio skonfigurowaną zaporą sieciową. _Router jest odpowiedzialny za przesyłanie żądań HTTP do odpowiednich maszyn wirtualnych i dalej do kontenerów i aplikacji.
Przy przydzielaniu zasobów stosuje się bardzo często tak zwany over-provisioning. Polega to na udostępnianiu większej ilości wirtualnych zasobów, niż jest fizycznie dostępne. Przyjmuje się założenie, że jest bardzo mało prawdopodobne aby jednocześnie wszystkie aplikacje potrzebowały 100% zadeklarowanych zasobów. Oczywiście w skrajnym przypadku może to powodować przerwy w działaniu niektórych aplikacji oraz spadki wydajności. Stąd tak ważna jest konfiguracja skalowania i instancji zapasowych. Z drugiej strony takie podejście pozwala drastycznie zmniejszyć koszty usługi. W porównaniu z dedykowanym serwerem redukcja kosztów może sięgać nawet 100x, a w porównaniu z zasobami wirtualnymi ale o dedykowanych zasobach 10x. Niektórzy dostawcy usług oferują wariant usługi w ramach, którego zasoby nie są współdzielone i/lub przydzielane wielokrotnie. Taka usługa jest znacznie droższa od standardowej, ale gwarantuje stabilną wydajność i dostępność nie zależnie od obciążenia zasobów usługodawcy.
Heroku: Jak już zostało to powiedziane wcześniej, Heroku nie utrzymuje swojej własnej dedykowanej infrastruktury. Korzysta natomiast z zasobów AWS, w szczególności z maszyn wirtualnych EC2, które stanowią podstawę mocy obliczeniowej dostępnej dla aplikacji. Jedna maszyna wirtualna nawet najsłabsza, może bez problemu obsłużyć wiele aplikacji. W ten sposób wdrożenie aplikacji na Heroku może nadal być bardziej opłacalne niż bezpośrednio na AWS EC2. Ciekawe opracowanie na ten temat znajduje się tu.
Zadania do realizacji w czasie zajęć
Konfiguracja środowiska
Zarejestruj się w usłudze Heroku korzystając z formularza pod adresem: https://signup.heroku.com/.
Zainstaluj narzędzie do zarządzania aplikacjami w chmurze Heroku:
cd /dev/shm && wget https://cli-assets.heroku.com/heroku-linux-x64.tar.gz && tar xvf heroku-linux-x64.tar.gz
Uwaga: Oczywiście Heroku CLI możesz zainstalować w dowolnej lokalizacji w systemie, jeśli chcesz aby komenda
heroku
była dostępna globalnie w całym systemie zainstaluj ją w/usr/local/bin
.Uwaga: Katalog
/dev/shm
to dość specjalne miejsce w każdym współczesnym systemie linuxowym. Jest to dysk zlokalizowany w pamięci operacyjnej, w związku z czym jego zawartość jest czyszczona przy każdym wyłączeniu komputera. Ponadto pamiętaj że umieszczając tam duże pliki ograniczasz ilość pamięci operacyjnej dostępnej dla systemu i innych procesów.Zaloguj się w zainstalowanym narzędziu:
/dev/shm/heroku/bin/heroku login
Komanda ta może poprosić Cię o otwarcie w przeglądarce internetowej wyświetlonego linku w celu wykonania logowania do serwisu Heroku.
Uwaga: Dane logowania (login i hasło) nie zostaną zapisane na komputerze. Heroku (podobnie jak większość współczesnych serwisów dbających o bezpieczeństwo użytkowników) korzysta z tak zwanych _tokenów dostępowych. Podając login i hasło, system weryfikuje Twoje uprawnienia, a następnie generuje specjalny token dostępowy, który następnie pozwala na dostęp do zasobów bez podawania/przechowywania hasła. Token może mieć ograniczone uprawnienia, a co najważniejsze można go unieważnić. Więcej informacji znajduje się w dokumentacji Heroku.
Przykładowa aplikacja
- Sklonuj repozytorium Git zawierające przykładowy projekt startowy w Javie (używający frameworka Spring):
```bash
cd ~
git clone https://github.com/heroku/java-getting-started.git
cd java-getting-started
```
Utwórz nową aplikację na Heroku i wyświetl informacje o niej:
appName=$(/dev/shm/heroku/bin/heroku create --stack heroku-20 --region eu --json | jq -r '.name') /dev/shm/heroku/bin/heroku apps:info $appName
Nowo utworzoną aplikację zobaczymy także w interfejsie webowym pod adresem: https://dashboard.heroku.com/apps.
Uwaga: Tak naprawdę wystarczyłaby komenda
heroku create
(ewentualnie z własną nazwą tworzonej aplikacji), reszta powyższego polecenia przechwytuje nazwę nowo utworzonego projektu i zapisuję ją w zmiennej powłoki do późniejszego użycia.Uwaga: Tworząc aplikację nie podaliśmy jej nazwy, Heroku generuje je automatycznie jako zbitki dwóch losowych słów z języka angielskiego.
Uwaga: Parametr
--stack
określa bazowy obraz kontenera na podstawie, którego utworzona zostanie aplikacja. Więcej informacji w dokumentacji.Polecenie
heroku create
nie tylko utworzyło aplikacje w infrastrukturze Heroku, ale jednocześnie zarejestrowało dodatkowe repozytorium docelowe (upstream) dla naszej aplikacji. Dzięki temu można bardzo łatwo wysłać kod aplikacji na serwery Heroku korzstając z narzędziagit
:git push heroku main
Aplikacja powinna być dostępna po podanym na końcu linkiem. Link można też uzyskać w interfejsie webowym, jest on stały dla danej aplikacji.
Modyfikacja aplikacji
Dodamy teraz obsługę nowej ścieżki w naszej przykładowej aplikacji. Zmodyfikuj plik
src/main/java/com/example/Main.java
dodając:w nagłówku pliku
import org.springframework.web.bind.annotation.PathVariable;
wewnątrz klasy Main
@RequestMapping("/hello/{name}") String hello(@PathVariable(value="name") String name) { return "hello"; }
Dodaj plik
src/main/resources/templates/hello.html
o następującej treści:<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'db')}"> <body> <div class="container"> <h1>Hello <span th:text="${name}">name</span>!</h1> </div> </body> </html>
Utwórz nowy commit i wyślij zmiany do Heroku.
git add src/main/resources/templates/hello.html git commit -a -m 'Hello' git push heroku main
Po krótkiej chwili aplikacja powinna zostać podmieniona.
Wskazówka: Kompletne logi z procesu kompilacji i wdrażania aplikacji dostępne są w interfejsie webowym.
Aplikację można usunąć za pomocą interfejsu webowego lub z linii poleceń:
/dev/shm/heroku/bin/heroku apps:delete $appName
Aplikacja od zera w Pythonie
W tym zadaniu utworzymy aplikację od zera i uruchomimy ją w chmurze Heroku. Skorzystamy z gotowej przykładowej aplikacji wyświetlającej losowe strony.
Stwórz nowy katalog i wewnątrz niego utwórz puste repozytorium
git
:mkdir newApp cd newApp git init
Skopiuj do tego katalogu cztery pliki (
layout.jinja2
,table.jinja2
,lipsum.jinja2
imain.py
) dołączone do materiałów do zajęć.Tworzymy nową aplikację na Heroku i wysyłamy naszą aplikację:
appName=$(/dev/shm/heroku/bin/heroku create --stack heroku-20 --region eu --json | jq -r '.name') git add . git commit -m 'Pierwsza wersja' git push heroku master
Ostatnia linijka się nie powiedzie, bo Heroku nie wie co zrobić z wysyłanym kodem. Możesz też sprawdzić w interfejsie webowym co się stało.
Heroku stara się automatycznie wykryć język aplikacji i odpowiednio ją skompilować/zbudować. Pisząc własną aplikację warto poczytać dokładnie jak to robi. Większość języków/frameworków ma pewne konwencje nazywania plików czy wyszczególniania zależności i wystarczy się do nich stosować, by Heroku obsłużyło je poprawnie.
Uwaga: Takie podejście znacząco ułatwia korzystanie z platformy, szczególnie początkującym użytkownikom. W dalszej części zajęć pokazane zostanie jak przejąć pełnie kontroli nad Heroku i podjąć wszystkie decyzje samodzielnie.
Obecność pliku
requirements.txt
jest sygnałem dla Heroku, że mamy do czynienia z aplikację napisaną w Pythonie, a w tym pliku są zależności do zainstalowania przez managera pakietówpip
. Tworzymy więc w naszym projekcie plikrequirements.txt
o zawartości:pyramid pyramid-jinja2 pyramid-debugtoolbar faker uwsgi
Następnie dodajemy go do repozytorium i wysyłamy do Heroku:
git add requirements.txt git commit -m 'requirements.txt' git push heroku master
Teraz powinniśmy widzieć jak ściągane są zależności naszej aplikacji i na końcu zobaczymy link pod jakim nasza aplikacja 'działa'. Odwiedź stronę w przeglądarce, czy strona działa zgodnie z oczekiwaniami?
Logi naszej aplikacji można zawsze sprawdzić w interfejsie webowym. Można to też zrobić bezpośrednio z konsoli:
/dev/shm/heroku/bin/heroku logs -a $appName
Aplikacja co prawda się zbudowała, ale Heroku dalej nie wie jak ją uruchomić.
Aby poinstruować Heroku jak zbudowaną aplikację uruchmić musimy utworzyć plik
Procfile
(uwaga na wielką literę) o zawartości:web: uwsgi --http-socket=:$PORT --die-on-term --module=main:app
Mówimy w nim, że nasza aplikacja jest aplikacją webową i można ją uruchomić podanym poleceniem (to samo zadziałałoby lokalnie po utworzeniu odpowiedniego środowiska wirtualnego). Tutaj chcemy, by uWSGI nasłuchiwał na porcie o numerze pochodzącym ze zmiennej środowiskowej
PORT
(definiowanej przez Heroku).Po wysłaniu zmian do Heroku wszystko powinno już działać.
Aplikację możemy łatwo usunąć:
/dev/shm/heroku/bin/heroku apps:delete $appName
Większa kontrola nad Heroku
Heroku bardzo dobrze wykrywa domyślne ustawienia i konfiguracje. Czasem jednak to nie wystarcza. Struktura wdrażanej aplikacji może być zbyt złożona, lub dostosowana pod inną infrastrukturę. W takiej sytuacji pomocą służą pliki konfiguracyjne Heroku:
- Procfile - samodzielnie sprawdza się dla najprostszych aplikacji, często działa w połączeniu z konfiguracją w
app.json
- app.json - pozwala definiować dodatkowe informacje na temat aplikacji oraz umożliwia zarządzanie dodatkami (np. bazą danych)
- heroku.yml - stworzony z myślą o wdrażaniu przy użyciu narzędzia
Docker
. Nie jest kompatybilnyProcfile
.
Przekształcimy teraz poprzednią aplikację tak aby uzyskać pełną kontrolę nad środowiskiem, w którym będzie działać. Wykorzystamy do tego narzędzie Docker
i konfigurację z wykorzystaniem pliku heroku.yml
.
Dodaj plik
heroku.yml
o następującej treści:build: docker: web: Dockerfile run: web: uwsgi --http-socket=:$PORT --die-on-term --module=main:app
Pierwsza część informuje Heroku w jaki sposób zbudować naszą aplikację, dla uzyskania pełnej elastyczności podajemy tylko nazwę pliku
Dockerfile
naszej aplikacji. W ten sposób możemy zrealizować dowolne operacje, skorzystać z dowolnych narzędzi.Wskazówka: W sekcji
docker
można umieścić wiele pozycji, co umożliwi zbudowanie wielu obrazów docker.Druga część pliku odpowiada za określenie jak uruchomić naszą aplikację. Działa podobnie jak Procfile.
Wskazówka: W sekcji
run
można umieścić wiele pozycji, co umożliwi uruchomienie wielu procesów w ramach jednej aplikacji.Utwórz plik
Dockerfile
o następującej treściFROM python:3 WORKDIR /usr/src/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . .
Bazujemy tu na oficjalnym obrazie Python. Na początek kopiujemy plik
requirements.txt
i instalujemy wszystkie zależności, następnie kopiujemy resztę projektu. Nie podajemy dyrektywENTRYPOINT
aniCMD
, gdyż zostaną one zastąpione przez konfigurację z sekcjirun
plikuheroku.yml
.
Usuwamy plik
Procfile
.rm Procfile
Zmieniamy _stack naszej aplikacji w Heroku tak aby oparta była ona o kontener:
/dev/shm/heroku/bin/heroku stack:set container
Dodajemy pliki do repozytorium i wysyłamy do Heroku. Nowa wersja strony powinna zostać zbudowana i uruchomiona. Zwróć uwagę na proces budowania, który obecnie pobiera i buduje odpowiednie obrazy dockerowe. Heroku nie korzysta już ze swoich domyślnych ustawień, zamiast tego buduje obraz docker zgodnie z dostarczaoną specyfikacją.