Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/config/image/sklearn/sagemaker-1.9-0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
image:
name: "sagemaker-scikit-learn"
description: "SageMaker Scikit-learn 1.9.0"

metadata:
framework: "sklearn"
framework_version: "1.9.0"
os_version: "amzn2023"
customer_type: "sagemaker"
platform: "sagemaker"
arch_type: "x86"
device_type: "cpu"
job_type: "general"
prod_image: "sagemaker-scikit-learn:1.9-0-cpu-py3"

build:
dockerfile: "docker/sklearn/1.9-0/Dockerfile"
target: "sklearn-sagemaker"
base_image: "amazonlinux:2023"
python_version: "3.12"
sklearn_container_branch: "master"

release:
release: false
force_release: false
public_registry: false
private_registry: true
enable_soci: false
environment: "gamma"
2 changes: 1 addition & 1 deletion .github/workflows/sklearn.dispatch-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
description: "Path to image config YAML"
required: true
type: string
default: ".github/config/image/sklearn/sagemaker-1.4-2-py312.yml"
default: ".github/config/image/sklearn/sagemaker-1.9-0.yml"

concurrency:
group: ${{ github.workflow }}-${{ inputs.config-file }}
Expand Down
106 changes: 106 additions & 0 deletions .github/workflows/sklearn.pr-1.9-0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
name: "PR - Scikit-learn 1.9.0"

on:
workflow_dispatch:
pull_request:
branches: [main]
types: [opened, reopened, synchronize]
paths:
- ".github/config/image/sklearn/sagemaker-1.9-0.yml"
- ".github/workflows/sklearn.pipeline.yml"
- ".github/workflows/sklearn.pr-1.9-0.yml"
- ".github/workflows/sklearn.tests-unit.yml"
- ".github/workflows/sklearn.tests-integ-local.yml"
- ".github/workflows/sklearn.tests-sagemaker.yml"
- "docker/sklearn/1.9-0/**"
- "docker/sklearn/resources/**"
- "scripts/ci/build/sklearn/**"
- "scripts/docker/common/**"
- "scripts/docker/telemetry/**"
- "test/sanity/**"
- "test/security/data/ecr_scan_allowlist/sklearn/**"
- "test/sklearn/**"
- "test/telemetry/**"
- "!docs/**"

permissions:
contents: read
pull-requests: read

jobs:
gatekeeper:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 1
- uses: ./.github/actions/pr-permission-gate

check-changes:
needs: [gatekeeper]
runs-on: ubuntu-latest
outputs:
build-change: ${{ steps.changes.outputs.build-change }}
integ-test-change: ${{ steps.changes.outputs.integ-test-change }}
sagemaker-test-change: ${{ steps.changes.outputs.sagemaker-test-change }}
sanity-test-change: ${{ steps.changes.outputs.sanity-test-change }}
telemetry-test-change: ${{ steps.changes.outputs.telemetry-test-change }}
unit-test-change: ${{ steps.changes.outputs.unit-test-change }}
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: changes
with:
filters: |
build-change:
- ".github/config/image/sklearn/sagemaker-1.9-0.yml"
- "docker/sklearn/1.9-0/**"
- "docker/sklearn/resources/**"
- "scripts/ci/build/sklearn/**"
- "scripts/docker/common/**"
- "scripts/docker/telemetry/**"
- "test/security/data/ecr_scan_allowlist/sklearn/**"
integ-test-change:
- "test/sklearn/**"
sagemaker-test-change:
- "test/sklearn/sagemaker/**"
- ".github/workflows/sklearn.tests-sagemaker.yml"
sanity-test-change:
- "test/sanity/**"
telemetry-test-change:
- "test/telemetry/**"
unit-test-change:
- "docker/sklearn/1.9-0/**"
discover:
needs: [gatekeeper]
runs-on: ubuntu-latest
outputs:
configs: ${{ steps.discover.outputs.configs }}
steps:
- uses: actions/checkout@v6
- id: discover
uses: ./.github/actions/discover-configs
with:
pattern: ".github/config/image/sklearn/sagemaker-1.9-0.yml"

pipeline:
needs: [check-changes, discover]
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.discover.outputs.configs) }}
uses: ./.github/workflows/sklearn.pipeline.yml
with:
config-file: ${{ matrix.config_file }}
tag-suffix: pr-${{ github.event.pull_request.number }}
build: ${{ needs.check-changes.outputs.build-change == 'true' }}
run-integ-test: ${{ needs.check-changes.outputs.build-change == 'true' || needs.check-changes.outputs.integ-test-change == 'true' }}
run-sagemaker-test: ${{ needs.check-changes.outputs.build-change == 'true' || needs.check-changes.outputs.sagemaker-test-change == 'true' }}
run-sanity-test: ${{ needs.check-changes.outputs.build-change == 'true' || needs.check-changes.outputs.sanity-test-change == 'true' }}
run-security-test: ${{ needs.check-changes.outputs.build-change == 'true' }}
run-telemetry-test: false
run-unit-test: ${{ needs.check-changes.outputs.build-change == 'true' || needs.check-changes.outputs.unit-test-change == 'true' }}
release: false
secrets: inherit
18 changes: 12 additions & 6 deletions .github/workflows/sklearn.tests-integ-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,29 @@ jobs:
echo "python-version=$(yq '.build.python_version' "$CONFIG_FILE")" >> $GITHUB_OUTPUT
echo "dockerfile=$(yq '.build.dockerfile' "$CONFIG_FILE")" >> $GITHUB_OUTPUT

- name: Verify pinned package versions match declared requirements.txt
- name: Verify pinned package versions match declared deps file
if: steps.check.outputs.exists == 'true'
env:
IMAGE_URI: ${{ steps.image.outputs.image-uri }}
DOCKERFILE: ${{ steps.config.outputs.dockerfile }}
run: |
REQS_FILE="$(dirname "$DOCKERFILE")/requirements.txt"
if [[ ! -f "$REQS_FILE" ]]; then
echo "No requirements.txt at $REQS_FILE — skipping version check"
DEPS_DIR="$(dirname "$DOCKERFILE")"
if [[ -f "$DEPS_DIR/requirements.txt" ]]; then
DEPS_FILE="$DEPS_DIR/requirements.txt"
MOUNT_NAME="requirements.txt"
elif [[ -f "$DEPS_DIR/pyproject.toml" ]]; then
DEPS_FILE="$DEPS_DIR/pyproject.toml"
MOUNT_NAME="pyproject.toml"
else
echo "No requirements.txt or pyproject.toml at $DEPS_DIR — skipping version check"
exit 0
fi
docker run --rm \
-v "$PWD/$REQS_FILE:/tmp/requirements.txt:ro" \
-v "$PWD/$DEPS_FILE:/tmp/$MOUNT_NAME:ro" \
-v "$PWD/test/sklearn/scripts/check_versions.py:/tmp/check_versions.py:ro" \
--entrypoint python3 \
"$IMAGE_URI" \
/tmp/check_versions.py /tmp/requirements.txt
/tmp/check_versions.py "/tmp/$MOUNT_NAME"

- name: Clone sagemaker-scikit-learn-container (tests + source)
if: steps.check.outputs.exists == 'true'
Expand Down
134 changes: 134 additions & 0 deletions docker/sklearn/1.9-0/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# ============================================================================
# Scikit-learn DLC — Amazon Linux 2023 / Python 3.12 (CPU)
# Multi-stage build:
#
# builder-base ──── (venv + uv deps)
# wheel-builder ─── (holds pre-built sagemaker_sklearn_container wheel)
# sklearn-sagemaker (runtime with SageMaker serving layer)
#
# ============================================================================

# ── Global ARGs ─────────────────────────────────────────────────────────────
ARG PYTHON_VERSION=3.12

# ── Stage: builder-base ─────────────────────────────────────────────────────
FROM amazonlinux:2023 AS builder-base
ARG PYTHON_VERSION

RUN dnf install -y --allowerasing \
python${PYTHON_VERSION} python${PYTHON_VERSION}-devel python${PYTHON_VERSION}-pip \
gcc gcc-c++ make cmake git openssl-devel ninja-build \
tar xz curl wget \
&& dnf clean all

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

ENV UV_PROJECT_ENVIRONMENT="/opt/venv"
RUN python${PYTHON_VERSION} -m venv /opt/venv
ENV PATH="/opt/venv/bin:${PATH}"

COPY docker/sklearn/1.9-0/pyproject.toml docker/sklearn/1.9-0/uv.lock /tmp/build/
WORKDIR /tmp/build
RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev --no-install-project

# ── Stage: wheel-builder ───────────────────────────────────────────────────
# Wheel is pre-built in CI and placed at docker/sklearn/1.9-0/prebuilt.whl
FROM scratch AS wheel-builder
COPY docker/sklearn/1.9-0/prebuilt.whl /build/dist/sagemaker_sklearn_container-2.0-py3-none-any.whl

# ── Stage: sklearn-sagemaker ───────────────────────────────────────────────
FROM amazonlinux:2023 AS sklearn-sagemaker

ARG PYTHON_VERSION
ARG FRAMEWORK
ARG FRAMEWORK_VERSION
ARG CONTAINER_TYPE

LABEL maintainer="Amazon AI"
LABEL dlc_major_version="1"

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONIOENCODING=utf-8 \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8

# System dependencies
RUN dnf update -y && dnf install -y --allowerasing \
python${PYTHON_VERSION} python${PYTHON_VERSION}-devel \
gcc gcc-c++ make git curl wget tar gzip unzip jq which \
awscli-2 \
java-1.8.0-amazon-corretto \
nginx expat libxml2 glib2 libffi zlib zstd \
openssl-devel libcurl-devel \
shadow-utils \
&& dnf clean all

# Copy venv from builder
COPY --from=builder-base /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:${PATH}"

# Telemetry
COPY ./scripts/docker/telemetry/deep_learning_container.py /usr/local/bin/deep_learning_container.py
COPY ./scripts/docker/telemetry/bash_telemetry.sh.template /tmp/bash_telemetry.sh.template
COPY ./scripts/docker/common/setup_oss_compliance.sh setup_oss_compliance.sh
RUN chmod +x /usr/local/bin/deep_learning_container.py \
&& sed -e "s/{{FRAMEWORK}}/${FRAMEWORK}/g" \
-e "s/{{FRAMEWORK_VERSION}}/${FRAMEWORK_VERSION}/g" \
-e "s/{{CONTAINER_TYPE}}/${CONTAINER_TYPE}/g" \
/tmp/bash_telemetry.sh.template >/usr/local/bin/bash_telemetry.sh \
&& chmod +x /usr/local/bin/bash_telemetry.sh \
&& rm /tmp/bash_telemetry.sh.template \
&& echo 'source /usr/local/bin/bash_telemetry.sh' >>/etc/bash.bashrc \
&& bash setup_oss_compliance.sh python${PYTHON_VERSION} && rm setup_oss_compliance.sh \
&& rm -rf /root/oss_compliance* \
&& rm -rf /tmp/tmp*

# Remove build-time packages to reduce CVE surface
RUN dnf remove -y kernel-headers gcc gcc-c++ make cpp || true \
&& dnf upgrade -y --security --releasever latest \
&& dnf clean all

# Patch sagemaker-inference decoder.py (CWE-502 fix)
COPY docker/sklearn/resources/patches/decoder.py /opt/venv/lib64/python${PYTHON_VERSION}/site-packages/sagemaker_inference/decoder.py

# Patch collections.Mapping deprecation in sagemaker-containers
RUN sed -i 's/collections\.Mapping/collections.abc.Mapping/g' \
/opt/venv/lib64/python${PYTHON_VERSION}/site-packages/sagemaker_containers/_mapping.py

# Install sagemaker_sklearn_container wheel
COPY --from=wheel-builder /build/dist/sagemaker_sklearn_container-2.0-py3-none-any.whl /tmp/
RUN pip install --no-cache --no-deps /tmp/sagemaker_sklearn_container-2.0-py3-none-any.whl \
&& rm /tmp/sagemaker_sklearn_container-2.0-py3-none-any.whl

# MMS setup
RUN useradd -m model-server \
&& mkdir -p /home/model-server/tmp && chown -R model-server /home/model-server
COPY docker/sklearn/resources/mms/config.properties.tmp /home/model-server
ENV SKLEARN_MMS_CONFIG=/home/model-server/config.properties
RUN mkdir -p /tmp/plugins
COPY docker/sklearn/resources/mms/endpoints-1.0.jar /tmp/plugins
RUN chmod +x /tmp/plugins/endpoints-1.0.jar

# Model directory
RUN mkdir -p /opt/ml/models && chmod +rwx /opt/ml/models

# Drop temp dirs left by pip installs (sanity check requires clean /tmp)
RUN rm -rf /tmp/tmp* /tmp/pip-*

# Labels
LABEL com.amazonaws.sagemaker.capabilities.multi-models=true
LABEL com.amazonaws.sagemaker.capabilities.accept-bind-to-port=true

# SageMaker environment
ENV SAGEMAKER_SKLEARN_VERSION=1.9-0 \
SM_INPUT=/opt/ml/input \
SM_INPUT_TRAINING_CONFIG_FILE=/opt/ml/input/config/hyperparameters.json \
SM_INPUT_DATA_CONFIG_FILE=/opt/ml/input/config/inputdataconfig.json \
SM_CHECKPOINT_CONFIG_FILE=/opt/ml/input/config/checkpointconfig.json \
SM_MODEL_DIR=/opt/ml/model \
SAGEMAKER_TRAINING_MODULE=sagemaker_sklearn_container.training:main \
SAGEMAKER_SERVING_MODULE=sagemaker_sklearn_container.serving:main \
TEMP=/home/model-server/tmp

EXPOSE 8080
28 changes: 28 additions & 0 deletions docker/sklearn/1.9-0/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[project]
name = "sklearn-dlc"
version = "1.9.0"
requires-python = ">=3.12,<3.13"
dependencies = [
"scikit-learn==1.9.0",
"numpy==2.5.1",
"scipy==1.18.0",
"pandas==2.2.3",
"pyarrow==25.0.0",
"boto3==1.43.46",
"Flask==3.1.3",
"gunicorn==26.0.0",
"multi-model-server==1.1.2",
"sagemaker-containers==2.8.6.post2",
"sagemaker-inference==1.5.5",
"sagemaker-training==4.8.0",
"PyYAML==6.0.3",
"requests==2.33.0",
"setuptools>=80.9.0,<81",
"protobuf==3.20.2",
]

[tool.uv]
override-dependencies = [
"flask==3.1.3",
"werkzeug==3.1.8",
]
Loading
Loading