-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(sagemaker): add Model L2 construct (#22549)
This is the first of three PRs to complete the implementation of RFC 431: aws/aws-cdk-rfcs#431 fixes #2809 Co-authored-by: Matt McClean <[email protected]> Co-authored-by: Long Yao <[email protected]> Co-authored-by: Drew Jetter <[email protected]> Co-authored-by: Murali Ganesh <[email protected]> Co-authored-by: Abilash Rangoju <[email protected]> ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
- Loading branch information
1 parent
834fab4
commit 93915f1
Showing
30 changed files
with
3,902 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import * as ecr from '@aws-cdk/aws-ecr'; | ||
import * as assets from '@aws-cdk/aws-ecr-assets'; | ||
import { Construct } from 'constructs'; | ||
import { Model } from './model'; | ||
import { hashcode } from './private/util'; | ||
|
||
/** | ||
* The configuration for creating a container image. | ||
*/ | ||
export interface ContainerImageConfig { | ||
/** | ||
* The image name. Images in Amazon ECR repositories can be specified by either using the full registry/repository:tag or | ||
* registry/repository@digest. | ||
* | ||
* For example, `012345678910.dkr.ecr.<region-name>.amazonaws.com/<repository-name>:latest` or | ||
* `012345678910.dkr.ecr.<region-name>.amazonaws.com/<repository-name>@sha256:94afd1f2e64d908bc90dbca0035a5b567EXAMPLE`. | ||
*/ | ||
readonly imageName: string; | ||
} | ||
|
||
/** | ||
* Constructs for types of container images | ||
*/ | ||
export abstract class ContainerImage { | ||
/** | ||
* Reference an image in an ECR repository | ||
*/ | ||
public static fromEcrRepository(repository: ecr.IRepository, tag: string = 'latest'): ContainerImage { | ||
return new EcrImage(repository, tag); | ||
} | ||
|
||
/** | ||
* Reference an image that's constructed directly from sources on disk | ||
* @param directory The directory where the Dockerfile is stored | ||
* @param options The options to further configure the selected image | ||
*/ | ||
public static fromAsset(directory: string, options: assets.DockerImageAssetOptions = {}): ContainerImage { | ||
return new AssetImage(directory, options); | ||
} | ||
|
||
/** | ||
* Called when the image is used by a Model | ||
*/ | ||
public abstract bind(scope: Construct, model: Model): ContainerImageConfig; | ||
} | ||
|
||
class EcrImage extends ContainerImage { | ||
constructor(private readonly repository: ecr.IRepository, private readonly tag: string) { | ||
super(); | ||
} | ||
|
||
public bind(_scope: Construct, model: Model): ContainerImageConfig { | ||
this.repository.grantPull(model); | ||
|
||
return { | ||
imageName: this.repository.repositoryUriForTag(this.tag), | ||
}; | ||
} | ||
} | ||
|
||
class AssetImage extends ContainerImage { | ||
private asset?: assets.DockerImageAsset; | ||
|
||
constructor(private readonly directory: string, private readonly options: assets.DockerImageAssetOptions = {}) { | ||
super(); | ||
} | ||
|
||
public bind(scope: Construct, model: Model): ContainerImageConfig { | ||
// Retain the first instantiation of this asset | ||
if (!this.asset) { | ||
this.asset = new assets.DockerImageAsset(scope, `ModelImage${hashcode(this.directory)}`, { | ||
directory: this.directory, | ||
...this.options, | ||
}); | ||
} | ||
|
||
this.asset.repository.grantPull(model); | ||
|
||
return { | ||
imageName: this.asset.imageUri, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
export * from './container-image'; | ||
export * from './model'; | ||
export * from './model-data'; | ||
|
||
// AWS::SageMaker CloudFormation Resources: | ||
export * from './sagemaker.generated'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import * as s3 from '@aws-cdk/aws-s3'; | ||
import * as assets from '@aws-cdk/aws-s3-assets'; | ||
import { Construct } from 'constructs'; | ||
import { IModel } from './model'; | ||
import { hashcode } from './private/util'; | ||
|
||
// The only supported extension for local asset model data | ||
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sagemaker-model-containerdefinition.html#cfn-sagemaker-model-containerdefinition-modeldataurl | ||
const ARTIFACT_EXTENSION = '.tar.gz'; | ||
|
||
/** | ||
* The configuration needed to reference model artifacts. | ||
*/ | ||
export interface ModelDataConfig { | ||
/** | ||
* The S3 path where the model artifacts, which result from model training, are stored. This path | ||
* must point to a single gzip compressed tar archive (.tar.gz suffix). | ||
*/ | ||
readonly uri: string; | ||
} | ||
|
||
/** | ||
* Model data represents the source of model artifacts, which will ultimately be loaded from an S3 | ||
* location. | ||
*/ | ||
export abstract class ModelData { | ||
/** | ||
* Constructs model data which is already available within S3. | ||
* @param bucket The S3 bucket within which the model artifacts are stored | ||
* @param objectKey The S3 object key at which the model artifacts are stored | ||
*/ | ||
public static fromBucket(bucket: s3.IBucket, objectKey: string): ModelData { | ||
return new S3ModelData(bucket, objectKey); | ||
} | ||
|
||
/** | ||
* Constructs model data that will be uploaded to S3 as part of the CDK app deployment. | ||
* @param path The local path to a model artifact file as a gzipped tar file | ||
* @param options The options to further configure the selected asset | ||
*/ | ||
public static fromAsset(path: string, options: assets.AssetOptions = {}): ModelData { | ||
return new AssetModelData(path, options); | ||
} | ||
|
||
/** | ||
* This method is invoked by the SageMaker Model construct when it needs to resolve the model | ||
* data to a URI. | ||
* @param scope The scope within which the model data is resolved | ||
* @param model The Model construct performing the URI resolution | ||
*/ | ||
public abstract bind(scope: Construct, model: IModel): ModelDataConfig; | ||
} | ||
|
||
class S3ModelData extends ModelData { | ||
constructor(private readonly bucket: s3.IBucket, private readonly objectKey: string) { | ||
super(); | ||
} | ||
|
||
public bind(_scope: Construct, model: IModel): ModelDataConfig { | ||
this.bucket.grantRead(model); | ||
|
||
return { | ||
uri: this.bucket.urlForObject(this.objectKey), | ||
}; | ||
} | ||
} | ||
|
||
class AssetModelData extends ModelData { | ||
private asset?: assets.Asset; | ||
|
||
constructor(private readonly path: string, private readonly options: assets.AssetOptions) { | ||
super(); | ||
if (!path.toLowerCase().endsWith(ARTIFACT_EXTENSION)) { | ||
throw new Error(`Asset must be a gzipped tar file with extension ${ARTIFACT_EXTENSION} (${this.path})`); | ||
} | ||
} | ||
|
||
public bind(scope: Construct, model: IModel): ModelDataConfig { | ||
// Retain the first instantiation of this asset | ||
if (!this.asset) { | ||
this.asset = new assets.Asset(scope, `ModelData${hashcode(this.path)}`, { | ||
path: this.path, | ||
...this.options, | ||
}); | ||
} | ||
|
||
this.asset.grantRead(model); | ||
|
||
return { | ||
uri: this.asset.httpUrl, | ||
}; | ||
} | ||
} |
Oops, something went wrong.