Esempio n. 1
0
    def create(
        self,
        deployment_name,
        namespace,
        bento_name,
        bento_version,
        platform,
        operator_spec,
        labels=None,
        annotations=None,
    ):
        # Make sure there is no active deployment with the same deployment name
        get_deployment_pb = self.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 = self.apply(deployment_dict)

        if apply_response.status.status_code == status_pb2.Status.OK:
            describe_response = self.describe(deployment_name, namespace)
            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
Esempio n. 2
0
def deployment_dict_to_pb(deployment_dict):
    deployment_pb = Deployment()
    if deployment_dict.get('spec'):
        spec_dict = deployment_dict.get('spec')
    else:
        raise YataiDeploymentException('"spec" is required field for deployment')
    platform = spec_dict.get('operator')
    if platform is not None:
        # converting platform parameter to DeploymentOperator name in proto
        # e.g. 'aws-lambda' to 'AWS_LAMBDA'
        deployment_pb.spec.operator = DeploymentSpec.DeploymentOperator.Value(
            platform.replace('-', '_').upper()
        )

    for field in ['name', 'namespace']:
        if deployment_dict.get(field):
            deployment_pb.__setattr__(field, deployment_dict.get(field))
    if deployment_dict.get('labels') is not None:
        deployment_pb.labels.update(deployment_dict.get('labels'))
    if deployment_dict.get('annotations') is not None:
        deployment_pb.annotations.update(deployment_dict.get('annotations'))

    if spec_dict.get('bento_name'):
        deployment_pb.spec.bento_name = spec_dict.get('bento_name')
    if spec_dict.get('bento_version'):
        deployment_pb.spec.bento_version = spec_dict.get('bento_version')

    if deployment_pb.spec.operator == DeploymentSpec.AWS_SAGEMAKER:
        sagemaker_config = spec_dict.get('sagemaker_operator_config', {})
        sagemaker_config_pb = deployment_pb.spec.sagemaker_operator_config
        for field in [
            'region',
            'api_name',
            'instance_type',
            'num_of_gunicorn_workers_per_instance',
            'instance_count',
        ]:
            if sagemaker_config.get(field):
                sagemaker_config_pb.__setattr__(field, sagemaker_config.get(field))
    elif deployment_pb.spec.operator == DeploymentSpec.AWS_LAMBDA:
        lambda_conf = spec_dict.get('aws_lambda_operator_config', {})
        for field in ['region', 'api_name', 'memory_size', 'timeout']:
            if lambda_conf.get(field):
                deployment_pb.spec.aws_lambda_operator_config.__setattr__(
                    field, lambda_conf.get(field)
                )
    elif deployment_pb.spec.operator == DeploymentSpec.KUBERNETES:
        k8s_config = spec_dict.get('kubernetes_operator_config', {})

        for field in ['kube_namespace', 'replicas', 'service_name', 'service_type']:
            if k8s_config.get(field):
                deployment_pb.spec.kubernetes_operator_config.__setattr__(
                    field, k8s_config.get(field)
                )
    else:
        raise InvalidArgument(
            'Platform "{}" is not supported in the current version of '
            'BentoML'.format(platform)
        )

    return deployment_pb
Esempio n. 3
0
def containerize_bento_service(
    bento_name,
    bento_version,
    saved_bundle_path,
    push=False,
    tag=None,
    build_arg=None,
    username=None,
    password=None,
):
    """Containerize specified BentoService.

    BENTO is the target BentoService to be containerized, referenced by its name
    and version in format of name:version. For example: "iris_classifier:v1.2.0"

    `bentoml containerize` command also supports the use of the `latest` tag
    which will automatically use the last built version of your Bento.

    You can provide a tag for the image built by Bento using the
    `--docker-image-tag` flag. Additionally, you can provide a `--push` flag,
    which will push the built image to the Docker repository specified by the
    image tag.

    You can also prefixing the tag with a hostname for the repository you wish
    to push to.
    e.g. `bentoml containerize IrisClassifier:latest --push --tag username/iris`
    would build a Docker image called `username/iris:latest` and push that to
    Docker Hub.

    By default, the `containerize` command will use the credentials provided by
    Docker. You may provide your own through `--username` and `--password`.
    """
    name = to_valid_docker_image_name(bento_name)
    version = to_valid_docker_image_version(bento_version)

    if not tag:
        tag = f"{name}:{version}"
    if ":" not in tag:
        tag = f"{tag}:{version}"

    docker_build_args = {}
    if build_arg:
        for arg in build_arg:
            key, value = arg.split("=")
            docker_build_args[key] = value

    import docker

    docker_api = docker.APIClient()
    try:
        logger.info("Building image")
        for line in docker_api.build(
                path=saved_bundle_path,
                tag=tag,
                decode=True,
                buildargs=docker_build_args,
        ):
            logger.debug(line)
    except docker.errors.APIError as error:
        raise YataiDeploymentException(
            f"Could not build Docker image: {error}")

    if push:
        auth_config_payload = ({
            "username": username,
            "password": password
        } if username or password else None)

        try:
            logger.info("Pushing image")
            for line in docker_api.push(
                    repository=tag,
                    stream=True,
                    decode=True,
                    auth_config=auth_config_payload,
            ):
                logger.debug(line)
        except docker.errors.APIError as error:
            raise YataiDeploymentException(
                f"Could not push Docker image: {error}")
    return tag