Skip to content

Commit 0b48b50

Browse files
authored
chore: Remove outer directories for building CPR images. (#1572)
* chore: Remove outer directories for building CPR images. * chore: Fixed coments.
1 parent 4e7175f commit 0b48b50

File tree

7 files changed

+176
-107
lines changed

7 files changed

+176
-107
lines changed

google/cloud/aiplatform/docker_utils/build.py

+21-30
Original file line numberDiff line numberDiff line change
@@ -117,31 +117,25 @@ def _prepare_dependency_entries(
117117
)
118118

119119
if requirements_path is not None:
120-
ret += _generate_copy_command(
121-
requirements_path,
122-
"./requirements.txt",
123-
comment="requirements.txt file specified, thus copy it to the docker container.",
124-
) + textwrap.dedent(
120+
ret += textwrap.dedent(
125121
"""
126-
RUN {} install --no-cache-dir {} -r ./requirements.txt
122+
RUN {} install --no-cache-dir {} -r {}
127123
""".format(
128124
pip_command,
129125
"--force-reinstall" if force_reinstall else "",
126+
requirements_path,
130127
)
131128
)
132129

133130
if extra_packages is not None:
134-
for extra in extra_packages:
135-
package_name = os.path.basename(extra)
131+
for package in extra_packages:
136132
ret += textwrap.dedent(
137133
"""
138-
{}
139134
RUN {} install --no-cache-dir {} {}
140135
""".format(
141-
_generate_copy_command(extra, package_name),
142136
pip_command,
143137
"--force-reinstall" if force_reinstall else "",
144-
quote(package_name),
138+
quote(package),
145139
)
146140
)
147141

@@ -190,24 +184,18 @@ def _prepare_entrypoint(package: Package, python_command: str = "python") -> str
190184
return "\nENTRYPOINT {}\n".format(exec_str)
191185

192186

193-
def _prepare_package_entry(package: Package) -> str:
194-
"""Returns the Dockerfile entries required to append at the end before entrypoint.
195-
196-
Including:
197-
- copy the parent directory of the main executable into a docker container.
198-
- inject an entrypoint that executes a script or python module inside that
199-
directory.
187+
def _copy_source_directory() -> str:
188+
"""Returns the Dockerfile entry required to copy the package to the image.
200189
201-
Args:
202-
package (Package):
203-
Required. The main application copied to and run in the container.
190+
The Docker build context has been changed to host_workdir. We copy all
191+
the files to the working directory of images.
204192
205193
Returns:
206-
The generated package related command used in Dockerfile.
194+
The generated package related copy command used in Dockerfile.
207195
"""
208196
copy_code = _generate_copy_command(
209197
".", # Dockefile context location has been changed to host_workdir
210-
Path(package.package_path).name,
198+
".", # Copy all the files to the working directory of images.
211199
comment="Copy the source directory into the docker container.",
212200
)
213201

@@ -275,14 +263,18 @@ def _get_relative_path_to_workdir(
275263
The relative path to the workdir or None if path is None.
276264
277265
Raises:
278-
ValueError: If the path is not relative to the workdir.
266+
ValueError: If the path does not exist or is not relative to the workdir.
279267
"""
280268
if path is None:
281269
return None
282270

271+
if not Path(path).is_file():
272+
raise ValueError(f'The {value_name} "{path}" must exist.')
283273
if not path_utils._is_relative_to(path, workdir):
284274
raise ValueError(f'The {value_name} "{path}" must be in "{workdir}".')
285-
return Path(path).relative_to(workdir).as_posix()
275+
abs_path = Path(path).expanduser().resolve()
276+
abs_workdir = Path(workdir).expanduser().resolve()
277+
return Path(abs_path).relative_to(abs_workdir).as_posix()
286278

287279

288280
def make_dockerfile(
@@ -382,8 +374,10 @@ def make_dockerfile(
382374
environment_variables=environment_variables
383375
)
384376

385-
# Installs packages from requirements_path which copies requirements_path
386-
# to the image before installing.
377+
# Copies user code to the image.
378+
dockerfile += _copy_source_directory()
379+
380+
# Installs packages from requirements_path.
387381
dockerfile += _prepare_dependency_entries(
388382
requirements_path=requirements_path,
389383
setup_path=None,
@@ -394,9 +388,6 @@ def make_dockerfile(
394388
pip_command=pip_command,
395389
)
396390

397-
# Copies user code to the image.
398-
dockerfile += _prepare_package_entry(main_package)
399-
400391
# Installs additional packages from user code.
401392
dockerfile += _prepare_dependency_entries(
402393
requirements_path=None,

google/cloud/aiplatform/prediction/local_model.py

+25-22
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#
1717

1818
from copy import copy
19-
from pathlib import Path
2019
from typing import Dict, List, Optional, Sequence, Type
2120

2221
from google.cloud import aiplatform
@@ -161,37 +160,42 @@ def build_cpr_model(
161160
This method builds a docker image to include user-provided predictor, and handler.
162161
163162
An example src_dir (e.g. "./user_src_dir") provided looks like:
164-
.
165-
|-- user_src_dir/
166-
|-- predictor.py
167-
|-- requirements.txt
168-
|-- user_code/
169-
| |-- utils.py
170-
| |-- custom_package.tar.gz
171-
| |-- ...
172-
|-- ...
163+
user_src_dir/
164+
|-- predictor.py
165+
|-- requirements.txt
166+
|-- user_code/
167+
| |-- utils.py
168+
| |-- custom_package.tar.gz
169+
| |-- ...
170+
|-- ...
173171
174172
To build a custom container:
175173
local_model = LocalModel.build_cpr_model(
176174
"./user_src_dir",
177-
"us-docker.pkg.dev/[PROJECT]/[REPOSITORY]/[IMAGE_NAME]",
178-
predictor=[CUSTOM_PREDICTOR_CLASS],
175+
"us-docker.pkg.dev/$PROJECT/$REPOSITORY/$IMAGE_NAME$",
176+
predictor=$CUSTOM_PREDICTOR_CLASS,
179177
requirements_path="./user_src_dir/requirements.txt",
180178
extra_packages=["./user_src_dir/user_code/custom_package.tar.gz"],
181179
)
182180
183181
In the built image, it will look like:
184182
container_workdir/
183+
|-- predictor.py
185184
|-- requirements.txt
186-
|-- custom_package.tar.gz
187-
|-- user_src_dir/
188-
|-- predictor.py
189-
|-- requirements.txt
190-
|-- user_code/
191-
| |-- utils.py
192-
| |-- custom_package.tar.gz
193-
| |-- ...
194-
|-- ...
185+
|-- user_code/
186+
| |-- utils.py
187+
| |-- custom_package.tar.gz
188+
| |-- ...
189+
|-- ...
190+
191+
If you have any files or directories in the src_dir you would like to exclude in built
192+
images, you could add a file, .dockerignore, to the root of the src_dir and list all of
193+
them in it. See https://docs.docker.com/engine/reference/builder/#dockerignore-file for
194+
more details about the .dockerignore file.
195+
196+
In order to save and restore class instances transparently with Pickle, the class definition
197+
must be importable and live in the same module as when the object was stored. If you want to
198+
use Pickle, you must save your objects right under the src_dir you provide.
195199
196200
The created CPR images default the number of model server workers to the number of cores.
197201
Depending on the characteristics of your model, you may need to adjust the number of workers.
@@ -252,7 +256,6 @@ def build_cpr_model(
252256
handler, src_dir
253257
)
254258
environment_variables = {
255-
"CPR_USER_DIR_NAME": Path(src_dir).name,
256259
"HANDLER_MODULE": handler_module,
257260
"HANDLER_CLASS": handler_class,
258261
}

google/cloud/aiplatform/prediction/model_server.py

-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import logging
2020
import multiprocessing
2121
import os
22-
import sys
2322
import traceback
2423

2524
try:
@@ -187,10 +186,6 @@ def set_number_of_workers_from_env() -> None:
187186

188187

189188
if __name__ == "__main__":
190-
cpr_user_dir_name = os.getenv("CPR_USER_DIR_NAME")
191-
if cpr_user_dir_name:
192-
sys.path.insert(0, os.path.join(os.getcwd(), cpr_user_dir_name))
193-
194189
set_number_of_workers_from_env()
195190
uvicorn.run(
196191
"google.cloud.aiplatform.prediction.model_server:CprModelServer",

google/cloud/aiplatform/utils/prediction_utils.py

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ def inspect_source_from_class(
7070
custom_class_import_path.stem
7171
)
7272
custom_class_import = custom_class_import_path.as_posix().replace(os.sep, ".")
73-
custom_class_import = f"{src_dir_abs_path.name}.{custom_class_import}"
7473

7574
return custom_class_import, custom_class_name
7675

0 commit comments

Comments
 (0)