Django + Docker
W tym wpisie chciałbym przedstawić Wam w jaki sposób rozwijać od zera aplikację Django z wykorzystaniem Dockera i Docker Compose. Bbędzie to bardzo prosta aplikacja, a wzasadzie jej zalążek, która będzie współpracowała z bazą danych PostgreSQL, a przy jej rozwijaniu będziemy wykorzystywali kontenery Dockera.
Czym jest Docker?
Docker jest to otwarta platforma, która realizuje wirtualizację na poziomie systemu operacyjnego zwaną także kontenerowaniem. Docker daje możliwość uruchamiania aplikacji w wydzielonym kontenerze bez konieczności emulowania warstwy sprzętowej i systemu operacyjnego. Każdy taki kontener posiada wydzielony obszar pamięci, odrębny interfejs sieciowy z własnym prywatnym adresem IP oraz wydzielony obszar na dysku, na którym znajduje się zainstalowany obraz systemu operacyjnego wraz z zależnościami i bibliotekami potrzebnymi do działania aplikacji.
Instalacja Dockera oraz docker-compose
Pierwszym naszym zadaniem jest instalacja Dockera oraz docker-compose. Docker w chwili obecnej dostępny jest dla najpopularniejszych systemów operacyjnych taki jak Windows, MacOS, Linux i jego instalacja nie powinna stanowić problemu. Po szczegóły dotyczące instalacji dla konkretnej wersji systemu operacyjnego polecam wejść na stronę dokumentacji Dockera. oraz dokumentacji docker-compose.
Jeśli wszystko wykonamy zgodnie z dokumentacją powinniśmy uzyskać wynik działania poleceń podobny do poniższego:
Przygotowujemy podwaliny pod naszą aplikację
Zaczniemy od utworzenia pliku requirements.txt zawierającego spis wymaganych pakietów języka Python, które będą nam niezbędne do uruchomienia aplikacji. Plik ten będzie wykorzystamy za chwilę w procesie budowania obrazu kontenera dla naszej aplikacji.
W kolejnym kroku tworzymy plik Dockerfile (Dockerfile_django), który zawierał będzie “przepis” na zbudowanie obrazu kontenera naszej aplikacji.
Analizując powyższy plik, możemy zauważyć, że obraz kontenera django będzie oparty na obrazie python w wersji 3.7 (linia 3), do katalogu głównego dodany zostanie plik requirements.txt (linia 4), a następnie przy pomocy polecenia pip zostaną zainstalowane wszystkie niezbędne zależności (linia 5). Wszystkie te powyższe kroki wykonają się podczas budowanie obrazu kontenera.
Budujemy obrazy kontenerów
Podczas procesu programowania naszej aplikacji będziemy wykorzystywać dwa kontenery, jeden z nich będzie odpowiedzialny za uruchamianie naszej aplikacji (framework django), a drugi będzie odpowiedzialny za uruchamienie silnika bazy danych PostgreSQL z którym będzie ona (aplikacja) współpracować. Definicję kontenerów umieścimy w pliku konfiguracyjnym docker-compose.yml w katalogu głównym naszego projektu:
Kontener postgres będzie oparty na domyślnym najnowszym obrazie PostgresSQL (linia 20), do przechowywania danych wykorzystywany będzie wewnętrzny nazwany wolumen (local_postgres_data), który to będzie podmontowany jako katalog /var/lib/postgresql/data wewnątrz tego kontenera (linia 22).
Obraz kontenera django zostanie zbudowany w oparciu o polecenia z pliku Dockerfile_django (linia 10), do swojej pracy będzie wymagał działającego kontenera postgres (linia 12), bieżący katalog projektu (.) zostanie wewnątrz kontenera podmontowany w katalogu /app (linia 14). Port 8000 kontenera zostanie zmapowany jako port 8000 w naszym systemie operacyjnym (linia 16), a w momencie uruchomienia kontenera zostanie wykonane polecenie python manage.py runserver (linia 17).
Wszystko co niezbędne mamy już gotowe, więc możemy przystąpić do właściwego procesu zbudowania obrazów kontenerów. Wydajemy polecenie budowania:
Startujemy z projektem
Mając już zbudowany obraz django (wewnątrz kontenera mamy zainstalowany framework django w wersji przynajmniej 2.1) możemy rozpocząć proces tworzenia projektu i pierwszej aplikacji. Wszystko to będziemy wykonwywać poprzez wywoływanie poleceń z wnętrza kontenera. Zaczniemy od utworzenia projektu:
Nastąpi teraz uruchomienie kontenera django (a także uruchomienie kontenera postgres poprzez system zależności), w kontenerze tym zostanie uruchomione polecenie django-admin, które będzie miało za zadanie stworzyć szkielet z kodem projektu. Gdy polecenie django-admin się wykona (zkończy działanie), to kontener django zakończy działanie i zostanie usunięty (–rm). Powinniśmy otrzymać następującą strukturę katalogów i plików w bieżącym katalogu projektu:
W tym miejscu, jeśli korzystamy z systemu Linux, zwracam jeszcze uwagę na uprawnienia plików. Właścicielem plików tworzonych wewnątrz kontenera będzie w tym przypadku użytkownik root (możemy się o tym przekonać wydając polecenie ls -l). Dzieje się tak, gdyż kontener uruchamiany jest domyślnie z poziomu użytkownika root i to on jest właścicielem tworzonych plików, pomimo tego, że samo uruchomienie kontenera nastąpiło z poziomu normalnego użytkownika. Korygujemy uprawnienia poleceniem:
Połączenie z bazą danych
Przed pierwszym uruchomieniem projektu niezbędne jeszcze jest skonfigurowanie połączenia z bazą danych. Jak już wspomniałem wcześniej nasz projekt będzie wykorzystwał bazę danych PostgreSQL, która będzie uruchomiona w niezależnym kontenerze o nazwie postgres. Gdy korzystamy z docker-compose (o ile nie wyspecyfikujemy inaczej w pliku konfiguracyjnym) kontenery między sobą komunikują się przy pomocy wydzielonej wirtualnej sieci ethernet. Mamy do dyspozycji nazwy kontenerów jako nazwy DNS z których możemy korzystać przy wzajemnych odwołaniach.
W przypadku naszego projektu django konfiguracja połączenia z bazą danych znajduje się w pliku mytodo/settings.py w słowniku DATABASES. Odszukujemy słownik DATABASES i zmieniamy na następujące dane:
Wartości NAME oraz USER ustawione na postgres wynikają z domyślnej konfiguracji obrazu dla kontenera postgres (podobnie ma się rzecz z PORT - domyślnie kontener postgres oparty na obrazie postgres nasłuchuje na porcie 5432). Wartość HOST ustawiona na ‘postgres’ wynika z nazwy jakiej użyliśmy dla kontenera bazy danych w pliku docker-compose.yml.
Uruchamiamy kontenery
Po tych zmianach przyszła wreszcie pora na uruchomienie naszego całego projektu: W tym celu wykorzystamy komendę up docker-compose, która uruchomi nam wszystkie kontenery i pozostanie w oczekiwaniu na ich ewentualne zakończenie działania.
Uruchamiamy przeglądarkę i wpisujemy w pasku adresu http://127.0.0.1:8000. Jeśli wszystko poszło zgodnie z planem powiniśmy otrzymać stronę zbliżoną do poniżeszej:
Rodzi się tutaj pytanie skąd docker-compose “wie” co w tym wypadku uruchomić w poszczególnych kontnerach przy ich startowaniu? Otóż w przypadku kontenera django mamy wprost podaną komendę do uruchomienia w pliku docker-compose.yml (linia 17, polecenie command). W przypadku kontenera postgres komenda do uruchomienia występuje w pliku Dockerfile użytym do zbudowania obrazu kontenera - ENTRYPOINT.
W tym miejscu w konsoli cały czas mamy uruchomiony docker-compose, więc musimy go przerwać poprzez naciśnięcie kombinacji klawiszy CONTROL-C. (Jednokrotne naciśnięcie CONTROL-C spowoduje rozpoczęcie procesu zatrzymywania kontenerów, co może czasami potrwać dłuższą chwile. Jeśli z jakiego powodu chcemy szybko “zabić” uruchomione kontenery to naciskamy ponownie CONTROL-C). Uruchamiając kontenery możemy do komendy up dodać przełącznik -d (detach) co spowoduje uruchomienie kontenerów w tle. W taki przypadku nie widzimy bezpośrednio na konsoli logów z ich działania - logi możemy oglądać na bieżąco np. na innym oknie konsoli poprzez wydanie polecenia: docker-compose logs -f
Teraz przyszła pora na wykonanie migracji (stosowny komunikat został zresztą wyświetlony podczas startu kontenera). W celu przeprowadzania migracji uruchomiamy kontener django i wewnątrz niego wykonujemy stosowne polecenie:
Gdy wykonywanie migracji zostanie zakończone, możemy ponownie uruchomić kontenery poprzez up i przystąpić do modyfikowania kodu naszej aplikacji.
Możemy stworzyć w naszym projekcie szkielet pierwszej aplikacji przy pomocy narzędzi django poprzez uruchomienie polecenia w kontenerze: docker-compose run –rm django python manage.py startapp todo lub możemy tworzyć ręcznie w katalogu projektu poszczególne pliki aplikacji.
Struktura naszego projektu będzie wyglądać następująco.
W tym miejscu mamy już w pełni gotowy i działający szkielet projektu do dalszego rozwoju.
Dla zainteresowanych kod źródłowy udostępniony jest na Gitlab. Zapraszam do samodzielnych eksperymentów i komentowania.
Zostaw komentarz