Python dependency management¶
General notes¶
Often, workflow engines manage Python dependencies on the step / operator level, i.e. a virtual environment ("venv") with a fixed / ideally locked set of Python dependencies is bound to the data science artifacts (e.g. workflows, components).
While appropriate for automation tasks, this is suboptimal for production Data Science / analytics / Machine Learning for several reasons:
- For each step a new process must be started and managed:
- This adds a startup latency to each step
- Importing Python data science libraries often adds notable memory overhead and takes time
- It requires data to be serialized/deserialized in-between steps, which is not feasible for efficient operation on large analytical datasets
hetida designer runtime's execution simply runs in the same process which handles the execution request, with dependencies being those of the runtime container / process. No new processes are initialized and data is passed from one to the next operator by providing is as a reference/variable, i.e. data stays in-memory. See design decisions.
That means adding dependencies requires adding them to the runtime image. Consequently
- all executions share the same set of dependencies.
- dependencies cannot be added ad-hoc or via the user interface.
- a higher trust level towards user code is required, see security hints, since operator execution is not sandboxed/isolated.
Remark
Summarized there is a trade-off between convenience and isolation on the one side and production performance / efficiency on the other side. hetida designer opts for the latter.
Runtime Included Dependencies
Since dependencies cannot be added via user interface / on the fly, the hetida designer runtime docker image comes with a comprehensive set of Python Data Science dependencies pre-installed, like numpy, pandas, scikit-learn, scipy and many more.
Adding custom Python dependencies¶
While the pre-installed dependencies may be sufficient for many use cases it is often necessary to employ more and specialised libraries.
In order to add such custom dependencies, as explained above, they need to be made available/installed in the runtime Docker image.
Example 1: Adding a library via pip install¶
Create a new file Dockerfile Dockerfile-runtime-custom-python-deps and enter the following lines:
Now save the docker-compose.yml file as a new file with name docker-compose-custom-dependencies.yml and edit the hetida designer runtime service section as follows:
...
hetida-designer-runtime:
build:
context: .
dockerfile: Dockerfile-runtime-custom-python-deps
...
After that build the modified runtime image with
Now you can run your new setup with
To test availability of the xgboost library you may write a small component importing it (import xgboost) and verify that the component can be run.
Example 2: Adding libraries with uv pip compile and uv pip sync¶
Using uv lock and uv sync guarantees that the installed versions of your custom dependencies are compatible with the pre-installed dependencies.
To do this, start by creating a file requirements-custom.in (here in the runtime subdirectory of the repository) and enter
You may add additional libraries to the file and even specify version ranges. See the uv documentation for further details.
Next create a a new file Dockerfile Dockerfile-runtime-custom-python-depswith:
FROM hetida/designer-runtime
USER root
COPY ./runtime/requirements-custom.in /app/requirements-custom.in
WORKDIR /app
RUN uv pip compile --generate-hashes ./requirements-custom.in -o ./requirements-custom.txt
RUN uv pip sync --system ./requirements-base.txt ./requirements.txt ./requirements-custom.txt
USER hd_app
Now save the docker-compose.yml file as a new file with name docker-compose-custom-dependencies.yml and edit the hetida designer runtime service section as follows:
...
hetida-designer-runtime:
build:
context: .
dockerfile: Dockerfile-runtime-custom-python-deps
...
After that build the modified runtime image with
Now you can run your new setup with
To test availability of the xgboost library you may write a small component importing it (import xgboost) and verify that the component can be run.
Employing separate runtime services with varying dependencies¶
Using HD_AUTH_RUNTIME_ENGINE_URL_BY_ROLE, multiple runtime services with varying dependencies and environment variables can be employed and are selected based on a token role.