Wstęp i opis
Bieżący skrypt zawiera instrukcje stworzenia pliku dockerfile, obrazu oraz kontenera dockerowego, wraz z przetestowaniem działania endpointów.
Dockerfile
W głównym folderze repozytorium (tam gdzie znajduje się solucja) dodajmy nowy plik - Dockerfile
(lub przenieśmy istniejący już wcześniej plik z folderu ApartmentRental.API
, który został wygenerowany podczas tworzenia projektu i usuńmy jego zawartość). Nasza instrukcja budowania obrazu powinna wykonać następujące czynności:
- Użyć do budowania obrazu .Net 6 SDK
- Skopiować kod źródłowy
- Zbudować aplikację
- Uruchomić testy jednostkowe
- Stworzyć lokalną bazę danych (w rozwiązaniach produkcyjnych ten krok nie występuje, ponieważ zazwyczaj korzysta się z baz danych wystawionych na zewnętrznych serwerach)
- Skopiować skompilowane pliki oraz bazę
- Uruchomić aplikację
Powyższe punkty można zamknąć w czterech krokach, które nazwiemy: build
, tests
, update-database
i final
.
Build
Krok ten powinien użyć obrazu .Net 6, skopiować kod źródłowy oraz zbudować aplikację. Wspomniane czynności przekładając na instrukcje w Dockerfile powinny prezentować się następująco:
Przejdźmy po kolei po każdej z instrukcji:
FROM
tworzy warstwę z gotowego obrazu .Net 6.0 opublikowanego przez Microsoft w Dockerhubie. Na jego podstawie będzie budowany nasz własny obraz.WORKDIR
określa katalog roboczy dla jakichkolwiek instrukcji/komend użytych w DockerFileCOPY
dodaje pliki z bieżącego folderu klienta Docker’a - użycie kropki powoduje skopiowanie wszystkich plikówWORKDIR /src/ApartmentRental.API
- określa przejście do nowego katalogu roboczego, w którym zostanie uruchomiana następna instrukcjaRUN
dotnet publish - instrukcjaRUN
pozwala na wykonanie komend wewnątrz obrazu. W tym przypadku wywołujemy komendę dotnet publish, która kompiluje aplikację, odczytuje jej zależności określone w pliku projektu i umieszcza wynikowy zestaw plików w katalogu. Flaga-c
określa konfigurację w której ma zostać skompilowany projekt (Release), zaś-o
katalog wyjściowy.
Tests
Krok w którym zostaną uruchomione testy jednostkowe. Powinien wyglądać następująco:
Powyższe instrukcje należy rozumieć następująco:
- FROM build AS tests - tworzymy nową warstwę wykorzystując poprzednio stworzoną warstwę
build
- Ustalamy katalog roboczy do projektu z testami
- Wykonujmy komendę dotnet test, która uruchamia testy jednostkowe wewnątrz obrazu
Update-database
W tym kroku zostanie wygenerowana baza danych, na podstawie kodu źródłowego migracji, które były tworzone na poprzednich laboratoriach. Instrukcje powinne przyjąć poniższą formę:
Gdzie:
- Tworzymy warstwę updare-database na podstawie warstwy build (nie musimy używać poprzedniej warstwy, jeśli nie potrzebujemy jej plików wynikowych Pozwala to na zmniejszenie wielkości wygenerowanego obrazu)
- Przechodzimy do głównego katalogu roboczego
- Instalujemy globalnie w obrazie narzędzie Entity Framework
- Definiujemy ścieżkę do narzędzi dotnetowych, która pozwali nam na ich uruchamianie jako zmienne środowiskowe
- Uruchamiamy komendę tworzenia/aktualizacji bazy danych wewnątrz obrazu
Final
Ostatni krok, w którym powinniśmy użyć skompilowane pliki do uruchomienia aplikacji. Przykład instrukcji, może wyglądać tak jak poniżej:
Gdzie:
- Wskazujemy że będziemy bazować na obrazie
aspnet:6.0
. Nie posiada on domyślnie zainstalowanego SDK .Neta (nie pozwala chociażby na uruchomienie komendydotnet publish
), natomiast jest on zoptymalizowany pod kątem uruchomienia aplikacji webowej. - Wskazujemy katalog roboczy
- Kopiujemy z poprzedniej warstwy
update-database
wygenerowaną bazę danych - Kopiujemy z poprzedniej warstwy
update-database
skompilowane pliki aplikacji - Instrukcja ENTRYPOINT określa punkt startowy kontenera. Co przekłada się na uruchomienia aplikacji
Cały plik
Uruchomienie kontenera
Kontener dockerowy można prosto uruchomić za pomocą środowiska programistycznego Rider. Prawym przyciskiem myszy kliknijmy w dwie strzałki przy pierwszej linijce pliku i wybierzmy EditDockerfile
. W pojawionym się oknie dialogowym uzupełnijmy pola image tag
oraz container name
i kliknijmy przycisk Run.
Po uruchomieniu kontenera, na dole środowiska programistycznego powinna wyświetlić się zakładka z takimi polami jak: build log
, log
, properties
, environment variables
, port bindings
, volumen binding
, Files
. W zakładce build log możemy prześledzić wszystkie wykonane instrukcje podczas budowy obrazu. W log
wyświetlą się logi naszej aplikacji - co ważne zauważmy że aplikacja w konfiguracji Realesowej wystartowała na porcie 80
wewnątrz kontenera. Stało się to dlatego że obraz aspnet
z kroku final
, domyślnie definiuje zmienną środowiskową ASPNETCORE_URLS
, którą możemy również podejrzeć w zakładce Environment Variables
.
Test zdockeryzowanej aplikacji
W tym kroku przetestujemy czy aplikacja która została zdockeryzowana poprawnie działa. Wyślijmy do aplikacji prosty request zwracający wszystkie apartamenty. Do przeprowadzenia testu posłuży nam Postman. Spróbujmy wysłać za pomocą niego request na adres aplikacji:
http://localhost:80/api/apartment/getall.
Wciśnijmy przycisk Send i sprawdźmy poprawnośc odpowiedzi.
I w tym miejscu pojawia się błąd. Co jest zachowaniem pożądanym. W poprzednim punkcie wspomniałem że port 80 został zdefiniowany wewnątrz kontenera. Oznacza to że ruch sieciowy do kontenera jest nie możliwy jeśli nie wystawimy/powiążemy portów na zewnątrz. Aby to zrobić, przejdźmy w Riderze do zakładki dockerowej Port Bindings
i tam za pomocą “plusika” dodajmy interesujące nas powiązanie. W oknie dialogowym w specify container port
podajemy port na którym działa nasz aplikacja - 80
. Musimy jeszcze rozwinąć listę Modify options
i wybrać host port. W polu specify host port
należy wpisać jakikolwiek wolny port w naszym systemie (w każdym przypadku może być inaczej, w zależności od uruchomionych usług w OS). Należy wybrać przycisk “Ok” i później “Save”.
Wrócimy teraz do Postmana. Zmieńmy w zapytaniu port, na taki który ustawiliśmy wyżej i wyślijmy request. Jeśli dostaliśmy pustą odpowiedź, to wszystko skonfigurowaliśmy poprawnie.
Sprawdźmy jeszcze endpointy dodające nowego najemcę oraz apartament oraz jeszcze raz wywołajmy end point zwracający listę wszystkich lokali.
Voila! Nasza skonteryzowana aplikacja dodała nowe encje i zwróciła odpowiednie dane.