Anaconda in Docker: een beheersbare Jupyter-omgeving

128315758 a99f1798 81a1 4313 b9b1 1ca4ec21501d 1038857159a

Anaconda is een krachtig Python-platform voor data-analyse en wetenschappelijk rekenen. In een Docker-context blijkt de standaard Anaconda-image echter beperkingen te hebben, met name rond reproduceerbaarheid, beveiliging en operationeel beheer. In dit artikel beschrijf ik de opzet van een aangepaste Anaconda Docker image, hoe deze is configureerd en hoe ik deze op een Jupyter Notebook veilig en gebruiksvriendelijk beschikbaar heb gemaakt via anaconda.adagia.eu achter een login scherm.

1. Wat is Anaconda en waarom is het handig bij het testen van Python-code?

Anaconda is een distributie van Python (en R) die specifiek is ontworpen voor data science, analytics en wetenschappelijk rekenen. De belangrijkste kenmerken zijn:

  • Een uitgebreide set vooraf geïnstalleerde packages (NumPy, pandas, SciPy, matplotlib, etc.)
  • Een geïntegreerde package manager (conda)
  • Ondersteuning voor geïsoleerde omgevingen
  • Nauwe integratie met Jupyter Notebook

Voor het testen en ontwikkelen van Python-code is Anaconda bijzonder geschikt omdat:

  • afhankelijkheden expliciet en reproduceerbaar beheerd kunnen worden;
  • complexe C/C++-afhankelijke libraries probleemloos te installeren zijn;
  • notebooks snelle iteratie en documentatie combineren (code, output en toelichting).

In combinatie met Docker ontstaat bovendien een volledig geïsoleerde en reproduceerbare ontwikkelomgeving, onafhankelijk van het onderliggende besturingssysteem.

2. Problemen met de standaard Anaconda Docker image

De officiële continuumio/anaconda3 image is functioneel correct, maar in de praktijk loop ik tegen meerdere beperkingen aan:

  1. Geen vooraf ingestelde authenticatie voor Jupyter. Jupyter start standaard met een token of zonder wachtwoord, wat ongeschikt is voor publieke of semi-publieke omgevingen.
  2. Onvoldoende controle over configuratie tijdens runtime. Veel instellingen moeten handmatig of via runtime-parameters worden geregeld, wat foutgevoelig is.
  3. Geen duidelijke scheiding tussen build-time en run-time configuratie. Dit bemoeilijkt beheer, auditing en hergebruik.
  4. Niet optimaal voor productie-achtige omgevingen. Zaken als healthchecks, vaste volumes en resourcebegrenzing zijn niet standaard ingericht.
3. Waarom een aangepaste Anaconda image?

Voor adagia.eu wilde ik een omgeving die:

  • reproduceerbaar is (zelfde gedrag na herstart of migratie);
  • veilig is (wachtwoord ingesteld, geen tokens nodig);
  • geschikt is voor langdurig gebruik;
  • eenvoudig te beheren is via Docker Compose en Portainer;
  • vermijden van herinstallatie en herconfiguratie bij upgrade van de anaconda image of bouw van de container bij herstart van de NAS.

architectuurdiagram dockerfile image anaconda

Bij het ontwerp is bewust gekozen voor een custom Anaconda-image in plaats van runtime-configuratie via Docker Compose. Door het Jupyter-wachtwoord al tijdens de image build vast te leggen, ontstaat een stabiele omgeving die direct inzetbaar is zonder handmatige stappen na deploy. Persistente volumes scheiden code, data en logs van de container, waardoor updates van de image geen invloed hebben op notebooks of resultaten. Het uitschakelen van tokens en het gebruik van een vaste poort vereenvoudigt integratie met een reverse proxy en maakt de omgeving geschikt voor structureel gebruik en testen, zonder de complexiteit van dynamische authenticatie of tijdelijke configuraties.

4. Hoe bouw je een aangepaste Anaconda image?

De basis is de officiële image:

FROM continuumio/anaconda3

Vervolgens worden werkdirectory, tijdzone en omgevingsvariabelen ingesteld:

WORKDIR /opt/notebooks
ENV TZ=Europe/Amsterdam
ENV JUPYTER_PASSWORD="*********"

Daarna installeren we expliciet de benodigde componenten:

RUN conda install -y notebook jupyter --quiet && \
    pip install --no-cache-dir psycopg2-binary && \
    conda clean -afy

Hiermee houden we de image compact en beheersbaar.

5. Wachtwoord vooraf instellen in de image

Een belangrijk punt is dat de gebruiker bij het openen van anaconda.adagia.eu alleen nog hoeft in te loggen, zonder tokens of setup-stappen.

Dit realiseren we met een klein Python-script dat tijdens de build wordt uitgevoerd.

Naam: jupyter_setup.py

import os
import json
from jupyter_server.auth import passwd

os.makedirs('/root/.jupyter', exist_ok=True)
pw_hash = passwd(os.environ['JUPYTER_PASSWORD'])
cfg = {'NotebookApp': {'password': pw_hash}}

with open('/root/.jupyter/jupyter_notebook_config.json', 'w') as f:
    json.dump(cfg, f)

In de Dockerfile:

COPY jupyter_setup.py /tmp/jupyter_setup.py
RUN python /tmp/jupyter_setup.py

Hiermee wordt het wachtwoord gehashed opgeslagen, conform de beveiligingsstandaarden van Jupyter.

6. Docker Compose configuratie

De container wordt aangestuurd via Docker Compose:

services:
  anaconda3:
    container_name: anaconda3
    image: anaconda3:latest
    restart: unless-stopped
    tty: true
    stdin_open: true
    mem_limit: 8g
    cpu_shares: 2048
    shm_size: 4g
    environment:
      TZ: Europe/Amsterdam
      JUPYTER_PASSWORD: "********"
    ports:
      - "8888:8888"
    volumes:
      - /volume1/Hoofdmap/OneDrive/Bart_Actueel/Interesse/Python/programma:/opt/notebooks:rw
      - /volume1/Hoofdmap/OneDrive/Bart_Actueel/Interesse/Python:/python:rw
      - /volume1/docker/anaconda/logs:/opt/notebooks/logs:rw
      - /dev/shm:/dev/shm
    command:
      - jupyter
      - notebook
      - --notebook-dir=/opt/notebooks
      - --ip=0.0.0.0
      - --port=8888
      - --no-browser
      - --allow-root
      - --NotebookApp.token=''
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://127.0.0.1:8888/ || exit 1"]
      interval: 60s
      retries: 5
      start_period: 30s
    labels:
      com.centurylinklabs.watchtower.enable: "false"

Belangrijke aandachtspunten:

  • volumes zorgen voor persistente notebooks;
  • healthcheck maakt monitoring mogelijk;
  • resource limits voorkomen verstoring van andere containers;
  • tokens zijn expliciet uitgeschakeld omdat authenticatie via wachtwoord verloopt.
Aanbeveling

De standaard image van anaconda werkt hartstikke goed. Waar ik tegenaan liep, is dat ik in de docker-compose moest vastleggen om een bepaalde librarypsycopg2-binary bij elke containerstart moest laden via het command:-blok. Dat werkt technisch, maar is niet robuust en niet best practice: bij elke herstart installeer ik opnieuw packages, wat traag is en foutgevoelig. Daarnaast moest ik het wachtwoord opnieuw instellen zodra de container opnieuw opgebouwd werd.

Voor mij was de juiste oplossing om de library en de wachtwoordconfiguratie in het image vast te leggen (Dockerfile). Mijn advies is om:

  • na te gaan of de standaard image voldoet aan je wensen. Zo niet, om een custom image m.b.v. Dockerfile aan te maken;
  • build-time configuratie te verkiezen boven runtime-aanpassingen;
  • beveiliging (wachtwoorden, geen tokens) expliciet te regelen;
  • Docker Compose te gebruiken voor beheer en herstartbaarheid.

Een aangepaste Anaconda image levert meer controle, hogere betrouwbaarheid en minder operationele verrassingen op.

(Verouderde) handmatige configuratie van Jupyter Notebook

Let op: onderstaande stappen zijn niet langer nodig sinds het gebruik van de custom Anaconda Docker image. Ze zijn opgenomen ter illustratie van de oorspronkelijke werkwijze.

Log in op de host en open een shell in de draaiende container:

docker exec -it <container-id> bash

Genereer het Jupyter-configuratiebestand:

jupyter notebook --generate-config

Stel handmatig een wachtwoord in voor Jupyter Notebook:

jupyter notebook password

Verlaat de container:

exit

Herstart uitsluitend de container (niet de volledige stack):

docker restart anaconda3

Het opnieuw starten van de stack creëert een nieuwe container, waardoor deze handmatige configuratie verloren gaat.

Na herstart is toegang tot Jupyter Notebook mogelijk met alleen het ingestelde wachtwoord (zonder token).

Bronnen

 

Geef een reactie

Je e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *