@aws/ml-container-creator 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/LICENSE-THIRD-PARTY +68620 -0
- package/NOTICE +2 -0
- package/README.md +106 -0
- package/bin/cli.js +365 -0
- package/config/defaults.json +32 -0
- package/config/presets/transformers-djl.json +26 -0
- package/config/presets/transformers-gpu.json +24 -0
- package/config/presets/transformers-lmi.json +27 -0
- package/package.json +129 -0
- package/servers/README.md +419 -0
- package/servers/base-image-picker/catalogs/model-servers.json +1191 -0
- package/servers/base-image-picker/catalogs/python-slim.json +38 -0
- package/servers/base-image-picker/catalogs/triton-backends.json +51 -0
- package/servers/base-image-picker/catalogs/triton.json +38 -0
- package/servers/base-image-picker/index.js +495 -0
- package/servers/base-image-picker/manifest.json +17 -0
- package/servers/base-image-picker/package.json +15 -0
- package/servers/hyperpod-cluster-picker/LICENSE +202 -0
- package/servers/hyperpod-cluster-picker/index.js +424 -0
- package/servers/hyperpod-cluster-picker/manifest.json +14 -0
- package/servers/hyperpod-cluster-picker/package.json +17 -0
- package/servers/instance-recommender/LICENSE +202 -0
- package/servers/instance-recommender/catalogs/instances.json +852 -0
- package/servers/instance-recommender/index.js +284 -0
- package/servers/instance-recommender/manifest.json +16 -0
- package/servers/instance-recommender/package.json +15 -0
- package/servers/lib/LICENSE +202 -0
- package/servers/lib/bedrock-client.js +160 -0
- package/servers/lib/custom-validators.js +46 -0
- package/servers/lib/dynamic-resolver.js +36 -0
- package/servers/lib/package.json +11 -0
- package/servers/lib/schemas/image-catalog.schema.json +185 -0
- package/servers/lib/schemas/instances.schema.json +124 -0
- package/servers/lib/schemas/manifest.schema.json +64 -0
- package/servers/lib/schemas/model-catalog.schema.json +91 -0
- package/servers/lib/schemas/regions.schema.json +26 -0
- package/servers/lib/schemas/triton-backends.schema.json +51 -0
- package/servers/model-picker/catalogs/jumpstart-public.json +66 -0
- package/servers/model-picker/catalogs/popular-diffusors.json +88 -0
- package/servers/model-picker/catalogs/popular-transformers.json +226 -0
- package/servers/model-picker/index.js +1693 -0
- package/servers/model-picker/manifest.json +18 -0
- package/servers/model-picker/package.json +20 -0
- package/servers/region-picker/LICENSE +202 -0
- package/servers/region-picker/catalogs/regions.json +263 -0
- package/servers/region-picker/index.js +230 -0
- package/servers/region-picker/manifest.json +16 -0
- package/servers/region-picker/package.json +15 -0
- package/src/app.js +1007 -0
- package/src/copy-tpl.js +77 -0
- package/src/lib/accelerator-validator.js +39 -0
- package/src/lib/asset-manager.js +385 -0
- package/src/lib/aws-profile-parser.js +181 -0
- package/src/lib/bootstrap-command-handler.js +1647 -0
- package/src/lib/bootstrap-config.js +238 -0
- package/src/lib/ci-register-helpers.js +124 -0
- package/src/lib/ci-report-helpers.js +158 -0
- package/src/lib/ci-stage-helpers.js +268 -0
- package/src/lib/cli-handler.js +529 -0
- package/src/lib/comment-generator.js +544 -0
- package/src/lib/community-reports-validator.js +91 -0
- package/src/lib/config-manager.js +2106 -0
- package/src/lib/configuration-exporter.js +204 -0
- package/src/lib/configuration-manager.js +695 -0
- package/src/lib/configuration-matcher.js +221 -0
- package/src/lib/cpu-validator.js +36 -0
- package/src/lib/cuda-validator.js +57 -0
- package/src/lib/deployment-config-resolver.js +103 -0
- package/src/lib/deployment-entry-schema.js +125 -0
- package/src/lib/deployment-registry.js +598 -0
- package/src/lib/docker-introspection-validator.js +51 -0
- package/src/lib/engine-prefix-resolver.js +60 -0
- package/src/lib/huggingface-client.js +172 -0
- package/src/lib/key-value-parser.js +37 -0
- package/src/lib/known-flags-validator.js +200 -0
- package/src/lib/manifest-cli.js +280 -0
- package/src/lib/mcp-client.js +303 -0
- package/src/lib/mcp-command-handler.js +532 -0
- package/src/lib/neuron-validator.js +80 -0
- package/src/lib/parameter-schema-validator.js +284 -0
- package/src/lib/prompt-runner.js +1349 -0
- package/src/lib/prompts.js +1138 -0
- package/src/lib/registry-command-handler.js +519 -0
- package/src/lib/registry-loader.js +198 -0
- package/src/lib/rocm-validator.js +80 -0
- package/src/lib/schema-validator.js +157 -0
- package/src/lib/sensitive-redactor.js +59 -0
- package/src/lib/template-engine.js +156 -0
- package/src/lib/template-manager.js +341 -0
- package/src/lib/validation-engine.js +314 -0
- package/src/prompt-adapter.js +63 -0
- package/templates/Dockerfile +300 -0
- package/templates/IAM_PERMISSIONS.md +84 -0
- package/templates/MIGRATION.md +488 -0
- package/templates/PROJECT_README.md +439 -0
- package/templates/TEMPLATE_SYSTEM.md +243 -0
- package/templates/buildspec.yml +64 -0
- package/templates/code/chat_template.jinja +1 -0
- package/templates/code/flask/gunicorn_config.py +35 -0
- package/templates/code/flask/wsgi.py +10 -0
- package/templates/code/model_handler.py +387 -0
- package/templates/code/serve +300 -0
- package/templates/code/serve.py +175 -0
- package/templates/code/serving.properties +105 -0
- package/templates/code/start_server.py +39 -0
- package/templates/code/start_server.sh +39 -0
- package/templates/diffusors/Dockerfile +72 -0
- package/templates/diffusors/patch_image_api.py +35 -0
- package/templates/diffusors/serve +115 -0
- package/templates/diffusors/start_server.sh +114 -0
- package/templates/do/.gitkeep +1 -0
- package/templates/do/README.md +541 -0
- package/templates/do/build +83 -0
- package/templates/do/ci +681 -0
- package/templates/do/clean +811 -0
- package/templates/do/config +260 -0
- package/templates/do/deploy +1560 -0
- package/templates/do/export +306 -0
- package/templates/do/logs +319 -0
- package/templates/do/manifest +12 -0
- package/templates/do/push +119 -0
- package/templates/do/register +580 -0
- package/templates/do/run +113 -0
- package/templates/do/submit +417 -0
- package/templates/do/test +1147 -0
- package/templates/hyperpod/configmap.yaml +24 -0
- package/templates/hyperpod/deployment.yaml +71 -0
- package/templates/hyperpod/pvc.yaml +42 -0
- package/templates/hyperpod/service.yaml +17 -0
- package/templates/nginx-diffusors.conf +74 -0
- package/templates/nginx-predictors.conf +47 -0
- package/templates/nginx-tensorrt.conf +74 -0
- package/templates/requirements.txt +61 -0
- package/templates/sample_model/test_inference.py +123 -0
- package/templates/sample_model/train_abalone.py +252 -0
- package/templates/test/test_endpoint.sh +79 -0
- package/templates/test/test_local_image.sh +80 -0
- package/templates/test/test_model_handler.py +180 -0
- package/templates/triton/Dockerfile +128 -0
- package/templates/triton/config.pbtxt +163 -0
- package/templates/triton/model.py +130 -0
- package/templates/triton/requirements.txt +11 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
set -u
|
|
7
|
+
set -o pipefail
|
|
8
|
+
|
|
9
|
+
# Source configuration
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
source "${SCRIPT_DIR}/config"
|
|
12
|
+
|
|
13
|
+
# ============================================================
|
|
14
|
+
# Register deployment to the deployment registry
|
|
15
|
+
# ============================================================
|
|
16
|
+
|
|
17
|
+
# Parse command line arguments
|
|
18
|
+
NOTES=""
|
|
19
|
+
STATUS="success"
|
|
20
|
+
PROJECT_FLAG=""
|
|
21
|
+
JSON_OUTPUT=false
|
|
22
|
+
CI_MODE=false
|
|
23
|
+
CI_TABLE_NAME="${CI_TABLE_NAME:-mlcc-ci-table}"
|
|
24
|
+
CI_BUILD_STRATEGY="codebuild-submit"
|
|
25
|
+
|
|
26
|
+
while [[ $# -gt 0 ]]; do
|
|
27
|
+
case "$1" in
|
|
28
|
+
--notes)
|
|
29
|
+
NOTES="$2"
|
|
30
|
+
shift 2
|
|
31
|
+
;;
|
|
32
|
+
--notes=*)
|
|
33
|
+
NOTES="${1#*=}"
|
|
34
|
+
shift
|
|
35
|
+
;;
|
|
36
|
+
--status)
|
|
37
|
+
STATUS="$2"
|
|
38
|
+
shift 2
|
|
39
|
+
;;
|
|
40
|
+
--status=*)
|
|
41
|
+
STATUS="${1#*=}"
|
|
42
|
+
shift
|
|
43
|
+
;;
|
|
44
|
+
--project)
|
|
45
|
+
PROJECT_FLAG="--project"
|
|
46
|
+
shift
|
|
47
|
+
;;
|
|
48
|
+
--json)
|
|
49
|
+
JSON_OUTPUT=true
|
|
50
|
+
shift
|
|
51
|
+
;;
|
|
52
|
+
--ci)
|
|
53
|
+
CI_MODE=true
|
|
54
|
+
JSON_OUTPUT=true
|
|
55
|
+
shift
|
|
56
|
+
;;
|
|
57
|
+
--ci-table)
|
|
58
|
+
CI_TABLE_NAME="$2"
|
|
59
|
+
shift 2
|
|
60
|
+
;;
|
|
61
|
+
--ci-table=*)
|
|
62
|
+
CI_TABLE_NAME="${1#*=}"
|
|
63
|
+
shift
|
|
64
|
+
;;
|
|
65
|
+
--build-strategy)
|
|
66
|
+
CI_BUILD_STRATEGY="$2"
|
|
67
|
+
shift 2
|
|
68
|
+
;;
|
|
69
|
+
--build-strategy=*)
|
|
70
|
+
CI_BUILD_STRATEGY="${1#*=}"
|
|
71
|
+
shift
|
|
72
|
+
;;
|
|
73
|
+
*)
|
|
74
|
+
echo "⚠️ Unknown option: $1"
|
|
75
|
+
echo ""
|
|
76
|
+
echo "Usage: ./do/register [--notes \"text\"] [--status success|partial|failed] [--project] [--json] [--ci] [--ci-table <name>] [--build-strategy <strategy>]"
|
|
77
|
+
exit 1
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
# Validate status
|
|
83
|
+
case "${STATUS}" in
|
|
84
|
+
success|partial|failed) ;;
|
|
85
|
+
*)
|
|
86
|
+
echo "❌ Invalid status: ${STATUS}"
|
|
87
|
+
echo " Valid values: success, partial, failed"
|
|
88
|
+
exit 1
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
|
|
92
|
+
# ============================================================
|
|
93
|
+
# Derive architecture and backend from DEPLOYMENT_CONFIG
|
|
94
|
+
# ============================================================
|
|
95
|
+
|
|
96
|
+
# DEPLOYMENT_CONFIG format: <architecture>-<backend> (e.g., transformers-vllm, http-flask, triton-fil)
|
|
97
|
+
ARCHITECTURE="${DEPLOYMENT_CONFIG%%-*}"
|
|
98
|
+
BACKEND="${DEPLOYMENT_CONFIG#*-}"
|
|
99
|
+
|
|
100
|
+
echo "📋 Registering deployment to registry"
|
|
101
|
+
echo " Project: ${PROJECT_NAME}"
|
|
102
|
+
echo " Deployment config: ${DEPLOYMENT_CONFIG}"
|
|
103
|
+
echo " Architecture: ${ARCHITECTURE}"
|
|
104
|
+
echo " Backend: ${BACKEND}"
|
|
105
|
+
echo ""
|
|
106
|
+
|
|
107
|
+
# ============================================================
|
|
108
|
+
# Engine prefix mapping for transformer env var filtering
|
|
109
|
+
# ============================================================
|
|
110
|
+
|
|
111
|
+
get_engine_prefix() {
|
|
112
|
+
local backend="$1"
|
|
113
|
+
case "${backend}" in
|
|
114
|
+
vllm) echo "VLLM_" ;;
|
|
115
|
+
vllm-omni) echo "VLLM_" ;;
|
|
116
|
+
sglang) echo "SGLANG_" ;;
|
|
117
|
+
tensorrt-llm) echo "TRTLLM_" ;;
|
|
118
|
+
lmi) echo "LMI_" ;;
|
|
119
|
+
djl) echo "DJL_" ;;
|
|
120
|
+
*) echo "" ;;
|
|
121
|
+
esac
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# ============================================================
|
|
125
|
+
# Build PARAMETERS JSON from shell environment (sourced by do/config)
|
|
126
|
+
# ============================================================
|
|
127
|
+
|
|
128
|
+
PARAMETERS="{}"
|
|
129
|
+
|
|
130
|
+
<%
|
|
131
|
+
// Build a helper to determine sensitive keys for redaction
|
|
132
|
+
const sensitivePatterns = ['HF_TOKEN', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN'];
|
|
133
|
+
const sensitiveSubstrings = ['SECRET', 'TOKEN'];
|
|
134
|
+
|
|
135
|
+
function isSensitiveKey(key) {
|
|
136
|
+
if (sensitivePatterns.includes(key)) return true;
|
|
137
|
+
const upper = key.toUpperCase();
|
|
138
|
+
return sensitiveSubstrings.some(sub => upper.includes(sub));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function redactValue(key, value) {
|
|
142
|
+
return isSensitiveKey(key) ? '***REDACTED***' : value;
|
|
143
|
+
}
|
|
144
|
+
%>
|
|
145
|
+
|
|
146
|
+
case "${ARCHITECTURE}" in
|
|
147
|
+
transformers|diffusors)
|
|
148
|
+
# Extract env vars matching engine prefix + HF_TOKEN + HF_MODEL_ID
|
|
149
|
+
ENGINE_PREFIX=$(get_engine_prefix "${BACKEND}")
|
|
150
|
+
|
|
151
|
+
PARAMETERS=$(python3 -c "
|
|
152
|
+
import os, json
|
|
153
|
+
prefix = '${ENGINE_PREFIX}'
|
|
154
|
+
result = {}
|
|
155
|
+
for key, value in os.environ.items():
|
|
156
|
+
if prefix and key.startswith(prefix):
|
|
157
|
+
result[key] = value
|
|
158
|
+
elif key == 'HF_TOKEN':
|
|
159
|
+
result[key] = '***REDACTED***'
|
|
160
|
+
elif key == 'HF_MODEL_ID':
|
|
161
|
+
result[key] = value
|
|
162
|
+
<% if (modelEnvVars && Object.keys(modelEnvVars).length > 0) { %>
|
|
163
|
+
# Include model env vars
|
|
164
|
+
<% Object.entries(modelEnvVars).forEach(([key, value]) => { %>
|
|
165
|
+
result['<%= key %>'] = '<%= redactValue(key, value) %>'
|
|
166
|
+
<% }); %>
|
|
167
|
+
<% } %>
|
|
168
|
+
<% if (serverEnvVars && Object.keys(serverEnvVars).length > 0) { %>
|
|
169
|
+
# Include server env vars (with engine prefix applied at generation time)
|
|
170
|
+
<% Object.entries(serverEnvVars).forEach(([key, value]) => { %>
|
|
171
|
+
result['<%= key %>'] = '<%= redactValue(key, value) %>'
|
|
172
|
+
<% }); %>
|
|
173
|
+
<% } %>
|
|
174
|
+
print(json.dumps(result))
|
|
175
|
+
" 2>/dev/null || echo "{}")
|
|
176
|
+
;;
|
|
177
|
+
|
|
178
|
+
http)
|
|
179
|
+
# Extract all env vars from do/config except system vars, redact secrets
|
|
180
|
+
PARAMETERS=$(python3 -c "
|
|
181
|
+
import os, json
|
|
182
|
+
exclude = {
|
|
183
|
+
'PATH', 'PYTHONPATH', 'SAGEMAKER_BIND_TO_PORT', 'LANG', 'GPG_KEY',
|
|
184
|
+
'PYTHON_VERSION', 'PYTHON_PIP_VERSION', 'PYTHON_SETUPTOOLS_VERSION',
|
|
185
|
+
'PYTHON_GET_PIP_URL', 'PYTHON_GET_PIP_SHA256'
|
|
186
|
+
}
|
|
187
|
+
redact = {'HF_TOKEN', 'AWS_SECRET_ACCESS_KEY', 'AWS_SESSION_TOKEN'}
|
|
188
|
+
sensitive_substrings = ['SECRET', 'TOKEN']
|
|
189
|
+
result = {}
|
|
190
|
+
for key, value in os.environ.items():
|
|
191
|
+
if key not in exclude:
|
|
192
|
+
is_sensitive = key in redact or any(sub in key.upper() for sub in sensitive_substrings)
|
|
193
|
+
result[key] = '***REDACTED***' if is_sensitive else value
|
|
194
|
+
<% if (modelEnvVars && Object.keys(modelEnvVars).length > 0) { %>
|
|
195
|
+
# Include model env vars
|
|
196
|
+
<% Object.entries(modelEnvVars).forEach(([key, value]) => { %>
|
|
197
|
+
result['<%= key %>'] = '<%= redactValue(key, value) %>'
|
|
198
|
+
<% }); %>
|
|
199
|
+
<% } %>
|
|
200
|
+
<% if (serverEnvVars && Object.keys(serverEnvVars).length > 0) { %>
|
|
201
|
+
# Include server env vars
|
|
202
|
+
<% Object.entries(serverEnvVars).forEach(([key, value]) => { %>
|
|
203
|
+
result['<%= key %>'] = '<%= redactValue(key, value) %>'
|
|
204
|
+
<% }); %>
|
|
205
|
+
<% } %>
|
|
206
|
+
print(json.dumps(result))
|
|
207
|
+
" 2>/dev/null || echo "{}")
|
|
208
|
+
;;
|
|
209
|
+
|
|
210
|
+
triton)
|
|
211
|
+
# Read config.pbtxt from model repository
|
|
212
|
+
TRITON_MODEL_REPO="${TRITON_MODEL_REPOSITORY:-/opt/ml/model/model_repository}"
|
|
213
|
+
CONFIG_PBTXT=""
|
|
214
|
+
|
|
215
|
+
# Try to find config.pbtxt in the project directory
|
|
216
|
+
PROJECT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
217
|
+
if [ -f "${PROJECT_DIR}/model_repository/model/config.pbtxt" ]; then
|
|
218
|
+
CONFIG_PBTXT=$(cat "${PROJECT_DIR}/model_repository/model/config.pbtxt" 2>/dev/null || echo "")
|
|
219
|
+
elif [ -f "${PROJECT_DIR}/model_repository/config.pbtxt" ]; then
|
|
220
|
+
CONFIG_PBTXT=$(cat "${PROJECT_DIR}/model_repository/config.pbtxt" 2>/dev/null || echo "")
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Read TRITON_MODEL_REPOSITORY from shell env (exported by do/config)
|
|
224
|
+
TRITON_REPO_VAR="${TRITON_MODEL_REPOSITORY:-}"
|
|
225
|
+
|
|
226
|
+
# Build parameters JSON (pipe config.pbtxt via stdin to avoid quoting issues)
|
|
227
|
+
PARAMETERS=$(printf '%s' "${CONFIG_PBTXT}" | python3 -c "
|
|
228
|
+
import sys, json
|
|
229
|
+
config_pbtxt = sys.stdin.read()
|
|
230
|
+
result = {}
|
|
231
|
+
if config_pbtxt.strip():
|
|
232
|
+
result['config.pbtxt'] = config_pbtxt.strip()
|
|
233
|
+
triton_repo = '${TRITON_REPO_VAR}'
|
|
234
|
+
if triton_repo:
|
|
235
|
+
result['TRITON_MODEL_REPOSITORY'] = triton_repo
|
|
236
|
+
<% if (modelEnvVars && Object.keys(modelEnvVars).length > 0) { %>
|
|
237
|
+
# Include model env vars
|
|
238
|
+
<% Object.entries(modelEnvVars).forEach(([key, value]) => { %>
|
|
239
|
+
result['<%= key %>'] = '<%= redactValue(key, value) %>'
|
|
240
|
+
<% }); %>
|
|
241
|
+
<% } %>
|
|
242
|
+
<% if (serverEnvVars && Object.keys(serverEnvVars).length > 0) { %>
|
|
243
|
+
# Include server env vars
|
|
244
|
+
<% Object.entries(serverEnvVars).forEach(([key, value]) => { %>
|
|
245
|
+
result['<%= key %>'] = '<%= redactValue(key, value) %>'
|
|
246
|
+
<% }); %>
|
|
247
|
+
<% } %>
|
|
248
|
+
print(json.dumps(result))
|
|
249
|
+
" 2>/dev/null || echo "{}")
|
|
250
|
+
;;
|
|
251
|
+
esac
|
|
252
|
+
|
|
253
|
+
# Count captured parameters
|
|
254
|
+
PARAM_COUNT=$(echo "${PARAMETERS}" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null || echo "0")
|
|
255
|
+
|
|
256
|
+
# ============================================================
|
|
257
|
+
# Display summary before writing
|
|
258
|
+
# ============================================================
|
|
259
|
+
|
|
260
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
261
|
+
echo "📋 Registration Summary"
|
|
262
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
263
|
+
echo " Deployment config: ${DEPLOYMENT_CONFIG}"
|
|
264
|
+
echo " Architecture: ${ARCHITECTURE}"
|
|
265
|
+
echo " Backend: ${BACKEND}"
|
|
266
|
+
<% if (framework === 'transformers') { %>
|
|
267
|
+
echo " Model name: ${MODEL_NAME:-N/A}"
|
|
268
|
+
<% } %>
|
|
269
|
+
<% if (deploymentTarget === 'managed-inference') { %>
|
|
270
|
+
echo " Instance type: ${INSTANCE_TYPE}"
|
|
271
|
+
<% } else if (deploymentTarget === 'batch-transform') { %>
|
|
272
|
+
echo " Instance: ${INSTANCE_TYPE} x ${BATCH_INSTANCE_COUNT}"
|
|
273
|
+
echo " S3 input: ${BATCH_INPUT_PATH}"
|
|
274
|
+
echo " S3 output: ${BATCH_OUTPUT_PATH}"
|
|
275
|
+
echo " Split type: ${BATCH_SPLIT_TYPE}"
|
|
276
|
+
echo " Strategy: ${BATCH_STRATEGY}"
|
|
277
|
+
<% } %>
|
|
278
|
+
echo " Region: ${AWS_REGION}"
|
|
279
|
+
echo " Status: ${STATUS}"
|
|
280
|
+
echo " Env var source: do/config"
|
|
281
|
+
echo " Parameters: ${PARAM_COUNT} captured"
|
|
282
|
+
if [ -n "${BASE_IMAGE:-}" ]; then
|
|
283
|
+
echo " Base image: ${BASE_IMAGE}"
|
|
284
|
+
fi
|
|
285
|
+
if [ -n "${NOTES}" ]; then
|
|
286
|
+
echo " Notes: ${NOTES}"
|
|
287
|
+
fi
|
|
288
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
289
|
+
echo ""
|
|
290
|
+
|
|
291
|
+
# ============================================================
|
|
292
|
+
# Compute configId (deterministic hash of canonical fields)
|
|
293
|
+
# ============================================================
|
|
294
|
+
|
|
295
|
+
compute_config_id() {
|
|
296
|
+
local input="${DEPLOYMENT_CONFIG}:${MODEL_NAME:-none}:${INSTANCE_TYPE}:${AWS_REGION}:${DEPLOYMENT_TARGET}"
|
|
297
|
+
# Use sha256sum (Linux) with fallback to shasum (macOS)
|
|
298
|
+
if command -v sha256sum &> /dev/null; then
|
|
299
|
+
echo -n "$input" | sha256sum | cut -c1-16
|
|
300
|
+
else
|
|
301
|
+
echo -n "$input" | shasum -a 256 | cut -c1-16
|
|
302
|
+
fi
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# ============================================================
|
|
306
|
+
# CI Table write logic (--ci flag)
|
|
307
|
+
# ============================================================
|
|
308
|
+
|
|
309
|
+
write_ci_record() {
|
|
310
|
+
local config_id="$1"
|
|
311
|
+
|
|
312
|
+
# Extract promoted attributes
|
|
313
|
+
local promoted_deployment_config="${DEPLOYMENT_CONFIG}"
|
|
314
|
+
local promoted_base_image="${BASE_IMAGE:-}"
|
|
315
|
+
local promoted_project_name="${PROJECT_NAME}"
|
|
316
|
+
|
|
317
|
+
# Extract baseImageVersion from BASE_IMAGE tag (e.g., "vllm/vllm-openai:v0.8.5" -> "v0.8.5")
|
|
318
|
+
local promoted_base_image_version=""
|
|
319
|
+
if [ -n "${promoted_base_image}" ]; then
|
|
320
|
+
case "${promoted_base_image}" in
|
|
321
|
+
*:*)
|
|
322
|
+
promoted_base_image_version="${promoted_base_image##*:}"
|
|
323
|
+
;;
|
|
324
|
+
esac
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
# Build the configJson object from current config variables
|
|
328
|
+
local config_json
|
|
329
|
+
config_json=$(cat <<CJEOF
|
|
330
|
+
{
|
|
331
|
+
"projectName": "${PROJECT_NAME}",
|
|
332
|
+
"deploymentConfig": "${DEPLOYMENT_CONFIG}",
|
|
333
|
+
"architecture": "${ARCHITECTURE}",
|
|
334
|
+
"backend": "${BACKEND}",
|
|
335
|
+
"modelName": "${MODEL_NAME:-}",
|
|
336
|
+
"instanceType": "${INSTANCE_TYPE}",
|
|
337
|
+
"awsRegion": "${AWS_REGION}",
|
|
338
|
+
"deploymentTarget": "${DEPLOYMENT_TARGET}",
|
|
339
|
+
"buildTarget": "${BUILD_TARGET}",
|
|
340
|
+
"baseImage": "${BASE_IMAGE:-}",
|
|
341
|
+
"buildStrategy": "${CI_BUILD_STRATEGY}",
|
|
342
|
+
<% if (endpointInitialInstanceCount != null || endpointDataCapturePercent != null || endpointVariantName != null || endpointVolumeSize != null) { %>
|
|
343
|
+
"endpointConfig": {
|
|
344
|
+
<% if (endpointInitialInstanceCount != null) { %>
|
|
345
|
+
"initialInstanceCount": ${ENDPOINT_INITIAL_INSTANCE_COUNT}<%= (endpointDataCapturePercent != null || endpointVariantName != null || endpointVolumeSize != null) ? ',' : '' %>
|
|
346
|
+
<% } %>
|
|
347
|
+
<% if (endpointDataCapturePercent != null) { %>
|
|
348
|
+
"dataCapturePercent": ${ENDPOINT_DATA_CAPTURE_PERCENT}<%= (endpointVariantName != null || endpointVolumeSize != null) ? ',' : '' %>
|
|
349
|
+
<% } %>
|
|
350
|
+
<% if (endpointVariantName != null) { %>
|
|
351
|
+
"variantName": "${ENDPOINT_VARIANT_NAME}"<%= (endpointVolumeSize != null) ? ',' : '' %>
|
|
352
|
+
<% } %>
|
|
353
|
+
<% if (endpointVolumeSize != null) { %>
|
|
354
|
+
"volumeSize": ${ENDPOINT_VOLUME_SIZE}
|
|
355
|
+
<% } %>
|
|
356
|
+
},
|
|
357
|
+
<% } %>
|
|
358
|
+
<% if (icCpuCount != null || icMemorySize != null || icGpuCount != null || icCopyCount != null || icModelWeight != null) { %>
|
|
359
|
+
"icConfig": {
|
|
360
|
+
<% if (icCpuCount != null) { %>
|
|
361
|
+
"cpuCount": ${IC_CPU_COUNT}<%= (icMemorySize != null || icGpuCount != null || icCopyCount != null || icModelWeight != null) ? ',' : '' %>
|
|
362
|
+
<% } %>
|
|
363
|
+
<% if (icMemorySize != null) { %>
|
|
364
|
+
"memorySize": ${IC_MEMORY_SIZE}<%= (icGpuCount != null || icCopyCount != null || icModelWeight != null) ? ',' : '' %>
|
|
365
|
+
<% } %>
|
|
366
|
+
<% if (icGpuCount != null) { %>
|
|
367
|
+
"gpuCount": ${IC_GPU_COUNT}<%= (icCopyCount != null || icModelWeight != null) ? ',' : '' %>
|
|
368
|
+
<% } %>
|
|
369
|
+
<% if (icCopyCount != null) { %>
|
|
370
|
+
"copyCount": ${IC_COPY_COUNT}<%= (icModelWeight != null) ? ',' : '' %>
|
|
371
|
+
<% } %>
|
|
372
|
+
<% if (icModelWeight != null) { %>
|
|
373
|
+
"modelWeight": ${IC_MODEL_WEIGHT}
|
|
374
|
+
<% } %>
|
|
375
|
+
},
|
|
376
|
+
<% } %>
|
|
377
|
+
"parameters": ${PARAMETERS}
|
|
378
|
+
}
|
|
379
|
+
CJEOF
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# Compact the JSON (remove whitespace)
|
|
383
|
+
config_json=$(echo "$config_json" | python3 -c "import sys,json; print(json.dumps(json.load(sys.stdin), separators=(',',':')))" 2>/dev/null || echo "$config_json")
|
|
384
|
+
|
|
385
|
+
local created_at
|
|
386
|
+
created_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
387
|
+
|
|
388
|
+
# Escape configJson for DynamoDB JSON attribute (double-escape quotes)
|
|
389
|
+
local escaped_config_json
|
|
390
|
+
escaped_config_json=$(echo "$config_json" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read().strip()))" 2>/dev/null)
|
|
391
|
+
# Remove the outer quotes added by json.dumps for string
|
|
392
|
+
escaped_config_json="${escaped_config_json:1:${#escaped_config_json}-2}"
|
|
393
|
+
|
|
394
|
+
# Try put-item with condition (new record)
|
|
395
|
+
if aws dynamodb put-item \
|
|
396
|
+
--table-name "${CI_TABLE_NAME}" \
|
|
397
|
+
--item "{
|
|
398
|
+
\"configId\": {\"S\": \"${config_id}\"},
|
|
399
|
+
\"schemaVersion\": {\"N\": \"1\"},
|
|
400
|
+
\"configJson\": {\"S\": \"${escaped_config_json}\"},
|
|
401
|
+
\"testStatus\": {\"S\": \"untested\"},
|
|
402
|
+
\"lastTestTimestamp\": {\"S\": \"1970-01-01T00:00:00Z\"},
|
|
403
|
+
\"deploymentConfig\": {\"S\": \"${promoted_deployment_config}\"},
|
|
404
|
+
\"baseImage\": {\"S\": \"${promoted_base_image}\"},
|
|
405
|
+
\"baseImageVersion\": {\"S\": \"${promoted_base_image_version}\"},
|
|
406
|
+
\"buildStrategy\": {\"S\": \"${CI_BUILD_STRATEGY}\"},
|
|
407
|
+
\"projectName\": {\"S\": \"${promoted_project_name}\"},
|
|
408
|
+
\"createdAt\": {\"S\": \"${created_at}\"}
|
|
409
|
+
}" \
|
|
410
|
+
--condition-expression "attribute_not_exists(configId)" 2>/dev/null; then
|
|
411
|
+
echo "✅ New CI record created: ${config_id}"
|
|
412
|
+
else
|
|
413
|
+
# Record already exists — update it (reset testStatus, update configJson, preserve createdAt)
|
|
414
|
+
if aws dynamodb update-item \
|
|
415
|
+
--table-name "${CI_TABLE_NAME}" \
|
|
416
|
+
--key "{\"configId\": {\"S\": \"${config_id}\"}}" \
|
|
417
|
+
--update-expression "SET configJson = :cj, testStatus = :ts, deploymentConfig = :dc, baseImage = :bi, baseImageVersion = :bv, buildStrategy = :bs, projectName = :pn, schemaVersion = :sv" \
|
|
418
|
+
--expression-attribute-values "{
|
|
419
|
+
\":cj\": {\"S\": \"${escaped_config_json}\"},
|
|
420
|
+
\":ts\": {\"S\": \"untested\"},
|
|
421
|
+
\":dc\": {\"S\": \"${promoted_deployment_config}\"},
|
|
422
|
+
\":bi\": {\"S\": \"${promoted_base_image}\"},
|
|
423
|
+
\":bv\": {\"S\": \"${promoted_base_image_version}\"},
|
|
424
|
+
\":bs\": {\"S\": \"${CI_BUILD_STRATEGY}\"},
|
|
425
|
+
\":pn\": {\"S\": \"${promoted_project_name}\"},
|
|
426
|
+
\":sv\": {\"N\": \"1\"}
|
|
427
|
+
}" 2>/dev/null; then
|
|
428
|
+
echo "✅ Existing CI record updated (testStatus reset to untested): ${config_id}"
|
|
429
|
+
else
|
|
430
|
+
echo "❌ Failed to update CI record: ${config_id}"
|
|
431
|
+
return 1
|
|
432
|
+
fi
|
|
433
|
+
fi
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
# ============================================================
|
|
437
|
+
# Handle --json and --ci output modes
|
|
438
|
+
# ============================================================
|
|
439
|
+
|
|
440
|
+
if [ "${JSON_OUTPUT}" = true ] || [ "${CI_MODE}" = true ]; then
|
|
441
|
+
# Build JSON deployment entry
|
|
442
|
+
DEPLOYMENT_JSON=$(cat <<DJEOF
|
|
443
|
+
{
|
|
444
|
+
"projectName": "${PROJECT_NAME}",
|
|
445
|
+
"deploymentConfig": "${DEPLOYMENT_CONFIG}",
|
|
446
|
+
"architecture": "${ARCHITECTURE}",
|
|
447
|
+
"backend": "${BACKEND}",
|
|
448
|
+
"modelName": "${MODEL_NAME:-}",
|
|
449
|
+
"instanceType": "${INSTANCE_TYPE}",
|
|
450
|
+
"awsRegion": "${AWS_REGION}",
|
|
451
|
+
"deploymentTarget": "${DEPLOYMENT_TARGET}",
|
|
452
|
+
"buildTarget": "${BUILD_TARGET}",
|
|
453
|
+
"baseImage": "${BASE_IMAGE:-}",
|
|
454
|
+
"status": "${STATUS}",
|
|
455
|
+
<% if (endpointInitialInstanceCount != null || endpointDataCapturePercent != null || endpointVariantName != null || endpointVolumeSize != null) { %>
|
|
456
|
+
"endpointConfig": {
|
|
457
|
+
<% if (endpointInitialInstanceCount != null) { %>
|
|
458
|
+
"initialInstanceCount": ${ENDPOINT_INITIAL_INSTANCE_COUNT}<%= (endpointDataCapturePercent != null || endpointVariantName != null || endpointVolumeSize != null) ? ',' : '' %>
|
|
459
|
+
<% } %>
|
|
460
|
+
<% if (endpointDataCapturePercent != null) { %>
|
|
461
|
+
"dataCapturePercent": ${ENDPOINT_DATA_CAPTURE_PERCENT}<%= (endpointVariantName != null || endpointVolumeSize != null) ? ',' : '' %>
|
|
462
|
+
<% } %>
|
|
463
|
+
<% if (endpointVariantName != null) { %>
|
|
464
|
+
"variantName": "${ENDPOINT_VARIANT_NAME}"<%= (endpointVolumeSize != null) ? ',' : '' %>
|
|
465
|
+
<% } %>
|
|
466
|
+
<% if (endpointVolumeSize != null) { %>
|
|
467
|
+
"volumeSize": ${ENDPOINT_VOLUME_SIZE}
|
|
468
|
+
<% } %>
|
|
469
|
+
},
|
|
470
|
+
<% } %>
|
|
471
|
+
<% if (icCpuCount != null || icMemorySize != null || icGpuCount != null || icCopyCount != null || icModelWeight != null) { %>
|
|
472
|
+
"icConfig": {
|
|
473
|
+
<% if (icCpuCount != null) { %>
|
|
474
|
+
"cpuCount": ${IC_CPU_COUNT}<%= (icMemorySize != null || icGpuCount != null || icCopyCount != null || icModelWeight != null) ? ',' : '' %>
|
|
475
|
+
<% } %>
|
|
476
|
+
<% if (icMemorySize != null) { %>
|
|
477
|
+
"memorySize": ${IC_MEMORY_SIZE}<%= (icGpuCount != null || icCopyCount != null || icModelWeight != null) ? ',' : '' %>
|
|
478
|
+
<% } %>
|
|
479
|
+
<% if (icGpuCount != null) { %>
|
|
480
|
+
"gpuCount": ${IC_GPU_COUNT}<%= (icCopyCount != null || icModelWeight != null) ? ',' : '' %>
|
|
481
|
+
<% } %>
|
|
482
|
+
<% if (icCopyCount != null) { %>
|
|
483
|
+
"copyCount": ${IC_COPY_COUNT}<%= (icModelWeight != null) ? ',' : '' %>
|
|
484
|
+
<% } %>
|
|
485
|
+
<% if (icModelWeight != null) { %>
|
|
486
|
+
"modelWeight": ${IC_MODEL_WEIGHT}
|
|
487
|
+
<% } %>
|
|
488
|
+
},
|
|
489
|
+
<% } %>
|
|
490
|
+
"parameters": ${PARAMETERS}
|
|
491
|
+
}
|
|
492
|
+
DJEOF
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
# Output JSON to stdout
|
|
496
|
+
echo "${DEPLOYMENT_JSON}" | python3 -c "import sys,json; print(json.dumps(json.load(sys.stdin), indent=2))" 2>/dev/null || echo "${DEPLOYMENT_JSON}"
|
|
497
|
+
|
|
498
|
+
if [ "${CI_MODE}" = true ]; then
|
|
499
|
+
echo ""
|
|
500
|
+
echo "⚠️ CI Integration is experimental and currently only tested for"
|
|
501
|
+
echo " SageMaker Managed Inference — Real Time endpoints."
|
|
502
|
+
echo ""
|
|
503
|
+
|
|
504
|
+
# Compute configId
|
|
505
|
+
CONFIG_ID=$(compute_config_id)
|
|
506
|
+
echo ""
|
|
507
|
+
echo "🔑 configId: ${CONFIG_ID}"
|
|
508
|
+
|
|
509
|
+
# Check if CI_Table exists before writing
|
|
510
|
+
if ! aws dynamodb describe-table --table-name "${CI_TABLE_NAME}" &>/dev/null; then
|
|
511
|
+
echo ""
|
|
512
|
+
echo "⚠️ CI infrastructure not provisioned. Run 'ml-container-creator bootstrap' with CI enabled."
|
|
513
|
+
echo " Skipping CI table write."
|
|
514
|
+
else
|
|
515
|
+
echo "📝 Writing to CI table (${CI_TABLE_NAME})..."
|
|
516
|
+
write_ci_record "${CONFIG_ID}"
|
|
517
|
+
fi
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
exit 0
|
|
521
|
+
fi
|
|
522
|
+
|
|
523
|
+
# ============================================================
|
|
524
|
+
# Call ml-container-creator registry log
|
|
525
|
+
# ============================================================
|
|
526
|
+
|
|
527
|
+
# Build the registry log command
|
|
528
|
+
CMD_ARGS=(
|
|
529
|
+
"registry" "log"
|
|
530
|
+
"--deployment-config" "${DEPLOYMENT_CONFIG}"
|
|
531
|
+
"--architecture" "${ARCHITECTURE}"
|
|
532
|
+
"--backend" "${BACKEND}"
|
|
533
|
+
"--region" "${AWS_REGION}"
|
|
534
|
+
"--status" "${STATUS}"
|
|
535
|
+
"--deployment-target" "${DEPLOYMENT_TARGET}"
|
|
536
|
+
"--build-target" "${BUILD_TARGET}"
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
<% if (framework === 'transformers' || framework === 'diffusors') { %>
|
|
540
|
+
if [ -n "${MODEL_NAME:-}" ]; then
|
|
541
|
+
CMD_ARGS+=("--model-name" "${MODEL_NAME}")
|
|
542
|
+
fi
|
|
543
|
+
<% } %>
|
|
544
|
+
|
|
545
|
+
<% if (deploymentTarget === 'managed-inference' || deploymentTarget === 'async-inference' || deploymentTarget === 'batch-transform') { %>
|
|
546
|
+
CMD_ARGS+=("--instance-type" "${INSTANCE_TYPE}")
|
|
547
|
+
<% } %>
|
|
548
|
+
|
|
549
|
+
<% if (modelFormat) { %>
|
|
550
|
+
if [ -n "${MODEL_FORMAT:-}" ]; then
|
|
551
|
+
CMD_ARGS+=("--model-format" "${MODEL_FORMAT}")
|
|
552
|
+
fi
|
|
553
|
+
<% } %>
|
|
554
|
+
|
|
555
|
+
if [ -n "${BASE_IMAGE:-}" ]; then
|
|
556
|
+
CMD_ARGS+=("--base-image" "${BASE_IMAGE}")
|
|
557
|
+
fi
|
|
558
|
+
|
|
559
|
+
if [ -n "${NOTES}" ]; then
|
|
560
|
+
CMD_ARGS+=("--notes" "${NOTES}")
|
|
561
|
+
fi
|
|
562
|
+
|
|
563
|
+
if [ -n "${PROJECT_FLAG}" ]; then
|
|
564
|
+
CMD_ARGS+=("${PROJECT_FLAG}")
|
|
565
|
+
fi
|
|
566
|
+
|
|
567
|
+
# Pass parameters as JSON string
|
|
568
|
+
CMD_ARGS+=("--parameters" "${PARAMETERS}")
|
|
569
|
+
|
|
570
|
+
# Pass generator version from package.json if available
|
|
571
|
+
GENERATOR_VERSION=""
|
|
572
|
+
if command -v node &> /dev/null; then
|
|
573
|
+
GENERATOR_VERSION=$(node -e "try{console.log(require('$(npm root -g)/@aws/ml-container-creator/package.json').version)}catch(e){console.log('')}" 2>/dev/null || echo "")
|
|
574
|
+
fi
|
|
575
|
+
if [ -n "${GENERATOR_VERSION}" ]; then
|
|
576
|
+
CMD_ARGS+=("--generator-version" "${GENERATOR_VERSION}")
|
|
577
|
+
fi
|
|
578
|
+
|
|
579
|
+
echo "📝 Writing to registry..."
|
|
580
|
+
ml-container-creator "${CMD_ARGS[@]}"
|
package/templates/do/run
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
set -u
|
|
7
|
+
set -o pipefail
|
|
8
|
+
|
|
9
|
+
# Source configuration
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
source "${SCRIPT_DIR}/config"
|
|
12
|
+
|
|
13
|
+
echo "🚀 Running Docker container locally for ${PROJECT_NAME}"
|
|
14
|
+
echo " Deployment config: ${DEPLOYMENT_CONFIG}"
|
|
15
|
+
echo " Framework: ${FRAMEWORK}"
|
|
16
|
+
echo " Model server: ${MODEL_SERVER}"
|
|
17
|
+
|
|
18
|
+
# Validate prerequisites
|
|
19
|
+
if ! command -v docker &> /dev/null; then
|
|
20
|
+
echo "❌ Docker is not installed"
|
|
21
|
+
echo " Install from: https://docs.docker.com/get-docker/"
|
|
22
|
+
exit 2
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Check if image exists
|
|
26
|
+
if ! docker image inspect "${PROJECT_NAME}:latest" &> /dev/null; then
|
|
27
|
+
echo "❌ Docker image not found: ${PROJECT_NAME}:latest"
|
|
28
|
+
echo " Build the image first: ./do/build"
|
|
29
|
+
exit 5
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Determine GPU support based on deployment configuration
|
|
33
|
+
GPU_FLAG=""
|
|
34
|
+
case "${DEPLOYMENT_CONFIG}" in
|
|
35
|
+
transformers-*)
|
|
36
|
+
echo "🎮 GPU support enabled for transformers deployment"
|
|
37
|
+
GPU_FLAG="--gpus all"
|
|
38
|
+
|
|
39
|
+
# Check if nvidia-docker is available
|
|
40
|
+
if ! docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi &> /dev/null; then
|
|
41
|
+
echo "⚠️ Warning: GPU support requested but nvidia-docker may not be available"
|
|
42
|
+
echo " The container will start but may fail if it requires GPU"
|
|
43
|
+
echo " Install nvidia-docker: https://github.com/NVIDIA/nvidia-docker"
|
|
44
|
+
fi
|
|
45
|
+
;;
|
|
46
|
+
sklearn-*|xgboost-*|tensorflow-*)
|
|
47
|
+
echo "💻 CPU-only mode for traditional ML deployment"
|
|
48
|
+
;;
|
|
49
|
+
*)
|
|
50
|
+
echo "❌ Unknown deployment configuration: ${DEPLOYMENT_CONFIG}"
|
|
51
|
+
exit 3
|
|
52
|
+
;;
|
|
53
|
+
esac
|
|
54
|
+
|
|
55
|
+
# Prepare model directory mount if specified
|
|
56
|
+
MODEL_MOUNT=""
|
|
57
|
+
if [ -n "${MODEL_DIR:-}" ]; then
|
|
58
|
+
if [ -d "${MODEL_DIR}" ]; then
|
|
59
|
+
echo "📁 Mounting model directory: ${MODEL_DIR}"
|
|
60
|
+
MODEL_MOUNT="-v ${MODEL_DIR}:/opt/ml/model"
|
|
61
|
+
else
|
|
62
|
+
echo "⚠️ Warning: MODEL_DIR specified but directory not found: ${MODEL_DIR}"
|
|
63
|
+
echo " Container will start without model directory mount"
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Prepare environment variables
|
|
68
|
+
ENV_VARS=""
|
|
69
|
+
<% if (framework === 'transformers') { %>
|
|
70
|
+
if [ -n "${HF_TOKEN:-}" ]; then
|
|
71
|
+
echo "🔑 Using HuggingFace token from environment"
|
|
72
|
+
ENV_VARS="${ENV_VARS} -e HF_TOKEN=${HF_TOKEN}"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ -n "${MODEL_NAME:-}" ]; then
|
|
76
|
+
ENV_VARS="${ENV_VARS} -e MODEL_NAME=${MODEL_NAME}"
|
|
77
|
+
fi
|
|
78
|
+
<% } %>
|
|
79
|
+
|
|
80
|
+
echo ""
|
|
81
|
+
echo "🏃 Starting container..."
|
|
82
|
+
echo " Port: 8080 (mapped to localhost:8080)"
|
|
83
|
+
echo " Image: ${PROJECT_NAME}:latest"
|
|
84
|
+
if [ -n "${GPU_FLAG}" ]; then
|
|
85
|
+
echo " GPU: Enabled"
|
|
86
|
+
fi
|
|
87
|
+
if [ -n "${MODEL_MOUNT}" ]; then
|
|
88
|
+
echo " Model mount: ${MODEL_DIR} -> /opt/ml/model"
|
|
89
|
+
fi
|
|
90
|
+
echo ""
|
|
91
|
+
echo "📝 Container logs will stream below"
|
|
92
|
+
echo " Press Ctrl+C to stop the container"
|
|
93
|
+
echo ""
|
|
94
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
95
|
+
echo ""
|
|
96
|
+
|
|
97
|
+
# Run container with cleanup on exit
|
|
98
|
+
docker run -it --rm \
|
|
99
|
+
-p 8080:8080 \
|
|
100
|
+
${GPU_FLAG} \
|
|
101
|
+
${MODEL_MOUNT} \
|
|
102
|
+
${ENV_VARS} \
|
|
103
|
+
"${PROJECT_NAME}:latest"
|
|
104
|
+
|
|
105
|
+
echo ""
|
|
106
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
107
|
+
echo ""
|
|
108
|
+
echo "✅ Container stopped and cleaned up"
|
|
109
|
+
echo ""
|
|
110
|
+
echo "Next steps:"
|
|
111
|
+
echo " • Test endpoints: curl http://localhost:8080/ping"
|
|
112
|
+
echo " • Push to ECR: ./do/push"
|
|
113
|
+
echo " • Deploy to SageMaker: ./do/deploy"
|