Jak zbudować lokalne środowisko do data science: Anaconda, Docker czy chmura?

0
6
Rate this post

Z tej publikacji dowiesz się...

Po co w ogóle myśleć o środowisku data science, zanim powstanie pierwszy model

Chaos startowy i jego skutki po kilku miesiącach

Pierwszy kontakt z data science zwykle wygląda podobnie: szybka instalacja Pythona, kilku bibliotek, Jupytera, byle tylko udało się uruchomić pierwszy notebook. Przez pierwsze dni lub tygodnie wszystko działa, modele liczą się poprawnie, a jedyny problem to raczej błędy w kodzie niż w konfiguracji. Po kilku miesiącach sytuacja co do zasady się zmienia: wersje bibliotek przestają do siebie pasować, nowy projekt wymaga innego TensorFlow niż poprzedni, a próba przeinstalowania jednej paczki psuje trzy inne.

Taki „chaos startowy” ma jeszcze jeden wymiar: brak zapisu tego, w jakim środowisku powstały dane wyniki. Po pół roku powrót do starego projektu staje się loterią. Co dokładnie było zainstalowane? Jaka wersja Pythona? Jaką wersję scikit-learnu użyto do trenowania modelu, który klient chwalił na prezentacji? Bez uporządkowanego środowiska odpowiedź wymaga wielu prób i błędów, a czasem jest po prostu niemożliwa.

Drugą konsekwencją jest utrudniona współpraca. Gdy jedna osoba odpala notebook bez problemu, a druga dostaje błędy zależności, projekt zaczyna się ślizgać. Debugowanie „u mnie działa” zamiast skupienia na modelu spala godziny, które można by przeznaczyć na analizę danych czy tuning hiperparametrów.

Środowisko „na szybko” kontra środowisko odtwarzalne

Środowisko „na szybko” to zestaw pakietów instalowanych w losowej kolejności: pip install tu, conda install tam, czasem coś zainstalowane ręcznie z GitHuba. Dopóki projekty są małe, a liczba zależności ograniczona, takie podejście wydaje się wystarczające. Problem pojawia się przy pierwszym większym skoku: nowy projekt, nowa biblioteka, konieczność powrotu do starego kodu na innej maszynie lub u innego członka zespołu.

Środowisko, które da się odtworzyć, działa odwrotnie. Zamiast „instalować, aż zadziała”, zaczyna się od jawnej specyfikacji: plik requirements.txt, environment.yml czy Dockerfile. To ta specyfikacja jest punktem odniesienia, a instalacja środowiska jest w zasadzie automatyczna. W praktyce oznacza to możliwość uruchomienia projektu:

  • na nowym komputerze,
  • na serwerze klienta,
  • na instancji chmurowej,
  • po kilku miesiącach przerwy,

bez ręcznego zgadywania, co było wgrane. Różnica między obiema filozofiami mocno wpływa na tempo pracy, szczególnie gdy projekt dojrzewa lub staje się częścią większego systemu.

Trzy warstwy pracy data scientysty: kod, dane, modele

Środowisko data science spina technicznie trzy kluczowe warstwy:

  • kod – skrypty, notebooki, biblioteki pomocnicze,
  • dane – pliki CSV, bazy danych, dane strumieniowe, dane w chmurze,
  • modele – wytrenowane artefakty, często zapisane jako pliki binarne lub zestawy parametrów.

Te warstwy są ze sobą ściśle powiązane. Ten sam kod może dać inne wyniki, jeśli zmieni się wersja biblioteki, format danych wejściowych albo sposób ładowania modelu. Środowisko, które jest świadomie zaprojektowane, umożliwia kontrolę nad tymi powiązaniami. Docelowo chodzi o sytuację, w której można odpowiedzieć na pytanie: „w jakich dokładnie warunkach powstał ten wynik, ten model lub ten wykres?”.

Anaconda, Docker i chmura rozwiązują ten problem na różnych poziomach. Anaconda ogarnia głównie warstwę kodu i bibliotek na jednej maszynie. Docker pakuje całe środowisko wykonawcze w kontener, łącznie z systemem i zależnościami systemowymi. Chmura dokłada skalę, infrastrukturalne usługi i często gotowe platformy MLOps, ale w wielu scenariuszach i tak opiera się na anacondzie lub dockerze pod spodem.

Kryteria wyboru środowiska: przewidywalność, powtarzalność, skala, koszt

Przy wyborze podejścia do środowiska data science warto jasno ustalić podstawowe kryteria:

  • Przewidywalność – czy po aktualizacji pakietu lub przeniesieniu na inną maszynę jesteśmy w stanie sensownie przewidzieć zachowanie systemu?
  • Powtarzalność – czy ten sam kod na tych samych danych da te same wyniki za tydzień lub miesiąc?
  • Skalowalność – czy środowisko nadaje się do pracy nie tylko na laptopie, ale też na serwerze GPU albo w klastrze chmurowym?
  • Koszt – ile czasu pochłania utrzymanie środowiska i jakie są realne koszty finansowe (np. licencje, chmura, zasoby sprzętowe)?

Anaconda zapewnia zwykle szybki start i niezłą przewidywalność na jednym komputerze, Docker daje wysoką powtarzalność i lepszą drogę do skalowania, a chmura otwiera możliwości, których lokalny sprzęt często nie osiągnie (szczególnie przy wielu GPU, dużej pamięci czy dużej liczbie jednoczesnych eksperymentów). W praktyce data scientist przechodzi często przez wszystkie te etapy – od lokalnej anacondy, przez dockera, aż po środowisko chmurowe.

Kluczowe wymagania wobec środowiska do data science

Wsparcie dla języków i bibliotek używanych w praktyce

W typowym środowisku data science rdzeń stanowią języki programowania i biblioteki:

  • Python – de facto standard w machine learning i deep learning,
  • R – popularny w statystyce, analizach eksploracyjnych, środowiskach akademickich,
  • Scikit-learn – klasyczne algorytmy ML,
  • TensorFlow, Keras, PyTorch – główne frameworki do uczenia głębokiego,
  • Pandas, NumPy – fundamenty pracy z danymi tabelarycznymi i macierzami,
  • Matplotlib, Seaborn, Plotly – wizualizacje, raporty, dashboardy,
  • Jupyter Notebook / JupyterLab – interaktywna praca, dokumentacja eksperymentów.

Środowisko, które ma sens długoterminowo, powinno umożliwiać instalację i utrzymanie tych komponentów w spójnej konfiguracji. Wybrana technologia (Anaconda, Docker, chmura) nie tyle zastępuje te narzędzia, co dostarcza mechanizmy do ich przewidywalnej instalacji, izolacji i aktualizacji.

Jeżeli na jednym komputerze prowadzonych jest kilka projektów, zwykle pojawia się potrzeba posiadania jednocześnie różnych wersji tych samych bibliotek lub nawet różnych wersji Pythona. Bez świadomego podejścia do środowisk oznacza to duże ryzyko konfliktów. Stąd tak duże znaczenie mają wirtualne środowiska Conda lub kontenery Dockera.

Izolacja projektów i unikanie konfliktów zależności

Data scientist rzadko pracuje nad jednym projektem w oderwaniu od innych. Zazwyczaj równolegle toczą się:

  • proste analizy eksploracyjne,
  • projekty pilotażowe z nowymi bibliotekami,
  • długotrwałe projekty badawczo-rozwojowe,
  • utrzymanie starych modeli, które trafiły do produkcji.

Każdy z tych elementów może mieć inne wymagania co do wersji bibliotek. Klasyczny przykład: jeden projekt wymaga najnowszego PyTorcha, inny – starej wersji TensorFlow, a jeszcze inny – specyficznej wersji scikit-learn zgodnej z modelem wdrożonym rok wcześniej. Gdy wszystko mieszka w jednym „globalnym” Pythonie, konflikty są nieuniknione.

Izolacja projektów oznacza, że każdy projekt ma swoje własne środowisko biblioteczne – niezależne od innych. Może to być:

  • osobne środowisko Conda dla każdego projektu,
  • osobny kontener Docker dla każdego projektu lub grupy projektów,
  • w chmurze – osobne environmenty w ramach platformy ML (np. Vertex AI, SageMaker), ale pod spodem i tak często w oparciu o kontenery.

Takie podejście zmniejsza ryzyko, że instalacja nowej biblioteki „zepsuje” kilka innych projektów. Zwiększa też odtwarzalność: środowisko projektu można opisać raz i przywrócić w razie potrzeby.

Reproducibility – odtwarzalność wyników i wymogi biznesowe

Odtwarzalność (ang. reproducibility) to zdolność do uzyskania tych samych wyników z tego samego kodu i danych, nawet po czasie lub na innej maszynie. Dla hobbystycznych projektów nie zawsze jest to priorytet, ale w środowisku profesjonalnym ma duże znaczenie:

  • klienci i przełożeni oczekują wyjaśnienia, jak powstały wyniki,
  • audyty wymagają możliwości prześledzenia procesu budowy modelu,
  • w systemach produkcyjnych trzeba mieć pewność, że aktualizacja modelu nie zaskoczy nieprzewidzianym zachowaniem.

Odtwarzalność środowiska to nie tylko zapis wersji bibliotek. Chodzi również o:

  • dokładne wersje Pythona i systemu operacyjnego,
  • konfigurację GPU / sterowników (szczególnie w deep learning),
  • zależności systemowe (np. biblioteki C, systemowe narzędzia).

Docker daje tu dużą przewagę nad „gołym” Pythonem – w kontenerze można spakować cały system i wszystkie zależności. Anaconda w połączeniu z plikami environment.yml lub requirements.txt również pozwala kierować wersjami, choć w mniejszym stopniu kontroluje warstwę systemową.

Wydajność: CPU, GPU i dostęp do danych

Środowisko data science musi nadążać nie tylko za wymaganiami bibliotecznymi, ale również za zasobami sprzętowymi. W praktyce można wyróżnić kilka obszarów:

  • CPU – projekty oparte o klasyczne algorytmy ML i analizy eksploracyjne intensywnie korzystają z procesora,
  • GPU – trenowanie sieci neuronowych, szczególnie modeli głębokich, wymaga efektywnego korzystania z kart graficznych,
  • dysk – praca z dużymi zbiorami danych wymaga wysokiej przepustowości i odpowiedniej organizacji struktury katalogów,
  • dostęp do danych zdalnych – bazy danych, hurtownie danych, zasoby w chmurze.

Lokalne środowisko na laptopie z Anacondą sprawdzi się w wielu przypadkach edukacyjnych i projektach o umiarkowanej skali. Jeżeli jednak pojawia się potrzeba uczenia dużych modeli, często pojawia się pytanie: GPU lokalnie czy w chmurze? Możliwość skorzystania z gotowych instancji GPU (np. w AWS, GCP, Azure) znacząco przyspiesza pracę, ale wymaga częściej użycia Dockera lub specyficznych środowisk chmurowych, które i tak pod spodem korzystają z konteneryzacji.

Integracja z Git, narzędziami eksperymentalnymi i CI/CD

Środowisko pracy data scientistki/data scientysty nie kończy się na Pythonie i Jupyterze. Coraz częściej w projektach pojawiają się:

  • Git – wersjonowanie kodu, branchowanie eksperymentów, code review,
  • narzędzia do śledzenia eksperymentów (MLflow, Weights & Biases, Neptune.ai) – zapis metryk, hiperparametrów, wersji kodu,
  • CI/CD – automatyczne testy, budowanie obrazów Docker, wdrażanie modeli na serwery lub do chmury.

Środowisko dobrze zaprojektowane pod data science powinno z nimi współgrać. Kontenery Docker bardzo łatwo integrują się z pipeline’ami CI/CD, co czyni je naturalnym wyborem przy przechodzeniu z fazy eksperymentów do fazy produkcyjnej. Anaconda z kolei dobrze sprawdza się w fazie eksploracyjnej – tu zwykle wystarczy lokalne środowisko z Gitem i prostym narzędziem do zapisu eksperymentów.

Anaconda – szybki start w świecie Pythona i R

Co to jest Anaconda i kiedy ma sens

Anaconda to dystrybucja Pythona i R wyposażona w menedżera pakietów conda oraz mechanizm tworzenia izolowanych środowisk. Obejmuje duży zestaw bibliotek używanych w data science i machine learning, a także narzędzia takie jak Jupyter Notebook, JupyterLab czy Spyder. Instalacja Anacondy na laptopie pozwala w krótkim czasie uruchomić kompletne środowisko do eksploracji danych.

W praktyce Anaconda ma szczególny sens w następujących scenariuszach:

  • samodzielna nauka Pythona i data science,
  • prototypowanie modeli na własnym komputerze,
  • warsztaty, kursy i szkolenia, gdzie uczestnicy mają szybko dojść do „pierwszego notebooka”,
  • indywidualna praca analityczna bez skomplikowanych wymagań produkcyjnych.

W odróżnieniu od „gołego” Pythona instalowanego z oficjalnej strony, Anaconda od razu dostarcza spójny zestaw pakietów związanych z analizą danych. Zamiast instalować Pandas, NumPy, Matplotlib i Jupytera osobno, użytkownik dostaje gotowy pakiet. Menedżer conda potrafi też zarządzać zależnościami binarnymi (np. bibliotekami C), co przy bardziej złożonych pakietach jest znaczącym ułatwieniem.

W większych organizacjach Anaconda przydaje się również jako standard „na biurku” – z góry wiadomo, jakie narzędzia są dostępne, jak je aktualizować i jak odtworzyć środowisko na innym komputerze. Z kolei w małych zespołach pozwala uniknąć sytuacji, w której każda osoba instaluje wszystko „po swojemu”, a potem trudno wspólnie debugować problemy czy odtwarzać wyniki.

Istotne jest jednak świadome korzystanie z możliwości, jakie daje conda. Praca wyłącznie w globalnym środowisku Anacondy prowadzi po pewnym czasie do typowego „dependency hell” – jedne projekty wymagają aktualizacji, inne przestają działać po zmianie wersji kluczowej biblioteki. Bez praktyki tworzenia odrębnych environmentów i zapisywania ich konfiguracji (np. w environment.yml) traci się jedną z głównych zalet tej dystrybucji, czyli kontrolę nad wersjami i odtwarzalnością.

W prostym, jednoosobowym scenariuszu można oprzeć się wyłącznie na Anacondzie i nie korzystać z Dockera. Dobrze zdefiniowane środowiska conda, zapisane w repozytorium Gita, w wielu projektach analitycznych będą w zupełności wystarczające. W miarę jak pojawiają się wymagania dotyczące standaryzacji środowisk na serwerach, integracji z CI/CD lub ścisłego odwzorowania konfiguracji systemu produkcyjnego, naturalnym krokiem jest stopniowe dokładanie Dockera – na początku choćby tylko do uruchamiania gotowych notebooków lub serwisów API.

Wybór pomiędzy Anacondą, Dockerem a środowiskiem w chmurze zwykle nie jest decyzją „albo–albo”. W praktyce wiele zespołów łączy te rozwiązania: lokalnie korzysta z Anacondy do eksperymentów i szybkiej pracy z danymi, a do wdrażania i utrzymania modeli wykorzystuje kontenery Docker uruchamiane na serwerach lub w chmurze. Taki podział ról pozwala zachować wygodę na etapie eksploracji i jednocześnie spełnić wymagania dotyczące skalowalności, bezpieczeństwa i odtwarzalności w środowiskach produkcyjnych.

Tworzenie i zarządzanie środowiskami w Anacondzie – dobre praktyki

Najwięcej korzyści z Anacondy pojawia się wtedy, gdy środowiska są tworzone świadomie, a nie „przy okazji” instalowania kolejnych pakietów. Kluczową zasadą jest: jeden projekt – jedno środowisko (czasem jedno środowisko na rodzinę blisko spokrewnionych projektów).

W praktyce wygodny schemat pracy wygląda tak:

  1. Tworzenie nowego środowiska:
    conda create -n myproject python=3.11
  2. Aktywacja środowiska:
    conda activate myproject
  3. Instalacja podstawowych pakietów:
    conda install numpy pandas scikit-learn jupyterlab
  4. Eksport konfiguracji do pliku:
    conda env export > environment.yml

Plik environment.yml warto trzymać w repozytorium razem z kodem. Druga osoba (albo my sami po kilku miesiącach) może wówczas odtworzyć środowisko poleceniem:

conda env create -f environment.yml

Taki plik bywa jednak bardzo szczegółowy (zawiera pełną listę zależności, w tym techniczne pakiety conda). W mniejszych projektach wygodny bywa kompromis: ręcznie utrzymywany environment.yml z listą głównych bibliotek (numpy, pandas, pytorch) i zaufaniem, że „drobne” zależności pobierze sam conda.

Łączenie conda i pip – jak nie wpaść w konflikt zależności

W wielu projektach nie da się całkowicie uniknąć Pypi i pip. Część mniej popularnych bibliotek jest dostępna wyłącznie jako paczki pip, bez odpowiedników w repozytoriach conda. Pojawia się wówczas pytanie, jak łączyć oba światy bez ryzyka dużych konfliktów.

Dość bezpieczny schemat wygląda następująco:

  • najpierw tworzone jest środowisko conda z kluczowymi bibliotekami (Python, NumPy, Pandas, PyTorch/TensorFlow itp.),
  • następnie, już po aktywowaniu środowiska, instalowane są brakujące pakiety poleceniem pip install ...,
  • w pliku environment.yml można dodać sekcję pip:, aby odtworzenie środowiska uwzględniało również te zależności.

Przykładowy fragment pliku:

name: myproject
dependencies:
  - python=3.11
  - numpy
  - pandas
  - pip
  - pip:
    - great-expectations
    - azure-storage-blob

Zwykle warto unikać instalowania tych samych bibliotek raz przez conda, a raz przez pip w ramach jednego środowiska. Jeśli pakiet jest dostępny w conda, lepiej trzymać się tej drogi – szczególnie gdy wymaga skompilowanych rozszerzeń C/C++ lub integracji z systemowymi bibliotekami.

Typowe problemy z Anacondą i jak im zapobiegać

Przy dłuższej pracy z Anacondą pojawiają się dość powtarzalne trudności, których da się uniknąć przy odrobinie dyscypliny. Najczęściej spotykane sytuacje to:

  • zbyt duży rozmiar środowisk – wiele środowisk z kopiami tych samych bibliotek zajmuje dużo miejsca na dysku,
  • „globalna Anaconda” z setkami pakietów – wszystko instalowane w jednym środowisku bazowym, bez izolacji projektów,
  • długi czas rozwiązywania zależności – skomplikowane kombinacje wersji pakietów skutkują długą pracą solvera conda.

Praktyczne środki zaradcze są dość proste:

  • regularne usuwanie nieużywanych środowisk (conda env remove -n nazwa),
  • nieinstalowanie niczego w środowisku bazowym (base) poza absolutnym minimum,
  • świadome ograniczanie liczby bibliotek w każdym środowisku do rzeczywiście potrzebnych w projekcie,
  • korzystanie z mamba (szybszy zamiennik dla conda) przy dużych środowiskach.

W większych firmach pojawia się często dodatkowa praktyka: utrzymywanie „referencyjnych” plików environment.yml dla typowych typów projektów (np. „analiza klasyczna”, „deep learning”, „NLP”), aby każdy nowy projekt zaczynał od sprawdzonej bazy, a nie od zera.

Docker – środowisko zapakowane w kontener

Na czym polega konteneryzacja w kontekście data science

Docker umożliwia spakowanie całego środowiska uruchomieniowego w obraz (ang. image): od systemu operacyjnego, przez Pythona i biblioteki, aż po własny kod. Kontener to uruchomiona instancja takiego obrazu. Dla data science ma to dwie główne konsekwencje:

  • środowisko staje się ściśle powtarzalne – ten sam obraz uruchomiony na laptopie, serwerze i w chmurze zachowuje się tak samo (pomijając różnice sprzętowe),
  • konfiguracja systemowa jest częścią projektu – w pliku Dockerfile można zdefiniować wszystko: wersję systemu, menedżera pakietów, wersję Pythona, bibliotek systemowych, sterowników.

W odróżnieniu od Anacondy, która skupia się na zarządzaniu środowiskami języków (Python, R), Docker obejmuje całe „pudełko”, w którym uruchamiany jest program. To daje znaczną kontrolę, ale wymaga odrobinę więcej wiedzy systemowej i infrastrukturalnej.

Prosty obraz Docker dla projektu data science

Typowy punkt wyjścia to użycie oficjalnych obrazów z Pythonem lub obrazów przygotowanych z myślą o data science. Przykładowy, uproszczony Dockerfile dla projektu opartego o Pythona i JupyterLab może wyglądać tak:

FROM python:3.11-slim

# Ustawienie katalogu roboczego
WORKDIR /app

# Instalacja systemowych zależności (przykład)
RUN apt-get update && apt-get install -y --no-install-recommends 
    build-essential 
    && rm -rf /var/lib/apt/lists/*

# Kopiowanie plików z zależnościami
COPY requirements.txt ./

# Instalacja bibliotek Pythona
RUN pip install --no-cache-dir -r requirements.txt

# Kopiowanie pozostałego kodu
COPY . .

# Domyślne polecenie: uruchomienie JupyterLab
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root"]

Po zbudowaniu obrazu:

docker build -t myproject:latest .

można uruchomić kontener na lokalnym komputerze:

docker run -it --rm -p 8888:8888 -v $(pwd):/app myproject:latest

W tym przykładzie:

  • -p 8888:8888 mapuje port kontenera na port lokalny,
  • -v $(pwd):/app montuje bieżący katalog jako wolumen, dzięki czemu pliki notatników i kodu pozostają na dysku hosta, a nie znikają z chwilą usunięcia kontenera.

Łączenie Dockera z Anacondą – kiedy ma to sens

Wbrew pozorom wybór „Docker czy Anaconda” często okazuje się fałszywą alternatywą. Popularny wzorzec to wykorzystanie obrazu opartego na Anacondzie. Pod spodem wciąż działa Linux, ale środowiskami Python/R zarządza conda. Przykład:

FROM continuumio/miniconda3

WORKDIR /app

# Kopiujemy plik environment.yml
COPY environment.yml ./

# Tworzymy środowisko conda
RUN conda env create -f environment.yml

# Ustawiamy domyślne środowisko
SHELL ["conda", "run", "-n", "myproject", "/bin/bash", "-c"]

COPY . .

CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root"]

Takie połączenie bywa szczególnie użyteczne w zespołach, które już mają ustandaryzowane pliki environment.yml, a chcą zacząć uruchamiać projekty na serwerach z Dockerem. Zamiast przepisywać środowiska na requirements.txt, można wykorzystać istniejącą wiedzę i narzędzia conda.

Typowe wzorce korzystania z Dockera w cyklu życia projektu

W projektach data science pojawiają się różne style korzystania z kontenerów. Kilka z nich powtarza się dość regularnie.

1. Docker tylko na etapie wdrożenia
Zespół pracuje lokalnie w Anacondzie lub „gołym” Pythonie, a kontenery pojawiają się dopiero w momencie przygotowania modelu do wdrożenia. Powstaje osobny repozytorium lub katalog „deployment” z Dockerfile i minimalnym kodem serwisowym (np. FastAPI, Flask). Taki scenariusz jest prosty dla data scientistów, ale czasem odsłania problemy dopiero na etapie przejścia do produkcji (różnice w wersjach bibliotek, brakujące zależności systemowe).

2. Docker jako główne środowisko pracy
W niektórych zespołach każda osoba uruchamia lokalnie te same obrazy, co na serwerach. Notebooki odpalane są w kontenerach, a dane i kod montowane jako wolumeny. Tego typu podejście:

  • niemal eliminuje różnice między środowiskiem „development” a „production”,
  • ułatwia odtwarzalność eksperymentów i diagnozowanie błędów,
  • wymaga jednak biegłości w Dockerze i dobrej dokumentacji dla nowych osób.

3. Kontenery jako „opakowanie” gotowych narzędzi
Część narzędzi (np. MLflow, lokalne bazy danych, serwery MinIO) jest uruchamiana w kontenerach, ale sam kod eksperymentalny działa w lokalnym środowisku Anacondy. To kompromis: infrastruktura wspierająca projekt jest konteneryzowana, natomiast data scientistki i data scientiści utrzymują swoje środowiska tak, jak im wygodnie.

Praca z danymi w kontenerach – wolumeny i bezpieczeństwo

Sposób obchodzenia się z danymi w kontenerach wymaga chwili namysłu. Dane zwykle nie powinny być wbudowane w obraz Dockera (szczególnie jeśli obraz jest wysyłany do zewnętrznego registry), lecz przechowywane osobno. Najczęstsze sposoby to:

  • wolumeny lokalne – katalog na hoście montowany do kontenera (-v /data:/data),
  • zdalne zasoby – kontener łączy się do bazy danych, hurtowni, S3/Blob Storage itp.,
  • systemy plików w chmurze (np. GCS Fuse, EFS, Azure Files) – montowane jako wolumeny do kontenerów uruchamianych na klastrach Kubernetes lub usługach typu ECS/AKS/GKE.

W praktyce wrażliwe dane produkcyjne są zwykle dostępne tylko z określonych sieci lub kontenerów uruchamianych na zaufanej infrastrukturze. Lokalna praca z danymi często wymaga pseudonimizacji, maskowania lub korzystania z wycinków danych. Obrazy Dockera nie powinny zawierać kluczy dostępowych „na stałe” – bezpieczniej przekazywać je jako zmienne środowiskowe (-e) lub korzystać z menedżerów sekretów (np. HashiCorp Vault, AWS Secrets Manager).

GPU, CUDA i obrazy specjalistyczne

Dla projektów wykorzystujących deep learning kluczowa jest możliwość korzystania z GPU. Sam Docker nie rozwiązuje tematu – potrzebny jest dodatkowy komponent (np. nvidia-container-toolkit) oraz odpowiednie obrazy bazowe. Przykładowy wzorzec:

  • na maszynie hosta instalowane są sterowniki NVIDIA oraz biblioteki CUDA zgodne z wymogami projektu,
  • Docker korzysta z obrazów typu nvidia/cuda:12.0.0-cudnn8-runtime-ubuntu22.04, na których budowane są środowiska Pythona,
  • kontenery uruchamiane są z uprawnieniami do korzystania z GPU (np. --gpus all).

W środowiskach chmurowych wiele z tych kroków jest zautomatyzowanych: dostawcy udostępniają gotowe obrazy z odpowiednimi sterownikami i bibliotekami. Własny Dockerfile opiera się wówczas o taki obraz bazowy, ograniczając się do instalacji pakietów Pythona i kodu projektu. Ułatwia to zarządzanie kompatybilnością, ale wymaga synchronizacji wersji między lokalnym środowiskiem a chmurą, jeżeli model ma być trenowany w obu miejscach.

Integracja Dockera z pipeline’ami CI/CD i eksperymentami

W momencie, gdy projekt wychodzi poza etap wstępnych eksperymentów, pojawia się kwestia powtarzalnego budowania obrazów i uruchamiania testów. Typowy pipeline w systemie CI (np. GitLab CI, GitHub Actions, Azure DevOps) obejmuje:

  1. pobranie kodu z repozytorium,
  2. zbudowanie obrazu Dockera na podstawie Dockerfile,
  3. uruchomienie testów jednostkowych/integracyjnych wewnątrz kontenera,
  4. weryfikację bezpieczeństwa zależności (np. skanowanie obrazu),
  5. wysłanie obrazu do registry (Docker Hub, ECR, GCR, ACR).

Dobrze zaprojektowany pipeline można rozszerzyć o automatyczne wersjonowanie obrazów (np. tagi z numerem wersji aplikacji oraz skrótem commita) oraz o testy „smoke tests” po stronie środowiska docelowego. Przykładowo: po wypchnięciu nowego obrazu do registry system CI/CD uruchamia tymczasowy kontener na serwerze stagingowym, wykonuje kilka prostych zapytań do API modelu lub przykładowe predykcje i dopiero po pozytywnym wyniku dopuszcza wdrożenie na środowisko produkcyjne. Ogranicza to ryzyko sytuacji, w której obraz jest poprawnie zbudowany, lecz nie działa w realnej infrastrukturze (np. z uwagi na brakujące zmienne środowiskowe lub uprawnienia do zasobów).

Drugim, rosnącym zastosowaniem Dockera są zautomatyzowane eksperymenty. Obraz z modelem i zależnościami może być uruchamiany w klastrze (np. Kubernetes) w wielu wariantach hiperparametrów jednocześnie, przy czym każde uruchomienie korzysta z tej samej, spójnej warstwy systemowej. W połączeniu z narzędziami typu MLflow lub Metaflow da się zbudować pipeline, w którym commit w repozytorium kodu:

  • buduje nowy obraz eksperymentalny,
  • wyzwala serię zadań treningowych na określonej infrastrukturze (CPU/GPU),
  • rejestruje wyniki i artefakty w centralnym magazynie,
  • oznacza najlepszy wynik jako kandydata do wdrożenia.

W praktyce kluczowe jest rozdzielenie ról: data scientist projektuje logikę eksperymentu (kod, konfiguracje), a zespół odpowiedzialny za platformę ML przygotowuje wzorcowe obrazy bazowe, definicje pipeline’ów i integrację z CI/CD. Ułatwia to utrzymanie bezpieczeństwa i zgodności z politykami organizacji, jednocześnie nie blokując pracy badawczej. Tam, gdzie zespołu platformowego nie ma, podobny efekt można osiągnąć prostymi skryptami i lekkim CI (np. GitHub Actions), zaczynając od jednego czy dwóch krytycznych projektów, a dopiero potem przenosząc standard na kolejne repozytoria.

Ostateczny wybór między „gołą” Anacondą, Dockerem a chmurą rzadko bywa zero-jedynkowy. Często najlepiej sprawdza się połączenie: lokalne prototypowanie w conda, obrazy Dockera jako kontrakt między zespołami oraz chmurowe zasoby obliczeniowe dla ciężkich eksperymentów. Kluczowe jest, aby świadomie zdecydować, które elementy środowiska mają być stabilne i odtwarzalne, a które mogą pozostać elastyczne i „piaskownicowe” – wtedy narzędzia stają się wsparciem, a nie przeszkodą w pracy z danymi.

Stanowisko laboratoryjne z komputerem do analizy danych
Źródło: Pexels | Autor: Plato Terentev

Środowiska w chmurze – kiedy lokalne maszyny przestają wystarczać

Przy pewnej skali danych lub złożoności modeli lokalna Anaconda czy nawet rozbudowany Docker na stacji roboczej przestają być wystarczające. Pojawia się konieczność sięgnięcia po zasoby chmurowe – czy to w formie gotowych notebooków w przeglądarce, czy klastrów obliczeniowych zarządzanych przez zespół platformowy.

Środowiska w chmurze można podzielić na kilka głównych kategorii:

  • notebooki zarządzane (np. Google Colab, Azure ML Notebooks, SageMaker Studio) – użytkownik dostaje gotowy Jupyter w przeglądarce,
  • serwery obliczeniowe na żądanie (VM-ki z GPU/CPU) – środowisko (conda, Docker) konfigurowane jest samodzielnie,
  • platformy MLOps (np. Vertex AI, Azure ML, SageMaker, Kubeflow) – łączą trening, eksperymenty, rejestr modeli i wdrożenia.

Różnica nie polega wyłącznie na „mocniejszym sprzęcie”. Chmura narzuca inne wzorce pracy z danymi, automatyzuje część zadań (np. skalowanie, planowanie zadań) i wprowadza nowe ograniczenia – w szczególności kosztowe oraz związane z bezpieczeństwem.

Zarządzane notebooki – szybki start bez instalacji

Usługi typu Google Colab, Databricks Notebooks czy SageMaker Studio pozwalają zacząć pracę praktycznie od razu po zalogowaniu. Środowisko jest z góry przygotowane: zainstalowane są popularne biblioteki, dostępne są przykładowe projekty, a konfiguracja sprzętu (CPU/GPU, pamięć) sprowadza się do wyboru z listy.

W praktyce ten model jest używany szczególnie:

  • na etapie proof-of-concept, gdy liczy się czas startu,
  • w zespołach, w których część osób nie ma uprawnień do instalacji narzędzi na lokalnych komputerach,
  • wspólnie z danymi już przechowywanymi w chmurze (np. w Data Lake, S3, Blob Storage).

Kluczowym pytaniem jest wtedy, gdzie znajduje się „granica” między środowiskiem zarządzanym a własnym. Trzeba podjąć decyzję m.in.:

  • czy instalujemy biblioteki dynamicznie w każdym notebooku (np. pip install w komórce),
  • czy budujemy obrazy bazowe lub „environmenty” powiązane z usługą (np. w postaci własnych Dockerów lub predefiniowanych kernelów conda),
  • czy przenosimy kod do repozytorium Git i traktujemy notebook jako interfejs, a nie główne źródło prawdy.

W projektach komercyjnych zwykle dąży się do tego, aby:

  1. notebook był jedynie „klientem” – środowisko definiuje się w repozytorium (np. environment.yml, Dockerfile),
  2. zmiany w bibliotekach przechodziły przez review w Git, a nie spontaniczne instalacje w notebooku,
  3. konfiguracja zasobów (typ maszyny, liczba GPU) była parametryzowana i łatwa do odtworzenia.

Serwery w chmurze z własnym środowiskiem

Drugi model korzystania z chmury to „zwykłe” maszyny wirtualne (VM) lub instancje GPU/CPU, na których konfiguruje się Anacondę lub Dockera tak samo jak na lokalnym komputerze. Różnica polega głównie na tym, że:

  • sprzęt jest skalowalny i dostępny na żądanie,
  • do maszyny zwykle łączymy się zdalnie (SSH, VS Code Remote, przeglądarkowy Jupyter),
  • trzeba zadbać o porządek: wyłączanie nieużywanych instancji, automatyczne snapshoty, aktualizacje bezpieczeństwa.

Ten scenariusz jest popularny tam, gdzie zespół:

  • ma już wypracowany standard środowiska (np. pliki environment.yml, gotowe obrazy Dockera),
  • chce zachować maksymalną kontrolę nad konfiguracją,
  • korzysta z niestandardowych bibliotek systemowych, które trudno włączyć w pakiet „zarządzanych notebooków”.

W praktyce często powstaje „złoty obraz” – przygotowana wcześniej maszyna (lub obraz Dockera) z:

  • ustawioną conda / mambą i bazowym środowiskiem,
  • zainstalowanymi sterownikami GPU,
  • przygotowaną konfiguracją narzędzi (git, tmux, jupyter, proxy do hurtowni danych).

Nowe instancje są uruchamiane właśnie z takiego obrazu. Ogranicza to rozbieżności między serwerami oraz skraca czas przygotowania środowiska dla nowych osób w zespole. Dalej można wykorzystywać dokładnie te same pliki Dockerfile czy environment.yml, które stosowane są lokalnie, co pozwala płynnie przenosić eksperymenty między laptopem a chmurą.

Platformy MLOps a środowisko pracy

Platformy takie jak Azure Machine Learning, Amazon SageMaker, Vertex AI czy rozwiązania oparte o Kubernetes (np. Kubeflow, MLflow + własny klaster) wprowadzają dodatkową warstwę abstrakcji. Środowisko pojedynczej osoby przestaje być najważniejszym punktem odniesienia – ważniejszy staje się pipeline i standardowy format „jobu”, który ma działać na współdzielonej infrastrukturze.

Zwykle sprowadza się to do następującej struktury:

  1. data scientist tworzy kod treningu i ewaluacji (np. w Pythonie),
  2. kod jest pakowany w Dockerfile lub wskazuje na wzorcowy obraz bazowy przygotowany przez zespół platformowy,
  3. konfiguracja eksperymentu (parametry, typ maszyny, ścieżki do danych) jest opisana w plikach YAML lub w UI platformy,
  4. pipeline ML uruchamia kolejne kroki (przygotowanie danych, trening, ewaluacja, rejestracja modelu) na klastra-ch obliczeniowych.

W takim układzie środowisko lokalne pozostaje ważne na etapie prototypowania, ale później głównym „źródłem prawdy” jest definicja jobu i obraz Dockera (lub analogiczny artefakt). Jeżeli środowisko lokalne i chmurowe znacząco się różnią, skutkiem bywają trudne do diagnozy rozbieżności w wynikach – dlatego w dojrzałych zespołach dąży się do:

  • wykorzystywania tych samych obrazów bazowych lokalnie i na klastrze (np. przez Docker Desktop lub podpięcie się do tego samego registry),
  • maksymalnego ograniczenia „ręcznej” instalacji bibliotek w notebookach,
  • wspólnego katalogu standardowych środowisk (np. ml-python-3.10-sklearn, ml-python-3.11-torch-gpu), z których korzystają wszystkie projekty.

Jak wybierać między Anacondą, Dockerem i chmurą w konkretnych sytuacjach

Decyzja rzadko jest „albo–albo”. W typowym cyklu życia projektu te narzędzia się uzupełniają – kwestia w tym, aby świadomie określić ich role.

Mały zespół, pojedynczy projekt, brak wymogów produkcyjnych

Scenariusz spotykany np. w zespołach analitycznych w mniejszych organizacjach lub w działach R&D na wczesnym etapie. Założenia:

  • najważniejsza jest szybkość eksperymentów,
  • modele nie muszą od razu trafiać na produkcję w postaci serwisu 24/7,
  • dane pochodzą z kilku źródeł (plików, baz), ale nie ma rozproszonej infrastruktury.

W takich warunkach sensowny, pragmatyczny standard to:

  • lokalna Anaconda/mamba jako główne środowisko pracy,
  • jeden environment.yml na repozytorium, aktualizowany w ramach PR-ów,
  • brak Dockera na starcie, ewentualnie prosty Docker do okazjonalnego wdrożenia (np. wewnętrzne API czy dashboard).

W praktyce dopiero w momencie, gdy projekt zaczyna być używany przez inne działy (np. jako źródło raportów, scoringów), pojawia się potrzeba:

  • uporządkowania wersjonowania modeli,
  • przepisania fragmentu kodu na serwis HTTP lub pipeline ETL,
  • wprowadzenia Dockera jako formy „opakowania” całości.

Środowisko regulowane, audyty i wymóg odtwarzalności

W branżach regulowanych (finanse, farmacja, telekom) kwestia odtwarzalności środowiska i możliwości jednoznacznego wskazania, na jakich bibliotekach trenowany był model, nie jest tylko dobrym zwyczajem, ale często wymogiem. Z perspektywy środowiska skutkuje to kilkoma decyzjami:

  • wyraźne rozróżnienie między środowiskiem eksperymentalnym a tym, na którym trenowane są modele „produkcyjne”,
  • obowiązkowe wersjonowanie plików environment.yml lub Dockerfile wraz z kodem,
  • często – zakaz instalacji z niezaufanych źródeł (własny mirror PyPI, wewnętrzne registry Docker).

Typowa konfiguracja bywa następująca:

  1. lokalne środowisko: Anaconda + Docker do wstępnych testów, większa swoboda (z zastrzeżeniem polityk bezpieczeństwa),
  2. środowisko treningowe: ściśle kontrolowane obrazy Dockera, uruchamiane na wyznaczonym klastrze (on-prem lub w chmurze),
  3. środowisko produkcyjne: osobne obrazy lub te same z dodatkową warstwą konfiguracji, wdrażane przez formalny pipeline CI/CD.

Dla data scientistów oznacza to konieczność zmiany nawyków: kluczowe staje się utrzymywanie deklaratywnego opisu środowiska (Docker/conda) i ograniczenie „ręcznych” modyfikacji. W zamian zespół zyskuje pewność, że w razie kontroli jest w stanie wskazać dokładny stan środowiska treningowego dla konkretnego modelu.

Zespół rozproszony geograficznie i hybrydowa praca

Gdy osoby w zespole działają w różnych lokalizacjach, korzystają z odmiennych systemów operacyjnych i różnią się doświadczeniem technicznym, spójne środowisko staje się krytyczne nie tylko dla jakości kodu, ale też dla samej komunikacji. Znane są sytuacje, w których połowa spotkania statusowego sprowadzała się do tłumaczenia, czemu „u mnie działa, a u ciebie nie”.

Przy takich założeniach praktyczny układ to:

  • Docker jako domyślny „target” – nawet jeśli nie każdy używa go na co dzień lokalnie, jest wspólnym mianownikiem,
  • wspólne obrazy bazowe (zespół platformowy lub wyznaczona osoba je utrzymuje),
  • opcjonalnie: dla mniej technicznych osób gotowe środowiska w chmurze (np. zarządzane notebooki z tymi samymi obrazami).

W takiej konfiguracji osoba, która ma problem z lokalną instalacją, może przejść na środowisko chmurowe, ale i tak korzysta z tego samego obrazu Dockera i tego samego Dockerfile, co reszta zespołu. Różnica polega jedynie na tym, gdzie faktycznie uruchamiany jest kontener (lokalnie czy w chmurze).

Standaryzacja środowisk w zespole data science

Niezależnie od wybranego narzędzia, moment przejścia od „każdy ma swoje środowisko” do ustalonych standardów zwykle następuje szybciej, niż się początkowo zakłada. Już przy dwóch–trzech osobach pracujących nad tym samym projektem rozbieżności w wersjach bibliotek zaczynają generować koszty.

Wspólne katalogi środowisk i obrazów

Skutecznym rozwiązaniem jest utrzymywanie katalogu standardowych środowisk, który obejmuje:

  • podstawowe obrazy Dockera (np. ds-base, ds-gpu, ds-spark),
  • odpowiadające im pliki environment.yml lub listy pakietów,
  • opis zasad: co można do nich dodawać i w jakim trybie (np. PR-y z uzasadnieniem).

Każdy projekt może na nich bazować, rozszerzając je o specyficzne zależności. W ten sposób:

  • nowe repozytoria startują od czegoś sprawdzonego,
  • aktualizacje bezpieczeństwa (np. nowa wersja Pythona, łaty dla bibliotek systemowych) można wprowadzać „od góry” – do obrazów bazowych,
  • liczba kombinacji środowisk w organizacji pozostaje kontrolowana.

Minimalizm w zależnościach

Pokusa instalowania „na wszelki wypadek” dziesiątek pakietów w jednym środowisku jest zrozumiała, ale prowadzi do problemów z kompatybilnością i bezpieczeństwem. W praktyce zdarza się, że projekt ma realnie kilka bezpośrednich zależności, a reszta to dziesiątki pakietów zainstalowanych kiedyś do jednego notebooka.

Bezpieczniejsza praktyka to:

  • utrzymywanie możliwie małego bazowego środowiska,
  • tworzenie osobnych środowisk pod konkretne projekty lub klasy zadań (np. NLP, CV, klasyczne ML), zamiast jednego „uniwersalnego” kombajnu,
  • regularny przegląd zależności – co jakiś czas można przejść po environment.yml / requirements.txt i usunąć biblioteki, które nie są już używane,
  • jasna zasada w zespole: każdy nowy pakiet trafia do deklaratywnego opisu środowiska (a nie jest instalowany „na dziko” tylko na lokalnej maszynie).

Dobrym punktem odniesienia jest kod: jeśli w repozytorium nie ma żadnego importu do danej biblioteki, jej obecność w środowisku zwykle jest sygnałem do dyskusji. Przy projektach wieloletnich, przechodzących przez ręce kilku osób, takie okresowe „odchudzanie” bywa jedynym sposobem, aby środowiska nie rozrosły się do setek pakietów, których nikt już świadomie nie utrzymuje.

Automatyzacja tworzenia i aktualizacji środowisk

Ręczne odtwarzanie środowiska na podstawie instrukcji w pliku README prędzej czy później prowadzi do rozjazdów. Znacznie bezpieczniejszym podejściem jest traktowanie tworzenia środowiska jak część procesu budowania projektu. Przykładowo:

  • w repozytorium znajdują się skrypty typu make env lub scripts/setup_env.sh, które na podstawie environment.yml lub Dockerfile budują kompletne środowisko,
  • pipeline CI uruchamia testy w tym samym obrazie Dockera / środowisku conda, które deklaratywnie zdefiniował zespół,
  • dla nowych osób w zespole instrukcja sprowadza się do kilku powtarzalnych kroków, a nie listy komend zależnej od systemu operacyjnego.

W praktyce dobrze sprawdza się zasada, że „źródłem prawdy” jest zawsze definicja środowiska w repozytorium. Jeśli ktoś lokalnie doinstaluje pakiet, a nie zaktualizuje pliku environment.yml lub requirements.txt, to tak, jakby ten pakiet w projekcie nie istniał. Taka dyscyplina wymaga wyrobienia nawyku, ale znacząco zmniejsza liczbę niespodzianek przy uruchamianiu kodu na innej maszynie.

Testowanie kompatybilności i kontrolowane aktualizacje

Drugim filarem standaryzacji, obok minimalizmu, jest sposób aktualizowania bibliotek. Zbyt agresywne podnoszenie wersji potrafi sparaliżować pracę zespołu, ale całkowite zamrożenie środowisk również rodzi ryzyka (brak poprawek bezpieczeństwa, brak wsparcia dla nowych funkcji GPU czy frameworków). Rozsądny kompromis polega na:

  • utrzymywaniu „stabilnych” obrazów / środowisk dla istniejących projektów oraz „eksperymentalnych” dla testowania nowszych wersji,
  • wyznaczeniu okien aktualizacji (np. raz na kwartał), kiedy zespół świadomie podnosi wersje kluczowych bibliotek,
  • automatycznym uruchamianiu zestawu testów i kilku reprezentatywnych notebooków na nowym środowisku przed jego upublicznieniem.

W mniejszych zespołach rolę „strażnika środowisk” często pełni jedna osoba z większym doświadczeniem technicznym; w większych – dedykowany zespół platformowy. W obu przypadkach chodzi o to, by aktualizacje nie działy się przypadkiem, tylko jako przewidywalny, powtarzalny proces, którego efekty można odwrócić (np. powrót do poprzedniego obrazu Dockera).

Świadome podejście do środowiska – od prostego conda env na laptopie, przez obrazy Dockera, aż po zarządzane klastry w chmurze – decyduje o tym, czy praca z danymi jest ciągiem jednorazowych eksperymentów, czy powtarzalnym procesem, który można rozwijać latami. Nawet w małym zespole uporządkowanie tych kwestii zwraca się bardzo szybko w postaci mniejszej liczby incydentów „u mnie działa”, sprawniejszego wdrażania i większej odporności na zmiany w technologiach czy składzie osobowym.

Źródła

  • Python 3.12 Documentation. Python Software Foundation (2023) – Oficjalna dokumentacja języka Python, ekosystem i zarządzanie pakietami
  • Anaconda Distribution User Guide. Anaconda Inc. – Opis środowisk Conda, zarządzania pakietami i izolacji projektów
  • Docker Overview and Best Practices. Docker Inc. – Podstawy konteneryzacji, obrazy, Dockerfile, powtarzalne środowiska
  • Google Cloud AI and Machine Learning Products Documentation. Google Cloud – Usługi chmurowe dla ML, skalowanie eksperymentów, środowiska wykonawcze
  • Azure Machine Learning Documentation. Microsoft – Zarządzanie eksperymentami, środowiskami i wdrożeniami modeli w chmurze
  • Amazon SageMaker Developer Guide. Amazon Web Services – Platforma MLOps w chmurze, kontenery, wersjonowanie i skalowanie treningu
  • scikit-learn: Machine Learning in Python. Journal of Machine Learning Research (2011) – Opis biblioteki scikit-learn, klasyczne algorytmy ML i API
  • NumPy Reference and User Guide. NumPy Developers – Tablice numeryczne, operacje macierzowe, fundament wielu bibliotek ML
  • pandas: Powerful Python Data Analysis Toolkit. pandas Development Team – Struktury danych, przetwarzanie tabelaryczne, typowe workflow analityczne