Skip to content

Commit d2e1f4e

Browse files
authored
Fixes for docker mounts (#150)
* Conditionally enable mitten from src * Fix docker mounts, docker_input_mapping -> input_mapping * Fix ENV string for mounts * Update userid if the docker user is existing
1 parent a6dcd31 commit d2e1f4e

File tree

21 files changed

+166
-73
lines changed

21 files changed

+166
-73
lines changed

.github/workflows/check-broken-links.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88
jobs:
99
markdown-link-check:
1010
runs-on: ubuntu-latest
11+
1112
# check out the latest version of the code
1213
steps:
1314
- uses: actions/checkout@v4

.github/workflows/test-mlc-script-features.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ jobs:
7171
mlc run script --tags=run,docker,container --adr.compiler.tags=gcc --docker_mlc_repo=mlcommons@mlperf-automations --docker_mlc_repo_branch=dev --image_name=mlc-script-app-image-classification-onnx-py --env.MLC_DOCKER_RUN_SCRIPT_TAGS=app,image-classification,onnx,python --env.MLC_DOCKER_IMAGE_BASE=ubuntu:22.04 --env.MLC_DOCKER_IMAGE_REPO=local --quiet
7272
7373
- name: Run MLPerf Inference Retinanet with native and virtual Python
74+
if: runner.os == 'linux'
7475
run: |
7576
mlcr --tags=app,mlperf,inference,generic,_cpp,_retinanet,_onnxruntime,_cpu --adr.python.version_min=3.8 --adr.compiler.tags=gcc --adr.openimages-preprocessed.tags=_50 --scenario=Offline --mode=accuracy --test_query_count=10 --rerun --quiet
7677

automation/script/docker.py

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,7 @@ def docker_run(self_module, i):
213213
env = i.get('env', {})
214214

215215
regenerate_docker_file = not i.get('docker_noregenerate', False)
216-
recreate_docker_image = i.get('docker_recreate', False)
217-
218-
if is_true(i.get('docker_skip_build', False)):
219-
regenerate_docker_file = False
220-
recreate_docker_image = False
221-
env['MLC_DOCKER_SKIP_BUILD'] = 'yes'
216+
rebuild_docker_image = i.get('docker_rebuild', False)
222217

223218
# Prune unnecessary Docker-related input keys
224219
r = prune_input({'input': i, 'extra_keys_starts_with': ['docker_']})
@@ -269,7 +264,10 @@ def docker_run(self_module, i):
269264
'alias', ''), meta.get(
270265
'uid', '')
271266

272-
mounts = copy.deepcopy(i.get('docker_mounts', []))
267+
mounts = copy.deepcopy(
268+
i.get(
269+
'docker_mounts',
270+
[])) # do we need a copy here?
273271
variations = meta.get('variations', {})
274272
docker_settings = meta.get('docker', {})
275273
state['docker'] = docker_settings
@@ -334,31 +332,45 @@ def docker_run(self_module, i):
334332
if r['return'] > 0:
335333
return r
336334

337-
# Handle environment variable-based mounts
338-
mounts = process_mounts(mounts, env, i, docker_settings)
339-
if mounts is None:
340-
return {'return': 1, 'error': 'Error processing mounts'}
341-
342335
# Prepare Docker-specific inputs
343336
docker_inputs, dockerfile_path = prepare_docker_inputs(
344337
i, docker_settings, script_path, True)
345338

346339
if docker_inputs is None:
347340
return {'return': 1, 'error': 'Error preparing Docker inputs'}
348341

342+
docker_input_mapping = docker_settings.get('input_mapping')
343+
344+
# Update env based on docker_input_mapping if they are in input
345+
if docker_input_mapping and i:
346+
env.update({docker_input_mapping[key]: i[key]
347+
for key in docker_input_mapping if key in i})
348+
349+
# Handle environment variable-based mounts
350+
res = process_mounts(mounts, env, docker_settings, f_run_cmd)
351+
if res['return'] > 0:
352+
return res
353+
docker_inputs['mounts'] = res['mounts']
354+
container_env_string = res['container_env_string']
355+
356+
res = update_docker_environment(
357+
docker_settings, env, container_env_string)
358+
if res['return'] > 0:
359+
return res
360+
349361
# Generate the run command
350362
r = regenerate_script_cmd({'script_uid': script_uid,
351363
'script_alias': script_alias,
352364
'tags': tags,
353365
'run_cmd': f_run_cmd})
354366
if r['return'] > 0:
355367
return r
356-
final_run_cmd = r['run_cmd_string']
368+
final_run_cmd = f"""{r['run_cmd_string']} {container_env_string} --docker_run_deps """
357369

358370
# Execute the Docker container
359371
mlc_docker_input = {
360372
'action': 'run', 'automation': 'script', 'tags': 'run,docker,container',
361-
'recreate': recreate_docker_image,
373+
'rebuild': rebuild_docker_image,
362374
'env': env, 'mounts': mounts,
363375
'script_tags': i.get('tags'), 'run_cmd': final_run_cmd, 'v': verbose,
364376
'quiet': True, 'real_run': True, 'add_deps_recursive': {'build-docker-image': {'dockerfile': dockerfile_path}},

automation/script/docker_utils.py

Lines changed: 122 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import copy
88

99

10-
def process_mounts(mounts, env, i, docker_settings):
10+
def process_mounts(mounts, env, docker_settings, f_run_cmd):
1111
"""
1212
Processes and updates the Docker mounts based on the provided inputs and environment variables.
1313
@@ -20,21 +20,71 @@ def process_mounts(mounts, env, i, docker_settings):
2020
Returns:
2121
Updated mounts list or None in case of an error.
2222
"""
23-
try:
24-
# Add mounts specified via `env` variables
25-
for mount_key in docker_settings.get('env_mounts', []):
26-
mount_path = env.get(mount_key, '')
27-
if mount_path:
28-
mounts.append(mount_path)
29-
30-
# Include user-specified additional mounts
31-
if 'docker_additional_mounts' in i:
32-
mounts.extend(i['docker_additional_mounts'])
33-
34-
return mounts
35-
except Exception as e:
36-
logging.error(f"Error processing mounts: {e}")
37-
return None
23+
if 'mounts' in docker_settings:
24+
mounts.extend(docker_settings['mounts'])
25+
26+
docker_input_mapping = docker_settings.get("input_mapping", {})
27+
container_env_string = ""
28+
29+
for index in range(len(mounts)):
30+
mount = mounts[index]
31+
32+
# Locate the last ':' to separate the mount into host and container
33+
# paths
34+
j = mount.rfind(':')
35+
if j <= 0:
36+
return {
37+
'return': 1,
38+
'error': f"Can't find separator ':' in the mount string: {mount}"
39+
}
40+
41+
host_mount, container_mount = mount[:j], mount[j + 1:]
42+
new_host_mount = host_mount
43+
new_container_mount = container_mount
44+
host_env_key, container_env_key = None, str(container_mount)
45+
46+
# Process host mount for environment variables
47+
host_placeholders = re.findall(r'\${{ (.*?) }}', host_mount)
48+
if host_placeholders:
49+
for placeholder in host_placeholders:
50+
if placeholder in env:
51+
host_env_key = placeholder
52+
new_host_mount = get_host_path(env[placeholder])
53+
else: # Skip mount if variable is missing
54+
mounts[index] = None
55+
break
56+
57+
# Process container mount for environment variables
58+
container_placeholders = re.findall(r'\${{ (.*?) }}', container_mount)
59+
if container_placeholders:
60+
for placeholder in container_placeholders:
61+
if placeholder in env:
62+
new_container_mount, container_env_key = get_container_path(
63+
env[placeholder])
64+
else: # Skip mount if variable is missing
65+
mounts[index] = None
66+
break
67+
68+
# Skip further processing if the mount was invalid
69+
if mounts[index] is None:
70+
continue
71+
72+
# Update mount entry
73+
mounts[index] = f"{new_host_mount}:{new_container_mount}"
74+
75+
# Update container environment string and mappings
76+
if host_env_key:
77+
container_env_string += f" --env.{host_env_key}={container_env_key} "
78+
for key, value in docker_input_mapping.items():
79+
if value == host_env_key:
80+
i[key] = container_env_key
81+
f_run_cmd[key] = container_env_key
82+
83+
# Remove invalid mounts and construct mount string
84+
mounts = [item for item in mounts if item is not None]
85+
86+
return {'return': 0, 'mounts': mounts,
87+
'container_env_string': container_env_string}
3888

3989

4090
def prepare_docker_inputs(input_params, docker_settings,
@@ -61,7 +111,7 @@ def prepare_docker_inputs(input_params, docker_settings,
61111
keys += [
62112
"skip_run_cmd", "pre_run_cmds", "run_cmd_prefix", "all_gpus", "num_gpus", "device", "gh_token",
63113
"port_maps", "shm_size", "pass_user_id", "pass_user_group", "extra_run_args", "detached", "interactive",
64-
"dt", "it"
114+
"dt", "it", "use_host_group_id", "use_host_user_id"
65115
]
66116
# Collect Dockerfile inputs
67117
docker_inputs = {
@@ -102,7 +152,57 @@ def prepare_docker_inputs(input_params, docker_settings,
102152
return docker_inputs, dockerfile_path
103153

104154

105-
def update_docker_paths(path, mounts=None, force_target_path=''):
155+
def update_docker_environment(docker_settings, env, container_env_string):
156+
"""
157+
Updates the Docker environment variables and build arguments.
158+
159+
Args:
160+
docker_settings (dict): Docker configuration settings.
161+
env (dict): The environment dictionary to update.
162+
container_env_string (str): A string to store Docker container environment variable options.
163+
164+
Returns:
165+
dict: A dictionary with a return code indicating success or failure.
166+
"""
167+
# Define proxy-related environment variable keys to propagate
168+
proxy_keys = [
169+
"ftp_proxy", "FTP_PROXY",
170+
"http_proxy", "HTTP_PROXY",
171+
"https_proxy", "HTTPS_PROXY",
172+
"no_proxy", "NO_PROXY",
173+
"socks_proxy", "SOCKS_PROXY",
174+
"GH_TOKEN"
175+
]
176+
177+
# Ensure the '+ CM_DOCKER_BUILD_ARGS' key exists in the environment
178+
if '+ MLC_DOCKER_BUILD_ARGS' not in env:
179+
env['+ MLC_DOCKER_BUILD_ARGS'] = []
180+
181+
# Add proxy environment variables to Docker build arguments and container
182+
# environment string
183+
for proxy_key in proxy_keys:
184+
proxy_value = os.environ.get(proxy_key)
185+
if proxy_value:
186+
container_env_string += f" --env.{proxy_key}={proxy_value} "
187+
env['+ MLC_DOCKER_BUILD_ARGS'].append(f"{proxy_key}={proxy_value}")
188+
189+
# Add host group ID if specified in the Docker settings and not on Windows
190+
if not is_false(docker_settings.get('pass_group_id')) and os.name != 'nt':
191+
env['+ MLC_DOCKER_BUILD_ARGS'].append(
192+
f"GID=\\\" $(id -g $USER) \\\""
193+
)
194+
195+
# Add host user ID if specified in the Docker settings and not on Windows
196+
if not is_false(docker_settings.get(
197+
'use_host_user_id')) and os.name != 'nt':
198+
env['+ MLC_DOCKER_BUILD_ARGS'].append(
199+
f"UID=\\\" $(id -u $USER) \\\""
200+
)
201+
202+
return {'return': 0}
203+
204+
205+
def update_container_paths(path, mounts=None, force_target_path=''):
106206
"""
107207
Update and return the absolute paths for a given host path and its container equivalent.
108208
Optionally updates a mounts list with the mapping of host and container paths.
@@ -275,7 +375,9 @@ def get_docker_default(key):
275375
"skip_run_cmd": False,
276376
"pre_run_cmds": [],
277377
"run_cmd_prefix": '',
278-
"port_maps": []
378+
"port_maps": [],
379+
"use_host_user_id": True,
380+
"use_host_group_id": True,
279381
}
280382
if key in defaults:
281383
return defaults[key]
@@ -317,5 +419,5 @@ def get_container_path(value):
317419
new_path_split2 = new_path_split + path_split[repo_entry_index:]
318420
return "/".join(new_path_split1), "/".join(new_path_split2)
319421
else:
320-
orig_path, target_path = update_path_for_docker(path=value)
422+
orig_path, target_path = update_container_paths(path=value)
321423
return target_path, target_path

automation/script/module.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -823,10 +823,6 @@ def _run(self, i):
823823
posthook_deps = meta.get('posthook_deps', [])
824824
input_mapping = meta.get('input_mapping', {})
825825
docker_settings = meta.get('docker')
826-
docker_input_mapping = {}
827-
if docker_settings:
828-
docker_input_mapping = docker_settings.get(
829-
'docker_input_mapping', {})
830826
new_env_keys_from_meta = meta.get('new_env_keys', [])
831827
new_state_keys_from_meta = meta.get('new_state_keys', [])
832828

@@ -6058,11 +6054,6 @@ def update_state_from_meta(meta, env, state, const, const_state, deps, post_deps
60586054
new_docker_settings = meta.get('docker')
60596055
if new_docker_settings:
60606056
docker_settings = state.get('docker', {})
6061-
# docker_input_mapping = docker_settings.get('docker_input_mapping', {})
6062-
# new_docker_input_mapping = new_docker_settings.get('docker_input_mapping', {})
6063-
# if new_docker_input_mapping:
6064-
# # update_env_from_input_mapping(env, i['input'], docker_input_mapping)
6065-
# utils.merge_dicts({'dict1':docker_input_mapping, 'dict2':new_docker_input_mapping, 'append_lists':True, 'append_unique':True})
60666057
utils.merge_dicts({'dict1': docker_settings,
60676058
'dict2': new_docker_settings,
60686059
'append_lists': True,

script/app-mlperf-inference-nvidia/meta.yaml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,15 +281,14 @@ deps:
281281
enable_if_env:
282282
MLC_RUN_STATE_DOCKER:
283283
- 'yes'
284-
- True
285-
- 'True'
286284

287285
- tags: get,nvidia,mitten
288286
skip_if_env:
289287
MLC_RUN_STATE_DOCKER:
290288
- 'yes'
291-
- True
292-
- 'True'
289+
enable_if_env:
290+
MLC_NVIDIA_MITTEN_FROM_SRC:
291+
- 'yes'
293292

294293
prehook_deps:
295294
########################################################################
@@ -446,7 +445,7 @@ variations:
446445
group: model
447446
env:
448447
MLC_MODEL: stable-diffusion-xl
449-
MLC_ML_MODEL_STARTING_WEIGHTS_FILENAME: "https://github.com/mlcommons/cm4mlops/blob/main/script/get-ml-model-stable-diffusion/_cm.json#L174"
448+
MLC_ML_MODEL_STARTING_WEIGHTS_FILENAME: "https://github.com/mlcommons/mlperf-automations/blob/main/script/get-ml-model-stable-diffusion/meta.yaml"
450449
MLC_ML_MODEL_WEIGHT_TRANSFORMATIONS: "quantization, affine fusion"
451450
MLC_ML_MODEL_INPUTS_DATA_TYPE: int32
452451
MLC_ML_MODEL_WEIGHTS_DATA_TYPE: int8

script/app-mlperf-inference/meta.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ variations:
519519
os: ubuntu
520520
real_run: false
521521
run: true
522-
docker_input_mapping:
522+
input_mapping:
523523
criteo_preprocessed_path: CRITEO_PREPROCESSED_PATH
524524
dlrm_data_path: DLRM_DATA_PATH
525525
intel_gptj_int8_model_path: MLC_MLPERF_INFERENCE_INTEL_GPTJ_INT8_MODEL_PATH
@@ -1905,7 +1905,7 @@ docker:
19051905
mlc_repo_branch: dev
19061906
real_run: False
19071907
os_version: '22.04'
1908-
docker_input_mapping:
1908+
input_mapping:
19091909
imagenet_path: IMAGENET_PATH
19101910
gptj_checkpoint_path: GPTJ_CHECKPOINT_PATH
19111911
criteo_preprocessed_path: CRITEO_PREPROCESSED_PATH

script/build-docker-image/customize.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from mlc import utils
22
import os
33
from os.path import exists
4+
from utils import *
45

56

67
def preprocess(i):
@@ -50,7 +51,7 @@ def preprocess(i):
5051
if env.get("MLC_DOCKER_IMAGE_TAG", "") == '':
5152
env['MLC_DOCKER_IMAGE_TAG'] = "latest"
5253

53-
if str(env.get("MLC_DOCKER_CACHE", "yes")).lower() in ["no", "false", "0"]:
54+
if is_false(env.get("MLC_DOCKER_CACHE", "True")):
5455
env["MLC_DOCKER_CACHE_ARG"] = " --no-cache"
5556

5657
CMD = ''
@@ -82,13 +83,8 @@ def preprocess(i):
8283

8384
CMD = ''.join(XCMD)
8485

85-
print('================================================')
86-
print('CM generated the following Docker build command:')
87-
print('')
8886
print(CMD)
8987

90-
print('')
91-
9288
env['MLC_DOCKER_BUILD_CMD'] = CMD
9389

9490
return {'return': 0}
@@ -108,7 +104,7 @@ def postprocess(i):
108104
env = i['env']
109105

110106
# Check if need to push docker image to the Docker Hub
111-
if env.get('MLC_DOCKER_PUSH_IMAGE', '') in ['True', True, 'yes']:
107+
if is_true(env.get('MLC_DOCKER_PUSH_IMAGE', '')):
112108
image_name = get_image_name(env)
113109

114110
# Prepare CMD to build image
@@ -122,9 +118,6 @@ def postprocess(i):
122118
with open(dockerfile_path + '.build.bat', 'w') as f:
123119
f.write(PCMD + '\n')
124120

125-
print('================================================')
126-
print('CM generated the following Docker push command:')
127-
print('')
128121
print(PCMD)
129122

130123
print('')

0 commit comments

Comments
 (0)