wsl python docker django

WSL, Python i Docker razem – jak zacząć programować pod Windows?

Wielu deweloperów ze względu na wygodę decyduję się na pracę w środowisku najbliższym aplikacji, na którym będzie wystawiona na produkcję. Pozwala to między innymi na uniknąć niepożądanych problemów integracji międzyplatformowej oraz redukuje to koszt złożonych i drogich narzędzi deweloperskich, które oferują do jakiegoś stopnia integrację wieloplatformową. Ci którzy nie dysponowali dużym budżetem i specjalistycznym wsparciem technicznym jeszcze do niedawna z góry byli skazani na różne formy obejścia, np. stawiając paralelnie dwa systemy operacyjne spięte pod jedną boot’owalną partycją efi, bądź instalacją maszyn wirtualnych z użyciem Virtualbox’a czy VMWare’a. Z inicjatywą na rozwiązanie potrzeb mniejszych deweloperów wyszedł Microsoft, który po raz pierwszy wprowadził wbudowany w system Win10 wsparcie dla obsługi systemów Linux’owych. WSL (Windows Subsystem for Linux) w pierwszej generacji nie był pełnym systemem i opierał się częściowo o zasoby systemowe Windows’a. Nie posiadał własnego jądra, stąd też nie mógł korzystać w pełni z własnych wywołań systemowych i bezpośrednio korzystać z zasobów sprzętowych. Z tego względu takie narzędzia deweloperskie jak konteneryzacja z użyciem Dockera nie było jeszcze możliwe do zrealizowania. Ale wraz z wejściem w życie najnowszej stabilnej wersji Win10 2004 w maju bieżącego roku deweloperzy otrzymali stabilne niezawodne narzędzie jakim jest WSL2. Microsoft w swojej dokumentacji WSL and WSL2 Comparision przedstawia podstawowe różnice jakie wprowadzono w aktualizacji. Te zmiany umożliwiły pełne wsparcie dla konteneryzacji Dockera i nie tylko. WSL2 nie jest uzależniony już od systemu plików Windows’a oraz umożliwia udostępnienie serwera X, co dla mniej wtajemniczonych oznacza: Tak jest możliwe uruchomienie aplikacji z interfejsem graficznym pod WSL. Więcej o tym i jak to zrobić można przeczytać tutaj: Running WSL GUI Apps on Windows 10

WSL na start – Instalacja najnowszej aktualizacji Windows’a maj 2020

W pierwszej kolejności wypadałoby sprawdzić jaką aktualnie mamy wersję systemu. Najprościej jest to zrobić wpisując w pasek zadań winver

WSL Python Django Docker 1
WSL Python Django Docker 2

Jeżeli wersja jest wyższa niż 1903 i build wyższy niż 18362 nie ma pilnej potrzeby aktualizacji do nowszej wersji w celu instalacji WSL2, ale zalecana jest mimo wszystko aktualizacja do najnowszej stabilnej wersji.

Klasycznie można zaktualizować Windowsa korzystająć z opcji Windows Update w ustawieniach systemu Win10. Jak należy to zrobić wytłumaczone jest w dokumentacji od Microsoftu pod linkiem Pobieranie aktualizacji systemu Windows 10 z maja 2020 r.. Jeżeli z jakichś przyczyn najnowsza aktualizacja się nie wyświetli, można spróbować zaktualizować system ręcznie korzystając z Asystenta aktualizacji. Możliwe jest też aktualizacja Windows’a do wersji preview, która nie jest oficjalnie wydana i żeby z niej skorzystać należy zasubskrybować opcję Windows Insider Program:

WSL Python Django Docker 3

Więcej o tym jak to zrobić można przeczytać pod linkiem Getting started with the Windows Insider Program, ale nie jest to zalecane, ze względu na bardzo zawodne funkcjonowanie WSL2.

WSL – Instalacja i aktualizacja do WSL2

Po tym jak upewniliśmy się, że system spełnia wymogi do instalacji WSL2 możemy przejść do głównego etapu stawiana środowiska. Bardzo pomocna będzie w tym szczegółowa dokumentacja Microsoftu: Install Windows Subsystem for Linux . W pierwszej kolejności trzeba uruchomić Windows’owy shell systemowy PowerShell z uprawnieniami administratora.

WSL Python Django Docker 4

Następnie wprowadzamy polecenie w shellu w celu umożliwienia instalacji WSL:

dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
WSL Python Django Docker 5

Następnie wprowadzamy polecenie w shellu w celu umożliwienia aktualizacji do WSL2:

dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
WSL Python Django Docker 6

Pobieramy i instalujemy aktualizację do WSL2 link

Na koniec w shellu poniższym poleceniem ustawiamy dla WSL domyślną wersję 2:

wsl --set-default-version 2

Powyższe operacje mogą zajać trochę czasu, a system będzie wymagał ponownego uruchomienia w celu wprowadzenia zmian. Po pomyślnym przejściu tego etapu możemy w końcu zainstalować wybraną przez nas dystrybucję Linux’a. Należy uruchomić aplikację Microsoft Store i wpisać dystrybucję, którą chcemy zainstalować. Do wyboru mamy:

  • Ubuntu 16.04 LTS
  • Ubuntu 18.04 LTS
  • Ubuntu 20.04 LTS
  • openSUSE Leap 15.1
  • SUSE Linux Enterprise Server 12 SP5
  • SUSE Linux Enterprise Server 15 SP1
  • Kali Linux
  • Debian GNU/Linux
  • Fedora Remix for WSL
  • Pengwin
  • Pengwin Enterprise
  • Alpine WSL
WSL Python Django Docker 7

Dobrym wyborem na start będzie Ubuntu 20.04 LTS, ze względu na najbardziej powszechne użycie i najlepsze wsparcie. Tu w celach prezentacyjnych pokażę OpenSUSE, ale nie ma większej różnicy w instalacji różnych dystrybucji pod WSL2.

Po pobraniu wpisujemy w pasku zadań naszą dystrubucję i uruchamiamy, należy odczekać chwilę i voilà mamy zainstalowaną dystrybucję linuksa pełną gębą.

WSL Python Django Docker 8
WSL Python Django Docker 9

Takimi rzeczami jak konfiguracja sieci i inne podstawowe rzeczy są z góry załatwione. Utworzony jest most sieciowy, z naszą dystrybucją, dzięki czemu mamy pełną łączność ze światem z poziomu dystrybucji. Możemy to sprawdzić wpisując w PowerShella ipconfig, a w WSL ip a show dev eth0:

WSL Python Django Docker 10
WSL Python Django Docker 11

Instalacja Docker Desktop

Kolejnym krokiem będzie instalacja zaplecza Dockera, dzięki któremu będzie możliwe korzystanie z wirtualizacji WSL2 do zarządzania kontenerami pod Windows’em. link

Po zainstalowaniu Docker-Desktop i wejściu w ustawienia, możemy zobaczyć, że demon Docker’a będzie korzystał z wirtualizacji WSL2.

WSL Python Django Docker 12

O developmencie z użyciem Docker’a i WSL2 można przeczytać tutaj: Docker Desktop WSL 2 backend

Teraz należałoby doinstalować również podstawowe narzędzia potrzebne do zarządzania kontenerami Docker’a. Należy uruchomić dystrybucję i uruchomić polecenie w shellu:

$ sudo apt -y install docker docker-compose
WSL Python Django Docker 13
WSL Python Django Docker 14

Instalacja Visual Studio Code i konfiguracja do pracy z WSL

Obecnie najlepszym środowiskiem deweloperskim, które spina wszystkie powyższe technologie jest Visual Studio Code od Microsoft’u.

WSL Python Django Docker 15

By otrzymać wsparcie VS Code’a dla WSL2 należy zainstalować odpowiedni plugin. Najprościej jest to zrobić wciskając kombinację klawiszy Ctrl+Shift+x i wpisać “WSL”. Pierwsza pozycja Remote – WSL, instalujemy wtyczkę i możemy się cieszyć pełnym wsparciem dla WSL2.

WSL Python Django Docker 16

Utworzenie bazowego projektu Django i jego deployment z użyciem Docker’a

W pierwszej kolejności należałoby utworzyć folder na naszą pierwszą aplikację.

WSL Python Django Docker 17
WSL Python Django Docker 18

Na ten moment folder z naszą aplikacją jest otwarty spod systemu Windows’a, aby otworzyć folder pod WSL2 należy wcisnąć zielony przełącznik w lewym dolnym rogu okna. Następnie wybieramy opcję Remote-WSL: Reopen Folder in WSL, a następnie naszą zainstalowaną dystrybucję.

WSL Python Django Docker 19
WSL Python Django Docker 20

Kolejno uruchamiamy terminal klikając prawym na pasku po lewej i wybieramy opcję Open in Integrated Terminal. Instalujemy podstawowe narzędzie, by móc przejść do dalszej pracy. W pierwszej kolejności pylint i pipenv. Pylint pomaga w pisaniu kodu w Pythonie i podpowiada składnię, a pipenv pozwoli nam na wprowadzenie wirtualnego środowiska w celu odizolowania narzędzi deweloperskich tylko na potrzebę tego projektu. Należy wykonać poniższe polecenie:

$ sudo apt -y install pylint pipenv
WSL Python Django Docker 21
WSL Python Django Docker 22

Na potrzebę naszej aplikacji django potrzebny będzie oddzielny katalog, który uprości nam później deployment.

$ mkdir src; cd src

Następnie uruchamiamy wirtualne środowisko wpisując polecenie:

$ pipenv shell .
WSL Python Django Docker 23

Instalujemy przy aktywowanym środowisku podstawowe narzędzia: django i silnik bazodanowy do obsługi PostgreSQL psycopg2

$ pipenv install django psycopg2-binary
WSL Python Django Docker 24

Tworzymy teraz nasz przykładowy projekt z aplikacją.

$ django-admin startproject example

Później wchodzimy do katalogu z projektem i tworzymy przykładową aplikację

$ django-admin startapp example_app

Kolejno potrzebujemy teraz skonfigurować nasz projekt korzystał z bazy danych Postgres’a i by dane do naszej bazy danych były pobierane jako zmienne środowiskowe. Otwieramy plik example/example/settings.py i na samym początku zaciągamy moduł os, który będzie potrzebny do pobrania zmiennych ze środowiska.

"""
Django settings for example project.

Generated by 'django-admin startproject' using Django 3.1.1.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""

from pathlib import Path
import os 

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
...

Dodajemy naszą aplikację do zainstalowanych:

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'example_app'
]
...

I modyfikujemy sekcję DATABASES:

# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.getenv('POSTGRES_DB'),
        'USER': os.getenv('POSTGRES_USER'),
        'PASSWORD': os.getenv('POSTGRES_PASSWORD'),
        'HOST': os.getenv('POSTGRES_HOST'),
        'PORT': '',
    }
}
...

Następnie w katalogu głównym całego projektu tworzymy plik .env i uzupełniamy go adekwatnymi zmiennymi:

WSL Python Django Docker 25

Kolejno potrzebujemy, aby nasza aplikacja była uruchamiana w kontenerze, stąd należy utworzyć plik Dockerfile, aplikacja opiera się cała na pythonie, więc zaciągniemy gotowy obraz python:3.8-slim.

FROM python:3.8-slim

Kolejno na potrzebę kontenera tworzymy domyślnego użytkownika z odpowiednimi ograniczonymi uprawnieniami, spod którego aplikacja będzie uruchamiana.

ENV ENVIRONMENT docker
ENV NAME example
ENV HOME /home/$NAME
ENV APP /home/$NAME/app

RUN useradd --create-home --home-dir $HOME -u 1000 $NAME \
    && mkdir -p $HOME \
    && chown -R $NAME:$NAME $HOME

Aby aplikacja w kontenerze działała prawidłowo potrzebuje dostępu do podstawowych narzędzi, bo mogła uruchomić aplikację:

RUN ["pip", "install", "pipenv"]

Wskazujemy, gdzie skopiować pliki naszej aplikacji do katalogu roboczego w kontenerze. Aby to zrobić prawidłowo należy wpierw zmienić użytkownika example, a następnie skopiować pliki z przekazaniem uprawnień dla naszego użytkownika.

USER $NAME
COPY --chown=$NAME ./src $APP
WORKDIR $APP

Na koniec instalujemy zależności dla naszego projektu i uruchamiamy skrypt inicjalizujący i uruchamiający naszą aplikację.

RUN ["pipenv", "sync"]
CMD ["/bin/bash", "entrypoint.sh"]

Całość pliku kontenera powinna wyglądać mniej więcej tak:

FROM python:3.8-slim

ENV ENVIRONMENT docker
ENV NAME example
ENV HOME /home/$NAME
ENV APP /home/$NAME/app

RUN useradd --create-home --home-dir $HOME -u 1000 $NAME \
    && mkdir -p $HOME \
    && chown -R $NAME:$NAME $HOME

RUN ["pip", "install", "pipenv"]

USER $NAME
COPY --chown=$NAME ./src $APP
WORKDIR $APP

RUN ["pipenv", "sync"]
CMD ["/bin/bash", "entrypoint.sh"]

Teraz potrzebujemy napisać skrypt, który jak wyżej zostało wspomniane zainicjalizuje i uruchomi aplikację django. Potrzebujemy zmigrować modele bazodanowe i odpalić aplikację:

#!/bin/bash

cd example
pipenv run python manage.py migrate
pipenv run python manage.py runserver 0.0.0.0:8000

Ostatnie co zostało do zrealizowanie to dostarczenie bazy danych dla naszej aplikacji, tu z pomocą przyjdzie nam docker-compose, który pozwala na wyrażenie jakie zależności zachodzą pomiędzy poszczególnym kontenerami. Upraszczając opis tych zależności, nasza aplikacja w swoim kontenerze nie posiada bazy danych, stąd potrzebujemy utworzyć kolejny kontener, w którym będzie uruchomiana instancja bazy PostgreSQL. Całość opisujemy w pliku docker-compose.yml i umieszczamy go w głównym katalogu projektu.

# version: '3.4'

version: '3'
services:
  example_app:
    env_file:
      - .env # wskazanie ścieżki do pliku ze zmiennymi środowiskowymi
    restart: unless-stopped # warunek restartu kontenera, gdyby coś poszło nie tak
    build: . # ścieżka do obrazu
    image: example # nazwa obrazu
    volumes: # wskazanie katalogów, które mają być zachowane nawet po tym jeśli kontener zakończy pracę
      - ./src:/home/jgportal/app
    ports: # porty które mają być wystawione (tutaj port dla naszej aplikacji django)
      - "8000:8000"
    depends_on:
      - example_db_host # wskazanie kontenera, który ma wpierw być uruchomiony jako kontener zależny (nasza baza danych)
    networks:
      - example # wskazanie do jakiej sieci należy kontener
  example_db_host:
    env_file:
      - .env
    restart: unless-stopped
    image: postgres:12-alpine
    volumes:
      - 'postgres_data:/var/lib/postgresql/data/' # baza danych musi zachowywać dane nawet jeśli ją zatrzymamy i uruchomimy ponownie
    ports:
      - "5432:5432" 
    networks:
      - example # baza danych jest w tej samej sieci co nasza aplikacja

networks:
  example:

volumes:
  postgres_data:
  src:

Nareszcie na tym etapie pracy możemy sprawdzić owoce naszej pracy, by uruchomić całą usługę w głównym katalogu wpisujemy dwa polecenia:

$ docker-compose build
$ docker-compose up

Jeżeli wszystko poszło zgodnie z planem, powinniśmy otrzymać podobny rezultat:

WSL Python Django Docker 26

Dowiedz się jeszcze o inny przydatnych rzeczach o Dockerze tutaj!

Utrzymujemy Państwa dane w tajemnicy. Więcej informacji można znaleźć w naszej Polityce Prywatności.