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
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
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