Skip to content

Commit

Permalink
feat: add SageMaker Endpoint module
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kukushkin <[email protected]>
  • Loading branch information
kukushking committed Feb 5, 2024
1 parent d6165af commit 82fd555
Show file tree
Hide file tree
Showing 15 changed files with 952 additions and 0 deletions.
76 changes: 76 additions & 0 deletions modules/sagemaker/sagemaker-endpoint/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# SageMaker Model Endpoint

## Description

This is an example module that creates SageMaker real-time inference endpoint for a model.

## Inputs/Outputs

### Input Paramenters

#### Required

- `vpc-id`: The VPC-ID that the endpoint will be created in
- `subnet-ids`: The subnets that the endpoint will be created in
- `model-package-arn`: Model package ARN or
- `model-package-group-name`: Model package group name to pull latest approved model from
- `model-bucket-arn`: Model bucket ARN
-
#### Optional

- `sagemaker-project-id`: The VPC-ID that the endpoint will be created in
- `sagemaker-project-name`: The subnets that the endpoint will be created in\
- `model-execution-role-arn`: Model execution role ARN. Will be created if not provided.
- `ecr-repo-arn`: ECR repository ARN if custom container is used
- `variant-name`: Endpoint config production variant name. `AllTraffic` by default.
- `initial-instance-count`: Initial instance count. `1` by default.
- `initial-variant-weight`: Initial variant weight. `1` by default.
- `instance-type`: instance type. `ml.m4.xlarge` by default.

### Sample manifest declaration

```yaml
name: endpoint
path: modules/sagemaker/sagemaker-endpoint
parameters:
- name: sagemaker_project_id
value: dummy123
- name: sagemaker_project_name
value: dummy123
- name: model_package_arn
value: arn:aws:sagemaker:<region>:<account>:model-package/<package_name>/1
- name: model_bucket_arn
value: arn:aws:s3:::<bucket_name>
- name: instance_type
value: ml.m5.large
- name: vpc_id
valueFrom:
moduleMetadata:
group: networking
name: networking
key: VpcId
- name: subnet_ids
valueFrom:
moduleMetadata:
group: networking
name: networking
key: PrivateSubnetIds
```
### Module Metadata Outputs
- `ModelExecutionRoleArn`: Model execution role ARN
- `ModelName`: Model name
- `EndpointName`: Endpoint name
- `EndpointUrl` Endpoint Url

#### Output Example

```json
{
"ModelExecutionRoleArn": "arn:aws:iam::xxx:role/xxx",
"ModelName": "xxx",
"EndpointName": "xxx-endpoint",
"EndpointUrl": "xxx-endpoint"
}
```
95 changes: 95 additions & 0 deletions modules/sagemaker/sagemaker-endpoint/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

import json
import os

import aws_cdk
from stack import DeployEndpointStack


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


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_SAGEMAKER_PROJECT_ID = None
DEFAULT_SAGEMAKER_PROJECT_NAME = None
DEFAULT_MODEL_PACKAGE_ARN = None
DEFAULT_MODEL_PACKAGE_GROUP_NAME = None
DEFAULT_MODEL_EXECUTION_ROLE_ARN = None
DEFAULT_MODEL_BUCKET_ARN = None
DEFAULT_ECR_REPO_ARN = None
DEFAULT_VARIANT_NAME = "AllTraffic"
DEFAULT_INITIAL_INSTANCE_COUNT = 1
DEFAULT_INITIAL_VARIANT_WEIGHT = 1
DEFAULT_INSTANCE_TYPE = "ml.m4.xlarge"

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

vpc_id = os.getenv(_param("VPC_ID"))
subnet_ids = json.loads(os.getenv(_param("SUBNET_IDS"), "[]"))
sagemaker_project_id = os.getenv(_param("SAGEMAKER_PROJECT_ID"), DEFAULT_SAGEMAKER_PROJECT_ID)
sagemaker_project_name = os.getenv(_param("SAGEMAKER_PROJECT_NAME"), DEFAULT_SAGEMAKER_PROJECT_NAME)
model_package_arn = os.getenv(_param("MODEL_PACKAGE_ARN"), DEFAULT_MODEL_PACKAGE_ARN)
model_package_group_name = os.getenv(_param("MODEL_PACKAGE_GROUP_NAME"), DEFAULT_MODEL_PACKAGE_GROUP_NAME)
model_execution_role_arn = os.getenv(_param("MODEL_EXECUTION_ROLE_ARN"), DEFAULT_MODEL_EXECUTION_ROLE_ARN)
model_bucket_arn = os.getenv(_param("MODEL_BUCKET_ARN"), DEFAULT_MODEL_BUCKET_ARN)
ecr_repo_arn = os.getenv(_param("ECR_REPO_ARN"), DEFAULT_ECR_REPO_ARN)
variant_name = os.getenv(_param("VARIANT_NAME"), DEFAULT_VARIANT_NAME)
initial_instance_count = int(os.getenv(_param("INITIAL_INSTANCE_COUNT"), DEFAULT_INITIAL_INSTANCE_COUNT))
initial_variant_weight = int(os.getenv(_param("INITIAL_VARIANT_WEIGHT"), DEFAULT_INITIAL_VARIANT_WEIGHT))
instance_type = os.getenv(_param("INSTANCE_TYPE"), DEFAULT_INSTANCE_TYPE)

if not vpc_id:
raise ValueError("Missing input parameter vpc-id")

if not model_package_arn and not model_package_group_name:
raise ValueError("Parameter model-package-arn or model-package-group-name is required")


app = aws_cdk.App()
stack = DeployEndpointStack(
scope=app,
id=app_prefix,
app_prefix=app_prefix,
sagemaker_project_id=sagemaker_project_id,
sagemaker_project_name=sagemaker_project_name,
model_package_arn=model_package_arn,
model_package_group_name=model_package_group_name,
model_execution_role_arn=model_execution_role_arn,
vpc_id=vpc_id,
subnet_ids=subnet_ids,
model_bucket_arn=model_bucket_arn,
ecr_repo_arn=ecr_repo_arn,
endpoint_config_prod_variant=dict(
initial_instance_count=initial_instance_count,
initial_variant_weight=initial_variant_weight,
instance_type=instance_type,
variant_name=variant_name,
),
env=environment,
)

aws_cdk.CfnOutput(
scope=stack,
id="metadata",
value=stack.to_json_string(
{
"ModelExecutionRoleArn": stack.model_execution_role_arn,
"ModelName": stack.model.model_name,
"EndpointName": stack.endpoint.endpoint_name,
"EndpointUrl": stack.endpoint.endpoint_name,
}
),
)


app.synth()
23 changes: 23 additions & 0 deletions modules/sagemaker/sagemaker-endpoint/deployspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
publishGenericEnvVariables: true
deploy:
phases:
install:
commands:
- env
# Install whatever additional build libraries
- npm install -g [email protected]
- pip install -r requirements.txt
build:
commands:
- cdk deploy --require-approval never --progress events --app "python app.py" --outputs-file ./cdk-exports.json
destroy:
phases:
install:
commands:
# Install whatever additional build libraries
- npm install -g [email protected]
- pip install -r requirements.txt
build:
commands:
# execute the CDK
- cdk destroy --force --app "python app.py"
26 changes: 26 additions & 0 deletions modules/sagemaker/sagemaker-endpoint/modulestack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
AWSTemplateFormatVersion: 2010-09-09
Description: This template deploys a Module specific IAM permissions

Parameters:
RoleName:
Type: String
Description: The name of the IAM Role
ModelPackageGroupName:
Type: String
Description: The name of the SageMaker Model Package Group
Default: NotPopulated

Resources:
Policy:
Type: AWS::IAM::Policy
Properties:
PolicyName: "sagemaker-endpoint-modulespecific-policy"
Roles: [!Ref RoleName]
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sagemaker:ListModelPackages
Resource:
- !Sub arn:${AWS::Partition}:sagemaker:${AWS::Region}:${AWS::AccountId}:model-package/${ModelPackageGroupName}/*
30 changes: 30 additions & 0 deletions modules/sagemaker/sagemaker-endpoint/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[tool.black]
line-length = 120
target-version = ["py36", "py37", "py38"]
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
src_paths = ["sagemaker-model-deploy"]
py_version = 36
skip_gitignore = false
14 changes: 14 additions & 0 deletions modules/sagemaker/sagemaker-endpoint/requirements-dev.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
awscli
black
cdk-nag
cfn-lint
check-manifest
flake8
isort
mypy
pip-tools
pydot
pyroma
pytest
types-PyYAML
types-setuptools
Loading

0 comments on commit 82fd555

Please sign in to comment.