Repository: cortexlabs/cortex (language: python)

Issue:
Add metrics to AsyncAPI
### Description

Add metrics to AsyncAPI kind by pushing them to StatsD.

### Motivation

Show metrics in the CLI, and allow for the creation of a dashboard


Discussion / hints:

Fix patch:
diff --git a/cli/cmd/lib_async_apis.go b/cli/cmd/lib_async_apis.go
index 114c88bca8..9d852b1806 100644
--- a/cli/cmd/lib_async_apis.go
+++ b/cli/cmd/lib_async_apis.go
@@ -62,6 +62,8 @@ func asyncAPIsTable(asyncAPIs []schema.APIResponse, envNames []string) table.Tab
 
 	var totalFailed int32
 	var totalStale int32
+	var total4XX int
+	var total5XX int
 
 	for i, asyncAPI := range asyncAPIs {
 		lastUpdated := time.Unix(asyncAPI.Spec.LastUpdated, 0)
@@ -74,10 +76,19 @@ func asyncAPIsTable(asyncAPIs []schema.APIResponse, envNames []string) table.Tab
 			asyncAPI.Status.Requested,
 			asyncAPI.Status.Updated.TotalFailed(),
 			libtime.SinceStr(&lastUpdated),
+			latencyStr(asyncAPI.Metrics),
+			code2XXStr(asyncAPI.Metrics),
+			code4XXStr(asyncAPI.Metrics),
+			code5XXStr(asyncAPI.Metrics),
 		})
 
 		totalFailed += asyncAPI.Status.Updated.TotalFailed()
 		totalStale += asyncAPI.Status.Stale.Ready
+
+		if asyncAPI.Metrics.NetworkStats != nil {
+			total4XX += asyncAPI.Metrics.NetworkStats.Code4XX
+			total5XX += asyncAPI.Metrics.NetworkStats.Code5XX
+		}
 	}
 
 	return table.Table{
@@ -90,6 +101,10 @@ func asyncAPIsTable(asyncAPIs []schema.APIResponse, envNames []string) table.Tab
 			{Title: _titleRequested},
 			{Title: _titleFailed, Hidden: totalFailed == 0},
 			{Title: _titleLastupdated},
+			{Title: _titleAvgRequest},
+			{Title: _title2XX},
+			{Title: _title4XX, Hidden: total4XX == 0},
+			{Title: _title5XX, Hidden: total5XX == 0},
 		},
 		Rows: rows,
 	}
diff --git a/cmd/dequeuer/main.go b/cmd/dequeuer/main.go
index 25746e4aa8..cb9e0a5685 100644
--- a/cmd/dequeuer/main.go
+++ b/cmd/dequeuer/main.go
@@ -136,11 +136,6 @@ func main() {
 		probes = append(probes, probe.NewDefaultProbe(fmt.Sprintf("http://localhost:%d", userContainerPort), log))
 	}
 
-	metricsClient, err := statsd.New(fmt.Sprintf("%s:%d", hostIP, statsdPort))
-	if err != nil {
-		exit(log, err, "unable to initialize metrics client")
-	}
-
 	adminHandler := http.NewServeMux()
 	adminHandler.Handle("/healthz", dequeuer.HealthcheckHandler(func() bool {
 		return probe.AreProbesHealthy(probes)
@@ -163,6 +158,11 @@ func main() {
 			TargetURL: targetURL,
 		}
 
+		metricsClient, err := statsd.New(fmt.Sprintf("%s:%d", hostIP, statsdPort))
+		if err != nil {
+			exit(log, err, "unable to initialize metrics client")
+		}
+
 		messageHandler = dequeuer.NewBatchMessageHandler(config, awsClient, metricsClient, log)
 		dequeuerConfig = dequeuer.SQSDequeuerConfig{
 			Region:           clusterConfig.Region,
@@ -203,7 +203,7 @@ func main() {
 			Addr:    ":" + strconv.Itoa(adminPort),
 			Handler: adminHandler,
 		}
-		log.Infof("Starting %s server on %s", "admin", server.Addr)
+		log.Infof("Starting %s server on %s", consts.AdminPortName, server.Addr)
 		errCh <- server.ListenAndServe()
 	}()
 
diff --git a/manager/manifests/grafana/grafana-dashboard-async.yaml b/manager/manifests/grafana/grafana-dashboard-async.yaml
new file mode 100644
index 0000000000..5e59d43626
--- /dev/null
+++ b/manager/manifests/grafana/grafana-dashboard-async.yaml
@@ -0,0 +1,2068 @@
+# Copyright 2021 Cortex Labs, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: grafana-dashboard-async
+  namespace: default
+data:
+  async.json: |-
+    {
+      "annotations": {
+        "list": [
+          {
+            "builtIn": 1,
+            "datasource": "prometheus",
+            "enable": true,
+            "hide": true,
+            "iconColor": "rgba(0, 211, 255, 1)",
+            "name": "Annotations & Alerts",
+            "type": "dashboard"
+          }
+        ]
+      },
+      "editable": true,
+      "gnetId": null,
+      "graphTooltip": 0,
+      "id": 6,
+      "iteration": 1623161267934,
+      "links": [],
+      "panels": [
+        {
+          "datasource": null,
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "gridPos": {
+            "h": 2,
+            "w": 24,
+            "x": 0,
+            "y": 0
+          },
+          "id": 15,
+          "options": {
+            "content": "<h1 style=\"text-align: center\">AsyncAPI</h1>",
+            "mode": "markdown"
+          },
+          "pluginVersion": "7.4.2",
+          "timeFrom": null,
+          "timeShift": null,
+          "title": "",
+          "transparent": true,
+          "type": "text"
+        },
+        {
+          "collapsed": false,
+          "datasource": null,
+          "gridPos": {
+            "h": 1,
+            "w": 24,
+            "x": 0,
+            "y": 2
+          },
+          "id": 22,
+          "panels": [],
+          "title": "API Stats",
+          "type": "row"
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Request rate, computed over every minute, of an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 0,
+            "y": 3
+          },
+          "hiddenSeries": false,
+          "id": 2,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "rightSide": false,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(rate(cortex_async_request_count{api_name=\"$api_name\"}[1m])) by (api_name)",
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "2XX"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Request Rate",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "reqps",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Active in-flight requests for an API.\n\nNote: In-flight requests are recorded every 10 seconds, which will correspond to the minimum resolution.",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 12,
+            "y": 3
+          },
+          "hiddenSeries": false,
+          "id": 4,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(cortex_async_queue_length{api_kind=\"AsyncAPI\",api_name=\"$api_name\"}) by (api_name)",
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "A"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "In-Flight Requests",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "decimals": 0,
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Request rate, computed over every minute, for responses with status code 2XX of an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 0,
+            "y": 12
+          },
+          "hiddenSeries": false,
+          "id": 8,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "rightSide": false,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(rate(cortex_async_request_count{api_kind=\"AsyncAPI\",api_name=\"$api_name\",status_code=~\"2.+\"}[1m])) by (api_name, status_code)",
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "2XX"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "2XX Responses",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "reqps",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 12,
+            "y": 12
+          },
+          "hiddenSeries": false,
+          "id": 7,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(kube_deployment_status_replicas_available{deployment=\"api-$api_name\"}) by (deployment)",
+              "interval": "",
+              "legendFormat": "{{deployment}}",
+              "refId": "Active Replicas"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Active Replicas",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [
+            {
+              "id": "renameByRegex",
+              "options": {
+                "regex": "api-(.*)",
+                "renamePattern": "$1"
+              }
+            }
+          ],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "decimals": 0,
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Request rate, computed over every minute, for responses with status code 4XX of an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 0,
+            "y": 21
+          },
+          "hiddenSeries": false,
+          "id": 9,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "rightSide": false,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(rate(cortex_async_request_count{api_kind=\"AsyncAPI\",api_name=\"$api_name\",status_code=~\"4.+\"}[1m])) by (api_name, status_code)",
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "2XX"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "4XX Responses",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "reqps",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Request rate, computed over every minute, for responses with status code 5XX of an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 12,
+            "y": 21
+          },
+          "hiddenSeries": false,
+          "id": 10,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "rightSide": false,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(rate(cortex_async_request_count{api_kind=\"AsyncAPI\",api_name=\"$api_name\",status_code=~\"5.+\"}[1m])) by (api_name, status_code)",
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "2XX"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "5XX Responses",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "reqps",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "99th percentile latency, computed over a minute, for an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 0,
+            "y": 30
+          },
+          "hiddenSeries": false,
+          "id": 6,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "histogram_quantile(0.99, sum by (api_name, le) (rate(cortex_async_latency_bucket{api_kind=\"AsyncAPI\",api_name=\"$api_name\"}[1m])))",
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "A"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "p99 Latency",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "s",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "90th percentile latency, computed over a minute, for an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 12,
+            "y": 30
+          },
+          "hiddenSeries": false,
+          "id": 11,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "histogram_quantile(0.90, sum by (api_name, le) (rate(cortex_async_latency_bucket{api_kind=\"AsyncAPI\",api_name=\"$api_name\"}[1m])))",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "B"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "p90 Latency",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "s",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "50th percentile latency, computed over a minute, for an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 0,
+            "y": 39
+          },
+          "hiddenSeries": false,
+          "id": 16,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "histogram_quantile(0.50, sum by (api_name, le) (rate(cortex_async_latency_bucket{api_kind=\"AsyncAPI\",api_name=\"$api_name\"}[1m])))",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "B"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "p50 Latency",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "s",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {},
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Average latency, computed over a minute, for an API",
+          "fieldConfig": {
+            "defaults": {
+              "custom": {}
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 9,
+            "w": 12,
+            "x": 12,
+            "y": 39
+          },
+          "hiddenSeries": false,
+          "id": 12,
+          "legend": {
+            "alignAsTable": true,
+            "avg": true,
+            "current": true,
+            "max": true,
+            "min": true,
+            "show": true,
+            "total": false,
+            "values": true
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(rate(cortex_async_latency_sum{api_kind=\"AsyncAPI\",api_name=\"$api_name\"}[1m])) by (api_name) / \nsum(rate(cortex_async_latency_count{api_kind=\"AsyncAPI\",api_name=\"$api_name\"}[1m])) by (api_name)",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "{{api_name}}",
+              "refId": "D"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Average Latency",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "transformations": [],
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "s",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": null,
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": true
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "collapsed": false,
+          "datasource": null,
+          "gridPos": {
+            "h": 1,
+            "w": 24,
+            "x": 0,
+            "y": 48
+          },
+          "id": 20,
+          "panels": [],
+          "title": "Aggregate Usage",
+          "type": "row"
+        },
+        {
+          "aliasColors": {
+            "Total CPU Request": "semi-dark-orange",
+            "Total CPU Usage": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Total CPU usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 0,
+            "y": 49
+          },
+          "hiddenSeries": false,
+          "id": 24,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "exemplar": false,
+              "expr": "sum(rate(container_cpu_usage_seconds_total{pod=~\"api-$api_name.+\", container!=\"POD\", name!=\"\"}[1m]))",
+              "format": "time_series",
+              "instant": false,
+              "interval": "",
+              "legendFormat": "Total CPU Usage",
+              "refId": "CPU Usage"
+            },
+            {
+              "expr": "sum(kube_pod_container_resource_requests_cpu_cores{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Total CPU Request",
+              "refId": "CPU Request"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Total CPU Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "core",
+              "label": "cpu",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {
+            "Total Memory Request": "semi-dark-orange",
+            "Total Memory Usage": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Total memory usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 12,
+            "y": 49
+          },
+          "hiddenSeries": false,
+          "id": 26,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "exemplar": false,
+              "expr": "sum(sum_over_time(container_memory_working_set_bytes{pod=~\"api-$api_name.+\", name!=\"\", container!=\"POD\"}[1m])) /\navg(count_over_time(container_memory_working_set_bytes{pod=~\"api-$api_name.+\", name!=\"\", container=\"api\"}[1m])) / 1024^2",
+              "format": "time_series",
+              "instant": false,
+              "interval": "",
+              "legendFormat": "Total Memory Usage",
+              "refId": "Memory Usage"
+            },
+            {
+              "expr": "sum(kube_pod_container_resource_requests_memory_bytes{exported_pod=~\"api-$api_name.+\"}) / 1024^2",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Total Memory Request",
+              "refId": "Memory Request"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Total Memory Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "MiB",
+              "label": "memory",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {
+            "Total GPU Capacity": "semi-dark-orange",
+            "Total GPU Usage": "semi-dark-green",
+            "Total GPU Utilization": "light-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Total GPU core usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 0,
+            "y": 57
+          },
+          "hiddenSeries": false,
+          "id": 28,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(DCGM_FI_DEV_GPU_UTIL{exported_pod=~\"api-$api_name.+\"}) / 100",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Total GPU Usage",
+              "refId": "GPU Usage"
+            },
+            {
+              "expr": "count(DCGM_FI_DEV_GPU_UTIL{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Total GPU Capacity",
+              "refId": "GPU Capacity"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Total GPU Core Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "gpuCore",
+              "label": "gpu",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {
+            "Total Capacity GPU Memory": "semi-dark-orange",
+            "Total Used GPU Memory": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Total GPU memory usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 12,
+            "y": 57
+          },
+          "hiddenSeries": false,
+          "id": 29,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(DCGM_FI_DEV_FB_USED{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Total Used GPU Memory",
+              "refId": "GPU Used Memory"
+            },
+            {
+              "exemplar": false,
+              "expr": "sum(DCGM_FI_DEV_FB_FREE{exported_pod=~\"api-$api_name.+\"}) + sum(DCGM_FI_DEV_FB_USED{exported_pod=~\"api-$api_name.+\"})",
+              "format": "time_series",
+              "instant": false,
+              "interval": "",
+              "legendFormat": "Total Capacity GPU Memory",
+              "refId": "GPU Capacity Memory"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Total GPU Memory Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "MiB",
+              "label": "memory",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "collapsed": false,
+          "datasource": null,
+          "gridPos": {
+            "h": 1,
+            "w": 24,
+            "x": 0,
+            "y": 65
+          },
+          "id": 18,
+          "panels": [],
+          "title": "Average Replica Usage",
+          "type": "row"
+        },
+        {
+          "aliasColors": {
+            "Avg CPU Request": "semi-dark-orange",
+            "Avg CPU Usage": "semi-dark-green",
+            "Total CPU Request": "semi-dark-orange",
+            "Total CPU Usage": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Avg CPU usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 0,
+            "y": 66
+          },
+          "hiddenSeries": false,
+          "id": 30,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "exemplar": false,
+              "expr": "sum(rate(container_cpu_usage_seconds_total{pod=~\"api-$api_name.+\", container!=\"POD\", name!=\"\"}[1m]))\n/\nsum(kube_pod_info{exported_pod=~\"api-$api_name.+\"})",
+              "format": "time_series",
+              "instant": false,
+              "interval": "",
+              "legendFormat": "Avg CPU Usage",
+              "refId": "CPU Usage"
+            },
+            {
+              "expr": "sum(kube_pod_container_resource_requests_cpu_cores{exported_pod=~\"api-$api_name.+\"})\n/\nsum(kube_pod_info{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Avg CPU Request",
+              "refId": "CPU Request"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Avg CPU Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "core",
+              "label": "cpu",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {
+            "Avg Memory Request": "semi-dark-orange",
+            "Avg Memory Usage": "semi-dark-green",
+            "Total Memory Request": "semi-dark-orange",
+            "Total Memory Usage": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Avg memory usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 12,
+            "y": 66
+          },
+          "hiddenSeries": false,
+          "id": 31,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "exemplar": false,
+              "expr": "sum(sum_over_time(container_memory_working_set_bytes{pod=~\"api-$api_name.+\", name!=\"\", container!=\"POD\"}[1m]))\n/\navg(count_over_time(container_memory_working_set_bytes{pod=~\"api-$api_name.+\", name!=\"\", container=\"api\"}[1m])) / 1024^2\n/\nsum(kube_pod_info{exported_pod=~\"api-$api_name.+\"})",
+              "format": "time_series",
+              "instant": false,
+              "interval": "",
+              "legendFormat": "Avg Memory Usage",
+              "refId": "Memory Usage"
+            },
+            {
+              "expr": "sum(kube_pod_container_resource_requests_memory_bytes{exported_pod=~\"api-$api_name.+\"}) / 1024^2\n/\nsum(kube_pod_info{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Avg Memory Request",
+              "refId": "Memory Request"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Avg Memory Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "MiB",
+              "label": "memory",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {
+            "Avg GPU Capacity": "semi-dark-orange",
+            "Avg GPU Usage": "semi-dark-green",
+            "Total GPU Capacity": "semi-dark-orange",
+            "Total GPU Utilization": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Avg GPU core usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 0,
+            "y": 74
+          },
+          "hiddenSeries": false,
+          "id": 32,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(DCGM_FI_DEV_GPU_UTIL{exported_pod=~\"api-$api_name.+\"}) / 100\n/\ncount(DCGM_FI_DEV_GPU_UTIL{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Avg GPU Usage",
+              "refId": "GPU Usage"
+            },
+            {
+              "expr": "count(DCGM_FI_DEV_GPU_UTIL{exported_pod=~\"api-$api_name.+\"})\n/\ncount(count(DCGM_FI_DEV_GPU_UTIL{exported_pod=~\"api-$api_name.+\"}) by (exported_pod))",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Avg GPU Capacity",
+              "refId": "GPU Capacity"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Avg GPU Core Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "gpuCore",
+              "label": "gpu",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        },
+        {
+          "aliasColors": {
+            "Avg Capacity GPU Memory": "semi-dark-orange",
+            "Avg Used GPU Memory": "semi-dark-green",
+            "Total Capacity GPU Memory": "semi-dark-orange",
+            "Total Used GPU Memory": "semi-dark-green"
+          },
+          "bars": false,
+          "dashLength": 10,
+          "dashes": false,
+          "datasource": null,
+          "description": "Avg GPU memory usage across all replicas of the API",
+          "fieldConfig": {
+            "defaults": {
+              "color": {},
+              "custom": {},
+              "thresholds": {
+                "mode": "absolute",
+                "steps": []
+              }
+            },
+            "overrides": []
+          },
+          "fill": 1,
+          "fillGradient": 0,
+          "gridPos": {
+            "h": 8,
+            "w": 12,
+            "x": 12,
+            "y": 74
+          },
+          "hiddenSeries": false,
+          "id": 33,
+          "legend": {
+            "avg": false,
+            "current": false,
+            "max": false,
+            "min": false,
+            "show": true,
+            "total": false,
+            "values": false
+          },
+          "lines": true,
+          "linewidth": 1,
+          "nullPointMode": "null",
+          "options": {
+            "alertThreshold": true
+          },
+          "percentage": false,
+          "pluginVersion": "7.4.2",
+          "pointradius": 2,
+          "points": false,
+          "renderer": "flot",
+          "seriesOverrides": [],
+          "spaceLength": 10,
+          "stack": false,
+          "steppedLine": false,
+          "targets": [
+            {
+              "expr": "sum(DCGM_FI_DEV_FB_USED{exported_pod=~\"api-$api_name.+\"})\n/\ncount(DCGM_FI_DEV_FB_USED{exported_pod=~\"api-$api_name.+\"})",
+              "hide": false,
+              "interval": "",
+              "legendFormat": "Avg Used GPU Memory",
+              "refId": "GPU Used Memory"
+            },
+            {
+              "exemplar": false,
+              "expr": "(sum(DCGM_FI_DEV_FB_FREE{exported_pod=~\"api-$api_name.+\"}) + sum(DCGM_FI_DEV_FB_USED{exported_pod=~\"api-$api_name.+\"}))\n/\ncount(DCGM_FI_DEV_FB_USED{exported_pod=~\"api-$api_name.+\"})",
+              "format": "time_series",
+              "instant": false,
+              "interval": "",
+              "legendFormat": "Avg Capacity GPU Memory",
+              "refId": "GPU Capacity Memory"
+            }
+          ],
+          "thresholds": [],
+          "timeFrom": null,
+          "timeRegions": [],
+          "timeShift": null,
+          "title": "Avg GPU Memory Usage",
+          "tooltip": {
+            "shared": true,
+            "sort": 0,
+            "value_type": "individual"
+          },
+          "type": "graph",
+          "xaxis": {
+            "buckets": null,
+            "mode": "time",
+            "name": null,
+            "show": true,
+            "values": []
+          },
+          "yaxes": [
+            {
+              "format": "MiB",
+              "label": "memory",
+              "logBase": 1,
+              "max": null,
+              "min": "0",
+              "show": true
+            },
+            {
+              "format": "short",
+              "label": "",
+              "logBase": 1,
+              "max": null,
+              "min": null,
+              "show": false
+            }
+          ],
+          "yaxis": {
+            "align": false,
+            "alignLevel": null
+          }
+        }
+      ],
+      "refresh": "30s",
+      "schemaVersion": 27,
+      "style": "dark",
+      "tags": [],
+      "templating": {
+        "list": [
+          {
+            "allValue": null,
+            "current": {},
+            "datasource": null,
+            "definition": "label_values(cortex_async_queue_length{api_kind=\"AsyncAPI\"}, api_name)",
+            "description": null,
+            "error": null,
+            "hide": 0,
+            "includeAll": false,
+            "label": "API Name",
+            "multi": true,
+            "name": "api_name",
+            "options": [],
+            "query": {
+              "query": "label_values(cortex_async_queue_length{api_kind=\"AsyncAPI\"}, api_name)",
+              "refId": "StandardVariableQuery"
+            },
+            "refresh": 1,
+            "regex": "",
+            "skipUrlSync": false,
+            "sort": 1,
+            "tagValuesQuery": "",
+            "tags": [],
+            "tagsQuery": "",
+            "type": "query",
+            "useTags": false
+          }
+        ]
+      },
+      "time": {
+        "from": "now-1h",
+        "to": "now"
+      },
+      "timepicker": {},
+      "timezone": "",
+      "title": "AsyncAPI",
+      "uid": "asyncapi",
+      "version": 2
+    }
diff --git a/manager/manifests/grafana/grafana.yaml b/manager/manifests/grafana/grafana.yaml
index 6c5b995371..91f10b5a13 100644
--- a/manager/manifests/grafana/grafana.yaml
+++ b/manager/manifests/grafana/grafana.yaml
@@ -132,6 +132,9 @@ spec:
             - mountPath: /grafana-dashboard-definitions/cortex/realtime
               name: grafana-dashboard-realtime
               readOnly: false
+            - mountPath: /grafana-dashboard-definitions/cortex/async
+              name: grafana-dashboard-async
+              readOnly: false
             - mountPath: /grafana-dashboard-definitions/cortex/batch
               name: grafana-dashboard-batch
               readOnly: false
@@ -158,6 +161,9 @@ spec:
         - name: grafana-dashboard-realtime
           configMap:
             name: grafana-dashboard-realtime
+        - name: grafana-dashboard-async
+          configMap:
+            name: grafana-dashboard-async
         - name: grafana-dashboard-batch
           configMap:
             name: grafana-dashboard-batch
diff --git a/manager/manifests/prometheus-monitoring.yaml.j2 b/manager/manifests/prometheus-monitoring.yaml.j2
index cbc8af579d..fa9aefe277 100644
--- a/manager/manifests/prometheus-monitoring.yaml.j2
+++ b/manager/manifests/prometheus-monitoring.yaml.j2
@@ -33,13 +33,11 @@ spec:
   podMonitorSelector:
     matchExpressions:
       - key: "monitoring.cortex.dev"
-        operator: "In"
-        values: [ "istio", "proxy", "statsd-exporter", "dcgm-exporter", "kube-state-metrics" ]
+        operator: "Exists"
   serviceMonitorSelector:
     matchExpressions:
       - key: "monitoring.cortex.dev"
-        operator: "In"
-        values: [ "kubelet-exporter", "node-exporter", "operator" ]
+        operator: "Exists"
   ruleSelector:
     matchLabels:
       prometheus: k8s
@@ -195,6 +193,59 @@ spec:
         - sourceLabels: [ __meta_kubernetes_pod_label_apiKind ]
           action: replace
           targetLabel: api_kind
+        - sourceLabels: [ __meta_kubernetes_pod_label_apiID ]
+          action: replace
+          targetLabel: api_id
+        - sourceLabels: [ __address__, __meta_kubernetes_pod_annotation_prometheus_io_port ]
+          action: replace
+          regex: ([^:]+)(?::\d+)?;(\d+)
+          replacement: $1:$2
+          targetLabel: __address__
+        - action: labeldrop
+          regex: "__meta_kubernetes_pod_label_(.+)"
+        - sourceLabels: [ __meta_kubernetes_namespace ]
+          action: replace
+          targetLabel: namespace
+        - sourceLabels: [ __meta_kubernetes_pod_name ]
+          action: replace
+          targetLabel: pod_name
+
+---
+
+apiVersion: monitoring.coreos.com/v1
+kind: PodMonitor
+metadata:
+  name: async-stats
+  labels:
+    monitoring.cortex.dev: "dequeuer-async"
+spec:
+  selector:
+    matchLabels:
+      apiKind: AsyncAPI
+      cortex.dev/async: api
+    matchExpressions:
+      - { key: prometheus-ignore, operator: DoesNotExist }
+  namespaceSelector:
+    any: true
+  jobLabel: async-stats
+  podMetricsEndpoints:
+    - path: /metrics
+      scheme: http
+      interval: 10s
+      port: admin
+      relabelings:
+        - action: keep
+          sourceLabels: [ __meta_kubernetes_pod_container_name ]
+          regex: "dequeuer"
+        - sourceLabels: [ __meta_kubernetes_pod_label_apiName ]
+          action: replace
+          targetLabel: api_name
+        - sourceLabels: [ __meta_kubernetes_pod_label_apiKind ]
+          action: replace
+          targetLabel: api_kind
+        - sourceLabels: [ __meta_kubernetes_pod_label_apiID ]
+          action: replace
+          targetLabel: api_id
         - sourceLabels: [ __address__, __meta_kubernetes_pod_annotation_prometheus_io_port ]
           action: replace
           regex: ([^:]+)(?::\d+)?;(\d+)
diff --git a/pkg/config/config.go b/pkg/config/config.go
index ea278fa48c..e347a8acac 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -132,6 +132,7 @@ func Init() error {
 			return err
 		}
 		clusterConfig.Bucket = cc.Bucket
+		clusterConfig.ClusterUID = cc.ClusterUID
 	}
 
 	exists, err := AWS.DoesBucketExist(clusterConfig.Bucket)
diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go
index 7bb71a82b8..1d1471fe76 100644
--- a/pkg/consts/consts.go
+++ b/pkg/consts/consts.go
@@ -27,17 +27,16 @@ var (
 	DefaultMaxQueueLength = int64(100)
 	DefaultMaxConcurrency = int64(1)
 
-	DefaultUserPodPortStr   = "8080"
 	DefaultUserPodPortInt32 = int32(8080)
 
 	ProxyListeningPortStr   = "8888"
 	ProxyListeningPortInt32 = int32(8888)
 
+	AdminPortName  = "admin"
 	AdminPortStr   = "15000"
 	AdminPortInt32 = int32(15000)
 
-	StatsDPortStr   = "9125"
-	StatsDPortInt32 = int32(9125)
+	StatsDPortStr = "9125"
 
 	AuthHeader = "X-Cortex-Authorization"
 
diff --git a/pkg/dequeuer/async_handler.go b/pkg/dequeuer/async_handler.go
index dfccdd8c33..de2b4f3719 100644
--- a/pkg/dequeuer/async_handler.go
+++ b/pkg/dequeuer/async_handler.go
@@ -185,15 +185,15 @@ func (h *AsyncMessageHandler) submitRequest(payload *userPayload, requestID stri
 		return nil, ErrorUserContainerNotReachable(err)
 	}
 
+	defer func() {
+		_ = response.Body.Close()
+	}()
+
 	requestEvent := RequestEvent{
 		StatusCode: response.StatusCode,
 		Duration:   time.Since(startTime),
 	}
 
-	defer func() {
-		_ = response.Body.Close()
-	}()
-
 	if response.StatusCode != http.StatusOK {
 		return nil, ErrorUserContainerResponseStatusCode(response.StatusCode)
 	}
diff --git a/pkg/lib/errors/error.go b/pkg/lib/errors/error.go
index fefb50d5f5..ecbbe0872b 100644
--- a/pkg/lib/errors/error.go
+++ b/pkg/lib/errors/error.go
@@ -84,6 +84,10 @@ func Wrap(err error, strs ...string) error {
 	return cortexError
 }
 
+func Wrapf(err error, template string, params ...string) error {
+	return Wrap(err, fmt.Sprintf(template, params))
+}
+
 // adds to the end of the error message (without adding any whitespace or punctuation)
 func Append(err error, str string) error {
 	if err == nil {
diff --git a/pkg/operator/resources/asyncapi/api.go b/pkg/operator/resources/asyncapi/api.go
index bf83c81bb8..8eaf5eb216 100644
--- a/pkg/operator/resources/asyncapi/api.go
+++ b/pkg/operator/resources/asyncapi/api.go
@@ -26,6 +26,7 @@ import (
 	"github.com/cortexlabs/cortex/pkg/lib/errors"
 	"github.com/cortexlabs/cortex/pkg/lib/k8s"
 	"github.com/cortexlabs/cortex/pkg/lib/parallel"
+	"github.com/cortexlabs/cortex/pkg/lib/pointer"
 	autoscalerlib "github.com/cortexlabs/cortex/pkg/operator/lib/autoscaler"
 	"github.com/cortexlabs/cortex/pkg/operator/lib/routines"
 	"github.com/cortexlabs/cortex/pkg/operator/operator"
@@ -42,6 +43,7 @@ import (
 const (
 	_stalledPodTimeout = 15 * time.Minute
 	_tickPeriodMetrics = 10 * time.Second
+	_asyncDashboardUID = "asyncapi"
 )
 
 var (
@@ -201,11 +203,20 @@ func GetAPIByName(deployedResource *operator.DeployedResource) ([]schema.APIResp
 		return nil, err
 	}
 
+	metrics, err := GetMetrics(*api)
+	if err != nil {
+		return nil, err
+	}
+
+	dashboardURL := pointer.String(getDashboardURL(api.Name))
+
 	return []schema.APIResponse{
 		{
-			Spec:     *api,
-			Status:   status,
-			Endpoint: apiEndpoint,
+			Spec:         *api,
+			Status:       status,
+			Endpoint:     apiEndpoint,
+			DashboardURL: dashboardURL,
+			Metrics:      metrics,
 		},
 	}, nil
 }
@@ -222,7 +233,12 @@ func GetAllAPIs(pods []kcore.Pod, deployments []kapps.Deployment) ([]schema.APIR
 		return nil, err
 	}
 
-	realtimeAPIs := make([]schema.APIResponse, len(apis))
+	allMetrics, err := GetMultipleMetrics(apis)
+	if err != nil {
+		return nil, err
+	}
+
+	asyncAPIs := make([]schema.APIResponse, len(apis))
 
 	for i := range apis {
 		api := apis[i]
@@ -231,14 +247,15 @@ func GetAllAPIs(pods []kcore.Pod, deployments []kapps.Deployment) ([]schema.APIR
 			return nil, err
 		}
 
-		realtimeAPIs[i] = schema.APIResponse{
+		asyncAPIs[i] = schema.APIResponse{
 			Spec:     api,
 			Status:   &statuses[i],
 			Endpoint: endpoint,
+			Metrics:  &allMetrics[i],
 		}
 	}
 
-	return realtimeAPIs, nil
+	return asyncAPIs, nil
 }
 
 func UpdateMetricsCron(deployment *kapps.Deployment) error {
@@ -347,7 +364,7 @@ func applyK8sResources(api spec.API, prevK8sResources resources, queueURL string
 	if err != nil {
 		return err
 	}
-	gatewayDeployment := gatewayDeploymentSpec(api, prevK8sResources.gatewayDeployment, queueURL)
+	gatewayDeployment := gatewayDeploymentSpec(api, queueURL)
 	gatewayHPA, err := gatewayHPASpec(api)
 	if err != nil {
 		return err
@@ -507,3 +524,17 @@ func deleteK8sResources(apiName string) error {
 
 	return err
 }
+
+func getDashboardURL(apiName string) string {
+	loadBalancerURL, err := operator.LoadBalancerURL()
+	if err != nil {
+		return ""
+	}
+
+	dashboardURL := fmt.Sprintf(
+		"%s/dashboard/d/%s/asyncapi?orgId=1&refresh=30s&var-api_name=%s",
+		loadBalancerURL, _asyncDashboardUID, apiName,
+	)
+
+	return dashboardURL
+}
diff --git a/pkg/operator/resources/asyncapi/k8s_specs.go b/pkg/operator/resources/asyncapi/k8s_specs.go
index e8a31f1dde..ecdd42e74f 100644
--- a/pkg/operator/resources/asyncapi/k8s_specs.go
+++ b/pkg/operator/resources/asyncapi/k8s_specs.go
@@ -32,7 +32,7 @@ var _terminationGracePeriodSeconds int64 = 60  // seconds
 var _gatewayHPATargetCPUUtilization int32 = 80 // percentage
 var _gatewayHPATargetMemUtilization int32 = 80 // percentage
 
-func gatewayDeploymentSpec(api spec.API, prevDeployment *kapps.Deployment, queueURL string) kapps.Deployment {
+func gatewayDeploymentSpec(api spec.API, queueURL string) kapps.Deployment {
 	volumeMounts := []kcore.VolumeMount{
 		{
 			Name:      "cluster-config",
@@ -228,6 +228,7 @@ func deploymentSpec(api spec.API, prevDeployment *kapps.Deployment, queueURL str
 			Labels: map[string]string{
 				"apiName":          api.Name,
 				"apiKind":          api.Kind.String(),
+				"apiID":            api.ID,
 				"deploymentID":     api.DeploymentID,
 				"podID":            api.PodID,
 				"cortex.dev/api":   "true",
diff --git a/pkg/operator/resources/asyncapi/metrics.go b/pkg/operator/resources/asyncapi/metrics.go
new file mode 100644
index 0000000000..35bcdebd05
--- /dev/null
+++ b/pkg/operator/resources/asyncapi/metrics.go
@@ -0,0 +1,238 @@
+/*
+Copyright 2021 Cortex Labs, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package asyncapi
+
+import (
+	"context"
+	"fmt"
+	"math"
+	"time"
+
+	"github.com/cortexlabs/cortex/pkg/config"
+	"github.com/cortexlabs/cortex/pkg/lib/errors"
+	"github.com/cortexlabs/cortex/pkg/lib/parallel"
+	"github.com/cortexlabs/cortex/pkg/types/metrics"
+	"github.com/cortexlabs/cortex/pkg/types/spec"
+	promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
+	"github.com/prometheus/common/model"
+)
+
+const (
+	_metricsWindowHours    = 336 // 2 weeks
+	_metricsRequestTimeout = 10  // seconds
+)
+
+func GetMultipleMetrics(apis []spec.API) ([]metrics.Metrics, error) {
+	allMetrics := make([]metrics.Metrics, len(apis))
+	fns := make([]func() error, len(apis))
+
+	for i := range apis {
+		localIdx := i
+		api := apis[i]
+		fns[i] = func() error {
+			apiMetrics, err := GetMetrics(api)
+			if err != nil {
+				return err
+			}
+			allMetrics[localIdx] = *apiMetrics
+			return nil
+		}
+	}
+
+	if len(fns) > 0 {
+		err := parallel.RunFirstErr(fns[0], fns[1:]...)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return allMetrics, nil
+}
+
+func GetMetrics(api spec.API) (*metrics.Metrics, error) {
+	var (
+		reqCount       float64
+		avgLatency     *float64
+		statusCodes2XX float64
+		statusCodes4XX float64
+		statusCodes5XX float64
+	)
+
+	err := parallel.RunFirstErr(
+		func() error {
+			var err error
+			reqCount, err = getRequestCountMetric(config.Prometheus, api)
+			return err
+		},
+		func() error {
+			var err error
+			avgLatency, err = getAvgLatencyMetric(config.Prometheus, api)
+			return err
+		},
+		func() error {
+			var err error
+			statusCodes2XX, err = getStatusCode2XXMetric(config.Prometheus, api)
+			return err
+		},
+		func() error {
+			var err error
+			statusCodes4XX, err = getStatusCode4XXMetric(config.Prometheus, api)
+			return err
+		},
+		func() error {
+			var err error
+			statusCodes5XX, err = getStatusCode5XXMetric(config.Prometheus, api)
+			return err
+		},
+	)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &metrics.Metrics{
+		APIName: api.Name,
+		NetworkStats: &metrics.NetworkStats{
+			Latency: avgLatency,
+			Code2XX: int(statusCodes2XX),
+			Code4XX: int(statusCodes4XX),
+			Code5XX: int(statusCodes5XX),
+			Total:   int(reqCount),
+		},
+	}, nil
+}
+
+func getRequestCountMetric(promAPIv1 promv1.API, apiSpec spec.API) (float64, error) {
+	query := fmt.Sprintf(
+		"cortex_async_request_count{api_name=\"%s\", api_id=\"%s\"} > 0",
+		apiSpec.Name, apiSpec.ID,
+	)
+
+	values, err := queryPrometheusVec(promAPIv1, query)
+	if err != nil {
+		return 0, err
+	}
+
+	if values.Len() == 0 {
+		return 0, nil
+	}
+
+	requestCount := float64(values[0].Value)
+	return requestCount, nil
+}
+
+func getAvgLatencyMetric(promAPIv1 promv1.API, apiSpec spec.API) (*float64, error) {
+	query := fmt.Sprintf(
+		"sum(rate(cortex_async_latency_sum{api_kind=\"AsyncAPI\",api_name=\"%s\", api_id=\"%s\"}[%dh])) by (api_name) "+
+			"/ sum(rate(cortex_async_latency_count{api_kind=\"AsyncAPI\",api_name=\"%s\", api_id=\"%s\"}[%dh])) by (api_name)",
+		apiSpec.Name, apiSpec.ID, _metricsWindowHours,
+		apiSpec.Name, apiSpec.ID, _metricsWindowHours,
+	)
+
+	values, err := queryPrometheusVec(promAPIv1, query)
+	if err != nil {
+		return nil, err
+	}
+
+	if values.Len() == 0 {
+		return nil, nil
+	}
+
+	avgLatency := float64(values[0].Value)
+
+	if math.IsNaN(avgLatency) {
+		return nil, nil
+	}
+
+	avgLatency = avgLatency * 1000 // seconds to milliseconds
+
+	return &avgLatency, nil
+}
+
+func getStatusCode2XXMetric(promAPIv1 promv1.API, apiSpec spec.API) (float64, error) {
+	query := fmt.Sprintf(
+		"cortex_async_request_count{api_kind=\"AsyncAPI\", api_name=\"%s\", api_id=\"%s\", status_code=~\"2.+\"} > 0",
+		apiSpec.Name, apiSpec.ID,
+	)
+
+	values, err := queryPrometheusVec(promAPIv1, query)
+	if err != nil {
+		return 0, err
+	}
+
+	if values.Len() == 0 {
+		return 0, nil
+	}
+
+	statusCodes2XX := float64(values[0].Value)
+	return statusCodes2XX, nil
+}
+
+func getStatusCode4XXMetric(promAPIv1 promv1.API, apiSpec spec.API) (float64, error) {
+	query := fmt.Sprintf(
+		"cortex_async_request_count{api_kind=\"AsyncAPI\", api_name=\"%s\", api_id=\"%s\", status_code=~\"4.+\"} > 0",
+		apiSpec.Name, apiSpec.ID,
+	)
+
+	values, err := queryPrometheusVec(promAPIv1, query)
+	if err != nil {
+		return 0, err
+	}
+
+	if values.Len() == 0 {
+		return 0, nil
+	}
+
+	statusCodes4XX := float64(values[0].Value)
+	return statusCodes4XX, nil
+}
+
+func getStatusCode5XXMetric(promAPIv1 promv1.API, apiSpec spec.API) (float64, error) {
+	query := fmt.Sprintf(
+		"cortex_async_request_count{api_kind=\"AsyncAPI\", api_name=\"%s\", api_id=\"%s\", status_code=~\"5.+\"} > 0",
+		apiSpec.Name, apiSpec.ID,
+	)
+
+	values, err := queryPrometheusVec(promAPIv1, query)
+	if err != nil {
+		return 0, err
+	}
+
+	if values.Len() == 0 {
+		return 0, nil
+	}
+
+	statusCodes5XX := float64(values[0].Value)
+	return statusCodes5XX, nil
+}
+
+func queryPrometheusVec(promAPIv1 promv1.API, query string) (model.Vector, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), _metricsRequestTimeout*time.Second)
+	defer cancel()
+
+	valuesQuery, _, err := promAPIv1.Query(ctx, query, time.Now())
+	if err != nil {
+		return nil, err
+	}
+
+	values, ok := valuesQuery.(model.Vector)
+	if !ok {
+		return nil, errors.ErrorUnexpected("failed to convert metric to vector")
+	}
+
+	return values, nil
+}
diff --git a/pkg/probe/probe.go b/pkg/probe/probe.go
index 5a8d2d80e8..bb47d55dfc 100644
--- a/pkg/probe/probe.go
+++ b/pkg/probe/probe.go
@@ -158,12 +158,15 @@ func AreProbesHealthy(probes []*Probe) bool {
 
 func (p *Probe) probeContainer() bool {
 	var err error
+	var probeType string
 
 	switch {
 	case p.HTTPGet != nil:
 		err = p.httpProbe()
+		probeType = "http"
 	case p.TCPSocket != nil:
 		err = p.tcpProbe()
+		probeType = "tcp"
 	case p.Exec != nil:
 		// Should never be reachable.
 		p.logger.Error("exec probe not supported")
@@ -174,7 +177,7 @@ func (p *Probe) probeContainer() bool {
 	}
 
 	if err != nil {
-		p.logger.Warn(errors.Wrap(err, "probe to user provided containers failed"))
+		p.logger.Warn(errors.Wrapf(err, "%s probe to user container failed", probeType))
 		return false
 	}
 	return true
diff --git a/pkg/workloads/k8s.go b/pkg/workloads/k8s.go
index 62a2329410..e75ca21cf3 100644
--- a/pkg/workloads/k8s.go
+++ b/pkg/workloads/k8s.go
@@ -65,9 +65,6 @@ const (
 )
 
 var (
-	_requestMonitorCPURequest = kresource.MustParse("10m")
-	_requestMonitorMemRequest = kresource.MustParse("10Mi")
-
 	_asyncGatewayCPURequest = kresource.MustParse("100m")
 	_asyncGatewayMemRequest = kresource.MustParse("100Mi")
 
@@ -143,6 +140,12 @@ func asyncDequeuerProxyContainer(api spec.API, queueURL string) (kcore.Container
 				},
 			},
 		}),
+		Ports: []kcore.ContainerPort{
+			{
+				Name:          consts.AdminPortName,
+				ContainerPort: consts.AdminPortInt32,
+			},
+		},
 		ReadinessProbe: &kcore.Probe{
 			Handler: kcore.Handler{
 				HTTPGet: &kcore.HTTPGetAction{
@@ -230,7 +233,7 @@ func realtimeProxyContainer(api spec.API) (kcore.Container, kcore.Volume) {
 			s.Int32(int32(api.Pod.MaxQueueLength)),
 		},
 		Ports: []kcore.ContainerPort{
-			{Name: "admin", ContainerPort: consts.AdminPortInt32},
+			{Name: consts.AdminPortName, ContainerPort: consts.AdminPortInt32},
 			{ContainerPort: consts.ProxyListeningPortInt32},
 		},
 		Env:     baseEnvVars,


Test patch:
diff --git a/test/apis/async/hello-world/build-cpu.sh b/test/apis/async/hello-world/build-cpu.sh
index 259930bcaa..680801e352 100755
--- a/test/apis/async/hello-world/build-cpu.sh
+++ b/test/apis/async/hello-world/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/async/text-generator/build-cpu.sh b/test/apis/async/text-generator/build-cpu.sh
index 2f7c3dbb35..b28a838813 100755
--- a/test/apis/async/text-generator/build-cpu.sh
+++ b/test/apis/async/text-generator/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/async/text-generator/build-gpu.sh b/test/apis/async/text-generator/build-gpu.sh
index 4048a3217f..c5a28e7b32 100755
--- a/test/apis/async/text-generator/build-gpu.sh
+++ b/test/apis/async/text-generator/build-gpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-gpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/batch/image-classifier-alexnet/build-cpu.sh b/test/apis/batch/image-classifier-alexnet/build-cpu.sh
index feb70a3d9c..c4c0eda6eb 100755
--- a/test/apis/batch/image-classifier-alexnet/build-cpu.sh
+++ b/test/apis/batch/image-classifier-alexnet/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/batch/image-classifier-alexnet/build-gpu.sh b/test/apis/batch/image-classifier-alexnet/build-gpu.sh
index a4ef824dbb..2569028516 100755
--- a/test/apis/batch/image-classifier-alexnet/build-gpu.sh
+++ b/test/apis/batch/image-classifier-alexnet/build-gpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-gpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/batch/sum/build-cpu.sh b/test/apis/batch/sum/build-cpu.sh
index 6dbeb797d2..a69542fc74 100755
--- a/test/apis/batch/sum/build-cpu.sh
+++ b/test/apis/batch/sum/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/hello-world/build-cpu.sh b/test/apis/realtime/hello-world/build-cpu.sh
index d85bf31a36..62909429e1 100755
--- a/test/apis/realtime/hello-world/build-cpu.sh
+++ b/test/apis/realtime/hello-world/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/image-classifier-resnet50/build-cpu.sh b/test/apis/realtime/image-classifier-resnet50/build-cpu.sh
index d6ccd20732..8d7c9bee58 100755
--- a/test/apis/realtime/image-classifier-resnet50/build-cpu.sh
+++ b/test/apis/realtime/image-classifier-resnet50/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/image-classifier-resnet50/build-gpu.sh b/test/apis/realtime/image-classifier-resnet50/build-gpu.sh
index 3c45cf67d0..292b2ac4b3 100755
--- a/test/apis/realtime/image-classifier-resnet50/build-gpu.sh
+++ b/test/apis/realtime/image-classifier-resnet50/build-gpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-gpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/prime-generator/build-cpu.sh b/test/apis/realtime/prime-generator/build-cpu.sh
index 08fa5bcafc..648d69c76f 100755
--- a/test/apis/realtime/prime-generator/build-cpu.sh
+++ b/test/apis/realtime/prime-generator/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/sleep/build-cpu.sh b/test/apis/realtime/sleep/build-cpu.sh
index f6e0431d08..fc7641c05b 100755
--- a/test/apis/realtime/sleep/build-cpu.sh
+++ b/test/apis/realtime/sleep/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/text-generator/build-cpu.sh b/test/apis/realtime/text-generator/build-cpu.sh
index c6cd41e8a0..c6f8d3d9b9 100755
--- a/test/apis/realtime/text-generator/build-cpu.sh
+++ b/test/apis/realtime/text-generator/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/realtime/text-generator/build-gpu.sh b/test/apis/realtime/text-generator/build-gpu.sh
index 1dcd765695..3255fb650b 100755
--- a/test/apis/realtime/text-generator/build-gpu.sh
+++ b/test/apis/realtime/text-generator/build-gpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-gpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test
 
diff --git a/test/apis/task/iris-classifier-trainer/build-cpu.sh b/test/apis/task/iris-classifier-trainer/build-cpu.sh
index 3690ec24fe..a2248ee38c 100755
--- a/test/apis/task/iris-classifier-trainer/build-cpu.sh
+++ b/test/apis/task/iris-classifier-trainer/build-cpu.sh
@@ -1,3 +1,5 @@
+#!/usr/bin/env bash
+
 # usage: build-cpu.sh [REGISTRY] [--skip-push]
 #   REGISTRY defaults to $CORTEX_DEV_DEFAULT_IMAGE_REGISTRY; e.g. 764403040460.dkr.ecr.us-west-2.amazonaws.com/cortexlabs or quay.io/cortexlabs-test