Skip to content

Commit

Permalink
Ersilia Pack Dockerization Clean up (#1274)
Browse files Browse the repository at this point in the history
* WIP

* Rework dockerfiles to build ersilia pack from source and not through a git url; make it easy to copy the necessary files from bundle when ersilia runs a dockerized model
  • Loading branch information
DhanshreeA committed Sep 29, 2024
1 parent 24336e5 commit e489af0
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 39 deletions.
11 changes: 6 additions & 5 deletions dockerfiles/dockerize-ersiliapack/base/Dockerfile.conda
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ARG VERSION=version
ENV VERSION=$VERSION
ENV PATH=$PATH:/usr/bin/conda/bin
WORKDIR /root
COPY . /ersilia-pack
RUN set -x && \
apt-get update && \
apt-get install -y wget && \
Expand All @@ -27,16 +28,16 @@ RUN set -x && \
echo "Unsupported architecture: $ARCH"; \
echo "$ARCH" > arch.sh; \
fi && \
apt-get install -y libxrender1 build-essential git && \
mkdir -p /usr/bin/conda && \
bash /root/miniconda.sh -b -u -p /usr/bin/conda && \
rm /root/miniconda.sh && \
. /usr/bin/conda/etc/profile.d/conda.sh && \
conda init && \
/usr/bin/conda/bin/conda clean -afy && \
/usr/bin/conda/bin/python -m pip install git+https://github.com/ersilia-os/ersilia-pack.git

COPY docker-entrypoint.sh /root/docker-entrypoint.sh
RUN chmod + /root/docker-entrypoint.sh
cd /ersilia-pack && \
/usr/bin/conda/bin/python -m pip install . && \
mv /ersilia-pack/docker-entrypoint.sh /root/docker-entrypoint.sh && \
chmod +x /root/docker-entrypoint.sh

EXPOSE 80
ENTRYPOINT [ "sh", "/root/docker-entrypoint.sh"]
11 changes: 6 additions & 5 deletions dockerfiles/dockerize-ersiliapack/base/Dockerfile.pip
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
FROM python:version
RUN apt-get update && \
apt-get install git -y && \
python -m pip install git+https://github.com/ersilia-os/ersilia-pack.git
COPY docker-entrypoint.sh /root/docker-entrypoint.sh
RUN chmod + /root/docker-entrypoint.sh
WORKDIR /root
COPY . /ersilia-pack
RUN apt-get clean && apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* && cd /ersilia-pack && pip install -e . && \
mv /ersilia-pack/docker-entrypoint.sh /root/docker-entrypoint.sh && \
chmod + /root/docker-entrypoint.sh
EXPOSE 80
ENTRYPOINT [ "sh", "/root/docker-entrypoint.sh"]
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
"3.12-slim-bullseye"
]

DOCKER_ENTRYPOINT = """
#!/bin/bash
# We serve the model at port 80 bec Ersilia port maps all containers to port 80
DOCKER_ENTRYPOINT = """#!/bin/bash
set -ex
if [ -z "${MODEL}" ];
then
echo "Model name has not been specified"
exit 1
fi
ersilia_model_serve --bundle_path /root/bundles/$MODEL --port 3000
ersilia_model_serve --bundle_path /root/bundles/$MODEL --port 80
echo "Serving model $MODEL..."
"""

Expand Down
4 changes: 3 additions & 1 deletion dockerfiles/dockerize-ersiliapack/local.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ Ersilia Pack dockerization is divided into two steps - building the base image t

And an entrypoint script called `docker-entrypoint.sh`.

4. Build the base docker image, eg for Python 3.12 conda image, as follows:
4. Copy the desired Dockerfile and `docker-entrypoint.sh` file into ersilia-pack directory.

5. This step assumes you are inside the ersilia-pack directory and you have completed Step 4. Build the base docker image, eg for Python 3.12 conda image, as follows:

```
docker build -f Dockerfile3.12-slim-bullseye -t ersiliaos/ersiliapack-py312:latest .
Expand Down
9 changes: 6 additions & 3 deletions dockerfiles/dockerize-ersiliapack/model/Dockerfile.conda
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ RUN conda install -c conda-forge conda-pack
RUN conda-pack -n baseclone -o /tmp/env.tar && \
mkdir /venv && cd /venv && tar -xf /tmp/env.tar && \
rm /tmp/env.tar
RUN /venv/bin/conda-unpack
RUN /venv/bin/conda-unpack



Expand All @@ -26,9 +26,12 @@ ENV PATH="/usr/bin/conda/bin/:$PATH"


COPY --from=build /root/bundles /root/bundles
COPY --from=build /root/docker-entrypoint.sh docker-entrypoint.sh
COPY --from=build /root/docker-entrypoint.sh /root/docker-entrypoint.sh
COPY --from=build /venv /usr/bin/conda

RUN chmod + docker-entrypoint.sh
RUN cp /root/bundles/$MODEL/*/information.json /root/information.json && \
cp /root/bundles/$MODEL/*/api_schema.json /root/api_schema.json && \
cp /root/bundles/$MODEL/*/status.json /root/status.json && \
chmod + docker-entrypoint.sh
EXPOSE 80
ENTRYPOINT [ "sh", "docker-entrypoint.sh"]
7 changes: 5 additions & 2 deletions dockerfiles/dockerize-ersiliapack/model/Dockerfile.pip
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
FROM ersiliapack-VERSION:latest
FROM ersiliaos/ersiliapack-VERSION:latest
ARG MODEL=eos_identifier
ENV MODEL=$MODEL
WORKDIR /root
COPY ./$MODEL /root/$MODEL
RUN mkdir /root/bundles && ersilia_model_pack --repo_path $MODEL --bundles_repo_path /root/bundles && \
rm -rf /root/$MODEL && rm -rf /root/.cache
rm -rf /root/$MODEL && rm -rf /root/.cache && \
cp /root/bundles/$MODEL/*/information.json /root/information.json && \
cp /root/bundles/$MODEL/*/api_schema.json /root/api_schema.json && \
cp /root/bundles/$MODEL/*/status.json /root/status.json
43 changes: 26 additions & 17 deletions ersilia/hub/fetch/lazy_fetchers/dockerhub.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from ...pull.pull import ModelPuller
from ....serve.services import PulledDockerImageService
from ....setup.requirements.docker import DockerRequirement
from ....utils.docker import SimpleDocker
from ....utils.docker import SimpleDocker, resolve_pack_method_docker, PACK_METHOD_BENTOML
from ....utils.exceptions_utils.fetch_exceptions import DockerNotActiveError
from .. import STATUS_FILE

Expand Down Expand Up @@ -50,9 +50,9 @@ def write_apis(self, model_id):
di.serve()
di.close()

def copy_information(self, model_id):
fr_file = "/root/eos/dest/{0}/{1}".format(model_id, INFORMATION_FILE)
to_file = "{0}/dest/{1}/{2}".format(EOS, model_id, INFORMATION_FILE)
def _copy_from_bentoml_image(self, model_id, file):
fr_file = "/root/eos/dest/{0}/{1}".format(model_id, file)
to_file = "{0}/dest/{1}/{2}".format(EOS, model_id, file)
self.simple_docker.cp_from_image(
img_path=fr_file,
local_path=to_file,
Expand All @@ -61,9 +61,9 @@ def copy_information(self, model_id):
tag=DOCKERHUB_LATEST_TAG,
)

def copy_metadata(self, model_id):
fr_file = "/root/eos/dest/{0}/{1}".format(model_id, API_SCHEMA_FILE)
to_file = "{0}/dest/{1}/{2}".format(EOS, model_id, API_SCHEMA_FILE)
def _copy_from_ersiliapack_image(self, model_id, file):
fr_file = "/root/{0}".format(file)
to_file = "{0}/dest/{1}/{2}".format(EOS, model_id, file)
self.simple_docker.cp_from_image(
img_path=fr_file,
local_path=to_file,
Expand All @@ -72,18 +72,27 @@ def copy_metadata(self, model_id):
tag=DOCKERHUB_LATEST_TAG,
)

def copy_status(self, model_id):
fr_file = "/root/eos/dest/{0}/{1}".format(model_id, STATUS_FILE)
to_file = "{0}/dest/{1}/{2}".format(EOS, model_id, STATUS_FILE)
self.simple_docker.cp_from_image(
img_path=fr_file,
local_path=to_file,
org=DOCKERHUB_ORG,
img=model_id,
tag=DOCKERHUB_LATEST_TAG,
)
def _copy_from_image_to_local(self, model_id, file):
pack_method = resolve_pack_method_docker(model_id)
if pack_method == PACK_METHOD_BENTOML:
self._copy_from_bentoml_image(model_id, file)
else:
self._copy_from_ersiliapack_image(model_id, file)

def copy_information(self, model_id):
self.logger.debug("Copying information file from model container")
self._copy_from_image_to_local(model_id, INFORMATION_FILE)

def copy_metadata(self, model_id):
self.logger.debug("Copying api_schema_file file from model container")
self._copy_from_image_to_local(model_id, API_SCHEMA_FILE)

def copy_status(self, model_id):
self.logger.debug("Copying status file from model container")
self._copy_from_image_to_local(model_id, STATUS_FILE)

def copy_example_if_available(self, model_id):
# TODO This also needs to change to accomodate ersilia pack
for pf in PREDEFINED_EXAMPLE_FILES:
fr_file = "/root/eos/dest/{0}/{1}".format(model_id, pf)
to_file = "{0}/dest/{1}/{2}".format(EOS, model_id, "input.csv")
Expand Down
16 changes: 15 additions & 1 deletion ersilia/utils/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,24 @@
from .terminal import run_command, run_command_check_output

from .. import logger
from ..default import DEFAULT_DOCKER_PLATFORM, DEFAULT_UDOCKER_USERNAME
from ..default import (DEFAULT_DOCKER_PLATFORM, DEFAULT_UDOCKER_USERNAME,
DOCKERHUB_ORG, DOCKERHUB_LATEST_TAG,
PACK_METHOD_BENTOML, PACK_METHOD_FASTAPI)
from ..utils.system import SystemChecker
from ..utils.logging import make_temp_dir

def resolve_pack_method_docker(model_id):
client = docker.from_env()
model_image = client.images.get(
f"{DOCKERHUB_ORG}/{model_id}:{DOCKERHUB_LATEST_TAG}"
)
image_history = model_image.history()
for hist in image_history:
# Very hacky, but works bec we don't have nginx in ersilia-pack images
if "nginx" in hist["CreatedBy"]:
return PACK_METHOD_BENTOML
return PACK_METHOD_FASTAPI


def resolve_platform():
if SystemChecker().is_arm64():
Expand Down
13 changes: 11 additions & 2 deletions ersilia/utils/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import collections
from pathlib import Path
from ersilia import logger
from .docker import resolve_pack_method_docker
from ..default import PACK_METHOD_BENTOML, PACK_METHOD_FASTAPI

MODELS_DEVEL_DIRNAME = "models"
Expand Down Expand Up @@ -58,11 +59,19 @@ def exists(path):
else:
return False


def resolve_pack_method(model_path):
def resolve_pack_method_source(model_path):
if os.path.exists(os.path.join(model_path, "installs", "install.sh")):
return PACK_METHOD_FASTAPI
elif os.path.exists(os.path.join(model_path, "bentoml.yml")):
return PACK_METHOD_BENTOML
logger.warning("Could not resolve pack method")
return None

def resolve_pack_method(model_path):
with open(os.path.join(model_path, "service_class.txt"), "r") as f:
service_class = f.read().strip()
if service_class == "pulled_docker":
model_id = Paths().model_id_from_path(model_path)
return resolve_pack_method_docker(model_id)
else:
return resolve_pack_method_source(model_path)

0 comments on commit e489af0

Please sign in to comment.