Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Application] Introducing application runtime phase I #5234

Merged
merged 50 commits into from Mar 20, 2024

Conversation

alonmr
Copy link
Member

@alonmr alonmr commented Mar 4, 2024

Application Runtime

MLRun allows user to deploy models and expose them for serving purposes.

We currently lack a way to enable users deploy an application atop those models. e.g. have a model that differentiate between cat and a dog and expose it while having a UI that allows the user to upload image to invoke the model with a given input. To do so, we introduce a way to deploy web application given user container image, port, and a command to run the http server.

The runtime is based on top of Nuclio and will add the application as a side-car to a Nuclio function while the actual function is a reverse proxy to that application.
The reason we want to use Nuclio for this runtime is to leverage its features such as scaling, triggers, monitoring, API gateway, etc.

Phase I

This phase is about making it work.

  1. Configure the side car application.
  2. Add the reverse proxy to the side car.

Usage example

# Create an application runtime (with pre built image)
application = project.set_function(name="my-vizro-dashboard", kind="application", image="repo/my-wabapp:latest")

# Set the port the side-car listens on
application.set_internal_app_port(port=8080)

# Deploy
application.deploy()

Jira - https://iguazio.atlassian.net/browse/ML-4601

@alonmr alonmr marked this pull request as ready for review March 12, 2024 09:06
Copy link
Member

@TomerShor TomerShor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good overall!
some comments below

Comment on lines 968 to 969
if not name:
raise ValueError("Sidecar name must be specified")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just enrich with f"{name}-sidecar" here?

assert fn.spec.image == "mlrun/mlrun"
assert fn.metadata.name == "application-test"
assert (
"Ly8gQ29weXJpZ2h0IDIwMjMgSWd1YXppbwovLwovLyBMaWN"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is base64 of the go reverse proxy func? perhaps add a comment on this

mlrun/projects/project.py Show resolved Hide resolved
Copy link
Member

@theSaarco theSaarco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool stuff. Have some comments and suggestions.

mlrun/run.py Outdated
Comment on lines 751 to 752
filename = str(ApplicationRuntime.reverse_proxy_file_path)
handler = "Handler"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
filename = str(ApplicationRuntime.reverse_proxy_file_path)
handler = "Handler"
filename, handler = ApplicationRuntime.get_filename_and_handler()

It's better to ask the ApplicationRuntime class what these are - add a class method to the class to return them. This way any change in these will be local to the runtime class, and will not require searching through the code to find the reference here.

mlrun/run.py Outdated
Comment on lines 723 to 733
def resolve_nuclio_sub_kind(_kind):
_is_nuclio = _kind.startswith("nuclio")
_sub_kind = (
_kind[_kind.find(":") + 1 :] if _is_nuclio and ":" in _kind else None
)
if _kind == RuntimeKinds.serving:
_is_nuclio = True
_sub_kind = serving_subkind
elif _kind == RuntimeKinds.application:
_is_nuclio = True
return _is_nuclio, _sub_kind
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're making these changes - isn't it better to move this to the RuntimeKinds class? Like resolve_nuclio_runtime?

mlrun/runtimes/__init__.py Show resolved Hide resolved
@@ -0,0 +1,15 @@
# Copyright 2023 Iguazio
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Copyright 2023 Iguazio
# Copyright 2024 Iguazio

😃

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpicker 🐛 😄
needs to be changed in all files

Comment on lines 145 to 146
def __init__(self, spec=None, metadata=None):
super().__init__(metadata, spec)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's terrible that the order of parameters is different between this class and its super(). How did that happen? As far as I can tell, the parent is KubeResource which also does the same reversing (there's RemoteRuntime which doesn't implement __init__). So, does this even work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wow fixed. copied it from KubeResource and fixed that one as well.

@@ -958,6 +957,38 @@ def invoke(
data = json.loads(data)
return data

def _with_sidecar(self, name: str, image: str, port: int = None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is now a general nuclio functionality, why not make this a public function, allowing users to add sidecars to nuclio functions not just in the application runtime case?

mlrun/runtimes/nuclio/function.py Show resolved Hide resolved
sidecar["image"] = image

if port:
sidecar["ports"] = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the sidecar function in nuclio supports multiple ports. Why not expose it here, and allow sending a list of ports to this function? Maybe change port to be ports: Optional[Union[int, list[int]]] = None?

"Ly8gQ29weXJpZ2h0IDIwMjMgSWd1YXppbwovLwovLyBMaWN"
in fn.spec.build.functionSourceCode
)
assert fn.spec.function_handler == "reverse_proxy:Handler"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you add the file-path and handler as class methods for the app runtime, you can use them to construct this value, rather than use a fixed string.


class ApplicationRuntime(RemoteRuntime):
kind = "application"
reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity - does nuclio understand that it needs to use Golang runtime based on the suffix of the file? There doesn't seem to be any indication in the code here about it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nuclio-jupyter enriches the spec.runtime from the file extension

mlrun/runtimes/nuclio/application/reverse_proxy.go Outdated Show resolved Hide resolved
tests/runtimes/test_application.py Outdated Show resolved Hide resolved
tests/runtimes/test_application.py Outdated Show resolved Hide resolved
tests/runtimes/test_application.py Outdated Show resolved Hide resolved
Copy link
Member

@theSaarco theSaarco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍 .

Copy link
Member

@TomerShor TomerShor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work mate :)

Copy link
Member

@liranbg liranbg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor

setup.py Outdated
@@ -59,6 +59,7 @@ def version():
"server",
]
),
package_data={"": ["*.go"]},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provide the path and dont take all go files


if self.status.container_image:
self.from_image(self.status.container_image)
self.spec.build.functionSourceCode = ""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.spec.build.functionSourceCode = ""
# nuclio implementation detail - when providing the image and emptying out the source code,
# nuclio skips rebuilding the image and simply takes the prebuilt image
self.spec.build.functionSourceCode = ""

@alonmr alonmr merged commit a10578e into mlrun:development Mar 20, 2024
10 checks passed
@alonmr alonmr deleted the ML-4601 branch March 20, 2024 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants