Contexte:
ZenML est un orchestrateur qui permet de faire tourner ses pipelines CI/CD/CT dans les stacks AWS (SageMaker, S3, …), Azure (Azure ML, Blob Storage, ACR, …). Pour ma part, j’ai fait le choix de la stack Azure.
Le problème :
Lorsqu’une stack est choisie (hormis la stack locale), ZenML exécute les steps dans des conteneurs Docker. ZenML peut automatiquement construire l’image Docker, mais on peut aussi lui fournir une image pré-construite. Dans mon cas, j’ai décidé de créer une image qui embarque mon code applicatif. Pour vous donner une idée, voici à quoi ressemblait mon Dockerfile :
FROM python:3.11-slim-bullseye AS release
ENV WORKSPACE_ROOT=/app/
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV POETRY_VERSION=1.8.5
ENV DEBIAN_FRONTEND=noninteractive
ENV POETRY_NO_INTERACTION=1
# Install other system dependencies.
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends build-essential \
gcc \
python3-dev \
build-essential \
libglib2.0-dev \
libnss3-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install Poetry using pip and clear cache
RUN pip install --no-cache-dir "poetry==$POETRY_VERSION"
RUN poetry config installer.max-workers 20
WORKDIR $WORKSPACE_ROOT
# Copy the poetry lock file and pyproject.toml file to install dependencies
COPY pyproject.toml poetry.lock $WORKSPACE_ROOT
# Install the dependencies and clear cache
RUN poetry config virtualenvs.create false && \
poetry install --no-root --no-interaction --no-cache --without dev && \
zenml integration install azure --yes && \
poetry self add 'poethepoet[poetry_plugin]' && \
rm -rf ~/.cache/pypoetry/cache/ && \
rm -rf ~/.cache/pypoetry/artifacts/
# Copy the rest of the code.
COPY . $WORKSPACE_ROOT
Une fois mon image correctement créée avec le code copié à l’intérieur, j’ai dû faire face à l’erreur suivante lors de l’exécution d’un job ZenML sur Azure ML. Ce job utilisait mon image, mais se plaignait de ne pas trouver mes packages :
Execution failed. User process 'python' exited with status code 1. Please check log file 'user_logs/std_log.txt' for error details. Error: │ 598 │
│ │
│ /usr/local/lib/python3.11/importlib/__init__.py:126 in import_module │
│ │
│ 123 │ │ │ if character != '.': │
│ 124 │ │ │ │ break │
│ 125 │ │ │ level += 1 │
│ ❱ 126 │ return _bootstrap._gcd_import(name[level:], package, level) │
│ 127 │
│ 128 │
│ 129 _RELOADING = {} │
│ in _gcd_import:1204 │
│ in _find_and_load:1176 │
│ in _find_and_load_unlocked:1126 │
│ in _call_with_frames_removed:241 │
│ in _gcd_import:1204 │
│ in _find_and_load:1176 │
│ in _find_and_load_unlocked:1140 │
╰──────────────────────────────────────────────────────────────────────────────╯
ModuleNotFoundError: No module named 'pipelines'
La solution :
Lors du lancement d’un pipeline ZenML, on peut passer des configurations pour indiquer comment créer une image Docker pour les steps ou quelle image personnalisée utiliser. En lisant la documentation, j’ai trouvé des paramètres Docker permettant de dire à ZenML d’utiliser le code présent dans l’image. Voici la configuration qui a finalement résolu mon problème :
settings:
docker:
parent_image: xxxxxx.azurecr.io/elevate-seller:latest
skip_build: True
allow_including_files_in_images: True
allow_download_from_code_repository: False
allow_download_from_artifact_store: False