<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">"use strict";(self.webpackChunkratify=self.webpackChunkratify||[]).push([[6288],{3905:(e,t,r)=&gt;{r.d(t,{Zo:()=&gt;m,kt:()=&gt;h});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&amp;&amp;(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t&lt;arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?i(Object(r),!0).forEach((function(t){a(e,t,r[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):i(Object(r)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))}))}return e}function l(e,t){if(null==e)return{};var r,n,a=function(e,t){if(null==e)return{};var r,n,a={},i=Object.keys(e);for(n=0;n&lt;i.length;n++)r=i[n],t.indexOf(r)&gt;=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n&lt;i.length;n++)r=i[n],t.indexOf(r)&gt;=0||Object.prototype.propertyIsEnumerable.call(e,r)&amp;&amp;(a[r]=e[r])}return a}var s=n.createContext({}),p=function(e){var t=n.useContext(s),r=t;return e&amp;&amp;(r="function"==typeof e?e(t):o(o({},t),e)),r},m=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},c=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,i=e.originalType,s=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),d=p(r),c=a,h=d["".concat(s,".").concat(c)]||d[c]||u[c]||i;return r?n.createElement(h,o(o({ref:t},m),{},{components:r})):n.createElement(h,o({ref:t},m))}));function h(e,t){var r=arguments,a=t&amp;&amp;t.mdxType;if("string"==typeof e||a){var i=r.length,o=new Array(i);o[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&amp;&amp;(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:a,o[1]=l;for(var p=2;p&lt;i;p++)o[p]=r[p];return n.createElement.apply(null,o)}return n.createElement.apply(null,r)}c.displayName="MDXCreateElement"},5982:(e,t,r)=&gt;{r.r(t),r.d(t,{assets:()=&gt;s,contentTitle:()=&gt;o,default:()=&gt;u,frontMatter:()=&gt;i,metadata:()=&gt;l,toc:()=&gt;p});var n=r(7462),a=(r(7294),r(3905));const i={},o="Instrumentation",l={unversionedId:"reference/instrumentation",id:"version-1.0/reference/instrumentation",title:"Instrumentation",description:"This page outlines current instrumentation support in Ratify. It also contains guides for installing metrics providers and supported dashboards.",source:"@site/versioned_docs/version-1.0/reference/instrumentation.md",sourceDirName:"reference",slug:"/reference/instrumentation",permalink:"/docs/1.0/reference/instrumentation",draft:!1,editUrl:"https://github.com/ratify-project/ratify-web/blob/main/versioned_docs/version-1.0/reference/instrumentation.md",tags:[],version:"1.0",frontMatter:{},sidebar:"tutorialSidebar",previous:{title:"Executor",permalink:"/docs/1.0/reference/executor"},next:{title:"Logger",permalink:"/docs/1.0/reference/logger"}},s={},p=[{value:"Metrics Supported",id:"metrics-supported",level:2},{value:"Azure Metrics",id:"azure-metrics",level:3},{value:"Metrics Providers Supported",id:"metrics-providers-supported",level:2},{value:"Prometheus",id:"prometheus",level:3},{value:"Prometheus and Grafana Setup",id:"prometheus-and-grafana-setup",level:3},{value:"Adding a new metric provider",id:"adding-a-new-metric-provider",level:3}],m={toc:p},d="wrapper";function u(e){let{components:t,...r}=e;return(0,a.kt)(d,(0,n.Z)({},m,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("h1",{id:"instrumentation"},"Instrumentation"),(0,a.kt)("p",null,"This page outlines current instrumentation support in Ratify. It also contains guides for installing metrics providers and supported dashboards."),(0,a.kt)("h2",{id:"metrics-supported"},"Metrics Supported"),(0,a.kt)("p",null,"Metrics Types:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},"Counter: Value that accumulates over time. (e.g request count, signatures verified)"),(0,a.kt)("li",{parentName:"ul"},"Gauge: Point-in-time value of a continuous data stream (e.g file system size, speed, pressure)"),(0,a.kt)("li",{parentName:"ul"},"Histogram: Aggregation of counters where each bin is bounded from the min value (0) to the upper bin boundary. For example if we had bin boundaries ","[0, 1, 2, 3, 4, 5]"," and the measured value is 3.5, then the resulting histogram would be ","[0, 0, 0, 1, 1]",".")),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:"center"},"Name"),(0,a.kt)("th",{parentName:"tr",align:null},"Type"),(0,a.kt)("th",{parentName:"tr",align:"center"},"Unit"),(0,a.kt)("th",{parentName:"tr",align:null},"Attributes"),(0,a.kt)("th",{parentName:"tr",align:"center"},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},"ratify_verification_request"),(0,a.kt)("td",{parentName:"tr",align:null},"Histogram"),(0,a.kt)("td",{parentName:"tr",align:"center"},"milliseconds"),(0,a.kt)("td",{parentName:"tr",align:null},"N/A"),(0,a.kt)("td",{parentName:"tr",align:"center"},"Duration of a single request to the ",(0,a.kt)("inlineCode",{parentName:"td"},"/verify")," endpoint. Histogram bins:   ",(0,a.kt)("inlineCode",{parentName:"td"},"[0, 10, 30, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800, 2000, 2300, 2600, 4000, 4400, 4900]"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},"ratify_mutation_request"),(0,a.kt)("td",{parentName:"tr",align:null},"Histogram"),(0,a.kt)("td",{parentName:"tr",align:"center"},"milliseconds"),(0,a.kt)("td",{parentName:"tr",align:null},"N/A"),(0,a.kt)("td",{parentName:"tr",align:"center"},"Duration of a single request to the ",(0,a.kt)("inlineCode",{parentName:"td"},"/mutate")," endpoint. Histogram bins: ",(0,a.kt)("inlineCode",{parentName:"td"},"[0, 10, 30, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1400, 1600, 1800]"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},"ratify_verifier_duration"),(0,a.kt)("td",{parentName:"tr",align:null},"Histogram"),(0,a.kt)("td",{parentName:"tr",align:"center"},"milliseconds"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"verifier"),": name of the verifier ",(0,a.kt)("br",null)," ",(0,a.kt)("inlineCode",{parentName:"td"},"subject"),": full subject reference ",(0,a.kt)("br",null)," ",(0,a.kt)("inlineCode",{parentName:"td"},"success"),": verifier result ",(0,a.kt)("br",null)," ",(0,a.kt)("inlineCode",{parentName:"td"},"error"),": if operation returned error"),(0,a.kt)("td",{parentName:"tr",align:"center"},"Duration of a single verifier's execution for a single referrer artifact. Histogram bins: ",(0,a.kt)("inlineCode",{parentName:"td"},"[0, 10, 50, 100, 200, 300, 400, 600, 800, 1100, 1500, 2000]"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},"ratify_system_error_count"),(0,a.kt)("td",{parentName:"tr",align:null},"Counter"),(0,a.kt)("td",{parentName:"tr",align:"center"},"N/A"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"error"),": error message"),(0,a.kt)("td",{parentName:"tr",align:"center"},"Count of errors emitted   by http handlers")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},"ratify_registry_request_count"),(0,a.kt)("td",{parentName:"tr",align:null},"Counter"),(0,a.kt)("td",{parentName:"tr",align:"center"},"N/A"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"status_code"),": registry request status code ",(0,a.kt)("br",null)," ",(0,a.kt)("inlineCode",{parentName:"td"},"registry_host"),": registry host name"),(0,a.kt)("td",{parentName:"tr",align:"center"},"Count of requests made to registry")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},"ratify_blob_cache_count"),(0,a.kt)("td",{parentName:"tr",align:null},"Counter"),(0,a.kt)("td",{parentName:"tr",align:"center"},"N/A"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"hit"),": boolean cache hit"),(0,a.kt)("td",{parentName:"tr",align:"center"},"Count of ORAS blob cache hit/miss")))),(0,a.kt)("h3",{id:"azure-metrics"},"Azure Metrics"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Name"),(0,a.kt)("th",{parentName:"tr",align:null},"Type"),(0,a.kt)("th",{parentName:"tr",align:null},"Unit"),(0,a.kt)("th",{parentName:"tr",align:null},"Attributes"),(0,a.kt)("th",{parentName:"tr",align:null},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"ratify_aad_exchange_duration"),(0,a.kt)("td",{parentName:"tr",align:null},"Histogram"),(0,a.kt)("td",{parentName:"tr",align:null},"milliseconds"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"resource_type"),": resource scope (ACR vs AKV)"),(0,a.kt)("td",{parentName:"tr",align:null},"Duration of federated JWT exchange for AAD resource scope token")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"ratify_acr_exchange_duration"),(0,a.kt)("td",{parentName:"tr",align:null},"Histogram"),(0,a.kt)("td",{parentName:"tr",align:null},"milliseconds"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"repository"),": full ACR repository name for token exchange scope"),(0,a.kt)("td",{parentName:"tr",align:null},"Duration of  exchange of AAD token for ACR refresh token")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},"ratify_akv_certificate_duration"),(0,a.kt)("td",{parentName:"tr",align:null},"Histogram"),(0,a.kt)("td",{parentName:"tr",align:null},"milliseconds"),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"certificate_name"),": name of AKV certificate object"),(0,a.kt)("td",{parentName:"tr",align:null},"Duration of AKV certificate fetch operation")))),(0,a.kt)("h2",{id:"metrics-providers-supported"},"Metrics Providers Supported"),(0,a.kt)("h3",{id:"prometheus"},"Prometheus"),(0,a.kt)("p",null,'Prometheus is a very popular metrics, monitoring, and database tool. It is time series based and is widely used in K8s. Prometheus metrics collection is pull-based. A prometheus instance is installed in the cluster. This instance is responsible for periodically "scraping" the metrics from the ',(0,a.kt)("inlineCode",{parentName:"p"},"/metrics")," endpoint for each resource. Ratify's prometheus metrics provider implementation  creates a new http server which binds the ",(0,a.kt)("inlineCode",{parentName:"p"},"/metrics")," endpoint to a prometheus handler. Once the OpenTelemetry Prometheus exporter is created, the handler takes care of publishing the correct Prometheus-formatted metrics. "),(0,a.kt)("p",null,"Scraping is achieved via annotations on the resources that expose metrics on the ",(0,a.kt)("inlineCode",{parentName:"p"},"/metrics")," endpoint. There are two parts to this:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"How does prometheus know which resources to consider and what annotations to look for?\nThe prometheus config is responsible for this. The configuration contains instructions on which annotations to look for to determine if a resource is eligible for scraping and which resources it should check. Depending on the prometheus configuration that exists on the cluster, users may need to append an additional scrape configuration (instruction below on how to do this).\nFor ratify, the metrics will be published by the Ratify pod. "),(0,a.kt)("li",{parentName:"ol"},"How do we tell Prometheus to look for Ratify metrics specifically from the ",(0,a.kt)("inlineCode",{parentName:"li"},"/metrics")," endpoint on the Ratify pod? The Ratify helm chart contains annotations to the on Deployment spec to indicate scraping and which port the metrics server is running on the pod:",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"annotations:\n    prometheus.io/scrape: 'true'\n    prometheus.io/port: '8888'\n")))),(0,a.kt)("h3",{id:"prometheus-and-grafana-setup"},"Prometheus and Grafana Setup"),(0,a.kt)("p",null,"This quick start provides instructions on installing and using Prometheus with Grafana to visualize the current supported metrics. This guide assumes neither is installed on the K8s cluster. (Warning: This guide is purely informational and should not be considered a production-grade solution for installing Prometheus and Grafana on cluster. Please consult the corresponding project guidelines for more scenario-specific information)"),(0,a.kt)("p",null,"Prior to installing Ratify on the cluster:"),(0,a.kt)("ol",null,(0,a.kt)("li",{parentName:"ol"},"Create a new namespace, if not yet created, for all instrumentation",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"kubectl create namespace monitoring\n"))),(0,a.kt)("li",{parentName:"ol"},"Apply an additional scrap configuration to Prometheus via a secret:",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"kubectl apply -f instrumentation/additional-scrape-configs.yaml -n monitoring\n")),(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"Note: if the ",(0,a.kt)("a",{parentName:"li",href:"https://github.com/ratify-project/ratify/blob/main/instrumentation/prometheus-additional.yaml"},"scrape config")," stored is updated and the secret needs to be regenerated. Run this command prior:",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"kubectl create secret generic additional-scrape-configs --from-file=instrumentation/prometheus-additional.yaml --dry-run=client -oyaml &gt; instrumentation/additional-scrape-configs.yaml\n"))))),(0,a.kt)("li",{parentName:"ol"},"Add the prometheus-community helm repository",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"helm repo add prometheus-community https://prometheus-community.github.io/helm-charts\nhelm repo update\n"))),(0,a.kt)("li",{parentName:"ol"},"Install the ",(0,a.kt)("inlineCode",{parentName:"li"},"prometheus-community/kube-prometheus-stack")," helm chart. This will install the standard kubernetes metrics including Grafana. It will also expose Prometheus and Grafana on an external IP and load a Ratify specific dashboard.",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --atomic\\\n    --set prometheus.prometheusSpec.additionalScrapeConfigsSecret.enabled=true \\\n    --set prometheus.prometheusSpec.additionalScrapeConfigsSecret.name=additional-scrape-configs \\\n    --set prometheus.prometheusSpec.additionalScrapeConfigsSecret.key=prometheus-additional.yaml \\\n    --set prometheus.service.type=LoadBalancer \\\n    --set grafana.service.type=LoadBalancer \\\n    --set grafana.sidecar.dashboards.enabled=true\n"))),(0,a.kt)("li",{parentName:"ol"},"Apply ConfigMap with Ratify Dashboard to monitoring namespace",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"kubectl apply -f instrumentation/grafana_configMap.yaml -n monitoring\n"))),(0,a.kt)("li",{parentName:"ol"},"Find the Grafana service external IP",(0,a.kt)("pre",{parentName:"li"},(0,a.kt)("code",{parentName:"pre"},"kubectl get svc prometheus-grafana -n monitoring -o jsonpath='{.status.loadBalancer.ingress[0].ip}'\n"))),(0,a.kt)("li",{parentName:"ol"},"Navigate to Grafana instance by using external IP and port 80 in browser"),(0,a.kt)("li",{parentName:"ol"},"Login using default credentials",(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"username: admin"),(0,a.kt)("li",{parentName:"ul"},"password: prom-operator"))),(0,a.kt)("li",{parentName:"ol"},'View Ratify dashboard under "General/Ratify"',(0,a.kt)("ul",{parentName:"li"},(0,a.kt)("li",{parentName:"ul"},"Note: Metrics will not appear until after Ratify has been installed and an external data request is processed")))),(0,a.kt)("h3",{id:"adding-a-new-metric-provider"},"Adding a new metric provider"),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Note: There is only support for a single metric exporter at a time")),(0,a.kt)("p",null,"Additional metrics exporters can be added as metrics backends.\nThe exporter must be initialized in the ",(0,a.kt)("inlineCode",{parentName:"p"},"InitMetricsExporter")," method as a new exporter type. Each exporter type is determined from the ",(0,a.kt)("inlineCode",{parentName:"p"},"metricsBackend")," parameter. During metric exporter initialization a new metric reader must be instantiated and assigned to the static global variable ",(0,a.kt)("inlineCode",{parentName:"p"},"MetricsReader"),". Once registered, the global ",(0,a.kt)("inlineCode",{parentName:"p"},"MetricsReader")," is used to initialize OpenTelemetry meter and instruments."))}u.isMDXComponent=!0}}]);</pre></body></html>