forked from mlflow/mlflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add experimental CLI command for building docker image capable of ser…
…ving an MLflow model (mlflow#1329) * WIP * WIP * WIP * WIP * WIP * Fix lint, test also seems to pass. Need to refactor docker logic & add more tests * Some refactoring * Fix test * WIP. Refactored build_image into a method within the FlavorBackend interface to allow extending the build-docker command to other backends in the future * Refactor utils into common module * Fix lint * Make 'serve' the default command in docker image * Make 'serve' the default command in docker image * Remove flavor arg * some cleanups from self-review pass * Test updates * Fix test * Address some review comments + improve test coverage a bit * Minor updates. * Minor update. * Minor update. * Minor update. * Minor fixes. * nits * Minor fix. * Run the new test * Added missing refactored files. * fixes * Fix. * Fix test. * Mark Sagemaker tests back as release. * Test fix * container fix - conda env needs to be copied from the read only directory in sagemaker.
- Loading branch information
1 parent
4aa1d21
commit 97c9b9d
Showing
18 changed files
with
482 additions
and
291 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,22 @@ | ||
.git | ||
mlruns | ||
docs | ||
apidocs | ||
mlflow.Rcheck | ||
outputs | ||
examples | ||
travis | ||
tests | ||
node_modules | ||
coverage | ||
build | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
__pycache__ | ||
.* | ||
~* | ||
*.swp | ||
*.pyc | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import os | ||
from subprocess import Popen, PIPE, STDOUT | ||
import logging | ||
|
||
import mlflow | ||
import mlflow.version | ||
from mlflow.utils.file_utils import TempDir, _copy_project | ||
from mlflow.utils.logging_utils import eprint | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
DISABLE_ENV_CREATION = "MLFLOW_DISABLE_ENV_CREATION" | ||
|
||
_DOCKERFILE_TEMPLATE = """ | ||
# Build an image that can serve mlflow models. | ||
FROM ubuntu:16.04 | ||
RUN apt-get -y update && apt-get install -y --no-install-recommends \ | ||
wget \ | ||
curl \ | ||
nginx \ | ||
ca-certificates \ | ||
bzip2 \ | ||
build-essential \ | ||
cmake \ | ||
openjdk-8-jdk \ | ||
git-core \ | ||
maven \ | ||
&& rm -rf /var/lib/apt/lists/* | ||
# Download and setup miniconda | ||
RUN curl https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh >> miniconda.sh | ||
RUN bash ./miniconda.sh -b -p /miniconda; rm ./miniconda.sh; | ||
ENV PATH="/miniconda/bin:$PATH" | ||
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 | ||
# Set up the program in the image | ||
WORKDIR /opt/mlflow | ||
{install_mlflow} | ||
{custom_setup_steps} | ||
{entrypoint} | ||
""" | ||
|
||
|
||
def _get_mlflow_install_step(dockerfile_context_dir, mlflow_home): | ||
""" | ||
Get docker build commands for installing MLflow given a Docker context dir and optional source | ||
directory | ||
""" | ||
if mlflow_home: | ||
mlflow_dir = _copy_project( | ||
src_path=mlflow_home, dst_path=dockerfile_context_dir) | ||
return ( | ||
"COPY {mlflow_dir} /opt/mlflow\n" | ||
"RUN pip install /opt/mlflow\n" | ||
"RUN cd /opt/mlflow/mlflow/java/scoring && " | ||
"mvn --batch-mode package -DskipTests && " | ||
"mkdir -p /opt/java/jars && " | ||
"mv /opt/mlflow/mlflow/java/scoring/target/" | ||
"mlflow-scoring-*-with-dependencies.jar /opt/java/jars\n" | ||
).format(mlflow_dir=mlflow_dir) | ||
else: | ||
return ( | ||
"RUN pip install mlflow=={version}\n" | ||
"RUN mvn " | ||
" --batch-mode dependency:copy" | ||
" -Dartifact=org.mlflow:mlflow-scoring:{version}:pom" | ||
" -DoutputDirectory=/opt/java\n" | ||
"RUN mvn " | ||
" --batch-mode dependency:copy" | ||
" -Dartifact=org.mlflow:mlflow-scoring:{version}:jar" | ||
" -DoutputDirectory=/opt/java/jars" | ||
).format(version=mlflow.version.VERSION) | ||
|
||
|
||
def _build_image(image_name, entrypoint, mlflow_home=None, custom_setup_steps_hook=None): | ||
""" | ||
Build an MLflow Docker image that can be used to serve a | ||
The image is built locally and it requires Docker to run. | ||
:param image_name: Docker image name. | ||
:param entry_point: String containing ENTRYPOINT directive for docker image | ||
:param mlflow_home: (Optional) Path to a local copy of the MLflow GitHub repository. | ||
If specified, the image will install MLflow from this directory. | ||
If None, it will install MLflow from pip. | ||
:param custom_setup_steps_hook: (Optional) Single-argument function that takes the string path | ||
of a dockerfile context directory and returns a string containing Dockerfile commands to | ||
run during the image build step. | ||
""" | ||
mlflow_home = os.path.abspath(mlflow_home) if mlflow_home else None | ||
with TempDir() as tmp: | ||
cwd = tmp.path() | ||
install_mlflow = _get_mlflow_install_step(cwd, mlflow_home) | ||
custom_setup_steps = custom_setup_steps_hook(cwd) if custom_setup_steps_hook else "" | ||
with open(os.path.join(cwd, "Dockerfile"), "w") as f: | ||
f.write(_DOCKERFILE_TEMPLATE.format( | ||
install_mlflow=install_mlflow, custom_setup_steps=custom_setup_steps, | ||
entrypoint=entrypoint)) | ||
_logger.info("Building docker image with name %s", image_name) | ||
os.system('find {cwd}/'.format(cwd=cwd)) | ||
proc = Popen(["docker", "build", "-t", image_name, "-f", "Dockerfile", "."], | ||
cwd=cwd, | ||
stdout=PIPE, | ||
stderr=STDOUT, | ||
universal_newlines=True) | ||
for x in iter(proc.stdout.readline, ""): | ||
eprint(x, end='') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.