Skip to content

Commit 8cf9631

Browse files
pintaoz-awsbeniericnargokulpravali96
committed
Support building image from Dockerfile (#1571)
* Base model trainer (#1521) * Base model trainer * flake8 * add testing notebook * add param validation & set defaults * Implement simple train method * feature: support script mode with local train.sh (#1523) * feature: support script mode with local train.sh * Stop tracking train.sh and add it to .gitignore * update message * make dir if not exist * fix docs * fix: docstyle * Address comments * fix hyperparams * Revert pydantic custom error * pylint * Image Spec refactoring and updates (#1525) * Image Spec refactoring and updates * Unit tests and update function for Image Spec * Fix hugging face test * Fix Tests * Add unit tests for ModelTrainer (#1527) * Add unit tests for ModelTrainer * Flake8 * format * Add example notebook (#1528) * Add testing notebook * format * use smaller data * remove large dataset * update * pylint * flake8 * ignore docstyle in directories with test * format * format * Add enviornment variable bootstrapping script (#1530) * Add enviornment variables scripts * format * fix comment * add docstrings * fix comment * feature: add utility function to capture local snapshot (#1524) * local snapshot * Update pip list command * Remove function calls * Address comments * Address comments * Support intelligent parameters (#1540) * Support intelligent parameters * fix codestyle * Revert Image Spec (#1541) * Cleanup ModelTrainer (#1542) * General image builder (#1546) * General image builder * General image builder * Fix codestyle * Fix codestyle * Move location * Add warnings * Add integ tests * Fix integ test * Fix integ test * Fix region error * Add region * Latest Container Image (#1545) * Latest Container Image * Test Fixes * Parameterized tests and some logic updates * Test fixes * Move to Image URI * Fixes for unit test * Fixes for unit test * Fix codestyle error checks * Cleanup ModelTrainer code (#1552) * feat: add pre-processing and post-processing logic to inference_spec (#1560) * add pre-processing and post-processing logic to inference_spec * fix format * make accept_type and content_type optional * remove accept_type and content_type from pre/post processing * correct typo * Add Distributed Training Support Model Trainer (#1536) * Add path to set Additional Settings in ModelTrainer (#1555) * Support building image from Dockerfile * Fix test * Fix test * Rename functions --------- Co-authored-by: Erick Benitez-Ramos <[email protected]> Co-authored-by: Gokul Anantha Narayanan <[email protected]> Co-authored-by: Pravali Uppugunduri <[email protected]>
1 parent debcdc2 commit 8cf9631

File tree

3 files changed

+76
-13
lines changed

3 files changed

+76
-13
lines changed

src/sagemaker/modules/image_builder.py

+58-9
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"""
8282

8383

84-
def build_image(
84+
def build_image_from_base(
8585
image_name: str = "sm-custom-image",
8686
env_name: str = "sm_custom_env",
8787
deploy_to_ecr: bool = False,
@@ -162,7 +162,7 @@ def build_image(
162162
return None
163163

164164

165-
def capture_local_environment(
165+
def build_image_from_local(
166166
image_name: str = "sm-local-capture",
167167
env_name: str = "saved_local_env",
168168
package_manager: str = "pip",
@@ -285,13 +285,13 @@ def capture_local_environment(
285285
f.write(additional_requirements)
286286
logger.info("Merged requirements file saved to %s", requirement_txt_path)
287287

288-
if not base_image:
289-
version = sys.version_info
290-
base_image = f"python:{version.major}.{version.minor}.{version.micro}"
291-
dockerfile_contents = PIP_DOCKERFILE_TEMPLATE.format(
292-
base_image=base_image,
293-
env_name=env_name,
294-
)
288+
if not base_image:
289+
version = sys.version_info
290+
base_image = f"python:{version.major}.{version.minor}.{version.micro}"
291+
dockerfile_contents = PIP_DOCKERFILE_TEMPLATE.format(
292+
base_image=base_image,
293+
env_name=env_name,
294+
)
295295

296296
else:
297297
raise ValueError(
@@ -306,6 +306,55 @@ def capture_local_environment(
306306
return None
307307

308308

309+
def build_image_from_dockerfile(
310+
image_name: str,
311+
dockerfile: str,
312+
deploy_to_ecr: bool = False,
313+
ecr_repo_name: Optional[str] = None,
314+
boto_session: Optional[boto3.Session] = None,
315+
region: Optional[str] = None,
316+
) -> Optional[str]:
317+
"""Build a Docker image with Dockerfile.
318+
319+
Args:
320+
image_name (str): The name of the docker image.
321+
dockerfile (str): The file path to the Dockerfile.
322+
deploy_to_ecr (bool): Whether to deploy the docker image to AWS ECR, defaults to False.
323+
If set to True, the AWS credentials must be configured in the environment.
324+
ecr_repo_name (Optional[str]): The AWS ECR repo to push the docker image. If not specified,
325+
it will use image_name as the ECR repo name. This parameter is only valid when
326+
deploy_to_ecr is True.
327+
boto_session (Optional[boto3.Session]): The boto3 session with AWS account info. If not
328+
provided, a new boto session will be created.
329+
region (Optional[str]): The AWS region.
330+
331+
Exceptions:
332+
docker.errors.DockerException: Error while fetching server API version:
333+
The docker engine is not running in your environment.
334+
docker.errors.BuildError: The docker failed to build the image. The most likely reason is:
335+
1) Some packages are not supported in the base image. 2) There are dependency conflicts
336+
between your local environment and additional dependencies.
337+
botocore.exceptions.ClientError: AWS credentials are not configured.
338+
"""
339+
absolute_path = os.path.abspath(dockerfile)
340+
directory = os.path.dirname(absolute_path)
341+
342+
client = docker.from_env()
343+
_, logs = client.images.build(
344+
path=directory,
345+
dockerfile=absolute_path,
346+
rm=True,
347+
tag=image_name,
348+
)
349+
for log in logs:
350+
logger.info(log.get("stream", "").strip())
351+
logger.info("Docker image %s built successfully", image_name)
352+
353+
if deploy_to_ecr:
354+
return _push_image_to_ecr(image_name, ecr_repo_name, boto_session, region)
355+
return None
356+
357+
309358
def _merge_environment_ymls(env_name: str, env_file1: str, env_file2: str, output_file: str):
310359
"""Merge two environment.yml files and save to a new environment.yml file.
311360
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
FROM python:3.12.4
2+
3+
WORKDIR /app
4+
5+
RUN pip install --no-cache-dir numpy scipy sagemaker

tests/integ/sagemaker/modules/test_image_builder.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,33 @@
1515

1616
import os
1717

18-
from sagemaker.modules.image_builder import build_image
18+
from sagemaker.modules.image_builder import build_image_from_base, build_image_from_dockerfile
1919

2020

2121
def test_build_public_image_locally():
22-
build_image(image_name="python_310", base_image="python:3.10")
22+
build_image_from_base(image_name="python_310", base_image="python:3.10")
2323

2424

2525
def test_build_with_dependency_file():
2626
dependency_file_path = os.getcwd() + "/tests/integ/sagemaker/modules/requirements.txt"
27-
build_image(image_name="ubuntu_with_dependencies", dependency_file=dependency_file_path)
27+
build_image_from_base(
28+
image_name="ubuntu_with_dependencies", dependency_file=dependency_file_path
29+
)
2830

2931

3032
def test_build_image_and_push_to_ecr():
3133
dependency_file_path = os.getcwd() + "/tests/integ/sagemaker/modules/environment.yml"
32-
build_image(
34+
build_image_from_base(
3335
image_name="ecr_test_image",
3436
dependency_file=dependency_file_path,
3537
base_image="debian",
3638
deploy_to_ecr=True,
3739
ecr_repo_name="image_builder_integ_test",
3840
)
41+
42+
43+
def test_build_image_from_dockerfile():
44+
dockerfile_path = os.getcwd() + "/tests/integ/sagemaker/modules/Dockerfile"
45+
build_image_from_dockerfile(
46+
image_name="image_from_dockerfile", dockerfile=dockerfile_path, deploy_to_ecr=True
47+
)

0 commit comments

Comments
 (0)