def apply_deployment(deployment_info, yatai_service=None): if yatai_service is None: from bentoml.yatai import get_yatai_service yatai_service = get_yatai_service() try: if isinstance(deployment_info, dict): deployment_pb = deployment_dict_to_pb(deployment_info) elif isinstance(deployment_info, str): deployment_pb = deployment_yaml_string_to_pb(deployment_info) else: raise YataiDeploymentException( 'Unexpected argument type, expect deployment info to be str in yaml ' 'format or a dict, instead got: {}'.format( str(type(deployment_info)))) validation_errors = validate_deployment_pb_schema(deployment_pb) if validation_errors: return ApplyDeploymentResponse(status=Status.INVALID_ARGUMENT( 'Failed to validate deployment: {errors}'.format( errors=validation_errors))) return yatai_service.ApplyDeployment( ApplyDeploymentRequest(deployment=deployment_pb)) except BentoMLException as error: return ApplyDeploymentResponse(status=Status.INTERNAL(str(error)))
def ApplyDeployment(self, request, context=None): try: # apply default namespace if not set request.deployment.namespace = ( request.deployment.namespace or self.default_namespace ) validation_errors = validate_deployment_pb_schema(request.deployment) if validation_errors: return ApplyDeploymentResponse( status=Status.INVALID_ARGUMENT( 'Failed to validate deployment. {errors}'.format( errors=validation_errors ) ) ) previous_deployment = self.deployment_store.get( request.deployment.name, request.deployment.namespace ) if previous_deployment: # check deployment platform if ( previous_deployment.spec.operator != request.deployment.spec.operator ): return ApplyDeploymentResponse( status=Status.ABORTED( 'Can not change the target deploy platform of existing ' 'active deployment. Try delete existing deployment and ' 'deploy to new target platform again' ) ) request.deployment.state.state = DeploymentState.PENDING else: request.deployment.created_at.GetCurrentTime() request.deployment.last_updated_at.GetCurrentTime() self.deployment_store.insert_or_update(request.deployment) # find deployment operator based on deployment spec operator = get_deployment_operator(request.deployment) # deploying to target platform response = operator.apply(request.deployment, self, previous_deployment) # update deployment state self.deployment_store.insert_or_update(response.deployment) return response except BentoMLException as e: logger.error("INTERNAL ERROR: %s", e) return ApplyDeploymentResponse(status=Status.INTERNAL(str(e)))
def apply(self, deployment_pb, yatai_service, prev_deployment=None): try: deployment_spec = deployment_pb.spec gcp_config = deployment_spec.gcp_function_operator_config bento_pb = yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, )) if bento_pb.bento.uri.type != BentoUri.LOCAL: raise BentoMLException( 'BentoML currently only support local repository') else: bento_path = bento_pb.bento.uri.uri bento_service_metadata = bento_pb.bento.bento_service_metadata api_names = ([gcp_config.api_name] if gcp_config.api_name else [api.name for api in bento_service_metadata.apis]) ensure_deploy_api_name_exists_in_bento( [api.name for api in bento_service_metadata.apis], api_names) with TempDirectory() as serverless_project_dir: init_serverless_project_dir( serverless_project_dir, bento_path, deployment_pb.name, deployment_spec.bento_name, 'google-python', ) generate_gcp_function_main_py(deployment_spec.bento_name, api_names, serverless_project_dir) generate_gcp_function_serverless_config( deployment_pb.name, api_names, serverless_project_dir, gcp_config.region, # BentoML namespace is mapping to serverless stage. stage=deployment_pb.namespace, ) call_serverless_command(["deploy"], serverless_project_dir) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) state = self.describe(res_deployment_pb, yatai_service).state res_deployment_pb.state.CopyFrom(state) return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb) except BentoMLException as error: return ApplyDeploymentResponse( status=exception_to_return_status(error))
def apply(self, deployment_pb, repo, prev_deployment=None): deployment_spec = deployment_pb.spec gcp_config = deployment_spec.gcp_function_operator_config bento_path = repo.get(deployment_spec.bento_name, deployment_spec.bento_version) bento_config = load_bentoml_config(bento_path) with TemporaryServerlessContent( archive_path=bento_path, deployment_name=deployment_pb.name, bento_name=deployment_spec.bento_name, template_type='google-python', ) as output_path: generate_main_py(bento_config['name'], bento_config['apis'], output_path) generate_serverless_configuration_for_gcp_function( service_name=bento_config['name'], apis=bento_config['apis'], output_path=output_path, region=gcp_config.region, stage=deployment_pb.namespace, ) call_serverless_command(["deploy"], output_path) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) state = self.describe(res_deployment_pb, repo).state res_deployment_pb.state.CopyFrom(state) return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb)
def apply(self, deployment_pb, yatai_service, prev_deployment=None): try: ensure_docker_available_or_raise() deployment_spec = deployment_pb.spec sagemaker_config = deployment_spec.sagemaker_operator_config if sagemaker_config is None: raise BentoMLDeploymentException('Sagemaker configuration is missing.') bento_pb = yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, ) ) if bento_pb.bento.uri.type not in (BentoUri.LOCAL, BentoUri.S3): raise BentoMLException( 'BentoML currently not support {} repository'.format( bento_pb.bento.uri.type ) ) return self._apply( deployment_pb, bento_pb, yatai_service, bento_pb.bento.uri.uri, prev_deployment, ) except BentoMLException as error: return ApplyDeploymentResponse(status=exception_to_return_status(error))
def update(self, deployment_pb, previous_deployment): try: ensure_sam_available_or_raise() ensure_docker_available_or_raise() deployment_spec = deployment_pb.spec bento_pb = self.yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, ) ) if bento_pb.bento.uri.type not in (BentoUri.LOCAL, BentoUri.S3): raise BentoMLException( 'BentoML currently not support {} repository'.format( BentoUri.StorageType.Name(bento_pb.bento.uri.type) ) ) return self._update( deployment_pb, previous_deployment, bento_pb, bento_pb.bento.uri.uri ) except BentoMLException as error: deployment_pb.state.state = DeploymentState.ERROR deployment_pb.state.error_message = f'Error: {str(error)}' return ApplyDeploymentResponse( status=error.status_code, deployment_pb=deployment_pb )
def _add(self, deployment_pb, bento_pb, bento_path): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._add(deployment_pb, bento_pb, local_path) deployment_spec = deployment_pb.spec lambda_deployment_config = deployment_spec.aws_lambda_operator_config bento_service_metadata = bento_pb.bento.bento_service_metadata lambda_s3_bucket = generate_aws_compatible_string( 'btml-{namespace}-{name}-{random_string}'.format( namespace=deployment_pb.namespace, name=deployment_pb.name, random_string=uuid.uuid4().hex[:6].lower(), ) ) try: create_s3_bucket_if_not_exists( lambda_s3_bucket, lambda_deployment_config.region ) _deploy_lambda_function( deployment_pb=deployment_pb, bento_service_metadata=bento_service_metadata, deployment_spec=deployment_spec, lambda_s3_bucket=lambda_s3_bucket, lambda_deployment_config=lambda_deployment_config, bento_path=bento_path, ) return ApplyDeploymentResponse(status=Status.OK(), deployment=deployment_pb) except BentoMLException as error: if lambda_s3_bucket and lambda_deployment_config: _cleanup_s3_bucket_if_exist( lambda_s3_bucket, lambda_deployment_config.region ) raise error
def ApplyDeployment(self, request, context=None): try: # apply default namespace if not set request.deployment.namespace = (request.deployment.namespace or self.default_namespace) # TODO: validate deployment config # create or update deployment spec record self.deployment_store.insert_or_update(request.deployment) # find deployment operator based on deployment spec operator = get_deployment_operator(request.deployment) # deploying to target platform response = operator.apply(request.deployment, self.repo) # update deployment state self.deployment_store.insert_or_update(response.deployment) return response except BentoMLException as e: logger.error("INTERNAL ERROR: %s", e) return ApplyDeploymentResponse(Status.INTERNAL(e))
def add(self, deployment_pb): try: deployment_spec = deployment_pb.spec deployment_spec.aws_lambda_operator_config.region = ( deployment_spec.aws_lambda_operator_config.region or get_default_aws_region()) if not deployment_spec.aws_lambda_operator_config.region: raise InvalidArgument('AWS region is missing') ensure_sam_available_or_raise() ensure_docker_available_or_raise() bento_pb = self.yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, )) if bento_pb.bento.uri.type not in (BentoUri.LOCAL, BentoUri.S3): raise BentoMLException( 'BentoML currently not support {} repository'.format( BentoUri.StorageType.Name(bento_pb.bento.uri.type))) return self._add(deployment_pb, bento_pb, bento_pb.bento.uri.uri) except BentoMLException as error: deployment_pb.state.state = DeploymentState.ERROR deployment_pb.state.error_message = f'Error: {str(error)}' return ApplyDeploymentResponse(status=error.status_proto, deployment=deployment_pb)
def add(self, deployment_pb): try: ensure_docker_available_or_raise() deployment_spec = deployment_pb.spec sagemaker_config = deployment_spec.sagemaker_operator_config if sagemaker_config is None: raise YataiDeploymentException('Sagemaker configuration is missing.') bento_pb = self.yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, ) ) if bento_pb.bento.uri.type not in (BentoUri.LOCAL, BentoUri.S3): raise BentoMLException( 'BentoML currently not support {} repository'.format( BentoUri.StorageType.Name(bento_pb.bento.uri.type) ) ) return self._add(deployment_pb, bento_pb, bento_pb.bento.uri.uri) except BentoMLException as error: deployment_pb.state.state = DeploymentState.ERROR deployment_pb.state.error_message = ( f'Error creating SageMaker deployment: {str(error)}' ) return ApplyDeploymentResponse( status=error.status_proto, deployment=deployment_pb )
def create_yatai_service_mock(): yatai_service_mock = Mock() yatai_service_mock.ApplyDeployment.return_value = ApplyDeploymentResponse() yatai_service_mock.DeleteDeployment.return_value = DeleteDeploymentResponse() yatai_service_mock.DescribeDeployment.return_value = DescribeDeploymentResponse() yatai_service_mock.GetDeployment.return_value = GetDeploymentResponse( status=Status.NOT_FOUND() ) yatai_service_mock.ListDeployments.return_value = ListDeploymentsResponse() return yatai_service_mock
def _apply(self, deployment_pb, bento_pb, yatai_service, bento_path): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._apply(deployment_pb, bento_pb, yatai_service, local_path) deployment_spec = deployment_pb.spec aws_config = deployment_spec.aws_lambda_operator_config bento_service_metadata = bento_pb.bento.bento_service_metadata template = 'aws-python3' if version.parse(bento_service_metadata.env.python_version ) < version.parse('3.0.0'): template = 'aws-python' api_names = ([aws_config.api_name] if aws_config.api_name else [api.name for api in bento_service_metadata.apis]) ensure_deploy_api_name_exists_in_bento( [api.name for api in bento_service_metadata.apis], api_names) with TempDirectory() as serverless_project_dir: init_serverless_project_dir( serverless_project_dir, bento_path, deployment_pb.name, deployment_spec.bento_name, template, ) generate_aws_lambda_handler_py(deployment_spec.bento_name, api_names, serverless_project_dir) generate_aws_lambda_serverless_config( bento_service_metadata.env.python_version, deployment_pb.name, api_names, serverless_project_dir, aws_config.region, # BentoML deployment namespace is mapping to serverless `stage` # concept stage=deployment_pb.namespace, ) logger.info( 'Installing additional packages: serverless-python-requirements' ) install_serverless_plugin("serverless-python-requirements", serverless_project_dir) logger.info('Deploying to AWS Lambda') call_serverless_command(["deploy"], serverless_project_dir) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) state = self.describe(res_deployment_pb, yatai_service).state res_deployment_pb.state.CopyFrom(state) return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb)
def _add(self, deployment_pb, bento_pb, bento_path): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._add(deployment_pb, bento_pb, local_path) deployment_spec = deployment_pb.spec sagemaker_config = deployment_spec.sagemaker_operator_config raise_if_api_names_not_found_in_bento_service_metadata( bento_pb.bento.bento_service_metadata, [sagemaker_config.api_name]) sagemaker_client = boto3.client('sagemaker', sagemaker_config.region) with TempDirectory() as temp_dir: sagemaker_project_dir = os.path.join(temp_dir, deployment_spec.bento_name) _init_sagemaker_project( sagemaker_project_dir, bento_path, bento_pb.bento.bento_service_metadata.env.docker_base_image, ) ecr_image_path = create_and_push_docker_image_to_ecr( sagemaker_config.region, deployment_spec.bento_name, deployment_spec.bento_version, sagemaker_project_dir, ) try: ( sagemaker_model_name, sagemaker_endpoint_config_name, sagemaker_endpoint_name, ) = _get_sagemaker_resource_names(deployment_pb) _create_sagemaker_model(sagemaker_client, sagemaker_model_name, ecr_image_path, sagemaker_config) _create_sagemaker_endpoint_config( sagemaker_client, sagemaker_model_name, sagemaker_endpoint_config_name, sagemaker_config, ) _create_sagemaker_endpoint( sagemaker_client, sagemaker_endpoint_name, sagemaker_endpoint_config_name, ) except AWSServiceError as e: delete_sagemaker_deployment_resources_if_exist(deployment_pb) raise e return ApplyDeploymentResponse(status=Status.OK(), deployment=deployment_pb)
def ApplyDeployment(self, request, context): try: deployment_pb = request.deployment operator = get_deployment_operator(deployment_pb) return operator.apply(request) except BentoMLException: response = ApplyDeploymentResponse() # response.status = ... # LOG.error(....) return response
def apply(self, deployment_pb, repo): # deploy code..... spec = deployment_pb.spec bento_path = repo.get(spec.bento_name, spec.bento_version) # config = load_bentoml_config(bento_path)... res_deployment_pb = Deployment() res_deployment_pb.CopyFrom(deployment_pb) # res_deployment_pb.state = ... return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb)
def apply(self, deployment_pb, repo, prev_deployment=None): ensure_docker_available_or_raise() deployment_spec = deployment_pb.spec aws_config = deployment_spec.aws_lambda_operator_config bento_path = repo.get(deployment_spec.bento_name, deployment_spec.bento_version) bento_config = load_bentoml_config(bento_path) template = 'aws-python3' minimum_python_version = version.parse('3.0.0') bento_python_version = version.parse( bento_config['env']['python_version']) if bento_python_version < minimum_python_version: template = 'aws-python' with TemporaryServerlessContent( archive_path=bento_path, deployment_name=deployment_pb.name, bento_name=deployment_spec.bento_name, template_type=template, ) as output_path: generate_handler_py(deployment_spec.bento_name, bento_config['apis'], output_path) generate_serverless_configuration_for_aws_lambda( service_name=deployment_pb.name, apis=bento_config['apis'], output_path=output_path, region=aws_config.region, stage=deployment_pb.namespace, ) logger.info( 'Installing additional packages: serverless-python-requirements, ' 'serverless-apigw-binary') install_serverless_plugin("serverless-python-requirements", output_path) install_serverless_plugin("serverless-apigw-binary", output_path) logger.info('Deploying to AWS Lambda') call_serverless_command(["deploy"], output_path) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) state = self.describe(res_deployment_pb, repo).state res_deployment_pb.state.CopyFrom(state) return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb)
def apply_deployment(self, deployment_info): if isinstance(deployment_info, dict): deployment_pb = deployment_dict_to_pb(deployment_info) elif isinstance(deployment_info, str): deployment_pb = deployment_yaml_string_to_pb(deployment_info) elif isinstance(deployment_info, Deployment): deployment_pb = deployment_info else: raise YataiDeploymentException( 'Unexpected argument type, expect deployment info to be str in yaml ' 'format or a dict or a deployment protobuf obj, instead got: {}' .format(str(type(deployment_info)))) validation_errors = validate_deployment_pb_schema(deployment_pb) if validation_errors: return ApplyDeploymentResponse(status=Status.INVALID_ARGUMENT( 'Failed to validate deployment: {errors}'.format( errors=validation_errors))) return self.yatai_service.ApplyDeployment( ApplyDeploymentRequest(deployment=deployment_pb))
def _update(self, deployment_pb, current_deployment, bento_pb, bento_path): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._update( deployment_pb, current_deployment, bento_pb, local_path ) updated_deployment_spec = deployment_pb.spec updated_lambda_deployment_config = ( updated_deployment_spec.aws_lambda_operator_config ) updated_bento_service_metadata = bento_pb.bento.bento_service_metadata describe_result = self.describe(deployment_pb) if describe_result.status.status_code != status_pb2.Status.OK: error_code, error_message = status_pb_to_error_code_and_message( describe_result.status ) raise YataiDeploymentException( f'Failed fetching Lambda deployment current status - ' f'{error_code}:{error_message}' ) latest_deployment_state = json.loads(describe_result.state.info_json) if 's3_bucket' in latest_deployment_state: lambda_s3_bucket = latest_deployment_state['s3_bucket'] else: raise BentoMLException( 'S3 Bucket is missing in the AWS Lambda deployment, please make sure ' 'it exists and try again' ) _deploy_lambda_function( deployment_pb=deployment_pb, bento_service_metadata=updated_bento_service_metadata, deployment_spec=updated_deployment_spec, lambda_s3_bucket=lambda_s3_bucket, lambda_deployment_config=updated_lambda_deployment_config, bento_path=bento_path, ) return ApplyDeploymentResponse(deployment=deployment_pb, status=Status.OK())
def apply(self, deployment_pb, repo, prev_deployment=None): deployment_spec = deployment_pb.spec sagemaker_config = deployment_spec.sagemaker_operator_config if sagemaker_config is None: raise BentoMLDeploymentException('Sagemaker configuration is missing.') archive_path = repo.get( deployment_spec.bento_name, deployment_spec.bento_version ) # config = load_bentoml_config(bento_path)... sagemaker_client = boto3.client('sagemaker', sagemaker_config.region) with TemporarySageMakerContent( archive_path, deployment_spec.bento_name, deployment_spec.bento_version ) as temp_path: ecr_image_path = create_push_image_to_ecr( deployment_spec.bento_name, deployment_spec.bento_version, temp_path ) execution_role_arn = get_arn_role_from_current_user() model_name = create_sagemaker_model_name( deployment_spec.bento_name, deployment_spec.bento_version ) sagemaker_model_info = { "ModelName": model_name, "PrimaryContainer": { "ContainerHostname": model_name, "Image": ecr_image_path, "Environment": { "API_NAME": sagemaker_config.api_name, "BENTO_SERVER_TIMEOUT": config().get( 'apiserver', 'default_timeout' ), "BENTO_SERVER_WORKERS": config().get( 'apiserver', 'default_gunicorn_workers_count' ), }, }, "ExecutionRoleArn": execution_role_arn, } logger.info("Creating sagemaker model %s", model_name) create_model_response = sagemaker_client.create_model(**sagemaker_model_info) logger.debug("AWS create model response: %s", create_model_response) production_variants = [ { "VariantName": generate_aws_compatible_string( deployment_spec.bento_name ), "ModelName": model_name, "InitialInstanceCount": sagemaker_config.instance_count, "InstanceType": sagemaker_config.instance_type, } ] endpoint_config_name = create_sagemaker_endpoint_config_name( deployment_spec.bento_name, deployment_spec.bento_version ) logger.info( "Creating Sagemaker endpoint %s configuration", endpoint_config_name ) create_endpoint_config_response = sagemaker_client.create_endpoint_config( EndpointConfigName=endpoint_config_name, ProductionVariants=production_variants, ) logger.debug( "AWS create endpoint config response: %s", create_endpoint_config_response ) endpoint_name = generate_aws_compatible_string( deployment_pb.namespace + '-' + deployment_spec.bento_name ) if prev_deployment: logger.info("Updating sagemaker endpoint %s", endpoint_name) update_endpoint_response = sagemaker_client.update_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name ) logger.debug("AWS update endpoint response: %s", update_endpoint_response) else: logger.info("Creating sagemaker endpoint %s", endpoint_name) create_endpoint_response = sagemaker_client.create_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name ) logger.debug("AWS create endpoint response: %s", create_endpoint_response) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb)
def create_deployment( deployment_name, namespace, bento_name, bento_version, platform, operator_spec, labels=None, annotations=None, yatai_service=None, ): if yatai_service is None: from bentoml.yatai import get_yatai_service yatai_service = get_yatai_service() # Make sure there is no active deployment with the same deployment name get_deployment_pb = yatai_service.GetDeployment( GetDeploymentRequest(deployment_name=deployment_name, namespace=namespace)) if get_deployment_pb.status.status_code == status_pb2.Status.OK: raise YataiDeploymentException( 'Deployment "{name}" already existed, use Update or Apply for updating ' 'existing deployment, delete the deployment, or use a different deployment ' 'name'.format(name=deployment_name)) if get_deployment_pb.status.status_code != status_pb2.Status.NOT_FOUND: raise YataiDeploymentException( 'Failed accesing YataiService deployment store. {error_code}:' '{error_message}'.format( error_code=Status.Name(get_deployment_pb.status.status_code), error_message=get_deployment_pb.status.error_message, )) deployment_dict = { "name": deployment_name, "namespace": namespace or config().get('deployment', 'default_namespace'), "labels": labels, "annotations": annotations, "spec": { "bento_name": bento_name, "bento_version": bento_version, "operator": platform, }, } operator = platform.replace('-', '_').upper() try: operator_value = DeploymentSpec.DeploymentOperator.Value(operator) except ValueError: return ApplyDeploymentResponse(status=Status.INVALID_ARGUMENT( 'Invalid platform "{}"'.format(platform))) if operator_value == DeploymentSpec.AWS_SAGEMAKER: deployment_dict['spec']['sagemaker_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region'), 'instance_count': operator_spec.get('instance_count'), 'instance_type': operator_spec.get('instance_type'), 'api_name': operator_spec.get('api_name', ''), } if operator_spec.get('num_of_gunicorn_workers_per_instance'): deployment_dict['spec']['sagemaker_operator_config'][ 'num_of_gunicorn_workers_per_instance'] = operator_spec.get( 'num_of_gunicorn_workers_per_instance') elif operator_value == DeploymentSpec.AWS_LAMBDA: deployment_dict['spec']['aws_lambda_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region') } for field in ['api_name', 'memory_size', 'timeout']: if operator_spec.get(field): deployment_dict['spec']['aws_lambda_operator_config'][ field] = operator_spec[field] elif operator_value == DeploymentSpec.KUBERNETES: deployment_dict['spec']['kubernetes_operator_config'] = { 'kube_namespace': operator_spec.get('kube_namespace', ''), 'replicas': operator_spec.get('replicas', 0), 'service_name': operator_spec.get('service_name', ''), 'service_type': operator_spec.get('service_type', ''), } else: raise YataiDeploymentException( 'Platform "{}" is not supported in the current version of ' 'BentoML'.format(platform)) apply_response = apply_deployment(deployment_dict, yatai_service) if apply_response.status.status_code == status_pb2.Status.OK: describe_response = describe_deployment(deployment_name, namespace, yatai_service) if describe_response.status.status_code == status_pb2.Status.OK: deployment_state = describe_response.state apply_response.deployment.state.CopyFrom(deployment_state) return apply_response return apply_response
def apply(self, deployment_pb, yatai_service, prev_deployment=None): try: ensure_docker_available_or_raise() deployment_spec = deployment_pb.spec sagemaker_config = deployment_spec.sagemaker_operator_config if sagemaker_config is None: raise BentoMLDeploymentException('Sagemaker configuration is missing.') bento_pb = yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, ) ) if bento_pb.bento.uri.type != BentoUri.LOCAL: raise BentoMLException( 'BentoML currently only support local repository' ) else: bento_path = bento_pb.bento.uri.uri ensure_deploy_api_name_exists_in_bento( [api.name for api in bento_pb.bento.bento_service_metadata.apis], [sagemaker_config.api_name], ) sagemaker_client = boto3.client('sagemaker', sagemaker_config.region) with TempDirectory() as temp_dir: sagemaker_project_dir = os.path.jon( temp_dir, deployment_spec.bento_name ) init_sagemaker_project(sagemaker_project_dir, bento_path) ecr_image_path = create_push_docker_image_to_ecr( deployment_spec.bento_name, deployment_spec.bento_version, sagemaker_project_dir, ) execution_role_arn = get_arn_role_from_current_aws_user() model_name = create_sagemaker_model_name( deployment_spec.bento_name, deployment_spec.bento_version ) sagemaker_model_info = { "ModelName": model_name, "PrimaryContainer": { "ContainerHostname": model_name, "Image": ecr_image_path, "Environment": { "API_NAME": sagemaker_config.api_name, "BENTO_SERVER_TIMEOUT": config().get( 'apiserver', 'default_timeout' ), "BENTO_SERVER_WORKERS": config().get( 'apiserver', 'default_gunicorn_workers_count' ), }, }, "ExecutionRoleArn": execution_role_arn, } logger.info("Creating sagemaker model %s", model_name) try: create_model_response = sagemaker_client.create_model( **sagemaker_model_info ) logger.debug("AWS create model response: %s", create_model_response) except ClientError as e: status = _parse_aws_client_exception_or_raise(e) status.error_message = ( 'Failed to create model for SageMaker Deployment: %s', status.error_message, ) return ApplyDeploymentResponse(status=status, deployment=deployment_pb) production_variants = [ { "VariantName": generate_aws_compatible_string( deployment_spec.bento_name ), "ModelName": model_name, "InitialInstanceCount": sagemaker_config.instance_count, "InstanceType": sagemaker_config.instance_type, } ] endpoint_config_name = create_sagemaker_endpoint_config_name( deployment_spec.bento_name, deployment_spec.bento_version ) logger.info( "Creating Sagemaker endpoint %s configuration", endpoint_config_name ) try: create_config_response = sagemaker_client.create_endpoint_config( EndpointConfigName=endpoint_config_name, ProductionVariants=production_variants, ) logger.debug( "AWS create endpoint config response: %s", create_config_response ) except ClientError as e: # create endpoint failed, will remove previously created model cleanup_model_error = _cleanup_sagemaker_model( sagemaker_client, deployment_spec.bento_name, deployment_spec.bento_version, ) if cleanup_model_error: cleanup_model_error.error_message = ( 'Failed to clean up model after unsuccessfully ' 'create endpoint config: %s', cleanup_model_error.error_message, ) return ApplyDeploymentResponse( status=cleanup_model_error, deployment=deployment_pb ) status = _parse_aws_client_exception_or_raise(e) status.error_message = ( 'Failed to create endpoint config for SageMaker deployment: %s', status.error_message, ) return ApplyDeploymentResponse(status=status, deployment=deployment_pb) endpoint_name = generate_aws_compatible_string( deployment_pb.namespace + '-' + deployment_spec.bento_name ) try: if prev_deployment: logger.debug("Updating sagemaker endpoint %s", endpoint_name) update_endpoint_response = sagemaker_client.update_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name, ) logger.debug( "AWS update endpoint response: %s", update_endpoint_response ) else: logger.debug("Creating sagemaker endpoint %s", endpoint_name) create_endpoint_response = sagemaker_client.create_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name, ) logger.debug( "AWS create endpoint response: %s", create_endpoint_response ) except ClientError as e: # create/update endpoint failed, will remove previously created config # and then remove the model cleanup_endpoint_config_error = _cleanup_sagemaker_endpoint_config( client=sagemaker_client, name=deployment_spec.bento_name, version=deployment_spec.bento_version, ) if cleanup_endpoint_config_error: cleanup_endpoint_config_error.error_message = ( 'Failed to clean up endpoint config after unsuccessfully ' 'apply SageMaker deployment: %s', cleanup_endpoint_config_error.error_message, ) return ApplyDeploymentResponse( status=cleanup_endpoint_config_error, deployment=deployment_pb ) cleanup_model_error = _cleanup_sagemaker_model( client=sagemaker_client, name=deployment_spec.bento_name, version=deployment_spec.bento_version, ) if cleanup_model_error: cleanup_model_error.error_message = ( 'Failed to clean up model after unsuccessfully ' 'apply SageMaker deployment: %s', cleanup_model_error.error_message, ) return ApplyDeploymentResponse( status=cleanup_model_error, deployment=deployment_pb ) status = _parse_aws_client_exception_or_raise(e) status.error_message = ( 'Failed to apply SageMaker deployment: %s', status.error_message, ) return ApplyDeploymentResponse(status=status, deployment=deployment_pb) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) return ApplyDeploymentResponse( status=Status.OK(), deployment=res_deployment_pb ) except BentoMLException as error: return ApplyDeploymentResponse(status=exception_to_return_status(error))
def _add(self, deployment_pb, bento_pb, bento_path): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._add(deployment_pb, bento_pb, local_path) deployment_spec = deployment_pb.spec lambda_deployment_config = deployment_spec.aws_lambda_operator_config bento_service_metadata = bento_pb.bento.bento_service_metadata lambda_s3_bucket = generate_aws_compatible_string( 'btml-{namespace}-{name}-{random_string}'.format( namespace=deployment_pb.namespace, name=deployment_pb.name, random_string=uuid.uuid4().hex[:6].lower(), )) try: py_major, py_minor, _ = bento_service_metadata.env.python_version.split( '.') if py_major != '3': raise BentoMLException( 'Python 2 is not supported for Lambda Deployment') python_runtime = 'python{}.{}'.format(py_major, py_minor) artifact_types = [ item.artifact_type for item in bento_service_metadata.artifacts ] if any(i in ['TensorflowSavedModelArtifact', 'KerasModelArtifact'] for i in artifact_types) and (py_major, py_minor) != ('3', '6'): raise BentoMLException( 'For Tensorflow and Keras model, only python3.6 is ' 'supported for AWS Lambda deployment') api_names = ([lambda_deployment_config.api_name] if lambda_deployment_config.api_name else [api.name for api in bento_service_metadata.apis]) raise_if_api_names_not_found_in_bento_service_metadata( bento_service_metadata, api_names) create_s3_bucket_if_not_exists(lambda_s3_bucket, lambda_deployment_config.region) deployment_path_prefix = os.path.join(deployment_pb.namespace, deployment_pb.name) with TempDirectory() as lambda_project_dir: logger.debug( 'Generating cloudformation template.yaml for lambda project at %s', lambda_project_dir, ) template_file_path = _create_aws_lambda_cloudformation_template_file( project_dir=lambda_project_dir, namespace=deployment_pb.namespace, deployment_name=deployment_pb.name, deployment_path_prefix=deployment_path_prefix, api_names=api_names, bento_service_name=deployment_spec.bento_name, s3_bucket_name=lambda_s3_bucket, py_runtime=python_runtime, memory_size=lambda_deployment_config.memory_size, timeout=lambda_deployment_config.timeout, ) logger.debug('Validating generated template.yaml') validate_lambda_template( template_file_path, lambda_deployment_config.region, lambda_project_dir, ) logger.debug( 'Initializing lambda project in directory: %s ...', lambda_project_dir, ) init_sam_project( lambda_project_dir, bento_path, deployment_pb.name, deployment_spec.bento_name, api_names, aws_region=lambda_deployment_config.region, ) for api_name in api_names: build_directory = os.path.join(lambda_project_dir, '.aws-sam', 'build', api_name) logger.debug( 'Checking is function "%s" bundle under lambda size ' 'limit', api_name, ) # Since we only use s3 get object in lambda function, and # lambda function pack their own boto3/botocore modules, # we will just delete those modules from function bundle # directory delete_list = ['boto3', 'botocore'] for name in delete_list: logger.debug('Remove module "%s" from build directory', name) shutil.rmtree(os.path.join(build_directory, name)) total_build_dir_size = total_file_or_directory_size( build_directory) if total_build_dir_size > LAMBDA_FUNCTION_MAX_LIMIT: raise BentoMLException( 'Build function size is over 700MB, max size ' 'capable for AWS Lambda function') if total_build_dir_size >= LAMBDA_FUNCTION_LIMIT: logger.debug( 'Function %s is over lambda size limit, attempting ' 'reduce it', api_name, ) reduce_bundle_size_and_upload_extra_resources_to_s3( build_directory=build_directory, region=lambda_deployment_config.region, s3_bucket=lambda_s3_bucket, deployment_prefix=deployment_path_prefix, function_name=api_name, lambda_project_dir=lambda_project_dir, ) else: logger.debug( 'Function bundle is within Lambda limit, removing ' 'download_extra_resources.py file from function bundle' ) os.remove( os.path.join(build_directory, 'download_extra_resources.py')) logger.info('Packaging AWS Lambda project at %s ...', lambda_project_dir) lambda_package( lambda_project_dir, lambda_deployment_config.region, lambda_s3_bucket, deployment_path_prefix, ) logger.info('Deploying lambda project') stack_name = generate_aws_compatible_string( deployment_pb.namespace + '-' + deployment_pb.name) lambda_deploy( lambda_project_dir, lambda_deployment_config.region, stack_name=stack_name, ) deployment_pb.state.state = DeploymentState.PENDING return ApplyDeploymentResponse(status=Status.OK(), deployment=deployment_pb) except BentoMLException as error: if lambda_s3_bucket and lambda_deployment_config: _cleanup_s3_bucket_if_exist(lambda_s3_bucket, lambda_deployment_config.region) raise error
def apply(self, deployment_pb, yatai_service, prev_deployment=None): try: ensure_docker_available_or_raise() deployment_spec = deployment_pb.spec aws_config = deployment_spec.aws_lambda_operator_config bento_pb = yatai_service.GetBento( GetBentoRequest( bento_name=deployment_spec.bento_name, bento_version=deployment_spec.bento_version, ) ) if bento_pb.bento.uri.type != BentoUri.LOCAL: raise BentoMLException( 'BentoML currently only support local repository' ) else: bento_path = bento_pb.bento.uri.uri bento_service_metadata = bento_pb.bento.bento_service_metadata template = 'aws-python3' if version.parse(bento_service_metadata.env.python_version) < version.parse( '3.0.0' ): template = 'aws-python' api_names = ( [aws_config.api_name] if aws_config.api_name else [api.name for api in bento_service_metadata.apis] ) ensure_deploy_api_name_exists_in_bento( [api.name for api in bento_service_metadata.apis], api_names ) with TempDirectory() as serverless_project_dir: init_serverless_project_dir( serverless_project_dir, bento_path, deployment_pb.name, deployment_spec.bento_name, template, ) generate_aws_lambda_handler_py( deployment_spec.bento_name, api_names, serverless_project_dir ) generate_aws_lambda_serverless_config( bento_service_metadata.env.python_version, deployment_pb.name, api_names, serverless_project_dir, aws_config.region, # BentoML deployment namespace is mapping to serverless `stage` # concept stage=deployment_pb.namespace, ) logger.info( 'Installing additional packages: serverless-python-requirements' ) install_serverless_plugin( "serverless-python-requirements", serverless_project_dir ) logger.info('Deploying to AWS Lambda') call_serverless_command(["deploy"], serverless_project_dir) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) state = self.describe(res_deployment_pb, yatai_service).state res_deployment_pb.state.CopyFrom(state) return ApplyDeploymentResponse( status=Status.OK(), deployment=res_deployment_pb ) except BentoMLException as error: return ApplyDeploymentResponse(status=exception_to_return_status(error))
def _update(self, deployment_pb, current_deployment, bento_pb, bento_path): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._update(deployment_pb, current_deployment, bento_pb, local_path) updated_deployment_spec = deployment_pb.spec updated_sagemaker_config = updated_deployment_spec.sagemaker_operator_config sagemaker_client = boto3.client('sagemaker', updated_sagemaker_config.region) try: raise_if_api_names_not_found_in_bento_service_metadata( bento_pb.bento.bento_service_metadata, [updated_sagemaker_config.api_name], ) describe_latest_deployment_state = self.describe(deployment_pb) current_deployment_spec = current_deployment.spec current_sagemaker_config = current_deployment_spec.sagemaker_operator_config latest_deployment_state = json.loads( describe_latest_deployment_state.state.info_json) current_ecr_image_tag = latest_deployment_state[ 'ProductionVariants'][0]['DeployedImages'][0]['SpecifiedImage'] if (updated_deployment_spec.bento_name != current_deployment_spec.bento_name or updated_deployment_spec.bento_version != current_deployment_spec.bento_version): logger.debug( 'BentoService tag is different from current deployment, ' 'creating new docker image and push to ECR') with TempDirectory() as temp_dir: sagemaker_project_dir = os.path.join( temp_dir, updated_deployment_spec.bento_name) _init_sagemaker_project( sagemaker_project_dir, bento_path, bento_pb.bento.bento_service_metadata.env. docker_base_image, ) ecr_image_path = create_and_push_docker_image_to_ecr( updated_sagemaker_config.region, updated_deployment_spec.bento_name, updated_deployment_spec.bento_version, sagemaker_project_dir, ) else: logger.debug('Using existing ECR image for Sagemaker model') ecr_image_path = current_ecr_image_tag ( updated_sagemaker_model_name, updated_sagemaker_endpoint_config_name, sagemaker_endpoint_name, ) = _get_sagemaker_resource_names(deployment_pb) ( current_sagemaker_model_name, current_sagemaker_endpoint_config_name, _, ) = _get_sagemaker_resource_names(current_deployment) if (updated_sagemaker_config.api_name != current_sagemaker_config.api_name or updated_sagemaker_config. num_of_gunicorn_workers_per_instance != current_sagemaker_config. num_of_gunicorn_workers_per_instance or ecr_image_path != current_ecr_image_tag): logger.debug( 'Sagemaker model requires update. Delete current sagemaker model %s' 'and creating new model %s', current_sagemaker_model_name, updated_sagemaker_model_name, ) _delete_sagemaker_model_if_exist(sagemaker_client, current_sagemaker_model_name) _create_sagemaker_model( sagemaker_client, updated_sagemaker_model_name, ecr_image_path, updated_sagemaker_config, ) # When bento service tag is not changed, we need to delete the current # endpoint configuration in order to create new one to avoid name collation if (current_sagemaker_endpoint_config_name == updated_sagemaker_endpoint_config_name): logger.debug( 'Current sagemaker config name %s is same as updated one, ' 'delete it before create new endpoint config', current_sagemaker_endpoint_config_name, ) _delete_sagemaker_endpoint_config_if_exist( sagemaker_client, current_sagemaker_endpoint_config_name) logger.debug( 'Create new endpoint configuration %s', updated_sagemaker_endpoint_config_name, ) _create_sagemaker_endpoint_config( sagemaker_client, updated_sagemaker_model_name, updated_sagemaker_endpoint_config_name, updated_sagemaker_config, ) logger.debug( 'Updating endpoint to new endpoint configuration %s', updated_sagemaker_endpoint_config_name, ) _update_sagemaker_endpoint( sagemaker_client, sagemaker_endpoint_name, updated_sagemaker_endpoint_config_name, ) logger.debug( 'Delete old sagemaker endpoint config %s', current_sagemaker_endpoint_config_name, ) _delete_sagemaker_endpoint_config_if_exist( sagemaker_client, current_sagemaker_endpoint_config_name) except AWSServiceError as e: delete_sagemaker_deployment_resources_if_exist(deployment_pb) raise e return ApplyDeploymentResponse(status=Status.OK(), deployment=deployment_pb)
def ApplyDeployment(self, request, context=None): try: # apply default namespace if not set request.deployment.namespace = ( request.deployment.namespace or self.default_namespace ) validation_errors = validate_deployment_pb_schema(request.deployment) if validation_errors: raise InvalidArgument( 'Failed to validate deployment. {errors}'.format( errors=validation_errors ) ) previous_deployment = self.deployment_store.get( request.deployment.name, request.deployment.namespace ) if not previous_deployment: request.deployment.created_at.GetCurrentTime() request.deployment.last_updated_at.GetCurrentTime() self.deployment_store.insert_or_update(request.deployment) # find deployment operator based on deployment spec operator = get_deployment_operator(self, request.deployment) # deploying to target platform if previous_deployment: response = operator.update(request.deployment, previous_deployment) else: response = operator.add(request.deployment) if response.status.status_code == status_pb2.Status.OK: # update deployment state if response and response.deployment: self.deployment_store.insert_or_update(response.deployment) else: raise BentoMLException( "DeploymentOperator Internal Error: failed to add or update " "deployment metadata to database" ) logger.info( "ApplyDeployment (%s, namespace %s) succeeded", request.deployment.name, request.deployment.namespace, ) else: if not previous_deployment: # When failed to create the deployment, delete it from active # deployments records self.deployment_store.delete( request.deployment.name, request.deployment.namespace ) logger.debug( "ApplyDeployment (%s, namespace %s) failed: %s", request.deployment.name, request.deployment.namespace, response.status.error_message, ) return response except BentoMLException as e: logger.error("RPC ERROR ApplyDeployment: %s", e) return ApplyDeploymentResponse(status=e.status_proto)
def _apply( self, deployment_pb, bento_pb, yatai_service, bento_path, prev_deployment=None ): if loader._is_remote_path(bento_path): with loader._resolve_remote_bundle_path(bento_path) as local_path: return self._apply( deployment_pb, bento_pb, yatai_service, local_path, prev_deployment ) deployment_spec = deployment_pb.spec sagemaker_config = deployment_spec.sagemaker_operator_config ensure_deploy_api_name_exists_in_bento( [api.name for api in bento_pb.bento.bento_service_metadata.apis], [sagemaker_config.api_name], ) sagemaker_client = boto3.client('sagemaker', sagemaker_config.region) with TempDirectory() as temp_dir: sagemaker_project_dir = os.path.join(temp_dir, deployment_spec.bento_name) init_sagemaker_project(sagemaker_project_dir, bento_path) ecr_image_path = create_push_docker_image_to_ecr( sagemaker_config.region, deployment_spec.bento_name, deployment_spec.bento_version, sagemaker_project_dir, ) try: model_name = _create_sagemaker_model( sagemaker_client, deployment_spec.bento_name, deployment_spec.bento_version, ecr_image_path, sagemaker_config.api_name, ) except ClientError as e: status = _parse_aws_client_exception(e) status.error_message = ( 'Failed to create model for SageMaker' ' Deployment: {}'.format(status.error_message) ) return ApplyDeploymentResponse(status=status, deployment=deployment_pb) try: endpoint_config_name = _create_sagemaker_endpoint_config( sagemaker_client, model_name, deployment_spec.bento_name, deployment_spec.bento_version, sagemaker_config, ) except ClientError as e: # create endpoint failed, will remove previously created model cleanup_model_error = _cleanup_sagemaker_model( sagemaker_client, deployment_spec.bento_name, deployment_spec.bento_version, ) if cleanup_model_error: cleanup_model_error.error_message = ( 'Failed to clean up model after unsuccessfully ' 'create endpoint config: %s', cleanup_model_error.error_message, ) return ApplyDeploymentResponse( status=cleanup_model_error, deployment=deployment_pb ) status = _parse_aws_client_exception(e) status.error_message = ( 'Failed to create endpoint config for SageMaker deployment: %s', status.error_message, ) return ApplyDeploymentResponse(status=status, deployment=deployment_pb) endpoint_name = generate_aws_compatible_string( deployment_pb.namespace + '-' + deployment_spec.bento_name ) try: if prev_deployment: logger.debug("Updating sagemaker endpoint %s", endpoint_name) update_endpoint_response = sagemaker_client.update_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name ) logger.debug( "AWS update endpoint response: %s", update_endpoint_response ) else: logger.debug("Creating sagemaker endpoint %s", endpoint_name) create_endpoint_response = sagemaker_client.create_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name ) logger.debug( "AWS create endpoint response: %s", create_endpoint_response ) except ClientError as e: # create/update endpoint failed, will remove previously created config # and then remove the model cleanup_endpoint_config_error = _cleanup_sagemaker_endpoint_config( client=sagemaker_client, name=deployment_spec.bento_name, version=deployment_spec.bento_version, ) if cleanup_endpoint_config_error: cleanup_endpoint_config_error.error_message = ( 'Failed to clean up endpoint config after unsuccessfully ' 'apply SageMaker deployment: %s', cleanup_endpoint_config_error.error_message, ) return ApplyDeploymentResponse( status=cleanup_endpoint_config_error, deployment=deployment_pb ) cleanup_model_error = _cleanup_sagemaker_model( client=sagemaker_client, name=deployment_spec.bento_name, version=deployment_spec.bento_version, ) if cleanup_model_error: cleanup_model_error.error_message = ( 'Failed to clean up model after unsuccessfully apply ' 'SageMaker deployment: {}'.format(cleanup_model_error.error_message) ) return ApplyDeploymentResponse( status=cleanup_model_error, deployment=deployment_pb ) status = _parse_aws_client_exception(e) status.error_message = 'Failed to apply SageMaker ' 'deployment: {}'.format( status.error_message ) return ApplyDeploymentResponse(status=status, deployment=deployment_pb) res_deployment_pb = Deployment(state=DeploymentState()) res_deployment_pb.CopyFrom(deployment_pb) return ApplyDeploymentResponse(status=Status.OK(), deployment=res_deployment_pb)
def create_deployment( deployment_name, namespace, bento_name, bento_version, platform, operator_spec, labels=None, annotations=None, yatai_service=None, ): if yatai_service is None: from bentoml.yatai import get_yatai_service yatai_service = get_yatai_service() try: # Make sure there is no active deployment with the same deployment name get_deployment_pb = yatai_service.GetDeployment( GetDeploymentRequest(deployment_name=deployment_name, namespace=namespace) ) if get_deployment_pb.status.status_code == status_pb2.Status.OK: raise BentoMLDeploymentException( 'Deployment "{name}" already existed, use Update or Apply for updating' 'existing deployment, or create the deployment with a different name or' 'under a different deployment namespace'.format(name=deployment_name) ) if get_deployment_pb.status.status_code != status_pb2.Status.NOT_FOUND: raise BentoMLDeploymentException( 'Failed accesing YataiService deployment store. {error_code}:' '{error_message}'.format( error_code=Status.Name(get_deployment_pb.status.status_code), error_message=get_deployment_pb.status.error_message, ) ) deployment_dict = { "name": deployment_name, "namespace": namespace or config().get('deployment', 'default_namespace'), "labels": labels, "annotations": annotations, "spec": { "bento_name": bento_name, "bento_version": bento_version, "operator": platform, }, } operator = platform.replace('-', '_').upper() try: operator_value = DeploymentSpec.DeploymentOperator.Value(operator) except ValueError: return ApplyDeploymentResponse( status=Status.INVALID_ARGUMENT('Invalid platform "{}"'.format(platform)) ) if operator_value == DeploymentSpec.AWS_SAGEMAKER: deployment_dict['spec']['sagemaker_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region'), 'instance_count': operator_spec.get('instance_count') or config().getint('sagemaker', 'default_instance_count'), 'instance_type': operator_spec.get('instance_type') or config().get('sagemaker', 'default_instance_type'), 'api_name': operator_spec.get('api_name', ''), } elif operator_value == DeploymentSpec.AWS_LAMBDA: deployment_dict['spec']['aws_lambda_operator_config'] = { 'region': operator_spec.get('region') or config().get('aws', 'default_region') } if operator_spec.get('api_name'): deployment_dict['spec']['aws_lambda_operator_config'][ 'api_name' ] = operator_spec['api_name'] elif operator_value == DeploymentSpec.GCP_FCUNTION: deployment_dict['spec']['gcp_function_operatorConfig'] = { 'region': operator_spec.get('region') or config().get('google-cloud', 'default_region') } if operator_spec.get('api_name'): deployment_dict['spec']['gcp_function_operator_config'][ 'api_name' ] = operator_spec['api_name'] elif operator_value == DeploymentSpec.KUBERNETES: deployment_dict['spec']['kubernetes_operator_config'] = { 'kube_namespace': operator_spec.get('kube_namespace', ''), 'replicas': operator_spec.get('replicas', 0), 'service_name': operator_spec.get('service_name', ''), 'service_type': operator_spec.get('service_type', ''), } else: raise BentoMLDeploymentException( 'Platform "{}" is not supported in the current version of ' 'BentoML'.format(platform) ) return apply_deployment(deployment_dict, yatai_service) except BentoMLException as error: return ApplyDeploymentResponse(status=Status.INTERNAL(str(error)))
def apply(self, deployment_pb): # deploy code..... deployment = self.get(deployment_pb).deployment return ApplyDeploymentResponse(status=Status.OK, deployment=deployment)