| 
 | 1 | +ARG TAG_FOR_BASE_MICROMAMBA_IMAGE  | 
 | 2 | +FROM mambaorg/micromamba:$TAG_FOR_BASE_MICROMAMBA_IMAGE  | 
 | 3 | + | 
 | 4 | +ARG CUDA_MAJOR_MINOR_VERSION=''  | 
 | 5 | +ARG ENV_IN_FILENAME  | 
 | 6 | +ARG PINNED_ENV_IN_FILENAME  | 
 | 7 | +ARG ARG_BASED_ENV_IN_FILENAME  | 
 | 8 | +ARG IMAGE_VERSION  | 
 | 9 | + | 
 | 10 | +# Amazon Q Agentic Chat version - update this default value when needed  | 
 | 11 | +ARG FLARE_SERVER_VERSION_JL=1.25.0  | 
 | 12 | +# IDE type for Amazon Q integration  | 
 | 13 | +ARG AMAZON_Q_IDE_TYPE=jupyterlab  | 
 | 14 | + | 
 | 15 | +LABEL "org.amazon.sagemaker-distribution.image.version"=$IMAGE_VERSION  | 
 | 16 | + | 
 | 17 | +ARG AMZN_BASE="/opt/amazon/sagemaker"  | 
 | 18 | +ARG DB_ROOT_DIR="/opt/db"  | 
 | 19 | +ARG DIRECTORY_TREE_STAGE_DIR="${AMZN_BASE}/dir-staging"  | 
 | 20 | + | 
 | 21 | +ARG NB_USER="sagemaker-user"  | 
 | 22 | +ARG NB_UID=1000  | 
 | 23 | +ARG NB_GID=100  | 
 | 24 | + | 
 | 25 | +# https://www.openssl.org/source/  | 
 | 26 | +ARG FIPS_VALIDATED_SSL=3.0.8  | 
 | 27 | +ARG MIN_REQUIRED_MICROMAMBA_VERSION=1.5.11  | 
 | 28 | + | 
 | 29 | +ENV SAGEMAKER_LOGGING_DIR="/var/log/sagemaker/"  | 
 | 30 | +ENV STUDIO_LOGGING_DIR="/var/log/studio/"  | 
 | 31 | +ENV EDITOR="nano"  | 
 | 32 | +ENV IMAGE_VERSION=$IMAGE_VERSION  | 
 | 33 | +ENV PINNED_MICROMAMBA_MINOR_VERSION="1.5.*"  | 
 | 34 | +ENV SAGEMAKER_RECOVERY_MODE_HOME=/tmp/sagemaker-recovery-mode-home  | 
 | 35 | + | 
 | 36 | +USER root  | 
 | 37 | +# Upgrade micromamba to the latest patch version in the pinned minor version range, if applicable  | 
 | 38 | +RUN CURRENT_MICROMAMBA_VERSION=$(micromamba --version) && \  | 
 | 39 | +    echo "Current micromamba version: $CURRENT_MICROMAMBA_VERSION" && \  | 
 | 40 | +    if [[ "$CURRENT_MICROMAMBA_VERSION" == $PINNED_MICROMAMBA_MINOR_VERSION ]]; then \  | 
 | 41 | +        echo "Upgrading micromamba to the latest $PINNED_MICROMAMBA_MINOR_VERSION version..." && \  | 
 | 42 | +        micromamba self-update -c conda-forge --version "$MIN_REQUIRED_MICROMAMBA_VERSION" && \  | 
 | 43 | +        micromamba clean --all --yes --force-pkgs-dirs; \  | 
 | 44 | +    else \  | 
 | 45 | +        echo "Micromamba is already at version $CURRENT_MICROMAMBA_VERSION (outside $PINNED_MICROMAMBA_MINOR_VERSION). No upgrade performed."; \  | 
 | 46 | +    fi  | 
 | 47 | + | 
 | 48 | +RUN usermod "--login=${NB_USER}" "--home=/home/${NB_USER}" --move-home "-u ${NB_UID}" "${MAMBA_USER}" && \  | 
 | 49 | +    groupmod "--new-name=${NB_USER}" --non-unique "-g ${NB_GID}" "${MAMBA_USER}" && \  | 
 | 50 | +    # Update the expected value of MAMBA_USER for the  | 
 | 51 | +    # _entrypoint.sh consistency check.  | 
 | 52 | +    echo "${NB_USER}" > "/etc/arg_mamba_user" && \  | 
 | 53 | +    :  | 
 | 54 | +ENV MAMBA_USER=$NB_USER  | 
 | 55 | +ENV USER=$NB_USER  | 
 | 56 | + | 
 | 57 | +COPY extract_amazon_q_agentic_chat_urls.py /tmp/  | 
 | 58 | +COPY download_amazon_q_agentic_chat_artifacts.sh /tmp/  | 
 | 59 | + | 
 | 60 | +RUN apt-get update && apt-get upgrade -y && \  | 
 | 61 | +    apt-get install -y --no-install-recommends sudo gettext-base wget curl unzip git rsync build-essential openssh-client nano cron less mandoc jq ca-certificates gnupg && \  | 
 | 62 | +    # We just install tzdata below but leave default time zone as UTC. This helps packages like Pandas to function correctly.  | 
 | 63 | +    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata krb5-user libkrb5-dev libsasl2-dev libsasl2-modules && \  | 
 | 64 | +    chmod g+w /etc/passwd && \  | 
 | 65 | +    echo "ALL    ALL=(ALL)    NOPASSWD:    ALL" >> /etc/sudoers && \  | 
 | 66 | +    touch /etc/krb5.conf.lock && chown ${NB_USER}:${MAMBA_USER} /etc/krb5.conf* && \  | 
 | 67 | +    # Note that we do NOT run `rm -rf /var/lib/apt/lists/*` here. If we did, anyone building on top of our images will  | 
 | 68 | +    # not be able to run any `apt-get install` commands and that would hamper customizability of the images.  | 
 | 69 | +    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \  | 
 | 70 | +    unzip awscliv2.zip && \  | 
 | 71 | +    sudo ./aws/install && \  | 
 | 72 | +    rm -rf aws awscliv2.zip && \  | 
 | 73 | +    : && \  | 
 | 74 | +    # Install Q CLI  | 
 | 75 | +    curl --proto '=https' --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/1.12.7/q-x86_64-linux.zip" -o "q.zip" && \  | 
 | 76 | +    unzip q.zip && \  | 
 | 77 | +    Q_INSTALL_GLOBAL=true ./q/install.sh --no-confirm && \  | 
 | 78 | +    rm -rf q q.zip && \  | 
 | 79 | +    echo "source /usr/local/bin/_activate_current_env.sh" | tee --append /etc/profile && \  | 
 | 80 | +# CodeEditor - create server, user data dirs  | 
 | 81 | +    mkdir -p /opt/amazon/sagemaker/sagemaker-code-editor-server-data /opt/amazon/sagemaker/sagemaker-code-editor-user-data \  | 
 | 82 | +    && chown $MAMBA_USER:$MAMBA_USER /opt/amazon/sagemaker/sagemaker-code-editor-server-data /opt/amazon/sagemaker/sagemaker-code-editor-user-data && \  | 
 | 83 | +# create dir to store user data files  | 
 | 84 | +    mkdir -p /opt/amazon/sagemaker/user-data \  | 
 | 85 | +    && chown $MAMBA_USER:$MAMBA_USER /opt/amazon/sagemaker/user-data && \  | 
 | 86 | +# Merge in OS directory tree contents.  | 
 | 87 | +    mkdir -p ${DIRECTORY_TREE_STAGE_DIR}  | 
 | 88 | +COPY dirs/ ${DIRECTORY_TREE_STAGE_DIR}/  | 
 | 89 | +RUN rsync -a ${DIRECTORY_TREE_STAGE_DIR}/ / && \  | 
 | 90 | +    rm -rf ${DIRECTORY_TREE_STAGE_DIR} && \  | 
 | 91 | +# CodeEditor - download the extensions  | 
 | 92 | +    mkdir -p /etc/code-editor/extensions && \  | 
 | 93 | +    while IFS= read -r url || [ -n "$url" ]; do \  | 
 | 94 | +        echo "Downloading extension from ${url}..." && \  | 
 | 95 | +        wget --no-check-certificate -P /etc/code-editor/extensions "${url}"; \  | 
 | 96 | +    done < /etc/code-editor/extensions.txt  | 
 | 97 | + | 
 | 98 | +USER $MAMBA_USER  | 
 | 99 | +COPY --chown=$MAMBA_USER:$MAMBA_USER $ENV_IN_FILENAME *.in /tmp/  | 
 | 100 | +COPY --chown=$MAMBA_USER:$MAMBA_USER $PINNED_ENV_IN_FILENAME *.in /tmp/  | 
 | 101 | + | 
 | 102 | +ARG MAMBA_DOCKERFILE_ACTIVATE=1  | 
 | 103 | +ARG CONDA_OVERRIDE_CUDA=$CUDA_MAJOR_MINOR_VERSION  | 
 | 104 | + | 
 | 105 | +# Make sure that $ENV_IN_FILENAME and $PINNED_ENV_IN_FILENAME has a newline at the end before the `tee` command runs.  | 
 | 106 | +# Otherwise, nasty things will happen.  | 
 | 107 | +RUN if [[ -z $ARG_BASED_ENV_IN_FILENAME ]] ; \  | 
 | 108 | +    then echo 'No ARG_BASED_ENV_IN_FILENAME passed' ; \  | 
 | 109 | +    else envsubst < /tmp/$ARG_BASED_ENV_IN_FILENAME | tee --append /tmp/$ENV_IN_FILENAME ; \  | 
 | 110 | +    fi && \  | 
 | 111 | +    # Enforce dependencies are all installed from conda-forge  | 
 | 112 | +    micromamba install -y --name base --file /tmp/$ENV_IN_FILENAME --file /tmp/$PINNED_ENV_IN_FILENAME && \  | 
 | 113 | +    mkdir -p $SAGEMAKER_RECOVERY_MODE_HOME && \  | 
 | 114 | +    chown $MAMBA_USER:$MAMBA_USER $SAGEMAKER_RECOVERY_MODE_HOME && \  | 
 | 115 | +    SUPERVISOR_VERSION=$(grep "^conda-forge::supervisor\[" /tmp/$ENV_IN_FILENAME) && \  | 
 | 116 | +    JUPYTERLAB_VERSION=$(grep "^conda-forge::jupyterlab\[" /tmp/$ENV_IN_FILENAME) && \  | 
 | 117 | +    SAGEMAKER_JUPYTERLAB_VERSION=$(grep "^conda-forge::sagemaker-jupyterlab-extension" /tmp/$ENV_IN_FILENAME) && \  | 
 | 118 | +    echo "Installing in sagemaker-recovery-mode micromamba environment: $JUPYTERLAB_VERSION $SAGEMAKER_JUPYTERLAB_VERSION" && \  | 
 | 119 | +    micromamba create -n sagemaker-recovery-mode && \  | 
 | 120 | +    micromamba install -n sagemaker-recovery-mode -y $JUPYTERLAB_VERSION $SAGEMAKER_JUPYTERLAB_VERSION $SUPERVISOR_VERSION && \  | 
 | 121 | +    micromamba clean --all --yes --force-pkgs-dirs && \  | 
 | 122 | +    rm -rf /tmp/*.in && \  | 
 | 123 | +    sudo ln -s $(which python3) /usr/bin/python && \  | 
 | 124 | +    # Download shared web client libraries  | 
 | 125 | +    sudo mkdir -p /etc/web-client/libs && \  | 
 | 126 | +    sudo curl -L --retry 3 --retry-delay 5 --fail "https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js" -o "/etc/web-client/libs/jszip.min.js" || (echo "Failed to download JSZip library" && exit 1) && \  | 
 | 127 | +    # Download Amazon Q artifacts for JupyterLab extension  | 
 | 128 | +    bash /tmp/download_amazon_q_agentic_chat_artifacts.sh $FLARE_SERVER_VERSION_JL /etc/amazon-q-agentic-chat/artifacts/$AMAZON_Q_IDE_TYPE $AMAZON_Q_IDE_TYPE && \  | 
 | 129 | +    # Fix ownership for JupyterLab access  | 
 | 130 | +    sudo chown -R $MAMBA_USER:$MAMBA_USER /etc/amazon-q-agentic-chat/ /etc/web-client/ && \  | 
 | 131 | +    # Update npm version  | 
 | 132 | +    npm i -g npm && \  | 
 | 133 | +    # Enforce to use `conda-forge` as only channel, by removing `defaults`  | 
 | 134 | +    conda config --remove channels defaults && \  | 
 | 135 | +    micromamba config append channels conda-forge --env && \  | 
 | 136 | +    # Configure CodeEditor - Install extensions and set preferences  | 
 | 137 | +    extensionloc=/opt/amazon/sagemaker/sagemaker-code-editor-server-data/extensions && mkdir -p "${extensionloc}" \  | 
 | 138 | +    # Loop through all vsix files in /etc/code-editor/extensions and install them  | 
 | 139 | +    && for ext in /etc/code-editor/extensions/*.vsix; do \  | 
 | 140 | +        echo "Installing extension ${ext}..."; \  | 
 | 141 | +        sagemaker-code-editor --install-extension "${ext}" --extensions-dir "${extensionloc}" --server-data-dir /opt/amazon/sagemaker/sagemaker-code-editor-server-data --user-data-dir /opt/amazon/sagemaker/sagemaker-code-editor-user-data; \  | 
 | 142 | +       done \  | 
 | 143 | +    # Copy the settings  | 
 | 144 | +    && cp /etc/code-editor/code_editor_machine_settings.json /opt/amazon/sagemaker/sagemaker-code-editor-server-data/data/Machine/settings.json && \  | 
 | 145 | +    cp /etc/code-editor/code_editor_user_settings.json /opt/amazon/sagemaker/sagemaker-code-editor-server-data/data/User/settings.json && \  | 
 | 146 | +    # Install glue kernels, and move to shared directory  | 
 | 147 | +    # Also patching base kernel so Studio background code doesn't start session silently  | 
 | 148 | +    install-glue-kernels && \  | 
 | 149 | +    SITE_PACKAGES=$(pip show aws-glue-sessions | grep Location | awk '{print $2}') && \  | 
 | 150 | +    jupyter-kernelspec install $SITE_PACKAGES/aws_glue_interactive_sessions_kernel/glue_pyspark --user && \  | 
 | 151 | +    jupyter-kernelspec install $SITE_PACKAGES/aws_glue_interactive_sessions_kernel/glue_spark --user && \  | 
 | 152 | +    mv /home/sagemaker-user/.local/share/jupyter/kernels/glue_pyspark /opt/conda/share/jupyter/kernels && \  | 
 | 153 | +    mv /home/sagemaker-user/.local/share/jupyter/kernels/glue_spark /opt/conda/share/jupyter/kernels && \  | 
 | 154 | +    sed -i '/if not store_history and (/i\        if "sm_analytics_runtime_check" in code:\n            return await self._complete_cell()\n' \  | 
 | 155 | +    "$SITE_PACKAGES/aws_glue_interactive_sessions_kernel/glue_kernel_base/BaseKernel.py" && \  | 
 | 156 | +    # Install FIPS Provider for OpenSSL, on top of existing OpenSSL installation  | 
 | 157 | +    # v3.0.8 is latest FIPS validated provider, so this is the one we install  | 
 | 158 | +    # But we need to run tests against the installed version.  | 
 | 159 | +    # see https://github.com/openssl/openssl/blob/master/README-FIPS.md https://www.openssl.org/source/  | 
 | 160 | +    INSTALLED_SSL=$(micromamba list | grep openssl | tr -s ' ' | cut -d ' ' -f 3 | head -n 1) && \  | 
 | 161 | +    # download source code for installed, and FIPS validated openssl versions  | 
 | 162 | +    curl -L https://github.com/openssl/openssl/releases/download/openssl-$FIPS_VALIDATED_SSL/openssl-$FIPS_VALIDATED_SSL.tar.gz > openssl-$FIPS_VALIDATED_SSL.tar.gz &&  \  | 
 | 163 | +    curl -L https://github.com/openssl/openssl/releases/download/openssl-$INSTALLED_SSL/openssl-$INSTALLED_SSL.tar.gz > openssl-$INSTALLED_SSL.tar.gz &&  \  | 
 | 164 | +    tar -xf openssl-$FIPS_VALIDATED_SSL.tar.gz && tar -xf openssl-$INSTALLED_SSL.tar.gz && cd openssl-$FIPS_VALIDATED_SSL && \  | 
 | 165 | +    # Configure both versions to enable FIPS and build  | 
 | 166 | +    ./Configure enable-fips --prefix=/opt/conda --openssldir=/opt/conda/ssl && make && \  | 
 | 167 | +    cd ../openssl-$INSTALLED_SSL && \  | 
 | 168 | +    ./Configure enable-fips --prefix=/opt/conda --openssldir=/opt/conda/ssl && make && \  | 
 | 169 | +    # Copy validated provider to installed version for testing  | 
 | 170 | +    cp ../openssl-$FIPS_VALIDATED_SSL/providers/fips.so providers/. && \  | 
 | 171 | +    cp ../openssl-$FIPS_VALIDATED_SSL/providers/fipsmodule.cnf providers/. && \  | 
 | 172 | +    make tests && cd ../openssl-$FIPS_VALIDATED_SSL && \  | 
 | 173 | +    # After tests pass, install FIPS provider and remove source code  | 
 | 174 | +    make install_fips && cd .. && rm -rf ./openssl-* && \  | 
 | 175 | +# Create new config file with fips-enabled. Then user can override OPENSSL_CONF to enable FIPS  | 
 | 176 | +# e.g. export OPENSSL_CONF=/opt/conda/ssl/openssl-fips.cnf  | 
 | 177 | +    cp /opt/conda/ssl/openssl.cnf /opt/conda/ssl/openssl-fips.cnf && \  | 
 | 178 | +    sed -i "s:# .include fipsmodule.cnf:.include /opt/conda/ssl/fipsmodule.cnf:" /opt/conda/ssl/openssl-fips.cnf && \  | 
 | 179 | +    sed -i 's:# fips = fips_sect:fips = fips_sect:' /opt/conda/ssl/openssl-fips.cnf && \  | 
 | 180 | +# Install Kerberos.  | 
 | 181 | +# Make sure no dependency is added/updated  | 
 | 182 | +    pip install "krb5>=0.5.1,<0.6" && \  | 
 | 183 | +    pip show krb5 | grep Require | xargs -i sh -c '[ $(echo {} | cut -d: -f2 | wc -w) -eq 0 ] ' && \  | 
 | 184 | +# https://stackoverflow.com/questions/122327  | 
 | 185 | +    SYSTEM_PYTHON_PATH=$(python3 -c "from __future__ import print_function;import sysconfig; print(sysconfig.get_paths().get('purelib'))") && \  | 
 | 186 | +    # Remove SparkRKernel as it's not supported \  | 
 | 187 | +    jupyter-kernelspec remove -f -y sparkrkernel && \  | 
 | 188 | +    # Patch Sparkmagic lib to support Custom Certificates \  | 
 | 189 | +    # https://github.com/jupyter-incubator/sparkmagic/pull/435/files \  | 
 | 190 | +    cp -a ${SYSTEM_PYTHON_PATH}/sagemaker_studio_analytics_extension/patches/configuration.py ${SYSTEM_PYTHON_PATH}/sparkmagic/utils/ && \  | 
 | 191 | +    cp -a ${SYSTEM_PYTHON_PATH}/sagemaker_studio_analytics_extension/patches/reliablehttpclient.py ${SYSTEM_PYTHON_PATH}/sparkmagic/livyclientlib/reliablehttpclient.py && \  | 
 | 192 | +    sed -i 's=  "python"=  "/opt/conda/bin/python"=g'  /opt/conda/share/jupyter/kernels/pysparkkernel/kernel.json /opt/conda/share/jupyter/kernels/sparkkernel/kernel.json && \  | 
 | 193 | +    sed -i 's="Spark"="SparkMagic Spark"=g'  /opt/conda/share/jupyter/kernels/sparkkernel/kernel.json && \  | 
 | 194 | +    sed -i 's="PySpark"="SparkMagic PySpark"=g'  /opt/conda/share/jupyter/kernels/pysparkkernel/kernel.json && \  | 
 | 195 | +    # Configure RTC - disable jupyter_collaboration by default  | 
 | 196 | +    jupyter labextension disable @jupyter/collaboration-extension && \  | 
 | 197 | +    sudo curl -o /opt/apache-maven-3.9.9-bin.tar.gz https://archive.apache.org/dist/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz  | 
 | 198 | + | 
 | 199 | +# Patch glue kernels to use kernel wrapper  | 
 | 200 | +COPY patch_glue_pyspark.json /opt/conda/share/jupyter/kernels/glue_pyspark/kernel.json  | 
 | 201 | +COPY patch_glue_spark.json /opt/conda/share/jupyter/kernels/glue_spark/kernel.json  | 
 | 202 | + | 
 | 203 | +USER root  | 
 | 204 | + | 
 | 205 | +# Create logging directories for supervisor  | 
 | 206 | +RUN mkdir -p $SAGEMAKER_LOGGING_DIR && \  | 
 | 207 | +    chmod a+rw $SAGEMAKER_LOGGING_DIR && \  | 
 | 208 | +    mkdir -p ${STUDIO_LOGGING_DIR} && \  | 
 | 209 | +    chown ${NB_USER}:${MAMBA_USER} ${STUDIO_LOGGING_DIR} && \  | 
 | 210 | +    # Create sagemaker pysdk admin default config directory  | 
 | 211 | +    mkdir -p /etc/xdg/sagemaker && \  | 
 | 212 | +    chmod a+rw /etc/xdg/sagemaker && \  | 
 | 213 | +    # Clean up CodeEditor artifacts  | 
 | 214 | +    rm -rf /etc/code-editor && \  | 
 | 215 | +    # Create supervisord runtime directory  | 
 | 216 | +    mkdir -p /var/run/supervisord && \  | 
 | 217 | +    chmod a+rw /var/run/supervisord && \  | 
 | 218 | +    # Create root directory for DB  | 
 | 219 | +    # Create logging directories for supervisor  | 
 | 220 | +    mkdir -p $DB_ROOT_DIR && \  | 
 | 221 | +    chmod a+rw $DB_ROOT_DIR && \  | 
 | 222 | +    HOME_DIR="/home/${NB_USER}/licenses" \  | 
 | 223 | +    && mkdir -p ${HOME_DIR} \  | 
 | 224 | +    && curl -o ${HOME_DIR}/oss_compliance.zip https://aws-dlinfra-utilities.s3.amazonaws.com/oss_compliance.zip \  | 
 | 225 | +    && unzip ${HOME_DIR}/oss_compliance.zip -d ${HOME_DIR}/ \  | 
 | 226 | +    && cp ${HOME_DIR}/oss_compliance/test/testOSSCompliance /usr/local/bin/testOSSCompliance \  | 
 | 227 | +    && chmod +x /usr/local/bin/testOSSCompliance \  | 
 | 228 | +    && chmod +x ${HOME_DIR}/oss_compliance/generate_oss_compliance.sh \  | 
 | 229 | +    && ${HOME_DIR}/oss_compliance/generate_oss_compliance.sh ${HOME_DIR} python \  | 
 | 230 | +    && rm -rf ${HOME_DIR}/oss_compliance*  | 
 | 231 | + | 
 | 232 | +# Explicitly disable BuildKit for SM Studio Docker functionality  | 
 | 233 | +ENV DOCKER_BUILDKIT=0  | 
 | 234 | +ENV PATH="/opt/conda/bin:/opt/conda/condabin:$PATH"  | 
 | 235 | +WORKDIR "/home/${NB_USER}"  | 
 | 236 | +ENV SHELL=/bin/bash  | 
 | 237 | +ENV OPENSSL_MODULES=/opt/conda/lib64/ossl-modules/  | 
 | 238 | +USER $MAMBA_USER  | 
0 commit comments