Wstęp
Kontenery to temat na pewno nie nowy, natomiast w ostatnich latach bijący chyba rekordy popularności. Podczas tej rosnącej fali ja również postanowiłem sprawdzić, z czym to się je i w czym Docker może pomóc mi. W niniejszym serii postów chciałbym podzielić się z własnymi doświadczeniami, notatkami, fragmentami kodów oraz pokazać, w jaki sposób można wykorzystać Docker’a. Będzie to Docker z perspektywy osoby, która pracuje z danymi. Począwszy od ich gromadzenia, po ich przechowywanie, aż po ich przetwarzanie. Na pewno nie zabraknie samego Docker’a, SQL Server’a i Python’a przedstawionych w jak najbardziej praktyczny sposób. Zapraszam!
Przekazywanie parametrów do kontenera
Jednym z najbardziej powszechnych scenariuszy wydaje się być przekazywanie parametrów do kontenera. W przypadku Dockera nie jest to duży problem i odbywa się zupełnie tak samo jak w przypadku uruchamiania każdej innej aplikacji z linii poleceń. Jako przykład posłuży aplikacji z poprzedniego postu, do której został dodany fragment odpowiedzialny za pobranie jednego parametru (argumentu) i przypisanie go do zmiennej. W dalszej części zmienna ta posłuży do wyświetlenia odpowiedniej ilości postów.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import sys import requests from bs4 import BeautifulSoup response = requests.get('https://pl.seequality.net/feed/') post_counter = int(sys.argv[1]) if response.status_code == 200: feed = BeautifulSoup(response.content, features="lxml") for item in feed.find_all("item")[0:post_counter]: print ("{0} : {1}".format(item.pubdate.get_text(), item.title.get_text())) else: print ('An error has occurred.') |
Dockerfile pozostaje niezmieniony.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Use an official Python runtime as a parent image FROM python:latest # Set the working directory to /app WORKDIR /app # Copy the current directory contents into the container at /app COPY . /app # Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt # Make port 80 available to the world outside this container EXPOSE 80 # Define environment variable ENV NAME World # Define entry point ENTRYPOINT ["python", "app.py"] |
Lista niestandardowych bibliotek również nie ulegnie zmianie.
1 2 3 |
requests beautifulsoup4 lxml |
W następnym kroku możemy przystąpić do zbudowania obrazu. Komenda wygląda tak samo jak poprzednio. Parametry nie są bowiem zależne od obrazu, natomiast od kontenera, czyli od konkretnego uruchomienia skryptu lub aplikacji.
1 |
docker build --tag='get-seequality-posts-args' . |
Zmiana występuje zatem w komendzie uruchamiającej kontener. W tym miejscu możemy przekazać jeden lub więcej parametrów do kontenera.
1 2 |
docker run get-seequality-posts-args 1 docker run get-seequality-posts-args 3 |
Powyższy przykład pokazuje, że przekazywanie parametrów jest możliwe oraz proste. Aplikacja zwróciła różne wyniki zgodnie z przekazanym parametrem. Należy jedynie pamiętać, że parametry przekazywane są do aplikacji podczas uruchamiania kontenera, czyli de facto w chwili uruchamiania kolejnej instancji aplikacji. Obraz kontenera pozostaje natomiast “uniwersalnym” obrazem.
Oczywiście istniałaby również możliwość skorzystania z dodatkowych bibliotek jak argparse w samym skrypcie, użycia parametru tekstowego lub większej ilości parametrów.
Przykładowo:
1 2 |
docker run nazwa_obrazu "wartość parametru tekstowego" docker run nazwa_obrazu "wartość parametru 1" "wartość parametru 2" 3 |
Domyślne wartości parametrów
Podczas próby uruchomienia kontenera z tego samego obrazu bez argumentu kontener zwróci błąd ze względu na brak argumentu.
1 |
docker run get-seequality-posts-args |
Oczywiście osobną kwestią jest prawidłowa obsługa takiego wyjątku w samej aplikacji, natomiast inną kwestią jest możliwość zdefiniowania domyślnych wartości parametrów w samym pliku DockerFile. W tym przykładzie jedyna zmiana następuje właśnie w tym pliku (zarówno kod aplikacji jak i lista zależności się nie zmienia).
Nowy plik DockerFIle wygląda następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# Use an official Python runtime as a parent image FROM python:latest # Set the working directory to /app WORKDIR /app # Copy the current directory contents into the container at /app COPY . /app # Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt # Make port 80 available to the world outside this container EXPOSE 80 # Define environment variable ENV NAME World # Define entry point ENTRYPOINT ["python", "app.py"] # Define default paramer value CMD ["5"] |
1 |
docker build --tag='get-seequality-posts-args2' . |
Obecnie podczas uruchomienia skryptu oraz podania parametru skrypt działa dokładnie tak samo jak poprzednio, natomiast w chwili, gdy parametr nie zostanie podany, zostanie wykorzystany domyślny parametr zdefiniowany w pliku DocerFile.
1 2 3 |
docker run get-seequality-posts-args2 1 docker run get-seequality-posts-args2 3 docker run get-seequality-posts-args2 |
Jak zostało zauważone w realnym przykładzie takie wyjątki powinny być obsłużone również w kodzie skryptu lub aplikacji, natomiast warto pamiętać o możliwości definiowania domyślnych wartości parametrów w tym miejscu.
Argparse
Powyższy przykład prezentuje tylko schemat działania i przekazywanie parametrów nie ogranicza się wyłącznie do tego co zostało pokazane, natomiast będzie działać również w innych scenariuszach. Przykładowo można zapewnić wsparcie dla “argparse”. Kod aplikacji został poszerzony o wykorzystanie właśnie tej biblioteki i obsługę parametrów z jej wykorzystaniem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import sys import requests from bs4 import BeautifulSoup import argparse response = requests.get('https://pl.seequality.net/feed/') parser = argparse.ArgumentParser(description='Simple script to get last N posts from seequality.net') parser.add_argument('-pc','--post_counter', type=int, help='Post counter - number of posts to get', required=True) args = parser.parse_args() post_counter = args.post_counter if response.status_code == 200: feed = BeautifulSoup(response.content, features="lxml") for item in feed.find_all("item")[0:post_counter]: print ("{0} : {1}".format(item.pubdate.get_text(), item.title.get_text())) else: print ('An error has occurred.') |
Nastepnie podobnie jak w poprzednim przykładzie istnieje możliwość zdefiniowania domyślnych parametrów w pliku DocerFile.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# Use an official Python runtime as a parent image FROM python:latest # Set the working directory to /app WORKDIR /app # Copy the current directory contents into the container at /app COPY . /app # Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt # Make port 80 available to the world outside this container EXPOSE 80 # Define environment variable ENV NAME World # Define entry point ENTRYPOINT ["python", "app.py"] # Define default parameter values CMD ["--post_counter=5"] |
Komenda do budowy pakietu nie ulega zmianie.
Następnie podczas uruchomienia kontenera:
Podczas uruchomienia istnieje również możliwość wykorzystania “argparse”. Docker zapewnia zatem łatwą obsługę parametrów i szerokie wsparcie w tym obszarze. W tym poście zostało to pokazane na przykładzie języka Python, natomiast podobnie będzie to działać dla innych aplikacji.
Zakończenie
Kontenery i Docker dla “amatora danych” mogą okazać się bardzo przydatne, nawet do zastosować w domu (nieprodukcyjnych). Osobiście aplikacje piszę najpierw na komputerze (zwykle język Python), następnie w chwili gdy już są przetestowane oraz sprawnie działają przygotowuję obraz w Dockerze dla takiej aplikacji i następnie uruchamiam aplikację w kontenerze w NAS albo na VPS, który można stosunkowo tanio znaleźć w sieci. Według mnie ułatwia to pracę osoby, która pracuje (hobbystycznie, ale nie tylko) z danymi pomijając już kwestię bardzo szerokiego wykorzystania Dockera w zastosowaniach czysto projektowych i cyklu rozwijania różnego rodzaju aplikacji. Oczywiście jest to tylko wstęp do tej tematyki, a kolejne tematy zostaną poruszone już wkrótce.
Uwaga
Nie jestem z całą pewnością ekspertem w zakresie Dockera oraz Pythona, a wpis ten traktuję bardziej jako zapis moich notatek i prób, które być może przydadzą się również Tobie. W przypadku jakichkolwiek błędów i usprawnień bardzo zapraszam do komentowania lub kontaktu.
- Docker dla amatora danych – Tworzenie środowiska (VM) w Azure i VirtualBox (skrypt) - April 20, 2020
- Power-up your BI project with PowerApps – materiały - February 5, 2020
- Docker dla “amatora” danych – kod źródłowy do prezentacji - November 18, 2019
Ostatnie komentarze