App developer guide
App bundle structure
Each app must be bundled as a zip archive (commonly used with the suffix .wave
or .zip
)
consisting of:
app.toml
- required; the platform configuration filestatic/
- static asset directory, including the app icon (a png file starting withicon
) and screenshots (files starting withscreenshot
)requirements.txt
- pip-managed dependencies of the app (can contain references to.whl
files included in the.wave
using paths relative to the archive root)packages.txt
- OS-managed dependencies of the app.appignore
- specifies which files to ignore while bundling your app (the format is similar to.gitignore
but with few exceptions)- app source code
You can quickly create a .wave
archive by running h2o bundle
in your app git repository
(see the CLI section).
H2O AI Cloud supports the following runtimes:
- Python 3.8 | CPU | deb11_py38_wlatest
- Python 3.9 | CPU | deb11_py39_wlatest
- Python 3.10 | CPU | deb11_py310_wlatest
- Python 3.8 | GPU | ub2004_cuda114_cudnn8_py38_wlatest
- Python 3.10 | GPU | ub2204_cuda121_cudnn8_py310_wlatest
app.toml
Each app archive has to contain an app.toml
configuration file in the TOML
format, placed in the root of the .wave
archive, example:
[App]
Name = "ai.h2o.wave.my-app"
Version = "0.0.1"
Title = "My awesome app"
Description = "This is my awesome app"
LongDescription = "LongDescription.md"
Tags = ["DATA_SCIENCE"]
InstanceLifecycle = "ON_DEMAND"
InstanceTimeout = "1h"
[Runtime]
Module = "app.run"
VolumeMount = "/data"
VolumeSize = "1Gi"
ResourceVolumeSize = "2Gi"
MemoryLimit = "500Mi"
MemoryReservation = "400Mi"
CPULimit = "1.5"
CPUReservation = "500m"
GPUCount = 1
GPUType = ""
EnableOIDC = false
EnableSHM = false
RoutingMode = "DOMAIN"
[[Env]]
Name = "ENVIRONMENT_VARIABLE_NAME"
Secret = "SecretName"
SecretKey = "SecretKeyName"
[[Env]]
Name = "ANOTHER_ENVIRONMENT_VARIABLE_NAME"
Value = "some value"
[[File]]
Path = "some/path.file"
Secret = "FileSecretName"
SecretKey = "FileSecretKeyName"
[[File]]
Path = "/another/path.file"
Value = '''
some
string
'''
Required attributes
[App]
Attribute | Type | Description |
---|---|---|
Name | string | A machine-oriented unique identifier of the app (links different app versions together) |
Version | string | A semver version of the app |
{{App.Name}}
and {{App.Version}}
must be 63 chars or less and match the regex ^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$
[Runtime]
Attribute | Type | Description |
---|---|---|
Module | string | The name of the main Python module of the app, that is, the app should be started via python3 -m $module_name (this is only required if the value of the App.InstanceLifecycle attribute is not LINK ; see Link apps for more information) |
{{App.Name}}-{{App.Version}}
must be 63 chars or less and match the regex ^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$
Optional attributes
[App]
Attribute | Type | Description |
---|---|---|
Title | string | A human-oriented name of the app for presentation in UI/CLI |
Description | string | A single-line description of the app for presentation in UI/CLI |
LongDescription | string | A path to a file, relative to the archive root, containing additional multi-line markdown description of the app. Although there is no actual restriction of the Markdown format, it is recommended to limit it to bullet-point lists (* ), H3 headings (### ), and hyperlinks ([]() ). |
Tags | list of strings | Tags to automatically assign to the app upon importing. Apps can be identified by tag name. If the tag is listed as a category tag in the server configuration, the app will be assigned that category upon import. |
InstanceLifecycle | string | Identifies the instance lifecycle, which can be set to [ON_DEMAND ] or [MANAGED ]. This config defaults to ON_DEMAND when empty. |
InstanceTimeout | string | Overrides server defaults for auto pausing an app instance ("s", "m", "h") are valid units, -1 for no timeout. Ex. "1h" |
[Runtime]
Attribute | Type | Description |
---|---|---|
RuntimeVersion | string | The name of the runtime version that the app will run on top of (similar to a docker base image, see Runtime environment). This config defaults to the platform's default when left empty/unspecified. Valid values differ based on the platform deployment and configuration. |
VolumeMount | string | The absolute path of the volume used to persist app instance data across restarts |
VolumeSize | string | The volume size. This config value must conform to the k8s resource model |
ResourceVolumeSize | string | The volume used to persist internal app resources (such as Python venv) across restarts. This is only recommended for production-quality apps with sizeable resources, due to cluster node limits. This config value must conform to the k8s resource model. |
MemoryLimit | string | A hard limit on the maximum amount of memory an instance can use before it is OOM-killed. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model. |
MemoryReservation | string | The amount of memory required to schedule an instance of the app. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model. |
CPULimit | string | Maximum CPU usage that an instance of the app can use. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model. |
CPUReservation | string | The number of CPU units required to schedule an instance of the app. This config defaults to service-wide settings managed by Admins (it is recommended to be conservative with these limits) and must conform to the k8s resource model. |
GPUCount | int | The number of GPU units required to schedule an instance of the app |
GPUType | string | GPU type required for the app. This config defaults to the platform's default when left empty/unspecified. Valid values differ based on the platform deployment and configuration. |
EnableOIDC | bool | Enable Wave to be set up with OIDC authorization, thereby giving access to the user's authentication and authorization information from your app (see Wave security for details). |
EnableSHM | bool | Enable extended docker shared memory for the app; some Libraries like pyTorch use Shared Memory for Multiprocessing (see this Kubernetes issue for more details on this topic. |
RoutingMode | string | The routing mode to be used for instances of the app can be set to either DOMAIN or, BASE_URL . This config defaults to DOMAIN when empty (see App routing mode for details). |
AppMode | string | App mode can be set to python or container . This config defaults to wave when empty. |
Port | int | Port number of the app only valid when AppMode is python or container. |
CustomImage | string | If AppMode is container set the a custom container for this App and must be imported using h2o admin app import |
AppArgs | []string | If AppMode is container set arguments to start the container. |
AppEntryPoint | []string | If AppMode is container set the entrypoint for the container. |
[Env]
This struct contains configs that request for a literal value/secret to be injected into an instance at startup-time as an Env variable (see Utilizing secrets for more details).
Attribute | Type | Description |
---|---|---|
Name | string | The name of the Env variable to the injected into the Python process. Names prefixed with H2O_CLOUD or prefixed with H2O_WAVE are disallowed (except H2O_WAVE_APP_MODE and names allowed by the administrator). See Configuring your app for a full list of environment variables that you can configure. |
Secret | string | The name of the shared secret to use. Each secret can contain multiple key-value pairs but cannot be used together with the Value config. |
Optional | bool | If set to true the secret will not be required to exist to be imported. This config cannot be used together with Value config. |
SecretKey | string | The name of the key within the secret that is to be used. This config cannot be used together with the Value config. |
Value | string | The literal value of the Env variable. This config cannot be used together with the Secret or SecretKey configs. |
[File]
This struct contains configs that request for a literal value/secret to be injected into an instance at startup-time as a file (see Utilizing secrets for more details).
Attribute | Type | Description |
---|---|---|
Path | string | The path to inject the file into. A relative path is relative to the directory with the app code as determined by the platform. The path dir cannot be / or . (only subdirs are allowed) and it must be unique across all other File configurations. Note that the /resources path dir is disallowed. |
Secret | string | The name of the shared secret to use. Each secret can contain multiple key-value pairs but cannot be used together with the Value config. |
Optional | bool | If set to true the secret will not be required to exist to be imported. This config cannot be used together with Value config. |
SecretKey | string | The name of the key within the secret that is to be used. This config cannot be used together with the Value config. |
Value | string | The literal value of the Env variable. This config cannot be used together with the Secret or SecretKey configs. |
[Link]
This struct is to be filled only if the value of App.InstanceLifecycle
is LINK
(see Link apps for details).
Attribute | Type | Description |
---|---|---|
Address | string | Full URL of the app link. |
Runtime environment
The platform executes each app in an environment given by its RuntimeVersion
.
The RuntimeVersion
determines the OS, Python version, location of the app code/venv, etc.
Developers can also specify the pip-managed dependencies of the app via standard requirements.txt
(can contain
references to .whl
files included in the .wave
using paths relative to the archive root)
Developers can also specify the OS-managed dependencies of the app via packages.txt
in a format
similar to requirements.txt
: one package name per line.
These will be installed as given using the package manager given by the RuntimeVersion
(e.g., apt-get install
).
Developers can further customize the runtime environment by Utilizing secrets.
The h2o env runtime-version list
command will list the runtime-versions available for use.
$ h2o env runtime-version list
NAME STATUS
deb11_py310_wlatest Default
ub2004_cuda114_cudnn8_py38_wlatest
...
The platform does not currently provide any provisions for developers to customize the OS and
Python versions beyond choosing a specific RuntimeVersion
.
We are actively working on improving this.
Standard environment variables
When running in an actual App Store deployment, each app instance will be configured with several
standard environment variables specifying its context
(these might be empty when running locally, e.g., via h2o exec
), including:
H2O_CLOUD_ENVIRONMENT
- typically URL of the App Store deploymentH2O_CLOUD_INSTANCE_ID
- ID of the app instanceH2O_CLOUD_APP_NAME
- name of the appH2O_CLOUD_APP_VERSION
- version of the appH2O_CLOUD_APP_ID
- ID of the app
How-To
Update an app to a newer version
The "Catalog" page shows apps with visibility ALL_USERS
, so rolling out a new app version is done by:
- importing a new version of the app as
PRIVATE
- testing the new version
- changing the visibility of the new version to
ALL USERS
- (optional) changing the visibility of the old version to
PRIVATE
This is based on the Basic concepts:
Apps are mostly immutable, meaning once uploaded, they cannot be changed (except for visibility). To "update" an app, one has to upload a new version.
and:
Internally, AI App Store treats every app name/version combination as a separate entity. The UI then uses the app name to link several versions together; however each can have different title, description, owner, instances, etc.
An important corollary is that instances of the old app version are not affected by the update (as they are completely separate from the new app version). The update only prevents users from starting new instances of the old version.
Pause or restart an app instance
The h2o instance suspend <instanceId>
command pauses a running instance of a particular app. The app status changes to "Paused". You can configure ResourceVolumeSize
in the app.toml file to utilize Wave checkpointing.
$ h2o instance suspend 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
ID 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
App
App ID 492dbac1-3230-413e-852f-11cb82b57436
Created At 2022-09-16 08:28:04
Updated At 2022-12-12 09:03:23
Status SUSPENDED
Visibility ALL_USERS
URL https://2efe9ed7-2bdd-4d02-9be6-f73a196d663d.cloud.h2o.ai
Owner oshini.nugapitiya@h2o.ai
Readonly false
Suspendable true
Suspend After
The h2o instance resume <instanceId>
command restarts a paused or suspended instance of a particular app. The app status changes to "Deployed". Any files that are saved to disk are not available after the restart unless they are in the directory in VolumeMount
.
VolumeMount
cannot be an existing folder in the App Bundle.
::note
There is no guarantee that the Wave on_shutdown
hook will be given time to complete when an app is suspended, the underlying Kubernetes pod moves nodes, or a node experiences a failure.
:::
$ h2o instance resume 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
ID 2efe9ed7-2bdd-4d02-9be6-f73a196d663d
App
App ID 492dbac1-3230-413e-852f-11cb82b57436
Created At 2022-09-16 08:28:04
Updated At 2022-12-12 08:56:32
Status DEPLOYED
Visibility ALL_USERS
URL https://2efe9ed7-2bdd-4d02-9be6-f73a196d663d.cloud.h2o.ai
Owner oshini.nugapitiya@h2o.ai
Readonly false
Suspendable true
Suspend After 2022-12-12 16:56:31
Utilize app secrets
Developers can pass secrets registered with the platform to apps, exposed as environment variables
using the [[Env]]
section within the app.toml
or as files using the [[File]]
section. Each
specific value from the secret that you want to use in your app needs its own environmental variable
or file path.
[[Env]]
Name = "USER_ENV_VAR"
Secret = "private-secret"
SecretKey = "user"
[[File]]
Path = "some/password.file"
Secret = "private-secret"
SecretKey = "password"
import os
@app('/')
async def serve(q: Q):
environment_variable = 'USER_ENV_VAR'
default_value = 'user'
value = os.getenv(environment_variable, default_value)
q.page['my_card'] = ui.form_card(box='1 1 4 4', items=[ui.text(value)])
await q.page.save()
This lets developers parametrize their apps with links to external dependencies (e.g., S3, Driverless AI) securely, while allowing easy overrides for local development or deployments outside the platform.
See CLI documentation for instructions on manipulating secrets.
Apps imported into the App Store directly
can reference only PRIVATE
secrets of the same user or ALL_USERS
secrets.
APP
secrets are reserved for apps imported via
the H2O marketplace.
App routing mode
The app routing mode (Runtime.RoutingMode
in app.toml) determines how the app
instances' UI is exposed to end users. The currently supported values are
DOMAIN
- each app instance is exposed on its own subdomain of the Appstore server, i.e.,uuid.appstore.domain/app/path...
. This is the default setting.BASE_URL
- each app instance is exposed on a sub-path of the appstore server, i.e.,appstore.domain/instance/uuid/app/path...
. This setting requires that the app itself supports serving behind a base URL/path. All apps using Wave SDK 0.20 and later should support this out of the box. The/app/path...
portion of the URL path is forwarded to the app container via theH2O_WAVE_BASE_URL
environment variable in case it is needed by your application for some reason (in most cases, however, the Wave SDK should handle this for you).
In both cases the app's webserver gets the full request URI as received by the Appstore server.
Redirects: The Appstore server's app router component implements a redirect (via 307
TemporaryRedirect HTTP status) in case an instance is accessed via the wrong URL, i.e. it redirects
from sub-path to subdomain for instances with DOMAIN
RoutingMode
and vice versa for BASE_URL
.
App route
While it is not a strict requirement, since the platform deploys each app with its own Wave server,
we advise that apps use /
as their main route:
@app('/')
async def serve(q: Q):
pass
Give an app instance a pretty URL
You can leverage App aliases to give your instances
pretty URLs, so that instead of 8184-810243981-23.cloud.h2o.ai
your users can access the instance
via something like my-pretty-name .cloud.h2o.ai
.
Prerequisite: You must be an admin of the environment to run these commands.
To create a single alias for an app for which you want to have a pretty URL, run:
$ h2o admin alias create my-pretty-name <instance id> True
This instance then can be accessed via my-pretty-name.cloud.h2o.ai
,
accesses to <instance id>.cloud.h2o.ai
will result in an HTTP 302
redirect to the alias.
When you’ve created a new app instance, usually because there’s a new version of the app, you may want to change which instance the alias points to. To do this, run:
h2o admin alias assign my-pretty-name <new instance id>`
h2o admin alias promote my-pretty-name
Note that there can be a slight delay before the change gets propagated.
See the CLI documentation for details on these commands.
Please note that if the environment requires base URL app routing for all apps, you will need to add this alias to the App Store TLS certificate.
Link apps
The App Store allows importing apps that do not bundle executable code (and thus cannot have instances) but only represent a link to an external website. This kind of app is referred to as a "Link App". The goal is to inject an external link into the App Store UI/CLI in a way that is consistent in terms of UX with regular apps (app icon, listing on App Store page, categorization, app details with long description and screenshots, etc.).
You can create a link app by setting LINK
as the value of App.InstanceLifecycle
in app.toml
.
In such a case, you also need to set the Link.Address
value to a URL of your choice.
The UI and CLI will then direct users to this URL instead of directing them to an instance of the app.
A link app bundle still contains app.toml
and static/
, but should not contain any source code or
requirements.txt
.
A link app can leverage all the parameters in the App
section, however the Runtime
and File
sections must be empty. For example:
[App]
Name = "ai.h2o.examples.link"
Version = "0.0.1"
Title = "Example: Link"
Description = "Showcasing the link functionality."
InstanceLifecycle = "LINK"
[Link]
Address = "https://h2o.ai"
Container Apps
The App Store allows importing containers in addition to Wave apps by admin users only.
You can create a container app by setting container
as the value of Runtime.AppMode
in app.toml
.
In such a case, you can also specify Runtime.Port
value to the port where the web application is running on. You can also specify Runtime.AppArgs
and Runtime.AppEntryPoint
to specify the entrypoint and arguments to start the container otherwise the default entrypoint or the container will be used. Runtime.CustomImage
can be set to specify where the container is.
A container app bundle still contains app.toml
and static/
, but should not contain any source code or
requirements.txt
.
In order for containerized apps to be imported the administrator must configure the server config option config.allowedCustomImageRegexes
with the allowed registries containers can be imported from.
allowedCustomImageRegexes:
- "^123456\\.dkr\\.ecr\\.us-east-1\\.amazonaws.com\\/.*"
[App]
Name = "ai.h2o.examples.container"
Version = "0.0.1"
Title = "Example: Container"
Description = "Showcasing the container functionality."
[Runtime]
AppMode = "container"
CustomImage = "customImage:latest"
AppArgs = ["--mode", "production"]
AppEntryPoint = ["./server"]
Port = 1337
To import this container app it must be bundled h2o app bundle
and then imported using
h2o admin app import
with the optional --set-image
flag where the container image address
can be overridden.
In the case a container app is imported without an image it will be not runnable. The container image can be set using h2o admin app set-image <appID> <image>
and then you must run h2o admin app preconditions refresh <appID>
or wait until the appstore refreshes all the apps periodically to check if they are runnable.
Configure an app via the user interface
While tags and visibility for an App can be configured via the CLI, these attributes can also be set using the user interface, as described below:
Since the user interface is being continually improved, it is possible that the images below will not match exactly what you see in your version of H2O AI Cloud.
On the "My Apps" page, click on the pencil icon for the app you wish to edit:
This will bring up a panel on the right side of the user interface which will allow you to edit the values for:
- Visibility (See Visibility for more information)
- Categories
- Badges
- Authorization Tags
Tags will show up in the "Categories", "Badges", or "Authorization Tags" select menus based on the following criteria:
If the tag has
isCategory
set totrue
it will be treated as a "Category", which allows it to be filtered on the App Store's main page.If the tag has one or more
visitorRoles
set, it will be treated as an "Authorization Tag".Otherwise, the tag will serve as a "Badge" in the App Store UI:
The badge tags let the developer or the system administrator share more information about the app with end users. For instance, the administrator can configure an open-source badge if your environment has many open-source applications.
Then the developers can tag their open source apps with this badge if they want to indicate to the user that the code is available in GitHub. This additional information shows on the App details page, the My apps page, and the Admin apps page.
See App Tag to learn more about tags.
- Submit and view feedback for this page
- Send feedback about AI App Store to cloud-feedback@h2o.ai