Repository: mlflow/mlflow

Issue:
[FR] Allow Proxies in Sagemaker Endpoints and Docker builds
### Willingness to contribute

Yes. I would be willing to contribute this feature with guidance from the MLflow community.

### Proposal Summary

I'd like to request that proxies (http proxy, https proxy, and no proxy) be allowed when performing the below two operations -

1. Build and Push Docker container - `mlflow sagemaker build-and-push-container`
2. Deploying models as Sagemaker Endpoints

### Motivation

> #### What is the use case for this feature?
The 'maven' framework accepts proxies via the 'settings.xml' file or extra command line arguments, the 'maven' commands inside the generated Dockerfile after triggering the 'mlflow sagemaker build-and-push-container' command fail with HTTP timeouts. Furthermore, when models are deployed as Sagemaker endpoints, the 'mlflow deploy' API does not provide the option to inject proxies as environment variables.

> #### Why is this use case valuable to support for MLflow users in general?
Proxies are common in every business, so users who want to use mlflow deployments to Sagemaker will require this feature.

> #### Why is this use case valuable to support for your project(s) or organization?
We plan to use `mlflow` to deploy models registered in the 'mlflow registry' to AWS Sagemaker.


### Details

We are partnering with `Databricks - @mshtelma` to integrate the proposed fix into open source mlflow -

Docker Build Maven Proxy Fix:
 
https://github.com/ameya-parab/mlflow/blob/feature/sagemaker_proxies/mlflow/models/docker_utils.py#L40-L52
https://github.com/ameya-parab/mlflow/blob/feature/sagemaker_proxies/mlflow/models/docker_utils.py#L100-L126
 
 
SageMaker Endpoints Proxies:
 
https://github.com/ameya-parab/mlflow/blob/feature/sagemaker_proxies/mlflow/sagemaker/__init__.py#L1241-L1249
 

### What component(s) does this bug affect?

- [ ] `area/artifacts`: Artifact stores and artifact logging
- [ ] `area/build`: Build and test infrastructure for MLflow
- [ ] `area/docs`: MLflow documentation pages
- [ ] `area/examples`: Example code
- [ ] `area/model-registry`: Model Registry service, APIs, and the fluent client calls for Model Registry
- [X] `area/models`: MLmodel format, model serialization/deserialization, flavors
- [ ] `area/projects`: MLproject format, project running backends
- [X] `area/scoring`: MLflow Model server, model deployment tools, Spark UDFs
- [ ] `area/server-infra`: MLflow Tracking server backend
- [ ] `area/tracking`: Tracking Service, tracking client APIs, autologging

### What interface(s) does this bug affect?

- [ ] `area/uiux`: Front-end, user experience, plotting, JavaScript, JavaScript dev server
- [X] `area/docker`: Docker use across MLflow's components, such as MLflow Projects and MLflow Models
- [ ] `area/sqlalchemy`: Use of SQLAlchemy in the Tracking Service or Model Registry
- [ ] `area/windows`: Windows support

### What language(s) does this bug affect?

- [ ] `language/r`: R APIs and clients
- [ ] `language/java`: Java APIs and clients
- [ ] `language/new`: Proposals for new client languages

### What integration(s) does this bug affect?

- [ ] `integrations/azure`: Azure and Azure ML integrations
- [X] `integrations/sagemaker`: SageMaker integrations
- [ ] `integrations/databricks`: Databricks integrations


Discussion:

Resolving patch:
diff --git a/mlflow/models/docker_utils.py b/mlflow/models/docker_utils.py
--- a/mlflow/models/docker_utils.py
+++ b/mlflow/models/docker_utils.py
@@ -1,5 +1,6 @@
 import os
 from subprocess import Popen, PIPE, STDOUT
+from urllib.parse import urlparse
 import logging
 
 import mlflow
@@ -37,6 +38,42 @@
 RUN pip install virtualenv
 """
 
+if os.getenv("http_proxy") is not None and os.getenv("https_proxy") is not None:
+
+    # Expects proxies as either PROTOCOL://{USER}:{PASSWORD}@HOSTNAME:PORT
+    # or PROTOCOL://HOSTNAME:PORT
+    parsed_http_proxy = urlparse(os.environ["http_proxy"])
+    assert parsed_http_proxy.hostname is not None, "Invalid `http_proxy` hostname."
+    assert isinstance(parsed_http_proxy.port, int), f"Invalid Proxy Port: {parsed_http_proxy.port}"
+
+    parsed_https_proxy = urlparse(os.environ["https_proxy"])
+    assert parsed_https_proxy.hostname is not None, "Invalid `https_proxy` hostname."
+    assert isinstance(
+        parsed_https_proxy.port, int
+    ), f"Invalid Proxy Port: {parsed_https_proxy.port}"
+
+    MAVEN_PROXY = (
+        " -DproxySet=true -Dhttp.proxyHost={http_proxy_host} "
+        "-Dhttp.proxyPort={http_proxy_port} -Dhttps.proxyHost={https_proxy_host} "
+        "-Dhttps.proxyPort={https_proxy_port} -Dhttps.nonProxyHosts=repo.maven.apache.org"
+    ).format(
+        http_proxy_host=parsed_http_proxy.hostname,
+        http_proxy_port=parsed_http_proxy.port,
+        https_proxy_host=parsed_https_proxy.hostname,
+        https_proxy_port=parsed_https_proxy.port,
+    )
+
+    if parsed_http_proxy.username is not None and parsed_http_proxy.password is not None:
+
+        MAVEN_PROXY += (
+            " -Dhttp.proxyUser={proxy_username} -Dhttp.proxyPassword={proxy_password}".format(
+                proxy_username=parsed_http_proxy.username, proxy_password=parsed_http_proxy.password
+            )
+        )
+
+else:
+    MAVEN_PROXY = ""  # No Proxy
+
 DISABLE_ENV_CREATION = "MLFLOW_DISABLE_ENV_CREATION"
 
 _DOCKERFILE_TEMPLATE = """
@@ -89,26 +126,27 @@ def _get_mlflow_install_step(dockerfile_context_dir, mlflow_home):
             "COPY {mlflow_dir} /opt/mlflow\n"
             "RUN pip install /opt/mlflow\n"
             "RUN cd /opt/mlflow/mlflow/java/scoring && "
-            "mvn --batch-mode package -DskipTests && "
+            "mvn --batch-mode package -DskipTests {maven_proxy} && "
             "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)
+        ).format(mlflow_dir=mlflow_dir, maven_proxy=MAVEN_PROXY)
     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"
+            " -DoutputDirectory=/opt/java {maven_proxy}\n"
             "RUN mvn"
             " --batch-mode dependency:copy"
             " -Dartifact=org.mlflow:mlflow-scoring:{version}:jar"
-            " -DoutputDirectory=/opt/java/jars\n"
+            " -DoutputDirectory=/opt/java/jars {maven_proxy}\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)
+            "--batch-mode dependency:copy-dependencies "
+            "-DoutputDirectory=/opt/java/jars {maven_proxy}\n"
+        ).format(version=mlflow.version.VERSION, maven_proxy=MAVEN_PROXY)
 
 
 def _build_image(
diff --git a/mlflow/sagemaker/__init__.py b/mlflow/sagemaker/__init__.py
--- a/mlflow/sagemaker/__init__.py
+++ b/mlflow/sagemaker/__init__.py
@@ -1237,6 +1237,16 @@ def _get_deployment_config(flavor_name):
         DEPLOYMENT_CONFIG_KEY_FLAVOR_NAME: flavor_name,
         SERVING_ENVIRONMENT: SAGEMAKER_SERVING_ENVIRONMENT,
     }
+
+    if os.getenv("http_proxy") is not None:
+        deployment_config.update({"http_proxy": os.environ["http_proxy"]})
+
+    if os.getenv("https_proxy") is not None:
+        deployment_config.update({"https_proxy": os.environ["https_proxy"]})
+
+    if os.getenv("no_proxy") is not None:
+        deployment_config.update({"no_proxy": os.environ["no_proxy"]})
+
     return deployment_config
 
 


Test patch:
diff --git a/tests/helper_functions.py b/tests/helper_functions.py
--- a/tests/helper_functions.py
+++ b/tests/helper_functions.py
@@ -27,6 +27,7 @@
     _CONSTRAINTS_FILE_NAME,
 )
 
+AWS_METADATA_IP = "169.254.169.254"  # Used to fetch AWS Instance and User metadata.
 LOCALHOST = "127.0.0.1"
 PROTOBUF_REQUIREMENT = "protobuf<4.0.0"
 
diff --git a/tests/sagemaker/test_deployment.py b/tests/sagemaker/test_deployment.py
--- a/tests/sagemaker/test_deployment.py
+++ b/tests/sagemaker/test_deployment.py
@@ -26,6 +26,7 @@
 from mlflow.store.artifact.s3_artifact_repo import S3ArtifactRepository
 from mlflow.tracking.artifact_utils import _download_artifact_from_uri
 
+from tests.helper_functions import AWS_METADATA_IP
 from tests.helper_functions import set_boto_credentials  # pylint: disable=unused-import
 from tests.sagemaker.mock import mock_sagemaker, Endpoint, EndpointOperation
 
@@ -215,14 +216,44 @@ def test_attempting_to_deploy_in_asynchronous_mode_without_archiving_throws_exce
     assert exc.value.error_code == ErrorCode.Name(INVALID_PARAMETER_VALUE)
 
 
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_deploy_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_local(
-    pretrained_model, sagemaker_client
+    proxies_enabled, pretrained_model, sagemaker_client
 ):
-    app_name = "test-app"
-    mfs.deploy(
-        app_name=app_name, model_uri=pretrained_model.model_uri, mode=mfs.DEPLOYMENT_MODE_CREATE
-    )
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
+
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "http://user:password@proxy.example.net:1234",
+            "no_proxy": f"localhost,{AWS_METADATA_IP}",
+        }  # Ensuring access to the isntance metadata service skips the proxy server.
+        expected_model_environment.update(proxy_variables)
+        app_name = "test-app-proxies"
+        with mock.patch.dict(os.environ, proxy_variables):
+            mfs.deploy(
+                app_name=app_name,
+                model_uri=pretrained_model.model_uri,
+                mode=mfs.DEPLOYMENT_MODE_CREATE,
+            )
+
+    else:
+        environment_variables = {
+            proxy: proxy_value
+            for proxy, proxy_value in os.environ.items()
+            if not "proxy" in proxy.lower()
+        }
+        app_name = "test-app"
+        with mock.patch.dict(os.environ, environment_variables, clear=True):
+            mfs.deploy(
+                app_name=app_name,
+                model_uri=pretrained_model.model_uri,
+                mode=mfs.DEPLOYMENT_MODE_CREATE,
+            )
 
     region_name = sagemaker_client.meta.region_name
     s3_client = boto3.client("s3", region_name=region_name)
@@ -246,29 +277,61 @@ def test_deploy_creates_sagemaker_and_s3_resources_with_expected_names_and_env_f
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
 
+    assert model_environment == expected_model_environment
 
+
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_local(
-    pretrained_model, sagemaker_client
+    proxies_enabled, pretrained_model, sagemaker_client
 ):
-    app_name = "test-app"
-    result = CliRunner(env={"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}).invoke(
-        mfscli.commands,
-        [
-            "deploy",
-            "-a",
-            app_name,
-            "-m",
-            pretrained_model.model_uri,
-            "--mode",
-            mfs.DEPLOYMENT_MODE_CREATE,
-        ],
-    )
+    environment_variables = {"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
+
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "https://user:password@proxy.example.net:1234",
+            "no_proxy": f"localhost,{AWS_METADATA_IP}",
+        }  # Ensuring access to the isntance metadata service skips the proxy server.
+        expected_model_environment.update(proxy_variables)
+        app_name = "test-app-proxies"
+        result = CliRunner(env={**environment_variables, **proxy_variables}).invoke(
+            mfscli.commands,
+            [
+                "deploy",
+                "-a",
+                app_name,
+                "-m",
+                pretrained_model.model_uri,
+                "--mode",
+                mfs.DEPLOYMENT_MODE_CREATE,
+            ],
+        )
+    else:
+        proxy_variables = {
+            "http_proxy": None,
+            "https_proxy": None,
+            "no_proxy": None,
+        }
+        app_name = "test-app"
+        result = CliRunner(env={**environment_variables, **proxy_variables}).invoke(
+            mfscli.commands,
+            [
+                "deploy",
+                "-a",
+                app_name,
+                "-m",
+                pretrained_model.model_uri,
+                "--mode",
+                mfs.DEPLOYMENT_MODE_CREATE,
+            ],
+        )
+
     assert result.exit_code == 0
 
     region_name = sagemaker_client.meta.region_name
@@ -293,15 +356,14 @@ def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_e
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
+
+    assert model_environment == expected_model_environment
 
 
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_deploy_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_s3(
-    pretrained_model, sagemaker_client
+    proxies_enabled, pretrained_model, sagemaker_client
 ):
     local_model_path = _download_artifact_from_uri(pretrained_model.model_uri)
     artifact_path = "model"
@@ -312,9 +374,30 @@ def test_deploy_creates_sagemaker_and_s3_resources_with_expected_names_and_env_f
     model_s3_uri = "s3://{bucket_name}/{artifact_path}".format(
         bucket_name=default_bucket, artifact_path=pretrained_model.model_path
     )
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
 
-    app_name = "test-app"
-    mfs.deploy(app_name=app_name, model_uri=model_s3_uri, mode=mfs.DEPLOYMENT_MODE_CREATE)
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "https://user:password@proxy.example.net:1234",
+            "no_proxy": f"localhost,{AWS_METADATA_IP}",
+        }  # Ensuring access to the isntance metadata service skips the proxy server.
+        expected_model_environment.update(proxy_variables)
+        app_name = "test-app-proxies"
+        with mock.patch.dict(os.environ, proxy_variables):
+            mfs.deploy(app_name=app_name, model_uri=model_s3_uri, mode=mfs.DEPLOYMENT_MODE_CREATE)
+    else:
+        environment_variables = {
+            proxy: proxy_value
+            for proxy, proxy_value in os.environ.items()
+            if not "proxy" in proxy.lower()
+        }
+        app_name = "test-app"
+        with mock.patch.dict(os.environ, environment_variables, clear=True):
+            mfs.deploy(app_name=app_name, model_uri=model_s3_uri, mode=mfs.DEPLOYMENT_MODE_CREATE)
 
     endpoint_description = sagemaker_client.describe_endpoint(EndpointName=app_name)
     endpoint_production_variants = endpoint_description["ProductionVariants"]
@@ -337,15 +420,14 @@ def test_deploy_creates_sagemaker_and_s3_resources_with_expected_names_and_env_f
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
+
+    assert model_environment == expected_model_environment
 
 
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_s3(
-    pretrained_model, sagemaker_client
+    proxies_enabled, pretrained_model, sagemaker_client
 ):
     local_model_path = _download_artifact_from_uri(pretrained_model.model_uri)
     artifact_path = "model"
@@ -356,12 +438,35 @@ def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_e
     model_s3_uri = "s3://{bucket_name}/{artifact_path}".format(
         bucket_name=default_bucket, artifact_path=pretrained_model.model_path
     )
+    environment_variables = {"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
 
-    app_name = "test-app"
-    result = CliRunner(env={"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}).invoke(
-        mfscli.commands,
-        ["deploy", "-a", app_name, "-m", model_s3_uri, "--mode", mfs.DEPLOYMENT_MODE_CREATE],
-    )
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "http://user:password@proxy.example.net:1234",
+            "no_proxy": f"localhost,{AWS_METADATA_IP}",
+        }  # Ensuring access to the isntance metadata service skips the proxy server.
+        expected_model_environment.update(proxy_variables)
+        app_name = "test-app-proxies"
+        result = CliRunner(env={**environment_variables, **proxy_variables}).invoke(
+            mfscli.commands,
+            ["deploy", "-a", app_name, "-m", model_s3_uri, "--mode", mfs.DEPLOYMENT_MODE_CREATE],
+        )
+    else:
+        proxy_variables = {
+            "http_proxy": None,
+            "https_proxy": None,
+            "no_proxy": None,
+        }
+        app_name = "test-app"
+        result = CliRunner(env={**environment_variables, **proxy_variables}).invoke(
+            mfscli.commands,
+            ["deploy", "-a", app_name, "-m", model_s3_uri, "--mode", mfs.DEPLOYMENT_MODE_CREATE],
+        )
     assert result.exit_code == 0
 
     region_name = sagemaker_client.meta.region_name
@@ -386,10 +491,8 @@ def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_e
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
+
+    assert model_environment == expected_model_environment
 
 
 @mock_sagemaker_aws_services
diff --git a/tests/sagemaker/test_sagemaker_deployment_client.py b/tests/sagemaker/test_sagemaker_deployment_client.py
--- a/tests/sagemaker/test_sagemaker_deployment_client.py
+++ b/tests/sagemaker/test_sagemaker_deployment_client.py
@@ -60,8 +60,10 @@ def sagemaker_deployment_client():
     )
 
 
-def create_sagemaker_deployment_through_cli(app_name, model_uri, region_name):
-    result = CliRunner(env={"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}).invoke(
+def create_sagemaker_deployment_through_cli(app_name, model_uri, region_name, env=None):
+    if env is None:
+        env = {"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}
+    result = CliRunner(env=env).invoke(
         cli_commands,
         [
             "create",
@@ -303,15 +305,36 @@ def test_attempting_to_deploy_in_asynchronous_mode_without_archiving_throws_exce
     assert exc.value.error_code == ErrorCode.Name(INVALID_PARAMETER_VALUE)
 
 
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_create_deployment_create_sagemaker_and_s3_resources_with_expected_names_and_env_from_local(
-    pretrained_model, sagemaker_client, sagemaker_deployment_client
+    proxies_enabled, pretrained_model, sagemaker_client, sagemaker_deployment_client
 ):
-    name = "test-app"
-    sagemaker_deployment_client.create_deployment(
-        name=name,
-        model_uri=pretrained_model.model_uri,
-    )
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
+
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "https://user:password@proxy.example.net:1234",
+            "no_proxy": "localhost",
+        }
+        expected_model_environment.update(proxy_variables)
+        name = "test-app-proxies"
+        with mock.patch.dict(os.environ, proxy_variables, clear=True):
+            sagemaker_deployment_client.create_deployment(
+                name=name,
+                model_uri=pretrained_model.model_uri,
+            )
+    else:
+        name = "test-app"
+        with mock.patch.dict(os.environ, {}, clear=True):
+            sagemaker_deployment_client.create_deployment(
+                name=name,
+                model_uri=pretrained_model.model_uri,
+            )
 
     region_name = sagemaker_client.meta.region_name
     s3_client = boto3.client("s3", region_name=region_name)
@@ -335,19 +358,49 @@ def test_create_deployment_create_sagemaker_and_s3_resources_with_expected_names
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
+
+    assert model_environment == expected_model_environment
 
 
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_local(
-    pretrained_model, sagemaker_client
+    proxies_enabled, pretrained_model, sagemaker_client
 ):
-    app_name = "test-app"
     region_name = sagemaker_client.meta.region_name
-    create_sagemaker_deployment_through_cli(app_name, pretrained_model.model_uri, region_name)
+    environment_variables = {"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
+
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "http://user:password@proxy.example.net:1234",
+            "no_proxy": "localhost",
+        }
+        expected_model_environment.update(proxy_variables)
+        app_name = "test-app-proxies"
+        create_sagemaker_deployment_through_cli(
+            app_name,
+            pretrained_model.model_uri,
+            region_name,
+            {**environment_variables, **proxy_variables},
+        )
+    else:
+        proxy_variables = {
+            "http_proxy": None,
+            "https_proxy": None,
+            "no_proxy": None,
+        }
+        app_name = "test-app"
+        create_sagemaker_deployment_through_cli(
+            app_name,
+            pretrained_model.model_uri,
+            region_name,
+            {**environment_variables, **proxy_variables},
+        )
 
     s3_client = boto3.client("s3", region_name=region_name)
     default_bucket = mfs._get_default_s3_bucket(region_name)
@@ -370,15 +423,14 @@ def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_e
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
+
+    assert model_environment == expected_model_environment
 
 
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_create_deployment_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_s3(
-    pretrained_model, sagemaker_client, sagemaker_deployment_client
+    proxies_enabled, pretrained_model, sagemaker_client, sagemaker_deployment_client
 ):
     local_model_path = _download_artifact_from_uri(pretrained_model.model_uri)
     artifact_path = "model"
@@ -389,12 +441,31 @@ def test_create_deployment_creates_sagemaker_and_s3_resources_with_expected_name
     model_s3_uri = "s3://{bucket_name}/{artifact_path}".format(
         bucket_name=default_bucket, artifact_path=pretrained_model.model_path
     )
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
 
-    name = "test-app"
-    sagemaker_deployment_client.create_deployment(
-        name=name,
-        model_uri=model_s3_uri,
-    )
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "http://user:password@proxy.example.net:1234",
+            "no_proxy": "localhost",
+        }
+        expected_model_environment.update(proxy_variables)
+        name = "test-app-proxies"
+        with mock.patch.dict(os.environ, proxy_variables, clear=True):
+            sagemaker_deployment_client.create_deployment(
+                name=name,
+                model_uri=model_s3_uri,
+            )
+    else:
+        name = "test-app"
+        with mock.patch.dict(os.environ, {}, clear=True):
+            sagemaker_deployment_client.create_deployment(
+                name=name,
+                model_uri=model_s3_uri,
+            )
 
     endpoint_description = sagemaker_client.describe_endpoint(EndpointName=name)
     endpoint_production_variants = endpoint_description["ProductionVariants"]
@@ -417,15 +488,14 @@ def test_create_deployment_creates_sagemaker_and_s3_resources_with_expected_name
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
 
+    assert model_environment == expected_model_environment
 
+
+@pytest.mark.parametrize("proxies_enabled", [True, False])
 @mock_sagemaker_aws_services
 def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_env_from_s3(
-    pretrained_model, sagemaker_client
+    proxies_enabled, pretrained_model, sagemaker_client
 ):
     local_model_path = _download_artifact_from_uri(pretrained_model.model_uri)
     artifact_path = "model"
@@ -436,9 +506,39 @@ def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_e
     model_s3_uri = "s3://{bucket_name}/{artifact_path}".format(
         bucket_name=default_bucket, artifact_path=pretrained_model.model_path
     )
+    environment_variables = {"LC_ALL": "en_US.UTF-8", "LANG": "en_US.UTF-8"}
+    expected_model_environment = {
+        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
+        "SERVING_ENVIRONMENT": "SageMaker",
+    }
 
-    app_name = "test-app"
-    create_sagemaker_deployment_through_cli(app_name, model_s3_uri, region_name)
+    if proxies_enabled:
+        proxy_variables = {
+            "http_proxy": "http://user:password@proxy.example.net:1234",
+            "https_proxy": "https://user:password@proxy.example.net:1234",
+            "no_proxy": "localhost",
+        }
+        expected_model_environment.update(proxy_variables)
+        app_name = "test-app-proxies"
+        create_sagemaker_deployment_through_cli(
+            app_name,
+            model_s3_uri,
+            region_name,
+            {**environment_variables, **proxy_variables},
+        )
+    else:
+        proxy_variables = {
+            "http_proxy": None,
+            "https_proxy": None,
+            "no_proxy": None,
+        }
+        app_name = "test-app"
+        create_sagemaker_deployment_through_cli(
+            app_name,
+            model_s3_uri,
+            region_name,
+            {**environment_variables, **proxy_variables},
+        )
 
     region_name = sagemaker_client.meta.region_name
     s3_client = boto3.client("s3", region_name=region_name)
@@ -462,10 +562,8 @@ def test_deploy_cli_creates_sagemaker_and_s3_resources_with_expected_names_and_e
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
-        "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
-        "SERVING_ENVIRONMENT": "SageMaker",
-    }
+
+    assert model_environment == expected_model_environment
 
 
 @mock_sagemaker_aws_services
@@ -1005,10 +1103,20 @@ def test_deploy_cli_updates_sagemaker_and_s3_resources_in_replace_mode(
     model_environment = sagemaker_client.describe_model(ModelName=model_name)["PrimaryContainer"][
         "Environment"
     ]
-    assert model_environment == {
+    expected_model_environment = {
         "MLFLOW_DEPLOYMENT_FLAVOR_NAME": "python_function",
         "SERVING_ENVIRONMENT": "SageMaker",
     }
+    if os.getenv("http_proxy") is not None:
+        expected_model_environment.update({"http_proxy": os.environ["http_proxy"]})
+
+    if os.getenv("https_proxy") is not None:
+        expected_model_environment.update({"https_proxy": os.environ["https_proxy"]})
+
+    if os.getenv("no_proxy") is not None:
+        expected_model_environment.update({"no_proxy": os.environ["no_proxy"]})
+
+    assert model_environment == expected_model_environment
 
 
 @mock_sagemaker_aws_services