Пример #1
0
    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
Пример #2
0
    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())
Пример #3
0
 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)
     try:
         _deploy_azure_functions(
             deployment_spec=deployment_pb.spec,
             deployment_name=deployment_pb.name,
             namespace=deployment_pb.namespace,
             bento_pb=bento_pb,
             bento_path=bento_path,
         )
         return ApplyDeploymentResponse(status=Status.OK(),
                                        deployment=deployment_pb)
     except AzureServiceError as error:
         resource_group_name, _, _, _, _, = _generate_azure_resource_names(
             deployment_pb.namespace, deployment_pb.name)
         logger.debug(
             'Failed to create Azure Functions. Cleaning up Azure resources'
         )
         try:
             _call_az_cli(
                 command=[
                     'az',
                     'group',
                     'delete',
                     '-y',
                     '--name',
                     resource_group_name,
                 ],
                 message='delete Azure resource group',
             )
         except AzureServiceError:
             pass
         raise error
Пример #4
0
    def _add(self, deployment_pb, bento_pb, bento_path):
        try:
            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
            aws_ec2_deployment_config = deployment_spec.aws_ec2_operator_config

            user_id = get_aws_user_id()
            artifact_s3_bucket_name = generate_aws_compatible_string(
                "btml-{user_id}-{namespace}".format(
                    user_id=user_id,
                    namespace=deployment_pb.namespace,
                ))
            create_s3_bucket_if_not_exists(artifact_s3_bucket_name,
                                           aws_ec2_deployment_config.region)
            self.deploy_service(
                deployment_pb,
                deployment_spec,
                bento_path,
                aws_ec2_deployment_config,
                artifact_s3_bucket_name,
                aws_ec2_deployment_config.region,
            )
        except BentoMLException as error:
            if artifact_s3_bucket_name and aws_ec2_deployment_config.region:
                cleanup_s3_bucket_if_exist(artifact_s3_bucket_name,
                                           aws_ec2_deployment_config.region)
            raise error
        return ApplyDeploymentResponse(status=Status.OK(),
                                       deployment=deployment_pb)
Пример #5
0
def save_to_dir(bento_service, path, version=None, silent=False):
    """Save given BentoService along with all its artifacts, source code and
    dependencies to target file path, assuming path exist and empty. If target path
    is not empty, this call may override existing files in the given path.

    :param bento_service (bentoml.service.BentoService): a Bento Service instance
    :param path (str): Destination of where the bento service will be saved. The
        destination can be local path or remote path. The remote path supports both
        AWS S3('s3://bucket/path') and Google Cloud Storage('gs://bucket/path').
    :param version (str): Override the service version with given version string
    :param silent (boolean): whether to hide the log message showing target save path
    """
    track_save(bento_service)

    from bentoml.service import BentoService

    if not isinstance(bento_service, BentoService):
        raise BentoMLException(
            "save_to_dir only works with instances of custom BentoService class"
        )

    if version is not None:
        # If parameter version provided, set bento_service version
        # Otherwise it will bet set the first time the `version` property get accessed
        bento_service.set_version(version)

    if _is_remote_path(path):
        # If user provided path is an remote location, the bundle will first save to
        # a temporary directory and then upload to the remote location
        logger.info(
            'Saving bento to an remote path. BentoML will first save the bento '
            'to a local temporary directory and then upload to the remote path.'
        )
        with TempDirectory() as temp_dir:
            _write_bento_content_to_dir(bento_service, temp_dir)
            with TempDirectory() as tarfile_dir:
                file_name = f'{bento_service.name}.tar'
                tarfile_path = f'{tarfile_dir}/{file_name}'
                with tarfile.open(tarfile_path, mode="w:gz") as tar:
                    tar.add(temp_dir, arcname=bento_service.name)
            _upload_file_to_remote_path(path, tarfile_path, file_name)
    else:
        _write_bento_content_to_dir(bento_service, path)

    copy_zip_import_archives(
        os.path.join(path, bento_service.name, ZIPIMPORT_DIR),
        bento_service.__class__.__module__,
        list(get_zipmodules().keys()),
        bento_service.env._zipimport_archives or [],
    )

    if not silent:
        logger.info(
            "BentoService bundle '%s:%s' created at: %s",
            bento_service.name,
            bento_service.version,
            path,
        )
Пример #6
0
    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)
Пример #7
0
 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)
     if (deployment_pb.spec.bento_name != current_deployment.spec.bento_name
             or deployment_pb.spec.bento_version !=
             current_deployment.spec.bento_version):
         logger.debug(
             'BentoService tag is different from current Azure Functions '
             'deployment, creating new Azure Functions project and push to ACR'
         )
         _update_azure_functions(
             deployment_spec=deployment_pb.spec,
             deployment_name=deployment_pb.name,
             namespace=deployment_pb.namespace,
             bento_pb=bento_pb,
             bento_path=bento_path,
         )
     (
         resource_group_name,
         _,
         function_plan_name,
         _,
         _,
     ) = _generate_azure_resource_names(namespace=deployment_pb.namespace,
                                        deployment_name=deployment_pb.name)
     _call_az_cli(
         command=[
             'az',
             'functionapp',
             'plan',
             'update',
             '--name',
             function_plan_name,
             '--resource-group',
             resource_group_name,
             '--max-burst',
             str(deployment_pb.spec.azure_functions_operator_config.
                 max_burst),
             '--min-instances',
             str(deployment_pb.spec.azure_functions_operator_config.
                 min_instances),
             '--sku',
             deployment_pb.spec.azure_functions_operator_config.
             premium_plan_sku,
         ],
         message='update Azure functionapp plan',
     )
     return ApplyDeploymentResponse(deployment=deployment_pb,
                                    status=Status.OK())
Пример #8
0
    def _update(self, deployment_pb, previous_deployment_pb, bento_path, region):
        if loader._is_remote_path(bento_path):
            with loader._resolve_remote_bundle_path(bento_path) as local_path:
                return self._update(
                    deployment_pb, previous_deployment_pb, local_path, region
                )

        updated_deployment_spec = deployment_pb.spec
        updated_deployment_config = updated_deployment_spec.aws_ec2_operator_config

        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 ec2 deployment current status - "
                f"{error_code}:{error_message}"
            )

        previous_deployment_state = json.loads(describe_result.state.info_json)
        if "S3Bucket" in previous_deployment_state:
            s3_bucket_name = previous_deployment_state.get("S3Bucket")
        else:
            raise BentoMLException(
                "S3 Bucket is missing in the AWS EC2 deployment, please make sure "
                "it exists and try again"
            )

        self.deploy_service(
            deployment_pb,
            updated_deployment_spec,
            bento_path,
            updated_deployment_config,
            s3_bucket_name,
            region,
        )

        return ApplyDeploymentResponse(status=Status.OK(), deployment=deployment_pb)
Пример #9
0
    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
            or get_default_aws_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,
            )
            if not (current_sagemaker_endpoint_config_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)