constructor()

in source/lib/ipc-ai-saas-stack.ts [10:233]


  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
    this.templateOptions.description = '(SO8016) - IP Camera AI SaaS Service Stack. Template version v1.0.0';

    // user choose service type
    const applicationType = new cdk.CfnParameter(this, 'applicationType', {
        description: 'Please choose your desired service type',
        type: 'String',
        default: 'face-detection',
        allowedValues: [
            'face-detection',
            'body-detection',
            'face-comparison'
        ]
    });

    // user choose the deploy instance
    // price reference: https://www.amazonaws.cn/sagemaker/pricing/
    const deployInstanceType = new cdk.CfnParameter(this, 'deployInstanceType', {
        description: 'Please specify the instance type for hosting inference service (ml.c5.xlarge: CPU instance, ml.g4dn.xlarge: GPU instance)',
        type: 'String',
        default: 'ml.g4dn.xlarge',
        allowedValues: [
            'ml.m5.xlarge',
            'ml.g4dn.xlarge',
        ]
    });

    // user choose detection model
    const detectorModelName = new cdk.CfnParameter(this, 'detectorModelName', {
        description: 'Model name for object detector (only valid for face-detection/body-detection scenarios)',
        type: 'String',
        default: 'yolo3_darknet53_coco',
        allowedValues: [
            'ssd_512_resnet50_v1_coco',
            'yolo3_darknet53_coco',
            'yolo3_mobilenet1.0_coco',
            'faster_rcnn_fpn_resnet101_v1d_coco'
        ]
    });

    // user choose face comparison model, detector model + representation model
    const faceDetectAndCompareModelName = new cdk.CfnParameter(this, 'faceDetectAndCompareModelName', {
        description: 'Model name for face detection and comparison (only valid for face-comparison scenario)',
        type: 'String',
        default: 'retinaface_mnet025_v2+MobileFaceNet',
        allowedValues: [
            'retinaface_mnet025_v2+LResNet100E-IR',
            // 'retinaface_mnet025_v2+LResNet50E-IR',
            // 'retinaface_mnet025_v2+LResNet34E-IR',
            'retinaface_mnet025_v2+MobileFaceNet',

            // 'retinaface_r50_v1+LResNet100E-IR',
            // 'retinaface_r50_v1+LResNet50E-IR',
            // 'retinaface_r50_v1+LResNet34E-IR',
            'retinaface_r50_v1+MobileFaceNet',
        ]
    });

    // user choose whether to save all request events
    const saveRequestEvents = new cdk.CfnParameter(this, 'saveRequestEvents', {
        description: 'Whether to save all request images and corresponding response for close-loop improvement',
        type: 'String',
        default: 'No',
        allowedValues: [
            'Yes',
            'No',
        ]
    });

    // build the mapping relationship between instance type and environment (cpu/gpu) configuration
    // NINGXIA region only support G series GPU, no P series GPU
    // BEIJING region support both G and P series GPU
    const map = new cdk.CfnMapping(this, 'instanceEnvMapping', {
      mapping: {
        'ml.m5.xlarge': { value: 'cpu' },
        'ml.g4dn.xlarge': { value: 'gpu' },
      },
    });

    /*-------------------------------------------------------------------------------*/
    /*-------------------          S3 Bucket Provision          ---------------------*/
    /*-------------------------------------------------------------------------------*/
    const events = new s3.Bucket(
        this,
        'events',
        {
            // bucketName: `ipc-ai-saas-${applicationType.valueAsString}-events`,
            removalPolicy: cdk.RemovalPolicy.DESTROY,
            autoDeleteObjects: true,
        }
    );

    /*-------------------------------------------------------------------------------*/
    /*---------  Sagemaker Model/Endpoint Configuration/Endpoint Provision  ---------*/
    /*-------------------------------------------------------------------------------*/
    const sagemakerExecuteRole = new iam.Role(
        this,
        'sagemakerExecuteRole',
        {
            roleName: `ipc-ai-saas-${applicationType.valueAsString}-sagemaker-execution-role`,
            assumedBy: new iam.ServicePrincipal('sagemaker.amazonaws.com'),
            managedPolicies: [
                iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonS3FullAccess'),
                iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonEC2ContainerRegistryFullAccess'),
                iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchLogsFullAccess'),
            ]
        }
    );

    // configure container image name
    new cdk.CfnCondition(this,
        'IsChinaRegionCondition',
        { expression: cdk.Fn.conditionEquals(cdk.Aws.PARTITION, 'aws-cn')});

    const computeEnv = map.findInMap(`${deployInstanceType.valueAsString}`, 'value');


    const imageUrl = cdk.Fn.conditionIf(
        'IsChinaRegionCondition',
        `753680513547.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com.cn/ipc-ai-saas-${applicationType.valueAsString}-${computeEnv}:latest`,
        `366590864501.dkr.ecr.${cdk.Aws.REGION}.amazonaws.com/ipc-ai-saas-${applicationType.valueAsString}-${computeEnv}:latest`
    );

    // create model
    const sagemakerEndpointModel = new sagemaker.CfnModel(
        this,
        'sagemakerEndpointModel',
        {
            modelName: `ipc-ai-saas-${applicationType.valueAsString}-endpoint-model`,
            executionRoleArn: sagemakerExecuteRole.roleArn,
            containers: [
                {
                    image: imageUrl.toString(),
                    mode: 'SingleModel',
                    environment: {
                        FACE_DETECTION_AND_COMPARISON_MODEL_NAME: `${faceDetectAndCompareModelName.valueAsString}`,
                        OBJECT_DETECTION_MODEL_NAME: `${detectorModelName.valueAsString}`,
                    }
                }
            ],
        }
    );

    // create endpoint configuration
    const sagemakerEndpointConfig = new sagemaker.CfnEndpointConfig(
        this,
        'sagemakerEndpointConfig',
        {
            endpointConfigName: `ipc-ai-saas-${applicationType.valueAsString}-endpoint-config`,
            productionVariants: [{
                initialInstanceCount: 1,
                initialVariantWeight: 1,
                instanceType: `${deployInstanceType.valueAsString}`,
                modelName: sagemakerEndpointModel.attrModelName,
                variantName: 'AllTraffic',
            }]
        }
    );

    // create endpoint
    const sagemakerEndpoint = new sagemaker.CfnEndpoint(
        this,
        'sagemakerEndpoint',
        {
            endpointName: `ipc-ai-saas-${applicationType.valueAsString}-endpoint`,
            endpointConfigName: sagemakerEndpointConfig.attrEndpointConfigName
        }
    );


    /*-------------------------------------------------------------------------------*/
    /*---------------    Lambda Function & API Gateway Provision   ------------------*/
    /*-------------------------------------------------------------------------------*/
    const lambdaAccessPolicy = new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: [
            "sagemaker:InvokeEndpoint",
            "s3:GetObject",
            "s3:PutObject",
        ],
        resources: ["*"]
    });
    lambdaAccessPolicy.addAllResources();

    // lambda function provision
    const ipcSaaSHandler = new lambda.Function(
        this,
        'ipcSaaSHandler',
        {
            functionName: `ipcSaaSHandler-${applicationType.valueAsString}`,
            code: new lambda.AssetCode( 'lambda'),
            handler: 'main.handler',
            runtime: lambda.Runtime.PYTHON_3_8,
            environment: {
                SAGEMAKER_ENDPOINT_NAME: sagemakerEndpoint.attrEndpointName,
                SERVICE_TYPE: `${applicationType.valueAsString}`,
                OBJECT_DETECTOR_MODEL_NAME: `${detectorModelName.valueAsString}`,
                EVENTS_S3_BUCKET_NAME: events.bucketName,
                REQUEST_EVENTS_SNAPSHOT_ENABLED: `${saveRequestEvents.valueAsString}`,
            },
            timeout: cdk.Duration.minutes(10),
            memorySize: 512,
        }
    );
    ipcSaaSHandler.addToRolePolicy(lambdaAccessPolicy);

    // api gateway provision
    const apiRouter = new agw.RestApi(
        this,
        'apiRouter',
        {
            endpointConfiguration: {
                types: [agw.EndpointType.REGIONAL]
            },
            defaultCorsPreflightOptions: {
                allowOrigins: agw.Cors.ALL_ORIGINS,
                allowMethods: agw.Cors.ALL_METHODS
            }
        }
    );
    apiRouter.root.addResource('inference').addMethod('POST', new agw.LambdaIntegration(ipcSaaSHandler));

  }