diff --git a/examples/pip-venv-kernel-image/Dockerfile b/examples/pip-venv-kernel-image/Dockerfile new file mode 100644 index 0000000..9edfe79 --- /dev/null +++ b/examples/pip-venv-kernel-image/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.11.1 + +RUN pip install ipykernel && \ + python -m ipykernel install --sys-prefix + +COPY requirements.txt requirements.txt +RUN pip install -r requirements.txt \ No newline at end of file diff --git a/examples/pip-venv-kernel-image/README.md b/examples/pip-venv-kernel-image/README.md new file mode 100644 index 0000000..01ba638 --- /dev/null +++ b/examples/pip-venv-kernel-image/README.md @@ -0,0 +1,121 @@ +## Pip Environments as Kernels + + +### Overview + +This custom image sample demonstrates how to create a custom Pip environment in a Docker image and use it as a custom kernel in SageMaker Studio. + +The Pip environment must have the appropriate kernel package installed, for e.g., `ipykernel` for a Python kernel. This example creates a Pip environment called `myenv` with a few Python packages (see [environment.yml](environment.yml)) and the `ipykernel`. SageMaker Studio will automatically recognize this Pip environment as a kernel named `pip-venv-myenv-py` (See [app-image-config-input.json](app-image-config-input.json)) + +### Building the image + +Build the Docker image. +``` +# Modify these as required. The Docker registry endpoint can be tuned based on your current region from https://docs.aws.amazon.com/general/latest/gr/ecr.html#ecr-docker-endpoints +REGION= +ACCOUNT_ID= +IMAGE_NAME=pip-venv-kernel + +# Create ECR Repository. Ignore if it exists. For simplcity, all examples in the repo +# use same ECR repo with different image tags +aws --region ${REGION} ecr create-repository --repository-name smstudio-custom + +# Build and push the image +aws --region ${REGION} ecr get-login-password | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom +docker build . -t ${IMAGE_NAME} -t ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom:${IMAGE_NAME} +``` + +### Local testing + +Run the image locally to verify that the kernels in the image are visible to a Kernel Gateway. +``` +docker run -it "$IMAGE_NAME" bash +``` + +Run the container with a KernelGateway to validate that the kernels are visible from the REST endpoint exposed to the host. + +``` +docker run -it -p 8888:8888 "$IMAGE_NAME" bash -c 'pip install jupyter_kernel_gateway && jupyter-kernelgateway --ip 0.0.0.0 --debug --port 8888' +``` + +Verify the Kernel Gateway is started successfully (e.g., *[KernelGatewayApp] Jupyter Kernel Gateway at http://0.0.0.0:8888* in the Docker logs) and validate that you can list the kernelspecs in the running container + +``` +curl http://0.0.0.0:8888/api/kernelspecs +``` + + +### Pushing the image +Push the Docker image to Amazon ECR +``` +docker push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom:${IMAGE_NAME} +``` + +### Using with SageMaker Studio +Create a SageMaker Image (SMI) with the image in ECR. Request parameter RoleArn value is used to get +ECR image information when and Image version is created. After creating Image, create an Image Version during which +SageMaker stores image metadata like SHA etc. Everytime an image is updated in ECR, a new image version should be created. +See [Update Image](#updating-image-with-sageMaker-studio) + +```bash +# Role in your account to be used for SageMakerImage. Modify as required. + +ROLE_ARN=arn:aws:iam::${ACCOUNT_ID}:role/RoleName +aws --region ${REGION} sagemaker create-image \ + --image-name ${IMAGE_NAME} \ + --role-arn ${ROLE_ARN} + +aws --region ${REGION} sagemaker create-image-version \ + --image-name ${IMAGE_NAME} \ + --base-image "${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom:${IMAGE_NAME}" + +# Verify the image-version is created successfully. Do NOT proceed if image-version is in CREATE_FAILED state or in any other state apart from CREATED. +aws --region ${REGION} sagemaker describe-image-version --image-name ${IMAGE_NAME} +``` + +Create a AppImageConfig for this image + +```bash +aws --region ${REGION} sagemaker create-app-image-config --cli-input-json file://app-image-config-input.json + +``` + +Create a Domain, providing the SageMaker Image and AppImageConfig in the Domain input. Replace the placeholders for VPC ID, Subnet IDs, and Execution Role in `create-domain-input.json` + +```bash +aws --region ${REGION} sagemaker create-domain --cli-input-json file://create-domain-input.json +``` + +If you have an existing Domain, you can also use the `update-domain`. Replace the placeholder for Domain ID in `update-domain-input.json` + +```bash +aws --region ${REGION} sagemaker update-domain --cli-input-json file://update-domain-input.json +``` + +Create a User Profile, and start a Notebook using the SageMaker Studio launcher. + +### Update Image with SageMaker Studio +If you found an issue with your image or want to update Image with new features, Use following steps + +Re-Build and push the image to ECR + +``` +# Build and push the image +aws --region ${REGION} ecr get-login-password | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom +docker build . -t ${IMAGE_NAME} -t ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom:${IMAGE_NAME} +docker push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom:${IMAGE_NAME} +``` + + +Create new App Image Version +``` +aws --region ${REGION} sagemaker create-image-version \ + --image-name ${IMAGE_NAME} \ + --base-image "${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/smstudio-custom:${IMAGE_NAME}" + +# Verify the image-version is created successfully. Do NOT proceed if image-version is in CREATE_FAILED state or in any other state apart from CREATED. +aws --region ${REGION} sagemaker describe-image-version --image-name ${IMAGE_NAME} +``` + + +Re-Create App in SageMaker studio. diff --git a/examples/pip-venv-kernel-image/app-image-config-input.json b/examples/pip-venv-kernel-image/app-image-config-input.json new file mode 100644 index 0000000..7902a08 --- /dev/null +++ b/examples/pip-venv-kernel-image/app-image-config-input.json @@ -0,0 +1,16 @@ +{ + "AppImageConfigName": "pip-venv-kernel-config", + "KernelGatewayImageConfig": { + "KernelSpecs": [ + { + "Name": "python3", + "DisplayName": "Python [pip venv: myenv]" + } + ], + "FileSystemConfig": { + "MountPath": "/root", + "DefaultUid": 0, + "DefaultGid": 0 + } + } +} \ No newline at end of file diff --git a/examples/pip-venv-kernel-image/create-domain-input.json b/examples/pip-venv-kernel-image/create-domain-input.json new file mode 100644 index 0000000..0bd0164 --- /dev/null +++ b/examples/pip-venv-kernel-image/create-domain-input.json @@ -0,0 +1,19 @@ +{ + "DomainName": "domain-with-custom-pip-venv", + "VpcId": "", + "SubnetIds": [ + "" + ], + "DefaultUserSettings": { + "ExecutionRole": "", + "KernelGatewayAppSettings": { + "CustomImages": [ + { + "ImageName": "pip-venv-kernel", + "AppImageConfigName": "pip-venv-kernel-config" + } + ] + } + }, + "AuthMode": "IAM" +} \ No newline at end of file diff --git a/examples/pip-venv-kernel-image/requirements.txt b/examples/pip-venv-kernel-image/requirements.txt new file mode 100644 index 0000000..ab020a0 --- /dev/null +++ b/examples/pip-venv-kernel-image/requirements.txt @@ -0,0 +1,3 @@ +boto3 +numpy +pandas diff --git a/examples/pip-venv-kernel-image/update-domain-input.json b/examples/pip-venv-kernel-image/update-domain-input.json new file mode 100644 index 0000000..8e68246 --- /dev/null +++ b/examples/pip-venv-kernel-image/update-domain-input.json @@ -0,0 +1,13 @@ +{ + "DomainId": "", + "DefaultUserSettings": { + "KernelGatewayAppSettings": { + "CustomImages": [ + { + "ImageName": "pip-venv-kernel", + "AppImageConfigName": "pip-venv-kernel-config" + } + ] + } + } +} \ No newline at end of file