def docker_example_base_image(): mlflow_home = os.environ.get("MLFLOW_HOME", None) if not mlflow_home: raise Exception( "MLFLOW_HOME environment variable is not set. Please set the variable to " "point to your mlflow dev root.") with TempDir() as tmp: cwd = tmp.path() mlflow_dir = _copy_project(src_path=mlflow_home, dst_path=cwd) import shutil shutil.copy(os.path.join(TEST_DOCKER_PROJECT_DIR, "Dockerfile"), tmp.path("Dockerfile")) with open(tmp.path("Dockerfile"), "a") as f: f.write(("COPY {mlflow_dir} /opt/mlflow\n" "RUN pip install -U -e /opt/mlflow\n").format( mlflow_dir=mlflow_dir)) client = docker.from_env() try: client.images.build( tag="mlflow-docker-example", forcerm=True, nocache=True, dockerfile="Dockerfile", path=cwd, ) except BuildError as build_error: for chunk in build_error.build_log: print(chunk) raise build_error except APIError as api_error: print(api_error.explanation) raise api_error
def build_image(name=DEFAULT_IMAGE_NAME, mlflow_home=None): """ This function builds an MLflow Docker image. The image is built locally and it requires Docker to run. :param name: image name """ with TempDir() as tmp: install_mlflow = "RUN pip install mlflow=={version}".format(version=mlflow.version.VERSION) cwd = tmp.path() if mlflow_home: mlflow_dir = _copy_project(src_path=mlflow_home, dst_path=tmp.path()) install_mlflow = "COPY {mlflow_dir} /opt/mlflow\n RUN pip install /opt/mlflow\n" install_mlflow = install_mlflow.format(mlflow_dir=mlflow_dir) with open(os.path.join(cwd, "Dockerfile"), "w") as f: f.write(_DOCKERFILE_TEMPLATE % install_mlflow) eprint("building docker image") os.system('find {cwd}/'.format(cwd=cwd)) proc = Popen(["docker", "build", "-t", name, "-f", "Dockerfile", "."], cwd=cwd, stdout=PIPE, stderr=STDOUT, universal_newlines=True) for x in iter(proc.stdout.readline, ""): eprint(x, end='')
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\n" "RUN cp /opt/java/mlflow-scoring-{version}.pom /opt/java/pom.xml\n" "RUN cd /opt/java && mvn " "--batch-mode dependency:copy-dependencies -DoutputDirectory=/opt/java/jars\n" ).format(version=mlflow.version.VERSION)
def build_image(name=DEFAULT_IMAGE_NAME, mlflow_home=None): """ This function builds an MLflow Docker image. The image is built locally and it requires Docker to run. :param name: image name """ with TempDir() as tmp: install_mlflow = "RUN pip install mlflow=={version}".format( version=mlflow.version.VERSION) cwd = tmp.path() if mlflow_home: mlflow_dir = _copy_project(src_path=mlflow_home, dst_path=tmp.path()) install_mlflow = ("COPY {mlflow_dir} /opt/mlflow\n" "RUN cd /opt/mlflow/mlflow/java/scoring &&" " mvn --batch-mode package -DskipTests \n" "RUN pip install /opt/mlflow\n") install_mlflow = install_mlflow.format(mlflow_dir=mlflow_dir) else: eprint("`mlflow_home` was not specified. The image will install" " MLflow from pip instead. As a result, the container will" " not support the MLeap flavor.") with open(os.path.join(cwd, "Dockerfile"), "w") as f: f.write(_DOCKERFILE_TEMPLATE % install_mlflow) eprint("building docker image") os.system('find {cwd}/'.format(cwd=cwd)) proc = Popen(["docker", "build", "-t", name, "-f", "Dockerfile", "."], cwd=cwd, stdout=PIPE, stderr=STDOUT, universal_newlines=True) for x in iter(proc.stdout.readline, ""): eprint(x, end='')
def build_image(name=DEFAULT_IMAGE_NAME, mlflow_home=None): """ Build an MLflow Docker image. The image is built locally and it requires Docker to run. :param name: Docker image name. :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. """ with TempDir() as tmp: cwd = tmp.path() if mlflow_home: mlflow_dir = _copy_project( src_path=mlflow_home, dst_path=cwd) install_mlflow = ( "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: install_mlflow = ( "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\n" "RUN cd /opt/java && mv mlflow-scoring-{version}.pom pom.xml &&" " mvn --batch-mode dependency:copy-dependencies -DoutputDirectory=/opt/java/jars\n" "RUN rm /opt/java/pom.xml\n" ).format(version=mlflow.version.VERSION) with open(os.path.join(cwd, "Dockerfile"), "w") as f: f.write(_DOCKERFILE_TEMPLATE % install_mlflow) _logger.info("building docker image") os.system('find {cwd}/'.format(cwd=cwd)) proc = Popen(["docker", "build", "-t", name, "-f", "Dockerfile", "."], cwd=cwd, stdout=PIPE, stderr=STDOUT, universal_newlines=True) for x in iter(proc.stdout.readline, ""): eprint(x, end='')
def build_image(model_uri, workspace, image_name=None, model_name=None, mlflow_home=None, description=None, tags=None, synchronous=True): """ Register an MLflow model with Azure ML and build an Azure ML ContainerImage for deployment. The resulting image can be deployed as a web service to Azure Container Instances (ACI) or Azure Kubernetes Service (AKS). The resulting Azure ML ContainerImage will contain a webserver that processes model queries. For information about the input data formats accepted by this webserver, see the :ref:`MLflow deployment tools documentation <azureml_deployment>`. :param model_uri: The location, in URI format, of the MLflow model for which to build an Azure ML deployment image, for example: - ``/Users/me/path/to/local/model`` - ``relative/path/to/local/model`` - ``s3://my_bucket/path/to/model`` - ``runs:/<mlflow_run_id>/run-relative/path/to/model`` For more information about supported URI schemes, see the `Artifacts Documentation <https://www.mlflow.org/docs/latest/tracking.html# supported-artifact-stores>`_. :param image_name: The name to assign the Azure Container Image that will be created. If unspecified, a unique image name will be generated. :param model_name: The name to assign the Azure Model will be created. If unspecified, a unique model name will be generated. :param workspace: The AzureML workspace in which to build the image. This is a `azureml.core.Workspace` object. :param mlflow_home: Path to a local copy of the MLflow GitHub repository. If specified, the image will install MLflow from this directory. Otherwise, it will install MLflow from pip. :param description: A string description to associate with the Azure Container Image and the Azure Model that will be created. For more information, see `<https://docs.microsoft.com/en-us/python/api/azureml-core/ azureml.core.image.container.containerimageconfig>`_ and `<https://docs.microsoft.com/en-us/python/api/azureml-core/ azureml.core.model.model?view=azure-ml-py#register>`_. :param tags: A collection of tags, represented as a dictionary of string key-value pairs, to associate with the Azure Container Image and the Azure Model that will be created. These tags will be added to a set of default tags that include the model path, the model run id (if specified), and more. For more information, see `<https://docs.microsoft.com/en-us/python/api/azureml-core/ azureml.core.image.container.containerimageconfig>`_ and `<https://docs.microsoft.com/en-us/python/api/azureml-core/ azureml.core.model.model?view=azure-ml-py#register>`_. :param synchronous: If `True`, this method will block until the image creation procedure terminates before returning. If `False`, the method will return immediately, but the returned image will not be available until the asynchronous creation process completes. The `azureml.core.Image.wait_for_creation()` function can be used to wait for the creation process to complete. :return: A tuple containing the following elements in order: - An `azureml.core.image.ContainerImage` object containing metadata for the new image. - An `azureml.core.model.Model` object containing metadata for the new model. >>> import mlflow.azureml >>> from azureml.core import Workspace >>> from azureml.core.webservice import AciWebservice, Webservice >>> >>> # Load or create an Azure ML Workspace >>> workspace_name = "<Name of your Azure ML workspace>" >>> subscription_id = "<Your Azure subscription ID>" >>> resource_group = "<Name of the Azure resource group in which to create Azure ML resources>" >>> location = "<Name of the Azure location (region) in which to create Azure ML resources>" >>> azure_workspace = Workspace.create(name=workspace_name, >>> subscription_id=subscription_id, >>> resource_group=resource_group, >>> location=location, >>> create_resource_group=True, >>> exist_okay=True) >>> >>> # Build an Azure ML Container Image for an MLflow model >>> azure_image, azure_model = mlflow.azureml.build_image( >>> model_path="<model_path>", >>> workspace=azure_workspace, >>> synchronous=True) >>> # If your image build failed, you can access build logs at the following URI: >>> print("Access the following URI for build logs: {}".format(azure_image.image_build_log_uri)) >>> >>> # Deploy the image to Azure Container Instances (ACI) for real-time serving >>> webservice_deployment_config = AciWebservice.deploy_configuration() >>> webservice = Webservice.deploy_from_image( >>> image=azure_image, workspace=azure_workspace, name="<deployment-name>") >>> webservice.wait_for_deployment() """ # The Azure ML SDK is only compatible with Python 3. However, the `mlflow.azureml` module should # still be accessible for import from Python 2. Therefore, we will only import from the SDK # upon method invocation. # pylint: disable=import-error from azureml.core.image import ContainerImage from azureml.core.model import Model as AzureModel absolute_model_path = _download_artifact_from_uri(model_uri) model_pyfunc_conf = _load_pyfunc_conf(model_path=absolute_model_path) model_python_version = model_pyfunc_conf.get(pyfunc.PY_VERSION, None) if model_python_version is not None and\ StrictVersion(model_python_version) < StrictVersion("3.0.0"): raise MlflowException(message=( "Azure ML can only deploy models trained in Python 3 or above! Please see" " the following MLflow GitHub issue for a thorough explanation of this" " limitation and a workaround to enable support for deploying models" " trained in Python 2: https://github.com/mlflow/mlflow/issues/668" ), error_code=INVALID_PARAMETER_VALUE) tags = _build_tags(model_uri=model_uri, model_python_version=model_python_version, user_tags=tags) if image_name is None: image_name = _get_mlflow_azure_resource_name() if model_name is None: model_name = _get_mlflow_azure_resource_name() with TempDir(chdr=True) as tmp: model_directory_path = tmp.path("model") tmp_model_path = os.path.join( model_directory_path, _copy_file_or_tree(src=absolute_model_path, dst=model_directory_path)) registered_model = AzureModel.register(workspace=workspace, model_path=tmp_model_path, model_name=model_name, tags=tags, description=description) _logger.info( "Registered an Azure Model with name: `%s` and version: `%s`", registered_model.name, registered_model.version) # Create an execution script (entry point) for the image's model server. Azure ML requires # the container's execution script to be located in the current working directory during # image creation, so we create the execution script as a temporary file in the current # working directory. execution_script_path = tmp.path("execution_script.py") _create_execution_script(output_path=execution_script_path, azure_model=registered_model) # Azure ML copies the execution script into the image's application root directory by # prepending "/var/azureml-app" to the specified script path. The script is then executed # by referencing its path relative to the "/var/azureml-app" directory. Unfortunately, # if the script path is an absolute path, Azure ML attempts to reference it directly, # resulting in a failure. To circumvent this problem, we provide Azure ML with the relative # script path. Because the execution script was created in the current working directory, # this relative path is the script path's base name. execution_script_path = os.path.basename(execution_script_path) if mlflow_home is not None: _logger.info( "Copying the specified mlflow_home directory: `%s` to a temporary location for" " container creation", mlflow_home) mlflow_home = os.path.join( tmp.path(), _copy_project(src_path=mlflow_home, dst_path=tmp.path())) image_file_dependencies = [mlflow_home] else: image_file_dependencies = None dockerfile_path = tmp.path("Dockerfile") _create_dockerfile(output_path=dockerfile_path, mlflow_path=mlflow_home) conda_env_path = None if pyfunc.ENV in model_pyfunc_conf: conda_env_path = os.path.join(tmp_model_path, model_pyfunc_conf[pyfunc.ENV]) image_configuration = ContainerImage.image_configuration( execution_script=execution_script_path, runtime="python", docker_file=dockerfile_path, dependencies=image_file_dependencies, conda_file=conda_env_path, description=description, tags=tags, ) image = ContainerImage.create(workspace=workspace, name=image_name, image_config=image_configuration, models=[registered_model]) _logger.info( "Building an Azure Container Image with name: `%s` and version: `%s`", image.name, image.version) if synchronous: image.wait_for_creation(show_output=True) return image, registered_model