Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add sm custom kernel module #106

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion manifests/local-isolated/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ groups:
path: manifests/local-isolated/compute-modules.yaml
- name: ml
path: manifests/local-isolated/ml-modules.yaml
- name: sm-kernels
path: manifests/local-isolated/kernels-modules.yaml
targetAccountMappings:
- alias: primary
accountId:
Expand All @@ -24,4 +26,3 @@ targetAccountMappings:
regionMappings:
- region: us-east-1
default: true

29 changes: 29 additions & 0 deletions manifests/local-isolated/kernels-modules.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: sagemaker-custom-kernel
path: modules/ml/sagemaker-custom-kernel/
parameters:
- name: ecr-repo-name
value: default
- name: studio-domain-id
valueFrom:
moduleMetadata:
group: ml
name: sagemaker-studio
key: StudioDomainId
- name: studio-domain-name
valueFrom:
moduleMetadata:
group: ml
name: sagemaker-studio
key: StudioDomainName
- name: sagemaker-image-name
value: echo-kernel
- name: app-image-config-name
value: echo-kernel-app-config
- name: custom-kernel-name
value: echo
- name: kernel-user-uid
value: '0'
- name: kernel-user-gid
value: '0'
- name: kernel-user-home-mount-path
value: /root
36 changes: 36 additions & 0 deletions modules/ml/sagemaker-custom-kernel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Custom Kernel Module

## Description

This module builds custom kernel for SageMaker studio from Dockerfile.

## Inputs/Outputs

### Input Paramenters

#### Required

- `ecr_repo_name`: Name of the ECR repo for the image.
- `studio_domain_id`: SageMaker studio domain to attach the kernel to.
- `studio_domain_name`: SageMaker studio name to attach the kernel to.
- `sagemaker_image_name`: Name of the sagemaker image. This variable is also used to find the Dockerfile. The docker build script will be looking for file inside `modules/mlops/custom-kernel/docker/{sagemaker_image_name}`. 1 Dockerfile is added already: `pytorch-10`.

#### Optional

- `app_image_config_name`: Name of the app image config. Defaults to `idf-{deployment_name}-app-config`
- `kernel_user_uuid`: Default Unix User ID, defaults to: 1000
- `kernel_user_guid`: Default Unix Group ID, defaults to 100
- `kernel_user_mount_path`: # Path to mount in SageMaker Studio, defaults to `/home/sagemaker-user`

### Module Metadata Outputs

- `SageMakerCustomKernelRoleArn`: Role for custom kernel

#### Output Example

```json
{
"SageMakerCustomKernelRoleArn": "arn:aws:iam::<account>:role/idf-shared-infra-kernels-addfsharedinfrakernelske-9O6FZXGI0MM8",
}

```
50 changes: 50 additions & 0 deletions modules/ml/sagemaker-custom-kernel/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

import os

import aws_cdk as cdk
from aws_cdk import CfnOutput

from stack import CustomKernelStack

project_name = os.getenv("SEEDFARMER_PROJECT_NAME", "")
deployment_name = os.getenv("SEEDFARMER_DEPLOYMENT_NAME", "")
module_name = os.getenv("SEEDFARMER_MODULE_NAME", "")
app_prefix = f"{project_name}-{deployment_name}-{module_name}"

DEFAULT_APP_IMAGE_CONFIG_NAME = f"{project_name}-{deployment_name}-app-config"


def _param(name: str) -> str:
return f"SEEDFARMER_PARAMETER_{name}"


sagemaker_image_name = os.getenv(_param("SAGEMAKER_IMAGE_NAME"))
app_image_config_name = os.getenv(_param("APP_IMAGE_CONFIG_NAME"), DEFAULT_APP_IMAGE_CONFIG_NAME)


environment = cdk.Environment(
account=os.environ["CDK_DEFAULT_ACCOUNT"],
region=os.environ["CDK_DEFAULT_REGION"],
)

app = cdk.App()
stack = CustomKernelStack(
scope=app,
construct_id=app_prefix,
app_prefix=app_prefix,
env=environment,
)

CfnOutput(
scope=stack,
id="metadata",
value=stack.to_json_string(
{
"SageMakerCustomKernelRoleArn": stack.sagemaker_studio_image_role.role_arn,
kukushking marked this conversation as resolved.
Show resolved Hide resolved
}
),
)

app.synth()
3 changes: 3 additions & 0 deletions modules/ml/sagemaker-custom-kernel/coverage.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[run]
omit =
tests/*
39 changes: 39 additions & 0 deletions modules/ml/sagemaker-custom-kernel/deployspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
publishGenericEnvVariables: True
deploy:
phases:
install:
commands:
- npm install -g [email protected]
- pip install -r requirements.txt
- apt-get install gettext-base
build:
commands:
- datetime=$(date -u +"%Y%m%d%H%M%S")
- export COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
- export IMAGE_TAG=${COMMIT_HASH:=$datetime}
- export REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$SEEDFARMER_PARAMETER_ECR_REPO_NAME
- export IMAGE_URI=$REPOSITORY_URI:latest
- echo $REPOSITORY_URI
- echo $IMAGE_URI
- ./scripts/build_docker.sh
- cdk deploy --require-approval never --progress events --app "python app.py" --outputs-file ./cdk-exports.json
- cat cdk-exports.json
- python scripts/create_kernel.py
# Export metadata
- seedfarmer metadata convert -f cdk-exports.json || true
- export SEEDFARMER_MODULE_METADATA=$(cat SEEDFARMER_MODULE_METADATA)
- echo $SEEDFARMER_MODULE_METADATA
- export SEEDFARMER_MODULE_METADATA=$(echo $SEEDFARMER_MODULE_METADATA | jq ".CustomKernelImageName |= \"${SEEDFARMER_PARAMETER_SAGEMAKER_IMAGE_NAME}\"" )
- export SEEDFARMER_MODULE_METADATA=$(echo $SEEDFARMER_MODULE_METADATA | jq ".AppImageConfigName |= \"${SEEDFARMER_PARAMETER_APP_IMAGE_CONFIG_NAME}\"" )

destroy:
phases:
install:
commands:
- npm install -g [email protected]
- pip install -r requirements.txt
build:
commands:
- cdk destroy --force --app "python app.py"

build_type: BUILD_GENERAL1_LARGE
37 changes: 37 additions & 0 deletions modules/ml/sagemaker-custom-kernel/docker/echo-kernel/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
FROM public.ecr.aws/docker/library/python:3.6

ARG NB_USER="sagemaker-user"
ARG NB_UID="1000"
ARG NB_GID="100"


######################
# OVERVIEW
# 1. Creates the `sagemaker-user` user with UID/GID 1000/100.
# 2. Ensures this user can `sudo` by default.
# 3. Install the echo kernel from PyPI and install its dependencies.
# 4. Make the default shell `bash`. This enhances the experience inside a Jupyter terminal as otherwise Jupyter defaults to `sh`
######################

# Setup the "sagemaker-user" user with root privileges.
RUN \
apt-get update && \
apt-get install -y sudo && \
useradd -m -s /bin/bash -N -u $NB_UID $NB_USER && \
chmod g+w /etc/passwd && \
echo "${NB_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
# Prevent apt-get cache from being persisted to this layer.
rm -rf /var/lib/apt/lists/*

# Install and configure the kernel.
RUN \
pip install echo_kernel \
# These are dependencies of echo_kernel but the version on PyPI is old and doesn't declare them correctly.
jupyter_client IPython ipykernel && \
# This ensures that the kernelspec.json is installed in location expected by Jupyter/KernelGateway.
python -m echo_kernel.install --sys-prefix

# Make the default shell bash (vs "sh") for a better Jupyter terminal UX
ENV SHELL=/bin/bash

USER $NB_UID
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Echo Kernel Image


The image creates a custom image in SageMaker Studio and bundles the [Jupyter echo kernel](https://github.com/jupyter/echo_kernel) along with it. This is meant as a "Hello World" example of how to a custom image with SageMaker Studio. For more samples, go to [sagemaker-studio-custom-image-samples](https://github.com/aws-samples/sagemaker-studio-custom-image-samples/blob/main/examples/echo-kernel-image/README.md)
45 changes: 45 additions & 0 deletions modules/ml/sagemaker-custom-kernel/modulestack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
AWSTemplateFormatVersion: 2010-09-09
Description: This stack deploys a Module specific IAM permissions

Parameters:

RoleName:
Type: String
Description: The name of the IAM Role

Resources:
Policy:
Type: "AWS::IAM::Policy"
Properties:
PolicyDocument:
Statement:
- Effect: Allow
Action:
- "ecr:Describe*"
- "ecr:Get*"
- "ecr:List*"
- "sagemaker:CreateImage"
- "sagemaker:CreateImageVersion"
- "sagemaker:DescribeImage"
- "sagemaker:DescribeImageVersion"
- "sagemaker:DescribeAppImageConfig"
- "sagemaker:CreateAppImageConfig"
- "sagemaker:UpdateAppImageConfig"
- "sagemaker:UpdateDomain"
- "sagemaker:DescribeDomain"
- "sagemaker:AddTags"
- "iam:PassRole"
Resource: "*"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too wide open....lets discuss

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks good catch, especially pass role, will scope down

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scoped down iam:PassRole.

One interesting caveat regarding ECR deployment IAM permissions - ECRDeployment construct always adds a policy that contains wildcard permissions, even if you provide it with a custom role. Filed an issue for that.

- Action:
- "ecr:Create*"
- "ecr:Delete*"
- "ecr:*LayerUpload"
- "ecr:UploadLayerPart"
- "ecr:Batch*"
- "ecr:Put*"
Effect: Allow
Resource:
- !Sub "arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/*"
Version: 2012-10-17
PolicyName: "idf-modulespecific-policy"
Roles: [!Ref RoleName]
35 changes: 35 additions & 0 deletions modules/ml/sagemaker-custom-kernel/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[tool.black]
line-length = 120
target-version = ["py36", "py37", "py38", "py39"]
exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| \.env
| _build
| buck-out
| build
| dist
| codeseeder.out
)/
'''

[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
line_length = 120
py_version = 38
skip_gitignore = false

[tool.pytest.ini_options]
addopts = "-v --cov=. --cov-report term --cov-config=coverage.ini --cov-fail-under=80"
kukushking marked this conversation as resolved.
Show resolved Hide resolved
pythonpath = [
"."
]
3 changes: 3 additions & 0 deletions modules/ml/sagemaker-custom-kernel/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
aws-cdk-lib==2.114.1
constructs>=10.0.0,<11.0.0
cdk-nag==2.12.29
11 changes: 11 additions & 0 deletions modules/ml/sagemaker-custom-kernel/scripts/build_docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

#!/usr/bin/env bash

aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
echo Building the Docker image...
cache_image=$IMAGE_URI
docker pull ${cache_image} 2>&1 > /dev/null || true
cd docker/$SEEDFARMER_PARAMETER_SAGEMAKER_IMAGE_NAME && docker build --progress plain --cache-from=${cache_image} -t $IMAGE_URI .
docker push $IMAGE_URI
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of going with the traditional way of building docker images via CLI, you could use CDK way of building docker images. Ref: https://github.com/srinivasreddych/demo-with-mps3-reinvent23/blob/main/modules/pre-processing/image-extraction/stack.py#L68

This will look for a dockerfile, build and push it to ECR and you can avoid the build script and build commands in the deployspec.yaml

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nice, didn't know about this. Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oooohhhh....this is nice

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored to use this. Thanks Srini!

Loading