Create functions#
Functions are the basic building blocks of MLRun: they are essentially Python objects that know how to run locally or on a Kubernetes cluster. This section covers how to create and customize an MLRun function, as well as common parameters across all functions.
In this section
See also Batch runs and workflows for more details and examples about running functions
Naming functions - best practice#
When you create a function, you specify its name. When you deploy a function using the SDK, MLRun appends the project name to the function name. Project names are limited to 63 characters. You must ensure that the combined function-project name does not exceed 63 characters. A function created in the UI has a default limit of 56 characters. MLRun adds to nuclio functions, by default, "nuclio-", giving a total of 63 characters.
Building images#
If your MLRun function requires additional libraries or files, you might need to build a new Docker image. You can do this by specifying a base image to use as the image, your requirements via requirements, and (optionally) your source code via with_repo=True (where the source is specified by project.set_source(...)). See the examples in Set requirements when creating a function
See more details about images in MLRun images and more information on when a build is required in Build function image.
Note
When using with_repo, the contents of the Git repo or archive are available in the current working directory of your MLRun function during runtime.
A good place to start is one of the default MLRun images:
mlrun/mlrun: An MLRun image includes preinstalled OpenMPI and other ML packages. Useful as a base image for simple jobs.mlrun/mlrun-gpu: The same asmlrun/mlrunbut for GPUs, including Open MPI.
Dockerfiles for the MLRun images can be found here.
Creating functions#
When creating a function, there are three main scenarios:
Single source file — when your code can be contained in a single file
Multiple source files — when your code requires additional files or dependencies
Import an existing function — when your function already exists elsewhere and you just want to import it
The MLRun project object has a method called set_function(), which is a one-size-fits-all way of creating an MLRun function. This method accepts a variety of sources including Python files, Jupyter Notebooks, Git repos, and more. Its usage has many variations, as you can see in the following code snippets. The return value of set_function is your MLRun function. You can immediately run it or apply additional configurations like resources, scaling, etc. See Customizing functions for more details. Depending on the source passed in, the project registers the function using some lower level functions. For specific use cases, you also have access to the lower level functions new_function(), code_to_function(), and import_function().
Functions should be created within a project (see create and use projects). The initial steps to create a function usually look like:
project = mlrun.get_or_create_project(...)
fn = project.set_function(...)
The examples in this page assume you are working within a project.
The set_function parameters, used across all function types and creation scenarios, are described in set_function().
Set requirements when creating a function#
You can list the Python dependencies, for example:
# By providing a list of packages
fn = project.set_function(
name="my-func", tag="latest", func="./src/my-code.py",
image="mlrun/mlrun", kind="job", handler="train_model",
requirements=["requests", "pandas==1.3.5"], with_repo=True
)
And you can list a path to a requirements.txt file, for example:
# By providing a path to a pip requirements file
proj.set_function("my-func", requirements="requirements.txt")
Best practice: set_function and get_function#
set_function() adds or updates a function object to the project based on the provided configuration. If the function does not exist in the project spec, it creates the function. In this method, you can define function's high-level configuration values such as image, name, kind, and more, that are used for creating the function object and are stored in the project.yaml.
get_function() gets the function object by name. This method gets the function object by default from the project spec, and by using the ignore_cache flag, you have the option to get the function from the database.
Both of these options return the function object. However, if you use the set_function after initially creating the function, you might overwrite the function configuration. Therefore, use set_function only once when initializing the function, and then use get_function when loading the project again or editing the functions.
A word about saving functions#
When working on a project, saving the project is independent from saving the function. You can manage your project locally and decide when you want to save the function. When you save a project with project.save(), the project.yaml is updated and the function is retrievable via project.get_function(), but the function is not saved to the DB, meaning it doesn't appear in MLRun function registry (db.list_functions) or in the UI. You can explicitly save your function to the DB with fn.save(tag="latest").
When running your function with project.run_function(local=False), the function is saved to the DB.
Create a function with a single source file#
The simplest way to create a function is to use a single file as the source. The code itself is embedded into the MLRun function object. This makes the function quite portable since it does not depend on any external files. You can use any source file supported by MLRun such as Python or Jupyter notebook. Files of type Bash, Go, etc. are also supported.
Create a function with a Python file#
This is the simplest way to create a function out of a given piece of code. Simply pass in the path to the Python file relative to your project context directory.
fn = project.set_function(
name="my-func", func="./src/my-code.py", kind="job",
image="mlrun/mlrun", handler="handler"
)
Create a function with Jupyter Notebook#
This is a great way to create a function out of a Jupyter Notebook. Just pass in the path to the Jupyter Notebook relative to your project context directory. You can use MLRun cell tags to specify which parts of the notebook should be included in the function.
Note
To ensure that the latest changes are included, make sure you save your notebook before creating/updating the function.
proj.set_function("http://.../my-nb.ipynb", "my-func", kind=job image="mlrun/mlrun" )
You can also create an MLRun function out of the current Jupyter Notebook you are running in. To do this, simply ommit the func parameter in set_function.
Create a function with multiple source files#
If your code requires additional files or external libraries, you need to use a source that supports multiple files such as Git, an archive (zip/tar), or V3IO file share. This approach (especially using a Git repo) pairs well with MLRun projects.
To do this, you must:
Provide
with_repo=Truewhen creating your function viaproject.set_function(...)Set the project source via
project.set_source(source=...)
This instructs MLRun to load source code from the git repo/archive/file share associated with the project. There are two ways to load these additional files:
Load code from container#
In this scenario, the function is built once. This is the preferred approach for production workloads. Here are a few examples:
Example of a python function and handler:
project.set_source(source="git://github.com/mlrun/project-archive.git")
fn = proj.set_function(
func="./src/my-code.py", name="my-func",
image="mlrun/mlrun", with_repo=True, handler="my_handler"
)
Example of a python function contained in the handler:
project.set_source(source="git://github.com/mlrun/project-archive.git")
fn = project.set_function(
name="my-func", handler="job_func.job_handler",
image="mlrun/mlrun", kind="job", with_repo=True,
)
When the py function is inside a folder, the handler looks like:
project.set_source(source="git://github.com/mlrun/project-archive.git")
fn = project.set_function(
name="my-func", handler="folder1.job_func.job_handler",
image="mlrun/mlrun", kind="job", with_repo=True,
)
Load code at runtime#
The function pulls the source code at runtime. This is a simpler approach during development that allows for making code changes without re-building the image each time. For example:
archive_url = "https://s3.us-east-1.wasabisys.com/iguazio/project-archive/project-archive.zip"
project.set_source(source=archive_url, pull_at_runtime=True)
fn = project.set_function(
name="my-func", handler="nuclio_func:nuclio_handler",
image="mlrun/mlrun", kind="nuclio", with_repo=True,
)
Load code at runtime using a non-default source#
If your project already has a default source, you can still use a different source at runtime or build of a function. Based on the previous example:
archive_url = "https://s3.us-east-1.wasabisys.com/iguazio/project-archive/project-archive.zip"
project.set_source(source=archive_url, pull_at_runtime=True)
fn = project.set_function(
name="my-func", handler="nuclio_func:nuclio_handler",
image="mlrun/mlrun", kind="nuclio", with_repo=True,
)
fn.with_source_archive("https://s3.us-east-1.wasabisys.com/some/other/archive.zip")
See LocalRuntime.with_source_archive, KubejobRuntime.with_source_archive, RemoteRuntime.with_source_archive.
Import or use an existing function#
If you already have an MLRun function that you want to import, you can do so from multiple locations such as YAML, Function Hub, and MLRun DB.
Note
In the UI, running a batch job from an existing function executes the generated spec merged with the function spec. Therefore, if you remove a function spec, for example env vars, it may re-appear in the final job spec.
YAML#
MLRun functions can be exported to YAML files via fn.export(). These YAML files can then be imported via the following:
fn = project.set_function(name="my-func", func="my-code.yaml")
Function hub#
Functions can also be imported from the MLRun Hub: simply import using the name of the function and the hub:// prefix:
Note
By default, the hub:// prefix points to the MLRun Hub. You can substitute your own repo. See Using a Git repo as a hub.
fn = project.set_function(name="describe", func="hub://describe")
MLRun DB#
You can also import functions directly from the MLRun DB. These could be functions that have not been pushed to a git repo, archive, or Function Hub. Import via the name of the function and the db:// prefix:
fn = project.set_function(name="my-func", func="db://import")
MLRun function#
You can also directly use an existing MLRun function object. This is usually used when more granular control over function parameters is required (e.g. advanced parameters that are not supported by set_function()).
This example uses a real-time serving pipeline (graph).
project = mlrun.get_or_create_project("my-project")
fn = project.set_function(name="my-func", kind="serving", image="mlrun/mlrun", with_repo=True)
graph = serving.set_topology("flow")
graph.to(name="double", handler="mylib.double") \
.to(name="add3", handler="mylib.add3") \
.to(name="echo", handler="mylib.echo").respond()
Note
To use a function "as-is", without modifying any parameters, use get_function. See Best practice: set_function and get_function.
Customizing functions#
Once you have created your MLRun function, there are many customizations you can add, including Memory / CPU / GPU resources, preemption mode, pod priority, node selection, and more. See full details in Configuring runs and functions.