Deploying Omniverse Kit Apps#
This guide focuses on the integration and management of containerized Omniverse Kit Apps with an Omniverse Application Streaming API instance. It explains the use of Kubernetes Custom Resource Definitions (CRDs) and Helm charts to manage application lifecycles, including application versions and runtime profiles. It also provides instructions for configuring application manifests, deploying streaming applications, and optimizing performance, enabling efficient management and scaling of streaming services in cloud environments.
Upon completion, you should be able to integrate, configure, and manage containerized Omniverse Kit Apps using Kubernetes CRDs and Helm charts, manage application versions and optimize streaming performance.
Overview
For an Omniverse Kit App Streaming instance to be able to stream a Omniverse Kit App, it requires information about it. This information is broken into three key concepts:
Application
- the overall Kit App that you want to make available for streaming.ApplicationVersion
- a specific release or version of an application.Application Profile
- the runtime configuration and deployment settings to use when instantiating a Kit App stream.
Application - Overview
This represents the overall Kit App that you want to make available for streaming; it is essentially the “product” that users interact with. Application Versions and Profiles are attached to Applications and define their specific configurations and deployment options.
An Application
is defined in an application.yaml
file that contains some basic information about it, consisting of a metadata
and spec
(specification) section.
1apiVersion: omniverse.nvidia.com/v1
2kind: Application
3metadata:
4 name: usd-viewer
5 labels:
6 USD: "true"
7 visualisation: "true"
8spec:
9 name: Omniverse USD Viewer
10 description: View Universal Scene Description files.
Application Version - Overview
This represents a specific release or version of an application. Each version might differ based on updates, bug fixes, or new features. Each application can have multiple versions. The version provides granular control over which iteration of the application is deployed and streamed. Profiles reference specific application versions for deployment.
An ApplicationVersion
is defined in the application-version.yaml
file that contains information about this specific application. In particular, the ApplicationVersion
is where you specify the URL of the container image of the Kit App.
1apiVersion: omniverse.nvidia.com/v1
2kind: ApplicationVersion
3metadata:
4 name: usd-viewer-0.2.0
5 labels:
6 app: usd-viewer
7 applicationName: usd-viewer
8 version: '0.2.0'
9spec:
10 helm_chart: ngc-omniverse/kit-appstream-session
11 helm_chart_version: '1.4.0'
12 container: nvcr.io/nvidia/omniverse/usd-viewer
13 container_version: '0.2.0'
Application Profile - Overview
An ApplicationProfile
defines how a specific version of a Kit Application should be deployed and managed within the cluster. The Application Profile includes key settings such as: supported applications and versions, resource allocation, network configuration, environment-specific settings and Helm chart mappings.
Application Profiles can have a many-to-many relationship with application versions, removing the need to create profiles for applications that have the same requirements, while also providing the ability to create profiles to differentiate between different types of execution workloads, such as those requiring additional memory, CPUs or GPUs.
An ApplicationProfile
is defined in an application-profile.yaml
file that provides methods for configuring or modifying many aspects of the Kit App deployment.
1apiVersion: omniverse.nvidia.com/v1
2kind: ApplicationProfile
3metadata:
4 name: default
5spec:
6 name: Default profile
7 description: Updated memory and CPU settings.
8 supportedApplications:
9 - name: "usd-viewer"
10 versions:
11 - "*"
12 chartMappings:
13 container: streamingKit.image.repository
14 container_version: streamingKit.image.tag
15 name: streamingKit.name
16 chartValues:
17 global:
18 imagePullSecrets:
19 - name: regcred
20 streamingKit:
21 image:
22 repository: nvcr.io/nvidia/omniverse/usd-viewer
23 pullPolicy: Always
24 tag: '0.2.0'
25 sessionId: session_id
26 name: kit-app
27 resources:
28 requests:
29 nvidia.com/gpu: "1"
30 limits:
31 cpu: "3"
32 memory: "20Gi"
33 nvidia.com/gpu: "1"
34 env:
35 - name: USD_PATH
36 value: "/app/data/Forklift_A/Forklift_A01_PR_V_NVD_01.usd"
1apiVersion: omniverse.nvidia.com/v1
2kind: ApplicationProfile
3metadata:
4 name: nlb
5spec:
6 name: NLB example profile
7 description: Default profile - uses an AWS NLB per stream
8 supportedApplications:
9 - name: "usd-viewer"
10 versions:
11 - "*"
12 chartMappings:
13 container: streamingKit.image.repository
14 container_version: streamingKit.image.tag
15 name: streamingKit.name
16 chartValues:
17 global:
18 imagePullSecrets:
19 - name: regcred
20 streamingKit:
21 image:
22 repository: nvcr.io/nvidia/omniverse/usd-viewer
23 pullPolicy: Always
24 tag: '0.2.0'
25 sessionId: session_id
26 service:
27 signalingPort: 31000
28 mediaPort: 31001
29 healthPort: 31002
30 annotations:
31 # NLB configuration
32 service.beta.kubernetes.io/aws-load-balancer-type: "external"
33 service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
34 service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
35 service.beta.kubernetes.io/load-balancer-source-ranges: "<replace with allowed ranged>"
36 service.beta.kubernetes.io/aws-load-balancer-attributes: "load_balancing.cross_zone.enabled=true"
37 # Health check
38 service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: "HTTP"
39 service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
40 service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: "/health"
41 type: LoadBalancer
42 name: kit-app
43 resources:
44 requests:
45 nvidia.com/gpu: "1"
46 limits:
47 cpu: "3"
48 memory: "20Gi"
49 nvidia.com/gpu: "1"
50 env:
51 - name: USD_PATH
52 value: "/app/data/Forklift_A/Forklift_A01_PR_V_NVD_01.usd"
1--- /builds/omniverse/omni-docs/docs/app_ovas/reference_data/crds/generic/application-profile.yaml
2+++ /builds/omniverse/omni-docs/docs/app_ovas/reference_data/crds/aws/application-profile-nlb.yaml
3@@ -1,10 +1,10 @@
4 apiVersion: omniverse.nvidia.com/v1
5 kind: ApplicationProfile
6 metadata:
7- name: default
8+ name: nlb
9 spec:
10- name: Default profile
11- description: Updated memory and CPU settings.
12+ name: NLB example profile
13+ description: Default profile - uses an AWS NLB per stream
14 supportedApplications:
15 - name: "usd-viewer"
16 versions:
17@@ -23,6 +23,22 @@
18 pullPolicy: Always
19 tag: '0.2.0'
20 sessionId: session_id
21+ service:
22+ signalingPort: 31000
23+ mediaPort: 31001
24+ healthPort: 31002
25+ annotations:
26+ # NLB configuration
27+ service.beta.kubernetes.io/aws-load-balancer-type: "external"
28+ service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
29+ service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
30+ service.beta.kubernetes.io/load-balancer-source-ranges: "<replace with allowed ranged>"
31+ service.beta.kubernetes.io/aws-load-balancer-attributes: "load_balancing.cross_zone.enabled=true"
32+ # Health check
33+ service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: "HTTP"
34+ service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
35+ service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: "/health"
36+ type: LoadBalancer
37 name: kit-app
38 resources:
39 requests:
1apiVersion: omniverse.nvidia.com/v1
2kind: ApplicationProfile
3metadata:
4 name: nlb-tls
5spec:
6 name: NLB example profile using TLS
7 description: Default profile - uses an AWS NLB per stream
8 supportedApplications:
9 - name: "usd-viewer"
10 versions:
11 - "*"
12 - name: "usd-explorer"
13 versions:
14 - "*"
15 chartValues:
16 global:
17 imagePullSecrets:
18 - name: regcred
19 streamingKit:
20 sessionId: session_id
21 service:
22 signalingPort: 443
23 mediaPort: 80
24 healthPort: 31002
25 annotations:
26 # NLB configuration
27 service.beta.kubernetes.io/aws-load-balancer-type: "external"
28 service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
29 service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
30 service.beta.kubernetes.io/load-balancer-source-ranges: "<replace with allowed ranged>"
31 # Cross zone
32 service.beta.kubernetes.io/aws-load-balancer-attributes: "load_balancing.cross_zone.enabled=true"
33 # Health check
34 service.beta.kubernetes.io/aws-load-balancer-healthcheck-protocol: "HTTP"
35 service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
36 service.beta.kubernetes.io/aws-load-balancer-healthcheck-path: "/health"
37 # DNS Annotations
38 external-dns.alpha.kubernetes.io/hostname: "<not set, will be done dynamically>"
39 # NLB TLS Annotations
40 service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "<replace me with SSL cert ARN>"
41 service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"
42 service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS13-1-2-2021-06"
43 service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "tcp"
44 type: LoadBalancer
45 name: kit-app
46 resources:
47 requests:
48 nvidia.com/gpu: "1"
49 limits:
50 cpu: "3"
51 memory: "20Gi"
52 nvidia.com/gpu: "1"
53 env:
54 - name: USD_PATH
55 value: "/app/data/Forklift_A/Forklift_A01_PR_V_NVD_01.usd"
1apiVersion: omniverse.nvidia.com/v1
2kind: ApplicationProfile
3metadata:
4 name: default-auth
5spec:
6 name: Default profile with authorisation check
7 description: Updated memory and CPU settings.
8 supportedApplications:
9 - name: "usd-viewer"
10 versions:
11 - "*"
12 chartMappings:
13 container: streamingKit.image.repository
14 container_version: streamingKit.image.tag
15 name: streamingKit.name
16 chartValues:
17 global:
18 imagePullSecrets:
19 - name: regcred # Refers to image pull secret for accessing private container registry.
20 streamingKit:
21 envoy:
22 config:
23 node:
24 id: node0
25 cluster: envoy-cluster # Defines the cluster that Envoy is part of.
26 static_resources:
27 listeners:
28 - name: webrtc_signaling_listener
29 address:
30 socket_address:
31 address: 0.0.0.0
32 port_value: 49200 # Listener for WebRTC signaling on port 49200.
33 filter_chains:
34 - filters:
35 # Parent HTTP filter to allow websocket, token extraction, JWT and routing of the signaling channel
36 - name: envoy.filters.network.http_connection_manager
37 typed_config:
38 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
39 stat_prefix: signaling_http
40 codec_type: AUTO
41 upgrade_configs:
42 - upgrade_type: websocket # Enables WebSocket upgrade for WebRTC signaling.
43 route_config:
44 name: local_route
45 virtual_hosts:
46 - name: local_service
47 domains: ["*"]
48 routes:
49 - match:
50 prefix: "/"
51 route:
52 cluster: service_cluster
53 # This Lua filter processes the Authorization header, extracting and validating the JWT token.
54 http_filters:
55 - name: envoy.filters.http.lua
56 typed_config:
57 "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
58 inline_code: |
59 function envoy_on_request(request_handle)
60 local headers = request_handle:headers()
61 local sec_websocket_protocol = headers:get("Sec-WebSocket-Protocol")
62
63 request_handle:logInfo("Lua filter: Checking Sec-WebSocket-Protocol header")
64 if sec_websocket_protocol == nil then
65 local checked = request_handle:streamInfo():dynamicMetadata():get("lua_checked")
66 if checked == nil then
67 request_handle:streamInfo():dynamicMetadata():set("lua_checked", "true")
68 request_handle:respond({[":status"] = "403"}, "Forbidden")
69 return
70 end
71 else
72 -- Correctly match and extract x-nv-sessionid and Authorization values
73 local sessionid, authorization = sec_websocket_protocol:match("x%-nv%-sessionid%.([%w%-]+)%-Authorization%.Bearer%-(.+)")
74 if sessionid and authorization then
75 headers:add("x-nv-sessionid", sessionid)
76 headers:add("Authorization", "Bearer " .. authorization)
77 request_handle:logInfo("Lua filter: Extracted x-nv-sessionid and Authorization headers")
78 else
79 request_handle:logInfo("Lua filter: Failed to extract x-nv-sessionid and Authorization headers")
80 request_handle:respond({[":status"] = "403"}, "Forbidden")
81 return
82 end
83 end
84 end
85 # JWT Authentication Filter - Validates the extracted JWT token.
86 - name: envoy.filters.http.jwt_authn
87 typed_config:
88 "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
89 providers:
90 keycloak:
91 issuer: "<replace with issuer>" # Replace with your JWT issuer (e.g., Keycloak URL).
92 remote_jwks:
93 http_uri:
94 uri: "<replace with jwks URL>" # Replace with JWKS URL to fetch public keys.
95 cluster: keycloak_cluster # Use the Keycloak cluster for fetching keys.
96 timeout: 60s
97 cache_duration:
98 seconds: 1 # Caches the JWKS for 1 second.
99 rules:
100 - match:
101 prefix: "/"
102 requires:
103 provider_name: "keycloak" # Use the Keycloak JWT provider for authentication.
104 - name: envoy.filters.http.router
105 access_log:
106 - name: envoy.access_loggers.stream
107 typed_config:
108 "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
109 log_format:
110 text_format: |
111 [START_TIME: %START_TIME%]
112 REQUEST_METHOD: %REQ(:METHOD)%
113 PATH: %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%
114 PROTOCOL: %PROTOCOL%
115 RESPONSE_CODE: %RESPONSE_CODE%
116 RESPONSE_FLAGS: %RESPONSE_FLAGS%
117 BYTES_RECEIVED: %BYTES_RECEIVED%
118 BYTES_SENT: %BYTES_SENT%
119 DURATION: %DURATION%
120 UPSTREAM_HOST: %UPSTREAM_HOST%
121 DOWNSTREAM_REMOTE_ADDRESS: %DOWNSTREAM_REMOTE_ADDRESS%
122 - name: health_listener
123 address:
124 socket_address:
125 address: 0.0.0.0
126 port_value: 8080
127 filter_chains:
128 - filters:
129 - name: envoy.filters.network.http_connection_manager
130 typed_config:
131 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
132 stat_prefix: health_check
133 codec_type: AUTO
134 route_config:
135 name: local_route
136 virtual_hosts:
137 - name: local_service
138 domains: ["*"]
139 routes:
140 - match:
141 prefix: "/health"
142 direct_response:
143 status: 200
144 body:
145 inline_string: "OK"
146 http_filters:
147 - name: envoy.filters.http.router
148 clusters:
149 - name: service_cluster
150 connect_timeout: 0.25s
151 type: STATIC
152 lb_policy: ROUND_ROBIN
153 load_assignment:
154 cluster_name: service_cluster
155 endpoints:
156 - lb_endpoints:
157 - endpoint:
158 address:
159 socket_address:
160 address: 127.0.0.1
161 port_value: 49100 # Forwarding to the stream
162 # Keycloak cluster - Used to securely validate JWT tokens.
163 - name: keycloak_cluster
164 connect_timeout: 0.25s
165 type: STRICT_DNS
166 lb_policy: ROUND_ROBIN
167 load_assignment:
168 cluster_name: keycloak_cluster
169 endpoints:
170 - lb_endpoints:
171 - endpoint:
172 address:
173 socket_address:
174 address: <replace with FQDN of keycloak server> # Replace with the Keycloak server address
175 port_value: 443 # Port for HTTPS.
176 transport_socket:
177 name: envoy.transport_sockets.tls # Use TLS for secure communication.
178 typed_config:
179 "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
180 # TLS context ensures encrypted communication with the Keycloak server.
181 image:
182 repository: nvcr.io/nvidia/omniverse/usd-viewer
183 pullPolicy: Always
184 tag: '0.2.0'
185 sessionId: session_id
186 name: kit-app
187 resources:
188 requests:
189 nvidia.com/gpu: "1"
190 limits:
191 cpu: "3"
192 memory: "20Gi"
193 nvidia.com/gpu: "1"
194 env:
195 - name: USD_PATH
196 value: "/app/data/Forklift_A/Forklift_A01_PR_V_NVD_01.usd"
These examples highlights a few of the many ways that an application profile can be used to tailor the execution of a Kit App to fit a broad-array of deployment needs.
Dynamically modifying the container image and version to be deployed, ensuring the latest or specified version is used consistently.
Configuring resource limits for CPU, memory, and GPU usage, guaranteeing optimal performance and preventing resource exhaustion.
Setting up an external Network Load Balancer (NLB) to manage traffic routing and ensure secure and reliable access to the application.
Defining custom health check protocols and paths, ensuring that only healthy instances of the application receive traffic.
Injecting environment variables into the container, allowing dynamic configuration of critical runtime paths and settings.
Managing secure image pulls with imagePullSecrets, ensuring access to private Docker registries for authenticated image retrieval.
Customizing network access controls by specifying acceptable IP ranges, enhancing security by limiting traffic to trusted sources.