def create_push_image_to_ecr(bento_service, snapshot_path): """Create BentoService sagemaker image and push to AWS ECR Example: https://github.com/awslabs/amazon-sagemaker-examples/blob/\ master/advanced_functionality/scikit_bring_your_own/container/build_and_push.sh 1. get aws account info and login ecr 2. create ecr repository, if not exist 3. build tag and push docker image Args: bento_service(BentoService) snapshot_path(Path) Returns: str: AWS ECR Tag """ ecr_client = boto3.client("ecr") token = ecr_client.get_authorization_token() logger.info("Getting docker login info from AWS") username, password = ( base64.b64decode(token["authorizationData"][0]["authorizationToken"]) .decode("utf-8") .split(":") ) registry_url = token["authorizationData"][0]["proxyEndpoint"] auth_config_payload = {"username": username, "password": password} docker_api = docker.APIClient() image_name = bento_service.name.lower() + "-sagemaker" ecr_tag = strip_scheme( "{registry_url}/{image_name}:{version}".format( registry_url=registry_url, image_name=image_name, version=bento_service.version, ) ) logger.info("Building docker image: %s", image_name) for line in docker_api.build( path=snapshot_path, dockerfile="Dockerfile-sagemaker", tag=image_name ): process_docker_api_line(line) try: ecr_client.describe_repositories(repositoryNames=[image_name])["repositories"] except ecr_client.exceptions.RepositoryNotFoundException: ecr_client.create_repository(repositoryName=image_name) if docker_api.tag(image_name, ecr_tag) is False: raise RuntimeError("Tag appeared to fail: " + ecr_tag) logger.info("Pushing image to AWS ECR at %s", ecr_tag) for line in docker_api.push(ecr_tag, stream=True, auth_config=auth_config_payload): process_docker_api_line(line) logger.info("Finished pushing image: %s", ecr_tag) return ecr_tag
def deploy_bentoml( clipper_conn, archive_path, api_name, input_type="strings", model_name=None, labels=None, ): """Deploy bentoml bundle to clipper cluster Args: clipper_conn(clipper_admin.ClipperConnection): Clipper connection instance archive_path(str): Path to the bentoml service archive. api_name(str): name of the api that will be used as prediction function for clipper cluster input_type(str): Input type that clipper accept. The default input_type for image handler is `bytes`, for other handlers is `strings`. Availabel input_type are `integers`, `floats`, `doubles`, `bytes`, or `strings` model_name(str): Model's name for clipper cluster labels(:obj:`list(str)`, optional): labels for clipper model Returns: tuple: Model name and model version that deployed to clipper """ bento_service = load(archive_path) api = bento_service.get_service_api(api_name) model_name = model_name or generate_clipper_compatiable_string( bento_service.name + "-" + api.name) version = generate_clipper_compatiable_string(bento_service.version) if isinstance(api.handler, ImageHandler): input_type = "bytes" try: clipper_conn.start_clipper() except docker.errors.APIError: clipper_conn.connect() except Exception: raise BentoMLException("Can't start or connect with clipper cluster") snapshot_path = generate_clipper_deployment_snapshot_path( bento_service.name, bento_service.version) entry_py_content = DEFAULT_CLIPPER_ENTRY.format(api_name=api.name, input_type=input_type) model_path = os.path.join(snapshot_path, "bento") shutil.copytree(archive_path, model_path) with open(os.path.join(snapshot_path, "clipper_entry.py"), "w") as f: f.write(entry_py_content) docker_content = DOCKERFILE_CLIPPER.format(model_name=model_name, model_version=version) with open(os.path.join(snapshot_path, "Dockerfile-clipper"), "w") as f: f.write(docker_content) docker_api = docker.APIClient() image_tag = bento_service.name.lower( ) + "-clipper:" + bento_service.version for line in docker_api.build(path=snapshot_path, dockerfile="Dockerfile-clipper", tag=image_tag): process_docker_api_line(line) clipper_conn.deploy_model( name=model_name, version=version, input_type=input_type, image=image_tag, labels=labels, ) return model_name, version
def deploy_bentoml( clipper_conn, bundle_path, api_name, model_name=None, labels=None, build_envs=None ): """Deploy bentoml bundle to clipper cluster Args: clipper_conn(clipper_admin.ClipperConnection): Clipper connection instance bundle_path(str): Path to the saved BentomlService bundle. api_name(str): name of the api that will be used as prediction function for clipper cluster model_name(str): Model's name for clipper cluster labels(:obj:`list(str)`, optional): labels for clipper model Returns: tuple: Model name and model version that deployed to clipper """ track("clipper-deploy", {'bento_service_bundle_path': bundle_path}) build_envs = {} if build_envs is None else build_envs # docker is required to build clipper model image ensure_docker_available_or_raise() if not clipper_conn.connected: raise BentoMLException( "No connection to Clipper cluster. CallClipperConnection.connect to " "connect to an existing cluster or ClipperConnnection.start_clipper to " "create a new one" ) bento_service_metadata = load_bento_service_metadata(bundle_path) try: api_metadata = next( (api for api in bento_service_metadata.apis if api.name == api_name) ) except StopIteration: raise BentoMLException( "Can't find API '{}' in BentoService bundle {}".format( api_name, bento_service_metadata.name ) ) if api_metadata.handler_type not in HANDLER_TYPE_TO_INPUT_TYPE: raise BentoMLException( "Only BentoService APIs using ClipperHandler can be deployed to Clipper" ) input_type = HANDLER_TYPE_TO_INPUT_TYPE[api_metadata.handler_type] model_name = model_name or get_clipper_compatiable_string( bento_service_metadata.name + "-" + api_metadata.name ) model_version = get_clipper_compatiable_string(bento_service_metadata.version) with TempDirectory() as tempdir: entry_py_content = CLIPPER_ENTRY.format(api_name=api_name) model_path = os.path.join(tempdir, "bento") shutil.copytree(bundle_path, model_path) with open(os.path.join(tempdir, "clipper_entry.py"), "w") as f: f.write(entry_py_content) if bento_service_metadata.env.python_version.startswith("3.6"): base_image = "clipper/python36-closure-container:0.4.1" elif bento_service_metadata.env.python_version.startswith("2.7"): base_image = "clipper/python-closure-container:0.4.1" else: raise BentoMLException( "Python version {} is not supported in Clipper".format( bento_service_metadata.env.python_version ) ) docker_content = CLIPPER_DOCKERFILE.format( model_name=model_name, model_version=model_version, base_image=base_image, pip_index_url=build_envs.get("PIP_INDEX_URL", ""), pip_trusted_url=build_envs.get("PIP_TRUSTED_HOST", ""), ) with open(os.path.join(tempdir, "Dockerfile-clipper"), "w") as f: f.write(docker_content) docker_api = docker.APIClient() clipper_model_docker_image_tag = "clipper-model-{}:{}".format( bento_service_metadata.name.lower(), bento_service_metadata.version ) for line in docker_api.build( path=tempdir, dockerfile="Dockerfile-clipper", tag=clipper_model_docker_image_tag, ): process_docker_api_line(line) logger.info( "Successfully built docker image %s for Clipper deployment", clipper_model_docker_image_tag, ) clipper_conn.deploy_model( name=model_name, version=model_version, input_type=input_type, image=clipper_model_docker_image_tag, labels=labels, ) track("clipper-deploy-success", {'bento_service_bundle_path': bundle_path}) return model_name, model_version