<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Apache SkyWalking – Engineering</title>
    <link>/tags/engineering/</link>
    <description>Recent content in Engineering on Apache SkyWalking</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Mon, 29 Jun 2026 00:00:00 +0000</lastBuildDate>
    
	  <atom:link href="/tags/engineering/feed.xml" rel="self" type="application/rss+xml" />
    
    
      
        
      
    
    
    <item>
      <title>Blog: Meet Horizon UI · 11/17: Runtime Rules &amp; Live Debugging</title>
      <link>/blog/2026-06-29-horizon-ui-runtime-rules-and-live-debugging/</link>
      <pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/blog/2026-06-29-horizon-ui-runtime-rules-and-live-debugging/</guid>
      <description>
        
        
        &lt;p&gt;This is the eleventh post in the &lt;a href=&#34;/blog/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; series, and it stays in &lt;strong&gt;Act 3 — operate it&lt;/strong&gt;. The &lt;a href=&#34;/blog/2026-06-29-horizon-ui-alarms-and-incident-triage/&#34;&gt;previous post&lt;/a&gt; was about reading what the backend already decided; this one is about changing &lt;em&gt;how&lt;/em&gt; it decides — and then proving the change does what you meant.&lt;/p&gt;
&lt;p&gt;Almost everything OAP computes runs through a small family of DSLs: &lt;strong&gt;OAL&lt;/strong&gt; turns traces into service and endpoint metrics, &lt;strong&gt;MAL&lt;/strong&gt; turns meters (OpenTelemetry, Telegraf) into metrics, &lt;strong&gt;LAL&lt;/strong&gt; turns logs into tags and metrics. Traditionally you edit those as YAML on the server and restart. Horizon brings both halves into the console — &lt;strong&gt;edit and hot-apply the rules&lt;/strong&gt;, and &lt;strong&gt;debug them against live data&lt;/strong&gt; — two capabilities new to the SkyWalking UI that ride OAP&amp;rsquo;s admin host.&lt;/p&gt;
&lt;h2 id=&#34;your-rules-live-in-the-console&#34;&gt;Your rules, live in the console&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Operate → DSL management&lt;/strong&gt; lists every analysis rule the cluster is running, grouped by source. Four catalogs are editable — &lt;strong&gt;MAL · OTel&lt;/strong&gt;, &lt;strong&gt;MAL · Telegraf&lt;/strong&gt;, &lt;strong&gt;LAL&lt;/strong&gt;, and &lt;strong&gt;LAL → MAL&lt;/strong&gt; (log-to-metric) — plus a read-only &lt;strong&gt;OAL&lt;/strong&gt; browser. Rules group by prefix (ActiveMQ, BanyanDB, Elasticsearch, Flink…), each tagged by status, and you can filter by &lt;strong&gt;active / inactive / bundled / modified&lt;/strong&gt; to see at a glance what an operator has changed versus what shipped.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-01-rule-catalog.webp&#34; alt=&#34;Figure 1: The DSL management catalog — analysis rules grouped by source, each with a status chip, and status-filter facets across the top.&#34;&gt;
Figure 1: DSL management — every OAL/MAL/LAL rule the cluster runs, grouped by source and filterable by status (active / inactive / bundled / modified). Here, the OpenTelemetry MAL catalog: 37 bundled rules.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;edit-and-hot-apply-safely&#34;&gt;Edit, and hot-apply safely&lt;/h2&gt;
&lt;p&gt;Open a rule and it&amp;rsquo;s a Monaco YAML editor with syntax highlighting and two diff modes — &lt;strong&gt;vs. server&lt;/strong&gt; (what&amp;rsquo;s live) and &lt;strong&gt;vs. bundled&lt;/strong&gt; (what shipped) — so you always see what you&amp;rsquo;re about to change. The green ▶ in the gutter beside each &lt;code&gt;- name:&lt;/code&gt; jumps that rule straight into the Live Debugger.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-02-rule-editor.webp&#34; alt=&#34;Figure 2: A MAL rule open in the Monaco YAML editor, with edit / diff-vs-server / diff-vs-bundled tabs and green play glyphs in the gutter.&#34;&gt;
Figure 2: Edit a rule as Monaco YAML — syntax-highlighted, with diffs against the live (server) and bundled versions, and a green ▶ in the gutter that jumps the rule into the Live Debugger.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Saving is where the care shows. A body- or filter-only edit applies instantly. But a &lt;strong&gt;structural&lt;/strong&gt; change — one that moves a metric&amp;rsquo;s scope, downsampling, or storage shape — reshapes the cluster&amp;rsquo;s storage, so Horizon runs it as a fenced rollout and tracks it on screen: &lt;strong&gt;Compiled → Confirming across the cluster → Committing → Done&lt;/strong&gt;, reporting success only once the change is durable. If a node lags the fence, the apply ends &lt;strong&gt;DEGRADED&lt;/strong&gt; — it names the laggard nodes (they self-converge on their next scan) rather than failing; a pre-commit error is &lt;strong&gt;rolled back&lt;/strong&gt; with the reason inline and your edit kept in the buffer; a compile error surfaces as an inline diagnostic. A one-click &lt;strong&gt;Force re-apply&lt;/strong&gt; re-runs a stuck rollout on byte-identical content to un-stick a node (it briefly pauses that one rule&amp;rsquo;s collection). Reverting a rule to its bundled default goes through the same fenced path; you can also inactivate it, delete it, or dump the whole catalog to a tarball.&lt;/p&gt;
&lt;h2 id=&#34;the-live-debugger-see-what-a-rule-actually-does&#34;&gt;The Live Debugger: see what a rule actually does&lt;/h2&gt;
&lt;p&gt;Editing a rule is the easy part. The hard part — the part that used to mean reading code and squinting at output — is knowing what a rule &lt;em&gt;computes&lt;/em&gt; against your real data. &lt;strong&gt;Operate → Live debugger&lt;/strong&gt; answers that directly: pick a rule, click &lt;strong&gt;start sampling&lt;/strong&gt;, and Horizon installs a bounded capture on &lt;strong&gt;every reachable OAP node&lt;/strong&gt;, grabs a handful of real records, and shows each one stepped through the rule.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-03-livedebug-session.webp&#34; alt=&#34;Figure 3: The Live Debugger MAL tab capturing — rule and metric pickers, record-cap and retention controls, and a node-coverage strip reading installed 2 of 2.&#34;&gt;
Figure 3: Start a capture and it installs on every reachable OAP node (here 2/2), grabs real records, and bounds itself with a record cap and a retention window — the same shell serves all three analysis languages.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;It has one tab per analysis language, because each works on a different kind of data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OAL → traces.&lt;/strong&gt; A captured source row — a real trace segment — flows clause by clause: &lt;code&gt;from(Service.*)&lt;/code&gt; reads the segment (you see its latency, status, endpoint), &lt;code&gt;build_metrics&lt;/code&gt; shapes it, &lt;code&gt;cpm()&lt;/code&gt; aggregates it. You watch a trace become a metric.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-04-oal-trace.webp&#34; alt=&#34;Figure 4: The OAL tab showing a trace segment stepped through input, function, and aggregation stages with its full payload.&#34;&gt;
Figure 4: OAL → traces — a real segment from &lt;code&gt;agent::gateway&lt;/code&gt; (latency 38, status 200, &lt;code&gt;/rcmd&lt;/code&gt;) stepped clause by clause, &lt;code&gt;from(Service.*)&lt;/code&gt; → &lt;code&gt;build_metrics&lt;/code&gt; → &lt;code&gt;cpm()&lt;/code&gt;, into the service-CPM metric it feeds.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MAL → metrics.&lt;/strong&gt; A meter sample flows input → filter → function → output. Because one metric fans out into many label-sets, the samples are grouped by metric, and a &lt;strong&gt;diff&lt;/strong&gt; dims the labels every sample shares and highlights only the ones that differ.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-05-mal-diff.webp&#34; alt=&#34;Figure 5: A MAL sample group expanded into a diff view, with a dimmed COMMON block of shared labels and only two labels highlighted per sample.&#34;&gt;
Figure 5: MAL → metrics — samples grouped by metric, with a diff that dims the 16 labels every sample shares and lights only the two that differ (&lt;code&gt;group&lt;/code&gt;, &lt;code&gt;pod_name&lt;/code&gt;), so four near-identical series read apart at a glance.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LAL → logs.&lt;/strong&gt; Each captured log record becomes a column and each DSL block (or statement) a row, so the whole capture reads as a matrix: you can see which records the &lt;code&gt;filter&lt;/code&gt; &lt;strong&gt;aborted&lt;/strong&gt; and what the &lt;code&gt;extractor&lt;/code&gt; pulled out of the ones that passed — and click any cell to open the record in full and compare it against another.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-06-lal-matrix.webp&#34; alt=&#34;Figure 6: The LAL matrix — the captured rule on the left, records by block on the right, the extractor cells showing the status.code tag it added to each abnormal record.&#34;&gt;
Figure 6: LAL → logs — every captured record is a column, every DSL block a row. The &lt;code&gt;filter&lt;/code&gt; aborts the normal logs; for each abnormal one that passes, the &lt;code&gt;extractor&lt;/code&gt; row shows the tag it added (&lt;code&gt;status.code=404&lt;/code&gt;) as a diff over the raw record.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-06-lal-matrix-2.webp&#34; alt=&#34;Figure 7: A popout showing one record&amp;rsquo;s complete captured data as raw JSON, with a &amp;ldquo;compare with&amp;rdquo; selector, alongside the full DSL.&#34;&gt;
Figure 7: &amp;ldquo;Show complete data&amp;rdquo; opens a record in full — the entire raw log payload (here an Envoy access log) — with a &lt;strong&gt;Compare with&lt;/strong&gt; selector to diff it field-by-field against any other captured record.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;where-it-runs&#34;&gt;Where it runs&lt;/h2&gt;
&lt;p&gt;Both surfaces are &lt;strong&gt;operate&lt;/strong&gt; features: they talk to OAP&amp;rsquo;s &lt;strong&gt;admin host&lt;/strong&gt;, not the query port — DSL management through the &lt;code&gt;receiver-runtime-rule&lt;/code&gt; module, the Live Debugger through &lt;code&gt;dsl-debugging&lt;/code&gt;. That admin host ships with OAP 11, so on today&amp;rsquo;s backend these two pages surface a clear &amp;ldquo;needs the admin host / module&amp;rdquo; banner and stay read-only, while every observe surface — dashboards, traces, logs, alarms, profiling — keeps working untouched. Access is role-gated: browsing rules and viewing captures are read permissions, while editing, structural apply, and running a capture each need their own write or execute verb — so a read-only operator can study captured samples all day without ever being able to change a rule or start a session. This is the slice of &amp;ldquo;operate&amp;rdquo; the open-source backend only just made possible.&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-next&#34;&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;For the field reference — every apply state, the dump format, the per-tab capture controls — see the &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/runtime-rules/&#34;&gt;Runtime Rules&lt;/a&gt; and &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/live-debugger/&#34;&gt;Live Debugger&lt;/a&gt; docs.&lt;/p&gt;
&lt;p&gt;Next up: &lt;strong&gt;Inspect — Cross-Layer Query Power-Tools&lt;/strong&gt; — the Operate-side surfaces for running metric, trace, and log queries straight across every layer.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: Meet Horizon UI · 12/17: Inspect — Cross-Layer Query Power-Tools</title>
      <link>/blog/2026-06-29-horizon-ui-inspect-cross-layer-query/</link>
      <pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/blog/2026-06-29-horizon-ui-inspect-cross-layer-query/</guid>
      <description>
        
        
        &lt;p&gt;This is the twelfth post in the &lt;a href=&#34;/blog/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; series, still in &lt;strong&gt;Act 3 — operate it&lt;/strong&gt;. The &lt;a href=&#34;/blog/2026-06-22-horizon-ui-trace-explorer/&#34;&gt;Trace Explorer&lt;/a&gt; and &lt;a href=&#34;/blog/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Log Explorer&lt;/a&gt; both start the same way: you pick a layer, then a service, &lt;em&gt;then&lt;/em&gt; you search. That&amp;rsquo;s the right shape when you&amp;rsquo;re already looking at a service. But sometimes you aren&amp;rsquo;t — you have a trace id and no idea which layer owns it, a failing service name from an alert, or a metric you just want to chart across everything. The &lt;strong&gt;Inspect&lt;/strong&gt; family under &lt;strong&gt;Operate&lt;/strong&gt; is built for exactly that: three cross-layer query power-tools that drop the layer-first step — and one of them has no per-layer equivalent at all.&lt;/p&gt;
&lt;h2 id=&#34;metrics-inspect-the-metric-catalog-and-the-rule-behind-every-number&#34;&gt;Metrics inspect: the metric catalog, and the rule behind every number&lt;/h2&gt;
&lt;p&gt;SkyWalking computes a &lt;em&gt;lot&lt;/em&gt; of metrics, and until now there was no way to simply &lt;em&gt;see them all&lt;/em&gt;. &lt;strong&gt;Metrics inspect&lt;/strong&gt; is that view. Its catalog drawer lists every metric the connected OAP computes — and groups them by the &lt;strong&gt;rule that defines them&lt;/strong&gt;: the OAL files and MAL rule sets you met in the &lt;a href=&#34;/blog/2026-06-29-horizon-ui-runtime-rules-and-live-debugging/&#34;&gt;previous post&lt;/a&gt;. Filter by source (OAL, MAL·OTel, MAL·Telegraf, LAL→MAL), search by name, and read each metric&amp;rsquo;s value type and scope at a glance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-01-metrics-catalog.webp&#34; alt=&#34;Figure 1: The Metrics inspect catalog drawer — metrics grouped by their defining OAL file or MAL rule set, with source filters, search, and per-metric type and scope.&#34;&gt;
Figure 1: Metrics inspect&amp;rsquo;s catalog — every metric the OAP computes, grouped by the rule that defines it (the OAL files and MAL rule sets from DSL management), filterable by source and scope. Pick metrics onto the board.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Pick metrics from the catalog and they land on a &lt;strong&gt;board&lt;/strong&gt; of charts, where you choose the entities to plot — a paginated top-N from OAP, or hand-entered ones — and read the values back as a line, bar, or area chart. Each widget carries its rule source and scope so you never lose the thread from &amp;ldquo;this number&amp;rdquo; to &amp;ldquo;the rule that produces it.&amp;rdquo; It&amp;rsquo;s an MQE scratchpad: the time range is browser-local but sent to OAP in server time, the board persists in your browser, and metrics that live only in shared storage (not defined on the connected OAP) can be added as &lt;em&gt;foreign&lt;/em&gt; metrics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-02-metrics-board.webp&#34; alt=&#34;Figure 2: A metric charted on the Inspect board, tagged with its OAL source and Service scope, with a per-widget entity paginator and a multi-entity line chart.&#34;&gt;
Figure 2: The board — chart any metric across entities; each widget keeps its rule source (OAL) and scope (Service), a per-widget entity paginator, and a browser-local range sent to OAP in server time.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;trace-inspect-find-a-trace-without-picking-a-layer&#34;&gt;Trace inspect: find a trace without picking a layer&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Trace inspect&lt;/strong&gt; is the &lt;a href=&#34;/blog/2026-06-22-horizon-ui-trace-explorer/&#34;&gt;Trace Explorer&lt;/a&gt; with the layer taken off. The &lt;strong&gt;Target&lt;/strong&gt; is optional: pick a service through the layer → service → instance → endpoint cascade, &lt;em&gt;type&lt;/em&gt; a name (with a real / conjectured flag), or &lt;strong&gt;leave it blank to query every service at once&lt;/strong&gt;. Set the usual conditions — trace id, status, order, duration bounds, tags, window — and &lt;strong&gt;Run query&lt;/strong&gt;. A resolved-query line spells out the exact call sent to OAP, and the results render as the same distribution scatter, trace list, and three-lens waterfall you already know — just not bound to any one layer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-03-trace.webp&#34; alt=&#34;Figure 3: Trace inspect with a blank target, the distribution scatter, a trace list, and a selected trace&amp;rsquo;s waterfall spanning five services.&#34;&gt;
Figure 3: Trace inspect — layer-less: leave Target blank to query every service (or pick/type one), then Run query. Here one trace crosses five services (&lt;code&gt;agent::ui&lt;/code&gt; → &lt;code&gt;frontend&lt;/code&gt; → &lt;code&gt;app&lt;/code&gt; → &lt;code&gt;gateway&lt;/code&gt; → &lt;code&gt;songs&lt;/code&gt;); the resolved-query line shows the exact OAP call (&lt;code&gt;native · queryTraces&lt;/code&gt;).&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;log-inspect-one-query-three-log-sources&#34;&gt;Log inspect: one query, three log sources&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Log inspect&lt;/strong&gt; does the same for logs — &lt;em&gt;&amp;ldquo;query any service across layers, pick it, type its name, or leave it blank&amp;rdquo;&lt;/em&gt; — and folds three different log worlds into one place via a &lt;strong&gt;Source&lt;/strong&gt; switch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Raw&lt;/strong&gt; — the stored service logs, streamed across services with tag and trace-id conditions, each row opening the same payload popout as the per-layer &lt;a href=&#34;/blog/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Log Explorer&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser&lt;/strong&gt; — the BROWSER layer&amp;rsquo;s JS errors by category, with the same source-map de-obfuscation from the Browser Errors post;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes Pod logs&lt;/strong&gt; — an on-demand live tail of a pod&amp;rsquo;s container logs, with Start / Pause and Include/Exclude regex filters, never persisted.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-04-log-sources.webp&#34; alt=&#34;Figure 4: Log inspect on the Raw source, with the Raw / Browser / Kubernetes Pod logs source switch, a blank target, and a cross-service log stream.&#34;&gt;
Figure 4: Log inspect — &amp;ldquo;query any service across layers, or leave it blank,&amp;rdquo; across three sources (Raw stored logs, Browser JS errors, Kubernetes Pod logs). Here raw logs stream from several services at once.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;where-it-runs&#34;&gt;Where it runs&lt;/h2&gt;
&lt;p&gt;All three live under &lt;strong&gt;Operate&lt;/strong&gt; and share one permission, &lt;code&gt;inspect:read&lt;/code&gt;. They split on the backend, though. &lt;strong&gt;Trace inspect&lt;/strong&gt; and &lt;strong&gt;Log inspect&lt;/strong&gt; ride OAP&amp;rsquo;s standard query protocol — the same one the dashboards and per-layer explorers use — so they&amp;rsquo;re always on and work on any OAP, including 10.x. &lt;strong&gt;Metrics inspect&lt;/strong&gt; is the exception: it reads OAP&amp;rsquo;s metric catalog and entity enumerator through the &lt;strong&gt;admin host&lt;/strong&gt;&amp;rsquo;s &lt;code&gt;inspect&lt;/code&gt; module, so it needs OAP 11; when that module is absent it shows a clear &lt;em&gt;&amp;ldquo;set &lt;code&gt;SW_INSPECT=default&lt;/code&gt;&amp;rdquo;&lt;/em&gt; banner instead of a broken page, while the other two keep working. Think of the trio as the cross-layer, Operate-side counterparts to the per-layer Trace and Log explorers — plus the metric catalog that finally answers &amp;ldquo;what does this backend even measure, and which rule measures it?&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-next&#34;&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;For the field reference — the metric catalog, entity enumeration, foreign metrics, and MQE execution — see the &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/inspect/&#34;&gt;Inspect docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next up: &lt;strong&gt;Platform &amp;amp; Cluster Introspection&lt;/strong&gt; — Cluster Status, OAP configuration, and data-retention, the last stop in Act 3 before we turn to governing and securing the console.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 认识 Horizon UI · 11/17：运行时规则与实时调试</title>
      <link>/zh/2026-06-29-horizon-ui-runtime-rules-and-live-debugging/</link>
      <pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-06-29-horizon-ui-runtime-rules-and-live-debugging/</guid>
      <description>
        
        
        &lt;p&gt;&lt;em&gt;译自英文原文：&lt;a href=&#34;/blog/2026-06-29-horizon-ui-runtime-rules-and-live-debugging/&#34;&gt;Meet Horizon UI · 11/17: Runtime Rules &amp;amp; Live Debugging&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这是 &lt;a href=&#34;/zh/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; 系列的第十一篇，仍然属于第三幕 &lt;strong&gt;operate it&lt;/strong&gt;。&lt;a href=&#34;/zh/2026-06-29-horizon-ui-alarms-and-incident-triage/&#34;&gt;上一篇&lt;/a&gt; 讲的是如何读取后端已经判断出的告警；这一篇更进一步，讲如何改变后端的判断逻辑，并验证这次修改确实按预期生效。&lt;/p&gt;
&lt;p&gt;OAP 里的大多数分析都经过一组小型 DSL：&lt;strong&gt;OAL&lt;/strong&gt; 把 trace 转成 service 和 endpoint 指标，&lt;strong&gt;MAL&lt;/strong&gt; 把 meter（OpenTelemetry、Telegraf）转成指标，&lt;strong&gt;LAL&lt;/strong&gt; 把 log 转成 tag 和指标。过去，这些规则通常要在服务器上改 YAML，再重启服务。Horizon 把两件事都放进控制台：&lt;strong&gt;编辑规则并在线生效&lt;/strong&gt;，以及&lt;strong&gt;用实时数据调试规则&lt;/strong&gt;。这两项都是 SkyWalking UI 新增的能力，底层走 OAP 的 admin host。&lt;/p&gt;
&lt;h2 id=&#34;规则直接在控制台里看&#34;&gt;规则直接在控制台里看&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Operate → DSL management&lt;/strong&gt; 会列出集群当前正在运行的分析规则，并按来源分组。四类目录可编辑：&lt;strong&gt;MAL · OTel&lt;/strong&gt;、&lt;strong&gt;MAL · Telegraf&lt;/strong&gt;、&lt;strong&gt;LAL&lt;/strong&gt; 和 &lt;strong&gt;LAL → MAL&lt;/strong&gt;（log-to-metric）；另外还有只读的 &lt;strong&gt;OAL&lt;/strong&gt; 浏览器。规则会按前缀归组，比如 ActiveMQ、BanyanDB、Elasticsearch、Flink 等。每条规则都有状态标记，也可以按 &lt;strong&gt;active / inactive / bundled / modified&lt;/strong&gt; 过滤，这样可以很快看出哪些是随版本发布的规则，哪些被 operator 改过。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-01-rule-catalog.webp&#34; alt=&#34;图 1：DSL management 目录：分析规则按来源分组，每条规则带状态标记，顶部提供状态过滤。&#34;&gt;
图 1：DSL management：集群运行的 OAL/MAL/LAL 规则按来源分组，并可按 active / inactive / bundled / modified 过滤。这里展示的是 OpenTelemetry MAL 目录，包含 37 条 bundled 规则。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;编辑并安全地在线生效&#34;&gt;编辑，并安全地在线生效&lt;/h2&gt;
&lt;p&gt;打开一条规则后，界面是一个 Monaco YAML 编辑器，带语法高亮和两种 diff：&lt;strong&gt;vs. server&lt;/strong&gt; 看当前线上版本，&lt;strong&gt;vs. bundled&lt;/strong&gt; 看随版本发布的默认版本。这样你在保存前就能确认自己改了什么。每个 &lt;code&gt;- name:&lt;/code&gt; 旁边还有绿色 ▶，点击后会把这条规则直接带到 Live Debugger。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-02-rule-editor.webp&#34; alt=&#34;图 2：Monaco YAML 编辑器中的一条 MAL 规则，带 edit / diff-vs-server / diff-vs-bundled 标签页，以及代码左侧的绿色播放按钮。&#34;&gt;
图 2：用 Monaco YAML 编辑规则：语法高亮、对比线上版本和 bundled 版本，并可通过代码左侧的绿色 ▶ 直接进入 Live Debugger。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;保存时，Horizon 会区分修改的风险。只改规则主体或 filter，可以立即生效。但如果是&lt;strong&gt;结构性变更&lt;/strong&gt;，比如改了指标的 scope、downsampling 或存储形态，就会影响集群存储结构。Horizon 会按带集群确认的流程执行，并在界面上展示进度：&lt;strong&gt;Compiled → Confirming across the cluster → Committing → Done&lt;/strong&gt;；只有变更在集群里确认持久化后才算成功。&lt;/p&gt;
&lt;p&gt;如果某个节点没有及时通过确认，应用结果会变成 &lt;strong&gt;DEGRADED&lt;/strong&gt;。界面会列出落后的节点，这些节点会在下次扫描时自行追上，而不是让整次应用直接失败。如果 commit 前出错，变更会 &lt;strong&gt;rolled back&lt;/strong&gt;，原因显示在界面里，你的编辑内容仍保留在 buffer 中。编译错误则会作为 inline diagnostic 展示。对于卡住的发布，可以点一次 &lt;strong&gt;Force re-apply&lt;/strong&gt;，用完全相同的内容重新跑一遍应用流程，让落后的节点恢复同步；这会短暂暂停那条规则的采集。把规则恢复到 bundled 默认版本也走同一套确认流程；此外也可以 inactivate、delete，或者把整个目录导出成 tarball。&lt;/p&gt;
&lt;h2 id=&#34;live-debugger看清规则实际算出了什么&#34;&gt;Live Debugger：看清规则实际算出了什么&lt;/h2&gt;
&lt;p&gt;编辑规则只是第一步。更难的是确认它跑在真实数据上到底会算出什么。过去通常要读代码、对输出、靠经验判断。&lt;strong&gt;Operate → Live debugger&lt;/strong&gt; 直接把这件事放到界面里：选择一条规则，点击 &lt;strong&gt;start sampling&lt;/strong&gt;，Horizon 会在&lt;strong&gt;每个可达的 OAP 节点&lt;/strong&gt;上安装一个受限采集任务，抓取少量真实记录，然后逐条展示这些记录如何经过规则处理。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-03-livedebug-session.webp&#34; alt=&#34;图 3：Live Debugger 的 MAL 采集中页面：规则和指标选择器、record cap、retention 控制，以及显示 installed 2 of 2 的节点覆盖条。&#34;&gt;
图 3：启动采集后，任务会安装到每个可达的 OAP 节点上（这里是 2/2），抓取真实记录，并用 record cap 和 retention window 控制边界。三种分析语言共用这套会话框架。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;Live Debugger 按分析语言分成三个标签页，因为三种规则处理的数据不同。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OAL → traces。&lt;/strong&gt; 捕获到的一行 source 是真实的 trace segment。它会按 clause 展开：&lt;code&gt;from(Service.*)&lt;/code&gt; 读取 segment（可以看到 latency、status、endpoint），&lt;code&gt;build_metrics&lt;/code&gt; 组织指标结构，&lt;code&gt;cpm()&lt;/code&gt; 做聚合。你可以直接看到一条 trace 如何变成指标。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-04-oal-trace.webp&#34; alt=&#34;图 4：OAL 标签页展示一个 trace segment 如何经过 input、function 和 aggregation 阶段，并显示完整 payload。&#34;&gt;
图 4：OAL → traces：来自 &lt;code&gt;agent::gateway&lt;/code&gt; 的真实 segment（latency 38，status 200，&lt;code&gt;/rcmd&lt;/code&gt;）逐步经过 &lt;code&gt;from(Service.*)&lt;/code&gt; → &lt;code&gt;build_metrics&lt;/code&gt; → &lt;code&gt;cpm()&lt;/code&gt;，最终进入 service-CPM 指标。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MAL → metrics。&lt;/strong&gt; 一个 meter sample 会按 input → filter → function → output 流动。因为同一个指标往往会展开成多组 label，样本会按 metric 分组；&lt;strong&gt;diff&lt;/strong&gt; 会淡化所有样本共有的 label，只高亮不同的部分。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-05-mal-diff.webp&#34; alt=&#34;图 5：一组 MAL sample 展开后的 diff 视图：COMMON 区块里的共享 label 被淡化，每个 sample 只高亮两个不同 label。&#34;&gt;
图 5：MAL → metrics：sample 按 metric 分组，diff 会淡化 16 个所有样本共有的 label，只高亮不同的两个 label（&lt;code&gt;group&lt;/code&gt;、&lt;code&gt;pod_name&lt;/code&gt;）。四条非常相似的时序因此能一眼区分。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LAL → logs。&lt;/strong&gt; 每条捕获到的 log record 是一列，每个 DSL block（或 statement）是一行，所以整个采集结果会变成一个矩阵。你可以看到哪些记录被 &lt;code&gt;filter&lt;/code&gt; &lt;strong&gt;aborted&lt;/strong&gt;，也能看到通过 filter 的记录被 &lt;code&gt;extractor&lt;/code&gt; 提取出了什么；点开任意一个格子，还能查看这条记录的完整内容，并和另一条记录逐字段对比。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-06-lal-matrix.webp&#34; alt=&#34;图 6：LAL 矩阵——左侧是捕获规则，右侧按 record 和 block 排开；extractor 行显示它给每条异常记录打上的 status.code 标签。&#34;&gt;
图 6：LAL → logs：每条捕获记录是一列，每个 DSL block 是一行。&lt;code&gt;filter&lt;/code&gt; 会丢弃正常日志；对通过的异常日志，&lt;code&gt;extractor&lt;/code&gt; 行会以 diff 的形式显示它新增的标签（这里是 &lt;code&gt;status.code=404&lt;/code&gt;）。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p11-runtime-06-lal-matrix-2.webp&#34; alt=&#34;图 7：弹窗以原始 JSON 展示某条记录的完整内容（一条 Envoy access log），并带有“与谁对比”的选择器，旁边还能看到完整的 DSL。&#34;&gt;
图 7：点击格子上的“show complete data”，即可查看整条捕获记录的原始内容（这里是一条 Envoy access log），并通过 &lt;strong&gt;Compare with&lt;/strong&gt; 选择器与其他任意记录逐字段对比。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;它在哪里运行&#34;&gt;它在哪里运行&lt;/h2&gt;
&lt;p&gt;这两个界面都属于 &lt;strong&gt;operate&lt;/strong&gt; 功能：它们访问的是 OAP 的 &lt;strong&gt;admin host&lt;/strong&gt;，不是 query port。DSL management 走 &lt;code&gt;receiver-runtime-rule&lt;/code&gt; 模块，Live Debugger 走 &lt;code&gt;dsl-debugging&lt;/code&gt;。admin host 随 OAP 11 提供；在当前后端上，这两个页面会明确提示“需要 admin host / module”，并保持只读。与此同时，所有 observe 界面不受影响：dashboard、trace、log、alarm、profiling 都照常工作。&lt;/p&gt;
&lt;p&gt;权限也按角色拆开：浏览规则、查看采集结果是读权限；编辑规则、执行结构性应用、启动采集分别需要对应的写权限或执行权限。因此，只读 operator 可以一直查看采集样本，但不能改规则，也不能启动新的调试会话。这正是开源后端最近才补上的那块 operate 能力。&lt;/p&gt;
&lt;h2 id=&#34;后续阅读&#34;&gt;后续阅读&lt;/h2&gt;
&lt;p&gt;字段参考，包括每个 apply state、dump 格式和各标签页的采集控制，可以看 &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/runtime-rules/&#34;&gt;Runtime Rules&lt;/a&gt; 和 &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/live-debugger/&#34;&gt;Live Debugger&lt;/a&gt; 文档。&lt;/p&gt;
&lt;p&gt;下一篇：&lt;a href=&#34;/zh/2026-06-29-horizon-ui-inspect-cross-layer-query/&#34;&gt;Inspect，跨 layer 查询工具&lt;/a&gt;：在 Operate 界面里跨 layer 直接运行 metric、trace 和 log 查询。&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 认识 Horizon UI · 12/17：Inspect，跨 layer 查询工具</title>
      <link>/zh/2026-06-29-horizon-ui-inspect-cross-layer-query/</link>
      <pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-06-29-horizon-ui-inspect-cross-layer-query/</guid>
      <description>
        
        
        &lt;p&gt;&lt;em&gt;译自英文原文：&lt;a href=&#34;/blog/2026-06-29-horizon-ui-inspect-cross-layer-query/&#34;&gt;Meet Horizon UI · 12/17: Inspect — Cross-Layer Query Power-Tools&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这是 &lt;a href=&#34;/zh/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; 系列的第十二篇，仍然属于第三幕 &lt;strong&gt;operate it&lt;/strong&gt;。&lt;a href=&#34;/zh/2026-06-22-horizon-ui-trace-explorer/&#34;&gt;Trace Explorer&lt;/a&gt; 和 &lt;a href=&#34;/zh/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Log Explorer&lt;/a&gt; 的入口方式很一致：先选 layer，再选 service，然后搜索。如果你已经知道要看哪个服务，这个流程很顺。但有些时候，你并不知道入口在哪：手里只有一个 trace id，却不知道它属于哪个 layer；告警里只有一个出问题的 service name；或者你只是想把某个指标拿出来，在所有实体上画一遍。&lt;strong&gt;Operate&lt;/strong&gt; 下的 &lt;strong&gt;Inspect&lt;/strong&gt; 家族就是为这些场景准备的：三个跨 layer 查询入口，去掉“先选 layer”这一步；其中一个甚至没有对应的单 layer 版本。&lt;/p&gt;
&lt;h2 id=&#34;metrics-inspect指标目录以及每个指标背后的规则&#34;&gt;Metrics inspect：指标目录，以及每个指标背后的规则&lt;/h2&gt;
&lt;p&gt;SkyWalking 会计算大量指标，但过去没有一个地方能把它们完整列出来。&lt;strong&gt;Metrics inspect&lt;/strong&gt; 补上了这个视图。它的 catalog drawer 会列出当前连接的 OAP 计算出的所有指标，并按&lt;strong&gt;定义这些指标的规则&lt;/strong&gt;分组：也就是上一篇里讲过的 OAL 文件和 MAL 规则集。你可以按来源过滤（OAL、MAL·OTel、MAL·Telegraf、LAL→MAL），也可以按名称搜索，并直接看到每个指标的 value type 和 scope。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-01-metrics-catalog.webp&#34; alt=&#34;图 1：Metrics inspect 的 catalog drawer：指标按定义它们的 OAL 文件或 MAL 规则集分组，支持 source 过滤、搜索，并展示每个指标的 type 和 scope。&#34;&gt;
图 1：Metrics inspect 的指标目录：OAP 计算的所有指标按定义规则分组，也就是 DSL management 里的 OAL 文件和 MAL 规则集；可以按 source 和 scope 过滤，再把指标选到看板上。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;从目录里选中指标后，它们会进入一个图表 &lt;strong&gt;board&lt;/strong&gt;。你可以选择要画哪些实体：从 OAP 返回的分页 top-N 里选，或者手动输入实体名；图表可以用 line、bar 或 area 展示。每个 widget 都会带上规则来源和 scope，所以你始终能从“这个数”追溯到“是哪条规则算出了这个数”。它也可以当作一个 MQE 临时看板：时间范围保存在浏览器本地，但发送给 OAP 时会转成服务端时间；board 本身也保存在浏览器里；那些只存在于共享存储、但不由当前连接的 OAP 定义的指标，也可以作为 &lt;em&gt;foreign&lt;/em&gt; metrics 加进来。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-02-metrics-board.webp&#34; alt=&#34;图 2：Inspect board 上的一张指标图，带 OAL 来源和 Service scope 标签；每个 widget 有自己的 entity 分页器，并用多实体折线图展示数值。&#34;&gt;
图 2：Inspect board：任选一个指标，在多个实体上画图；每个 widget 保留规则来源（OAL）、scope（Service）、独立的 entity 分页器，以及一个浏览器本地保存、提交给 OAP 时转换成服务端时间的时间范围。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;trace-inspect不用先选-layer也能找-trace&#34;&gt;Trace inspect：不用先选 layer，也能找 trace&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Trace inspect&lt;/strong&gt; 可以理解成拿掉 layer 限制的 &lt;a href=&#34;/zh/2026-06-22-horizon-ui-trace-explorer/&#34;&gt;Trace Explorer&lt;/a&gt;。&lt;strong&gt;Target&lt;/strong&gt; 是可选的：你可以通过 layer → service → instance → endpoint 级联选择服务，也可以直接输入一个名字（并标记它是真实存在还是推测出来的），还可以&lt;strong&gt;留空 Target，一次查询所有 service&lt;/strong&gt;。之后照常设置查询条件：trace id、status、排序、duration 范围、tags 和时间窗口，然后点击 &lt;strong&gt;Run query&lt;/strong&gt;。界面会显示一行解析后的查询，写清楚实际发给 OAP 的调用；结果仍然是你熟悉的分布散点图、trace 列表和三视角 waterfall，只是不再绑在某个 layer 上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-03-trace.webp&#34; alt=&#34;图 3：Trace inspect 留空 target 后的查询结果：分布散点图、trace 列表，以及一条跨五个服务的 trace waterfall。&#34;&gt;
图 3：Trace inspect 不需要先选 layer：Target 留空即可查询所有 service，也可以选择或输入某个 service。这里一条 trace 跨过五个服务（&lt;code&gt;agent::ui&lt;/code&gt; → &lt;code&gt;frontend&lt;/code&gt; → &lt;code&gt;app&lt;/code&gt; → &lt;code&gt;gateway&lt;/code&gt; → &lt;code&gt;songs&lt;/code&gt;）；解析后的查询行展示了实际 OAP 调用（&lt;code&gt;native · queryTraces&lt;/code&gt;）。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;log-inspect一次入口三类日志&#34;&gt;Log inspect：一次入口，三类日志&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Log inspect&lt;/strong&gt; 对 log 做同样的事：可以跨 layer 查询任意 service，选择它、输入它的名字，或者直接留空。它还通过 &lt;strong&gt;Source&lt;/strong&gt; 切换，把三类日志放到同一个入口里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Raw&lt;/strong&gt;：存储下来的 service logs，可以跨 service 流式查询，支持 tag 和 trace id 条件；每一行都能打开和单 layer &lt;a href=&#34;/zh/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Log Explorer&lt;/a&gt; 相同的 payload 弹窗；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser&lt;/strong&gt;：来自 BROWSER layer 的 JS errors，按 category 查询，并使用 Browser Errors 那篇里讲过的 source map 反混淆；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes Pod logs&lt;/strong&gt;：按需 live tail 某个 pod 的 container logs，支持 Start / Pause 和 Include/Exclude 正则过滤，不会持久化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p12-inspect-04-log-sources.webp&#34; alt=&#34;图 4：Log inspect 的 Raw source：Raw / Browser / Kubernetes Pod logs 三种来源切换、留空 target，以及跨 service 的日志流。&#34;&gt;
图 4：Log inspect 可以跨 layer 查询任意 service，也可以留空 target；三种 source 分别对应 Raw 存储日志、Browser JS errors 和 Kubernetes Pod logs。这里展示的是多个 service 同时输出的 raw logs。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;它在哪里运行&#34;&gt;它在哪里运行&lt;/h2&gt;
&lt;p&gt;这三个入口都在 &lt;strong&gt;Operate&lt;/strong&gt; 下，共用一个权限：&lt;code&gt;inspect:read&lt;/code&gt;。但它们访问后端的方式不同。&lt;strong&gt;Trace inspect&lt;/strong&gt; 和 &lt;strong&gt;Log inspect&lt;/strong&gt; 走 OAP 标准 query protocol，也就是 dashboard 和单 layer explorer 使用的同一套接口；所以它们始终可用，也兼容 10.x OAP。&lt;strong&gt;Metrics inspect&lt;/strong&gt; 是例外：它通过 &lt;strong&gt;admin host&lt;/strong&gt; 的 &lt;code&gt;inspect&lt;/code&gt; 模块读取 OAP 的指标目录和实体枚举，因此需要 OAP 11。如果模块不存在，页面会给出明确的 &lt;em&gt;&amp;ldquo;set &lt;code&gt;SW_INSPECT=default&lt;/code&gt;&amp;rdquo;&lt;/em&gt; 提示，而不是只显示一个不可用的页面；另外两个入口仍然可以正常使用。可以把这组三个入口看成 Trace 和 Log explorer 在 Operate 侧的跨 layer 版本，再加上一个终于能回答“这个后端到底在算哪些指标、这些指标由哪条规则定义”的指标目录。&lt;/p&gt;
&lt;h2 id=&#34;后续阅读&#34;&gt;后续阅读&lt;/h2&gt;
&lt;p&gt;字段参考，包括指标目录、实体枚举、foreign metrics 和 MQE 执行，可以看 &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/inspect/&#34;&gt;Inspect 文档&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下一篇是 &lt;strong&gt;Platform &amp;amp; Cluster Introspection&lt;/strong&gt;：Cluster Status、OAP configuration 和 data-retention。它是第三幕的最后一站，之后这个系列会转向控制台的治理和安全。&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: Meet Horizon UI · 9/17: Five Profilers, One Flame Graph</title>
      <link>/blog/2026-06-26-horizon-ui-profiling/</link>
      <pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/blog/2026-06-26-horizon-ui-profiling/</guid>
      <description>
        
        
        &lt;p&gt;This is the ninth post in the &lt;a href=&#34;/blog/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; series. Metrics tell you &lt;em&gt;what&lt;/em&gt; slowed down; &lt;a href=&#34;/blog/2026-06-22-horizon-ui-trace-explorer/&#34;&gt;traces&lt;/a&gt; tell you &lt;em&gt;which hop&lt;/em&gt;. Profiling goes one level deeper — into the call stacks, kernel events, and process-to-process conversations of a running service — to tell you &lt;em&gt;where in the code&lt;/em&gt;. SkyWalking has five different profilers for that, and Horizon surfaces all of them. The headline of this post: &lt;strong&gt;four of the five pour into one shared flame graph&lt;/strong&gt;, and the fifth is a deliberate exception.&lt;/p&gt;
&lt;h2 id=&#34;one-renderer-four-profilers&#34;&gt;One renderer, four profilers&lt;/h2&gt;
&lt;p&gt;Trace, async, eBPF, and pprof profiling all produce the same fundamental thing — a tree of stack frames with sample counts — so Horizon normalizes them into one shape and renders them through &lt;strong&gt;one flame-graph component&lt;/strong&gt; (a wrapper over &lt;code&gt;d3-flame-graph&lt;/code&gt;). The payoff is that you learn the view once and it works the same everywhere:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;each frame&amp;rsquo;s width is its share of the samples, and the hover card reads out the code signature, the dump count, the time spent (including and &lt;em&gt;excluding&lt;/em&gt; children), and the frame&amp;rsquo;s &lt;strong&gt;% of root&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;clicking a frame zooms into it and pins a highlight on it — and that selected-frame highlight is consistent across all four profilers;&lt;/li&gt;
&lt;li&gt;a dim, per-frame color keyed off the method name keeps a thousand-frame graph legible on the dark canvas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-01-flame.webp&#34; alt=&#34;Figure 1: The shared flame graph — stack frames sized by sample share, a method-keyed color, and a hover card reading the code signature, dump count, time with and without children, and % of root.&#34;&gt;
Figure 1: One flame graph for four profilers — frames by sample share, the selected frame pinned, the hover card with % of root.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;On the &lt;strong&gt;Trace&lt;/strong&gt; and &lt;strong&gt;eBPF&lt;/strong&gt; tabs you can flip the same data to a &lt;strong&gt;Tree&lt;/strong&gt; view instead — an indented stack table with each method&amp;rsquo;s total vs &lt;strong&gt;self&lt;/strong&gt; duration and its dump count, expandable frame by frame. (Async and pprof are flame-graph-only; the toggle shows up where both views apply.)&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-01-tree.webp&#34; alt=&#34;Figure 2: The same analyze result as a Tree — an indented stack table with each method&amp;rsquo;s total and self duration and its dump count, expandable frame by frame.&#34;&gt;
Figure 2: The same result, one toggle away — the Tree view swaps the flame for an indented stack table carrying total vs self duration and dump count.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;what-each-of-the-four-catches&#34;&gt;What each of the four catches&lt;/h2&gt;
&lt;p&gt;The four stack profilers share the renderer but answer different questions, and each has its own New Task form:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trace Profiling&lt;/strong&gt; samples the call stacks of &lt;em&gt;slow trace segments&lt;/em&gt;. Scope a task to a service (and optionally one endpoint), set a slowness &lt;strong&gt;threshold&lt;/strong&gt; and a &lt;strong&gt;dump period&lt;/strong&gt;, and the agent snapshots thread stacks from segments that cross the threshold. Then you pick a sampled trace, drill to a profiled span, and &lt;strong&gt;Analyze&lt;/strong&gt; — with a &lt;em&gt;data mode&lt;/em&gt; that includes or excludes child-span time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Async Profiling&lt;/strong&gt; runs the JVM &lt;strong&gt;async-profiler&lt;/strong&gt; against a live Java service with no restart. A task can target several instances and several events at once — &lt;code&gt;CPU&lt;/code&gt;, &lt;code&gt;ALLOC&lt;/code&gt;, &lt;code&gt;LOCK&lt;/code&gt;, &lt;code&gt;WALL&lt;/code&gt;, and the timer events — and an event-type selector re-draws the flame for whichever one you want to read.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;eBPF Profiling&lt;/strong&gt; captures &lt;em&gt;kernel-level&lt;/em&gt; stacks with no in-process agent, driven by &lt;a href=&#34;https://github.com/apache/skywalking-rover&#34;&gt;SkyWalking Rover&lt;/a&gt;: &lt;strong&gt;ON_CPU&lt;/strong&gt; (where the process burns CPU) or &lt;strong&gt;OFF_CPU&lt;/strong&gt; (where it&amp;rsquo;s blocked — on locks, I/O, scheduling). A process picker lets you expand a process&amp;rsquo;s attributes and pin the ones to profile, and an aggregate toggle counts samples or sums blocked time (the latter only makes sense off-CPU).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pprof&lt;/strong&gt; profiles a live &lt;strong&gt;Go&lt;/strong&gt; service through the standard runtime profiler — exactly &lt;em&gt;one&lt;/em&gt; event per task, chosen from &lt;code&gt;CPU&lt;/code&gt;, &lt;code&gt;HEAP&lt;/code&gt;, &lt;code&gt;BLOCK&lt;/code&gt;, &lt;code&gt;MUTEX&lt;/code&gt;, &lt;code&gt;GOROUTINE&lt;/code&gt;, &lt;code&gt;ALLOCS&lt;/code&gt;, and &lt;code&gt;THREADCREATE&lt;/code&gt;. The dialog adapts to the choice: a duration for the timed captures, a sampling rate for &lt;code&gt;BLOCK&lt;/code&gt;/&lt;code&gt;MUTEX&lt;/code&gt;, and a one-shot snapshot for the rest.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-02-pprof-tasks.webp&#34; alt=&#34;Figure 3: The pprof (Go) tab — a list of single-event tasks (GOROUTINE, MUTEX, CPU), each carrying its own duration and sampling rate, with one selected and analyzed into the shared flame graph.&#34;&gt;
Figure 3: pprof takes exactly one Go event per task — GOROUTINE, MUTEX, and CPU are separate tasks, each with its own duration and sampling rate; select one and Analyze pours it into the same flame graph.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;network-profiling-the-deliberate-exception&#34;&gt;Network Profiling: the deliberate exception&lt;/h2&gt;
&lt;p&gt;The fifth profiler answers a different kind of question — not &amp;ldquo;where is one process spending time&amp;rdquo; but &amp;ldquo;which processes are talking to which, and over what&amp;rdquo; — so it renders differently on purpose. &lt;strong&gt;Network Profiling&lt;/strong&gt; captures the network conversations between the processes of a service instance and draws them as a &lt;strong&gt;honeycomb topology&lt;/strong&gt;: each process is a hexagon, the instance&amp;rsquo;s own processes pack into the centre under a dashed pod boundary, and external peers ring the edge. The links between them are directed and animated, and &lt;strong&gt;colored by protocol&lt;/strong&gt; — HTTPS, TLS, HTTP, and plain TCP each get their own hue and a small pill.&lt;/p&gt;
&lt;p&gt;It also runs differently: instead of a fixed duration, a network task carries &lt;strong&gt;sampling rules&lt;/strong&gt; — match by URI pattern, by 4xx/5xx responses, or by a minimum duration, and choose how much of each request/response body to keep — and keeps running until you stop it. Click an edge and a &lt;strong&gt;Client side | Server side&lt;/strong&gt; panel opens with that conversation&amp;rsquo;s call rate, latency, and bytes charted over the window. It&amp;rsquo;s drawn from the same process-relation data that powers the &lt;a href=&#34;/blog/2026-06-22-horizon-ui-3d-infrastructure-map/&#34;&gt;3D Infrastructure Map&lt;/a&gt; — and there&amp;rsquo;s not a flame graph in sight.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-03-network-honeycomb.webp&#34; alt=&#34;Figure 4: Network Profiling — an instance&amp;rsquo;s process conversations as a honeycomb: in-pod processes inside the dashed pod boundary, external peers ringing it, and edges colored by protocol (HTTP, HTTPS, TCP).&#34;&gt;
Figure 4: The odd one out — process conversations as a honeycomb. In-pod processes pack inside the dashed pod boundary, external peers ring it, and every edge is colored by protocol; clicking one opens its client-vs-server metrics.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;one-task-model-two-permissions&#34;&gt;One task model, two permissions&lt;/h2&gt;
&lt;p&gt;For all the differences in what they capture, every profiling tab is the same workflow: a &lt;strong&gt;task list&lt;/strong&gt; on the left, a &lt;strong&gt;New Task&lt;/strong&gt; control, and a &lt;strong&gt;result panel&lt;/strong&gt; on the right. Create a task and the list polls for a few rounds until OAP has dispatched it and the instances report back; select a task to analyze it.&lt;/p&gt;
&lt;p&gt;That create-versus-read split is also a permission boundary. Starting a task needs &lt;strong&gt;&lt;code&gt;profile:enable&lt;/code&gt;&lt;/strong&gt; (an operator-and-above default) — because an unbounded profile could peg a production instance&amp;rsquo;s CPU, so the task forms are duration- and size-capped on the server. &lt;em&gt;Reading&lt;/em&gt; a result needs only &lt;strong&gt;&lt;code&gt;profile:read&lt;/code&gt;&lt;/strong&gt; (part of the read-only data catalog). So a viewer can sit with a flame graph all day and never be able to launch a profile.&lt;/p&gt;
&lt;p&gt;Which tabs you even see depends on the service: a tab appears only when OAP reports that the service supports that kind of profiling. In practice the General agent layer carries the four stack engines (trace, eBPF, async, pprof), eBPF rides wherever Rover is deployed, and network profiling lights up on the service mesh.&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-next&#34;&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;For the field reference — every task field, the eBPF aggregate modes, the network sampling rules — see the &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/profiling/&#34;&gt;Profiling docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next up: &lt;strong&gt;Alarms &amp;amp; Incident Triage&lt;/strong&gt; — the incident-centric alarm surface, and replaying the MQE snapshot that fired a rule.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 认识 Horizon UI · 9/17：五种 Profiler，一套火焰图</title>
      <link>/zh/2026-06-26-horizon-ui-profiling/</link>
      <pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-06-26-horizon-ui-profiling/</guid>
      <description>
        
        
        &lt;p&gt;&lt;em&gt;译自英文原文：&lt;a href=&#34;/blog/2026-06-26-horizon-ui-profiling/&#34;&gt;Meet Horizon UI · 9/17: Five Profilers, One Flame Graph&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这是 &lt;a href=&#34;/zh/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; 系列的第九篇。指标告诉你 &lt;em&gt;什么&lt;/em&gt; 变慢了；&lt;a href=&#34;/zh/2026-06-22-horizon-ui-trace-explorer/&#34;&gt;Trace&lt;/a&gt; 告诉你慢在哪一跳；Profiling 再往下一层，进入运行中服务的调用栈、内核事件和进程间通信，告诉你问题落在 &lt;em&gt;哪段代码&lt;/em&gt;。SkyWalking 为这件事提供了五种 profiler，Horizon 会把它们都展示出来。这篇的主线是：&lt;strong&gt;五种里有四种进入同一套火焰图&lt;/strong&gt;，第五种则是刻意设计的例外。&lt;/p&gt;
&lt;h2 id=&#34;一个渲染器四种-profiler&#34;&gt;一个渲染器，四种 profiler&lt;/h2&gt;
&lt;p&gt;Trace、async、eBPF 和 pprof 这四类 profiling 最终都会产出同一类数据：一棵带采样计数的 stack frame 树。Horizon 先把它们归一成同一种结构，再交给 &lt;strong&gt;同一个火焰图组件&lt;/strong&gt;（基于 &lt;code&gt;d3-flame-graph&lt;/code&gt; 封装）渲染。好处很直接：你只需要学一次这个视图，之后四种 profiling 都按同样方式读：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个 frame 的宽度代表它占全部样本的比例；hover 卡片会显示代码签名、dump 次数、耗时（包含和 &lt;em&gt;不包含&lt;/em&gt; 子调用），以及该 frame 占根节点的 &lt;strong&gt;% of root&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;点击一个 frame 会缩放进去，并把选中高亮固定住；这个选中态在四种 profiler 里保持一致；&lt;/li&gt;
&lt;li&gt;每个 frame 使用由方法名决定的低饱和度颜色，让上千个 frame 的图在暗色画布上仍然能读。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-01-flame.webp&#34; alt=&#34;图 1：共用火焰图。stack frame 按样本占比决定宽度，颜色由方法名决定；hover 卡片显示代码签名、dump 次数、包含/不包含子调用的耗时，以及 % of root。&#34;&gt;
图 1：四种 profiler 共用一套火焰图：frame 按样本占比展开，选中 frame 会固定高亮，hover 卡片显示 % of root。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;在 &lt;strong&gt;Trace&lt;/strong&gt; 和 &lt;strong&gt;eBPF&lt;/strong&gt; 标签页里，同一份分析结果还可以切到 &lt;strong&gt;Tree&lt;/strong&gt; 视图：它是一张缩进的 stack 表，逐帧展示每个方法的 total 和 &lt;strong&gt;self&lt;/strong&gt; duration，以及 dump count。（Async 和 pprof 只提供火焰图；只有同时支持两种视图的地方才会出现这个切换。）&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-01-tree.webp&#34; alt=&#34;图 2：同一份分析结果的 Tree 视图：缩进的 stack 表展示每个方法的 total/self duration 和 dump count，并可逐帧展开。&#34;&gt;
图 2：同一份结果，一次切换即可从火焰图变成 Tree：缩进 stack 表展示 total/self duration 和 dump count。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;四种-stack-profiler-分别抓什么&#34;&gt;四种 stack profiler 分别抓什么&lt;/h2&gt;
&lt;p&gt;这四种 stack profiler 共用渲染器，但回答的问题不同，每种也有自己的 &lt;strong&gt;New Task&lt;/strong&gt; 表单：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trace Profiling&lt;/strong&gt; 会对 &lt;em&gt;慢 trace segment&lt;/em&gt; 的调用栈采样。创建任务时指定 service（也可以限定 endpoint）、慢调用 &lt;strong&gt;threshold&lt;/strong&gt; 和 &lt;strong&gt;dump period&lt;/strong&gt;。segment 超过阈值时，agent 会抓取线程栈快照。之后你选择一条采样到的 Trace，下钻到带 profiling 的 span，再点 &lt;strong&gt;Analyze&lt;/strong&gt;。这里还有一个 &lt;em&gt;data mode&lt;/em&gt;，可以选择是否把 child span 时间计入结果。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Async Profiling&lt;/strong&gt; 在运行中的 Java 服务上启动 JVM &lt;strong&gt;async-profiler&lt;/strong&gt;，不需要重启。一个任务可以同时覆盖多个实例和多个事件：&lt;code&gt;CPU&lt;/code&gt;、&lt;code&gt;ALLOC&lt;/code&gt;、&lt;code&gt;LOCK&lt;/code&gt;、&lt;code&gt;WALL&lt;/code&gt; 以及 timer 类事件。选择不同 event type 后，火焰图会按对应事件重新绘制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;eBPF Profiling&lt;/strong&gt; 不需要进程内 agent，由 &lt;a href=&#34;https://github.com/apache/skywalking-rover&#34;&gt;SkyWalking Rover&lt;/a&gt; 在内核层抓 stack：&lt;strong&gt;ON_CPU&lt;/strong&gt; 看进程把 CPU 花在哪里，&lt;strong&gt;OFF_CPU&lt;/strong&gt; 看它阻塞在哪里，比如锁、I/O、调度。进程选择器可以展开进程属性，固定要剖析的进程；聚合开关可以选择统计样本数，或者累加 blocked time（后者只适合 off-CPU）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pprof&lt;/strong&gt; 通过 Go 标准 runtime profiler 剖析运行中的 &lt;strong&gt;Go&lt;/strong&gt; 服务。每个任务只能选择 &lt;em&gt;一个&lt;/em&gt; event，来自 &lt;code&gt;CPU&lt;/code&gt;、&lt;code&gt;HEAP&lt;/code&gt;、&lt;code&gt;BLOCK&lt;/code&gt;、&lt;code&gt;MUTEX&lt;/code&gt;、&lt;code&gt;GOROUTINE&lt;/code&gt;、&lt;code&gt;ALLOCS&lt;/code&gt; 和 &lt;code&gt;THREADCREATE&lt;/code&gt;。对话框会跟随 event 调整：定时采集需要 duration，&lt;code&gt;BLOCK&lt;/code&gt;/&lt;code&gt;MUTEX&lt;/code&gt; 需要 sampling rate，其余则是一次性快照。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-02-pprof-tasks.webp&#34; alt=&#34;图 3：pprof（Go）标签页。列表里是单 event 任务（GOROUTINE、MUTEX、CPU），每个任务都有自己的 duration 和 sampling rate；选中任务后 Analyze，结果进入共用火焰图。&#34;&gt;
图 3：pprof 每个任务只采一个 Go event：GOROUTINE、MUTEX 和 CPU 是不同任务，各自带 duration 和 sampling rate；选中后 Analyze，同样进入火焰图。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;network-profiling刻意设计的例外&#34;&gt;Network Profiling：刻意设计的例外&lt;/h2&gt;
&lt;p&gt;第五种 profiler 问的是另一类问题：不是“一个进程把时间花在哪里”，而是“哪些进程在通信、通过什么协议通信”。所以它刻意不用火焰图。&lt;strong&gt;Network Profiling&lt;/strong&gt; 会捕获某个服务实例内进程之间的网络会话，并画成 &lt;strong&gt;蜂窝拓扑&lt;/strong&gt;：每个进程是一个六边形，实例自身的进程聚在虚线 pod 边界内，外部 peers 围在边缘。它们之间的边有方向、有动画，并按协议着色：HTTPS、TLS、HTTP 和普通 TCP 都有自己的颜色和小标签。&lt;/p&gt;
&lt;p&gt;它的运行方式也不同：network task 不是固定时长，而是带 &lt;strong&gt;sampling rules&lt;/strong&gt;。你可以按 URI pattern、4xx/5xx 响应或最小时延匹配，并配置保留多少 request/response body。任务会一直运行，直到你手动停止。点击一条边，会打开 &lt;strong&gt;Client side | Server side&lt;/strong&gt; 面板，展示这段会话在当前窗口内的调用速率、时延和字节数图表。它使用的是和 &lt;a href=&#34;/zh/2026-06-22-horizon-ui-3d-infrastructure-map/&#34;&gt;3D Infrastructure Map&lt;/a&gt; 同源的 process-relation 数据。这里看不到火焰图，这正是设计。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p09-profiling-03-network-honeycomb.webp&#34; alt=&#34;图 4：Network Profiling。一个实例的进程会话显示成蜂窝拓扑：pod 内进程位于虚线 pod 边界内，外部 peers 围在外侧，边按协议（HTTP、HTTPS、TCP）着色。&#34;&gt;
图 4：这个 profiler 是例外：进程通信画成蜂窝拓扑。pod 内进程聚在虚线边界内，外部 peers 围在外侧，每条边按协议着色；点击边会打开 client-vs-server 指标。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;同一套任务模型两类权限&#34;&gt;同一套任务模型，两类权限&lt;/h2&gt;
&lt;p&gt;虽然五种 profiler 抓取的内容不同，每个 profiling 标签页的操作流程是一样的：左侧是 &lt;strong&gt;任务列表&lt;/strong&gt;，上方有 &lt;strong&gt;New Task&lt;/strong&gt;，右侧是 &lt;strong&gt;结果面板&lt;/strong&gt;。创建任务后，列表会轮询几轮，等待 OAP 下发任务、实例回报结果；选中一个任务后再分析。&lt;/p&gt;
&lt;p&gt;创建任务和读取结果也是一条权限边界。启动任务需要 &lt;strong&gt;&lt;code&gt;profile:enable&lt;/code&gt;&lt;/strong&gt;（默认 operator 及以上拥有），因为没有边界的 profile 可能把生产实例 CPU 打满，所以任务表单的时长和数据大小都在服务端限额。&lt;em&gt;读取&lt;/em&gt; 结果只需要 &lt;strong&gt;&lt;code&gt;profile:read&lt;/code&gt;&lt;/strong&gt;（属于只读数据权限）。所以 viewer 可以一直看火焰图，但不能发起 profiling 任务。&lt;/p&gt;
&lt;p&gt;你能看到哪些标签页，也取决于当前服务：只有 OAP 上报该服务支持某类 profiling 时，对应标签页才会出现。实际使用中，General agent Layer 会带上四个 stack 引擎（trace、eBPF、async、pprof）；部署了 Rover 的地方会有 eBPF；service mesh 上会出现 network profiling。&lt;/p&gt;
&lt;h2 id=&#34;后续阅读&#34;&gt;后续阅读&lt;/h2&gt;
&lt;p&gt;字段参考，包括每个任务字段、eBPF 聚合模式和 network sampling rules，可以看 &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/profiling/&#34;&gt;Profiling 文档&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下一篇：&lt;a href=&#34;/zh/2026-06-29-horizon-ui-alarms-and-incident-triage/&#34;&gt;告警与 Incident 排查&lt;/a&gt;：Horizon 如何把重复告警归并成 incident，并回放触发规则时的 MQE 指标快照。&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: Meet Horizon UI · 8/17: Browser Errors &amp; Source Maps</title>
      <link>/blog/2026-06-23-horizon-ui-browser-errors-and-source-maps/</link>
      <pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/blog/2026-06-23-horizon-ui-browser-errors-and-source-maps/</guid>
      <description>
        
        
        &lt;p&gt;This is the eighth post in the &lt;a href=&#34;/blog/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; series. &lt;a href=&#34;/blog/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Part 7&lt;/a&gt; was your services&amp;rsquo; logs; this one is your &lt;em&gt;users&amp;rsquo;&lt;/em&gt; errors — the JavaScript exceptions the browser agent reports — and the one capability that turns them from noise into something you can act on.&lt;/p&gt;
&lt;p&gt;A production JavaScript stack is unreadable. Your code shipped minified and bundled, so the browser reports an error at &lt;code&gt;app.min.js:1:98412&lt;/code&gt; — a position into machine-generated soup that tells you nothing. The point of this feature is to walk that stack back to &lt;em&gt;your&lt;/em&gt; source: the original file, line, column, symbol name, and a snippet of the code around it — frame by frame — by pointing the error at the right &lt;strong&gt;source map&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-browser-error-feed&#34;&gt;The browser-error feed&lt;/h2&gt;
&lt;p&gt;On the &lt;strong&gt;BROWSER&lt;/strong&gt; layer, the &lt;strong&gt;Browser Logs&lt;/strong&gt; tab (the on-screen label — it&amp;rsquo;s specifically the JavaScript-error feed) lists what your browser agent reports. The BROWSER layer renames its slots to match its world — services become &lt;strong&gt;Applications&lt;/strong&gt;, instances &lt;strong&gt;Versions&lt;/strong&gt;, endpoints &lt;strong&gt;Pages&lt;/strong&gt; — and the feed reads like the &lt;a href=&#34;/blog/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Log Explorer&lt;/a&gt;: a clickable &lt;strong&gt;category&lt;/strong&gt; legend with counts and a density histogram over a stream of rows. Each row carries the time, the &lt;strong&gt;category&lt;/strong&gt;, the page, the app version, and the error message — with the minified &lt;code&gt;line:col&lt;/code&gt; shown as a chip when there is one.&lt;/p&gt;
&lt;p&gt;You scope it with the same triage instincts as the trace and log tabs: it owns its own &lt;strong&gt;Time range&lt;/strong&gt; (the global topbar is paused), and you narrow by &lt;strong&gt;Version&lt;/strong&gt;, &lt;strong&gt;Page&lt;/strong&gt;, or &lt;strong&gt;Category&lt;/strong&gt; and hit &lt;strong&gt;Run query&lt;/strong&gt; — there&amp;rsquo;s no background polling to shift the view under you, and no query language to learn, just structured controls. Click a row and it expands inline, right there in the stream.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p08-browser-01-stream.webp&#34; alt=&#34;Figure 1: The Browser Logs tab on the BROWSER layer — the category legend and density histogram over a stream of reported JS errors, each row showing its category, page, app version, and the minified line:col.&#34;&gt;
Figure 1: The browser agent&amp;rsquo;s error feed — categorized, charted, and scoped to one app&amp;rsquo;s version and pages.&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;That minified &lt;code&gt;line:col&lt;/code&gt; is the whole problem in miniature. It&amp;rsquo;s a real position — but into your &lt;em&gt;built&lt;/em&gt; bundle, not your source. Which is where the rest of this post comes in.&lt;/p&gt;
&lt;h2 id=&#34;from-a-minified-stack-back-to-your-source&#34;&gt;From a minified stack back to your source&lt;/h2&gt;
&lt;p&gt;Expand an error and the panel splits in two: on the left, the &lt;strong&gt;raw stack&lt;/strong&gt; exactly as the browser reported it (the gibberish); on the right, where you resolve it. Pick a &lt;strong&gt;source map&lt;/strong&gt; from the dropdown and click &lt;strong&gt;Resolve&lt;/strong&gt;, and Horizon parses the stack and maps &lt;strong&gt;every frame&lt;/strong&gt; through that map:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;each frame&amp;rsquo;s original &lt;strong&gt;&lt;code&gt;file:line:column&lt;/code&gt;&lt;/strong&gt;,&lt;/li&gt;
&lt;li&gt;the original &lt;strong&gt;symbol name&lt;/strong&gt; (when the map carries it), and&lt;/li&gt;
&lt;li&gt;a few lines of the &lt;strong&gt;original source&lt;/strong&gt; around the offending line, with the hit line highlighted (when the map embeds &lt;code&gt;sourcesContent&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A frame the map doesn&amp;rsquo;t cover is shown honestly as &lt;code&gt;unmapped&lt;/code&gt;. So a stack whose top frame read &lt;code&gt;app.min.js:1:45&lt;/code&gt; resolves to &lt;code&gt;computeCartTotal&lt;/code&gt; at &lt;code&gt;checkout.ts:2:20&lt;/code&gt;, with the lines of &lt;code&gt;checkout.ts&lt;/code&gt; around it — the &lt;code&gt;cart.items.reduce(...)&lt;/code&gt; that actually threw — sitting right there, the whole stack top to bottom, not just the first frame.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s careful about the details that make this either trustworthy or quietly wrong: browser stacks count columns from 1 while source maps count from 0, so the resolver shifts before each lookup — and that path is tested against real bundler output, not a hand-made fixture.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p08-browser-02-resolve.webp&#34; alt=&#34;Figure 2: An expanded error — the raw minified stack on the left, and on the right the resolved stack, each frame showing the original file:line:column, the symbol, and a highlighted source snippet.&#34;&gt;
Figure 2: The hero — point a minified stack at the right map and read it back in your own source, frame by frame.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;which-errors-carry-a-stack-to-resolve&#34;&gt;Which errors carry a stack to resolve&lt;/h2&gt;
&lt;p&gt;Not every category has something to translate. &lt;strong&gt;&lt;code&gt;JS&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;PROMISE&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;VUE&lt;/code&gt;&lt;/strong&gt; are real JavaScript errors whose stack points into your bundle — these resolve. &lt;strong&gt;&lt;code&gt;AJAX&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;RESOURCE&lt;/code&gt;&lt;/strong&gt; are network and load failures; their &amp;ldquo;stack&amp;rdquo; is an HTTP status or a failed URL, not code, so there&amp;rsquo;s simply nothing for a source map to map (Horizon doesn&amp;rsquo;t block them — there&amp;rsquo;s just no JavaScript there to walk back). Frames from code with no source map, or from &lt;code&gt;eval&lt;/code&gt;/inline scripts, stay &lt;code&gt;unmapped&lt;/code&gt; too. (&lt;code&gt;JS&lt;/code&gt; is also the only category the browser reports a top-level &lt;code&gt;line:col&lt;/code&gt; for; the others carry their position inside the stack string, which the resolver parses.)&lt;/p&gt;
&lt;h2 id=&#34;getting-maps-in-upload-or-mount&#34;&gt;Getting maps in: upload, or mount&lt;/h2&gt;
&lt;p&gt;A map has to be available before you can resolve against it, and there are two ways to provide one — deliberately different in durability:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Upload&lt;/strong&gt; a &lt;code&gt;.map&lt;/code&gt; straight from the tab. It&amp;rsquo;s held in the server&amp;rsquo;s &lt;strong&gt;memory only&lt;/strong&gt; — there&amp;rsquo;s no backend storage — and it&amp;rsquo;s temporary by design: it counts against a memory budget, is evicted least-recently-used under pressure, is &lt;strong&gt;lost when the server restarts&lt;/strong&gt;, and (in a multi-instance deployment) lives only on the instance that received it. This is the fast path for ad-hoc triage: drag a map in, resolve, move on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mount&lt;/strong&gt; &lt;code&gt;.map&lt;/code&gt; files into the server&amp;rsquo;s &lt;strong&gt;source-map directory&lt;/strong&gt; (&lt;code&gt;/app/sourcemaps&lt;/code&gt; in the container image, via &lt;code&gt;HORIZON_SOURCEMAPS_DIR&lt;/code&gt;). These are validated as Source Map v3 at boot, read from disk on demand (so they never sit in the memory budget), survive restarts, reload on their own, and &lt;strong&gt;can&amp;rsquo;t be deleted from the UI&lt;/strong&gt;. This is the durable, production path — bake your builds&amp;rsquo; maps into the image and they&amp;rsquo;re always there.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The manager shows each map&amp;rsquo;s origin (an &lt;em&gt;uploaded · temporary&lt;/em&gt; map vs a &lt;em&gt;mounted · durable&lt;/em&gt; one) and the live memory usage against the budget; budgets (a per-file cap and a total resident-upload cap, 64 MiB and 512 MiB by default) live in a &lt;code&gt;sourceMaps&lt;/code&gt; block in &lt;code&gt;horizon.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p08-browser-03-sourcemap-manager.webp&#34; alt=&#34;Figure 3: The source-map manager — the Upload .map control, the memory-budget bar (here 0 B / 512 MB, max 64 MB/file), and the loaded maps; the one shown is Mounted and durable, so it can&amp;rsquo;t be deleted here, while uploaded maps appear as temporary.&#34;&gt;
Figure 3: Two ways to provide a map — upload for a quick triage, mount for the durable, production set.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;you-pick-the-map--on-purpose&#34;&gt;You pick the map — on purpose&lt;/h2&gt;
&lt;p&gt;One thing Horizon deliberately does &lt;em&gt;not&lt;/em&gt; do is guess. The browser agent reports an app &lt;strong&gt;version&lt;/strong&gt; but no exact build fingerprint, so there&amp;rsquo;s no safe way to auto-match an error to a map — and applying a map from the &lt;em&gt;wrong&lt;/em&gt; build gives you confidently wrong line numbers, which is worse than no answer. So the choice is yours: pick the map that matches the error&amp;rsquo;s build, and keep your maps labelled by version. (One caution worth stating plainly: a source map&amp;rsquo;s &lt;code&gt;sourcesContent&lt;/code&gt; embeds your original source code, so treat the maps you upload or mount as sensitive, and provision them only on servers you trust.)&lt;/p&gt;
&lt;p&gt;That manual-by-design choice also draws a clean &lt;strong&gt;permission&lt;/strong&gt; line. Viewing the errors, listing the maps, and &lt;strong&gt;resolving&lt;/strong&gt; a stack are all reads, gated by &lt;code&gt;browser-errors:read&lt;/code&gt;; &lt;strong&gt;uploading or removing&lt;/strong&gt; a map is a write, gated by &lt;code&gt;source-map:write&lt;/code&gt;. So a read-only viewer can de-obfuscate stacks all day without ever being able to change what maps are loaded — reading is reading, mutating the map store is a write.&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-next&#34;&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;For the field reference — the categories, the two provisioning paths, the budgets and the matching-maps-to-builds guidance — see the &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/browser-source-maps/&#34;&gt;Browser Logs &amp;amp; Source Maps docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next up: &lt;strong&gt;Profiling&lt;/strong&gt; — five profilers (trace, async, eBPF, Go pprof, network) rendered through one flame graph.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 认识 Horizon UI · 8/17：浏览器错误与 Source Map</title>
      <link>/zh/2026-06-23-horizon-ui-browser-errors-and-source-maps/</link>
      <pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-06-23-horizon-ui-browser-errors-and-source-maps/</guid>
      <description>
        
        
        &lt;p&gt;&lt;em&gt;译自英文原文：&lt;a href=&#34;/blog/2026-06-23-horizon-ui-browser-errors-and-source-maps/&#34;&gt;Meet Horizon UI · 8/17: Browser Errors &amp;amp; Source Maps&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这是 &lt;a href=&#34;/zh/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; 系列的第八篇。&lt;a href=&#34;/zh/2026-06-23-horizon-ui-log-explorer/&#34;&gt;第七篇&lt;/a&gt;讲的是服务日志；这一篇讲 &lt;em&gt;用户&lt;/em&gt; 遇到的错误，也就是浏览器端 agent 上报的 JavaScript 异常，以及把这些错误定位到源码的关键一步。&lt;/p&gt;
&lt;p&gt;生产环境 JavaScript stack 基本不可读。代码经过压缩和打包后发布，浏览器只会报告错误出现在 &lt;code&gt;app.min.js:1:98412&lt;/code&gt;，也就是一段机器生成代码里的位置，几乎不给你任何线索。Horizon 要做的是找到正确的 &lt;strong&gt;source map&lt;/strong&gt;，把 stack 里的每一帧映射回源码：原始文件、行、列、符号名，以及出错位置附近的代码片段。&lt;/p&gt;
&lt;h2 id=&#34;浏览器端错误流&#34;&gt;浏览器端错误流&lt;/h2&gt;
&lt;p&gt;在 &lt;strong&gt;BROWSER&lt;/strong&gt; Layer 上，&lt;strong&gt;Browser Logs&lt;/strong&gt; 标签页（屏幕上的标签名，专指 JavaScript 错误流）会列出浏览器端 agent 上报的内容。BROWSER Layer 会把槽位重命名成自己的语义：services 变成 &lt;strong&gt;Applications&lt;/strong&gt;，instances 变成 &lt;strong&gt;Versions&lt;/strong&gt;，endpoints 变成 &lt;strong&gt;Pages&lt;/strong&gt;。这个列表的阅读方式类似 &lt;a href=&#34;/zh/2026-06-23-horizon-ui-log-explorer/&#34;&gt;Log Explorer&lt;/a&gt;：可点击的 &lt;strong&gt;category&lt;/strong&gt; legend 带计数，density histogram 位于日志流之上。每一行都有时间、&lt;strong&gt;category&lt;/strong&gt;、page、app version 和错误消息；如果带有压缩后的 &lt;code&gt;line:col&lt;/code&gt;，也会显示成标记。&lt;/p&gt;
&lt;p&gt;排查方式和 trace/log 标签页一致：它使用独立的 &lt;strong&gt;Time range&lt;/strong&gt;（全局顶栏暂停），你可以按 &lt;strong&gt;Version&lt;/strong&gt;、&lt;strong&gt;Page&lt;/strong&gt; 或 &lt;strong&gt;Category&lt;/strong&gt; 收窄，然后点击 &lt;strong&gt;Run query&lt;/strong&gt;。没有后台轮询把视图不断往前推，也没有要学习的查询语言，只有结构化控件。点击一行后，它会在日志流里原地展开。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p08-browser-01-stream.webp&#34; alt=&#34;图 1：BROWSER Layer 上的 Browser Logs 标签页。category legend 和 density histogram 位于 JS 错误流上方，每行显示 category、page、app version 和压缩后的 line:col。&#34;&gt;
图 1：浏览器端 agent 上报的错误流：按 category 组织、带图表，并限定到某个 app 版本和页面。&lt;/br&gt;&lt;/p&gt;
&lt;p&gt;压缩后的 &lt;code&gt;line:col&lt;/code&gt; 就是最典型的问题。它是真实位置，但位置在 &lt;em&gt;构建后&lt;/em&gt; 的 bundle 里，不在你的源码里。后面的解析流程就是为了解决这个落差。&lt;/p&gt;
&lt;h2 id=&#34;从压缩后的-stack-定位到源码&#34;&gt;从压缩后的 stack 定位到源码&lt;/h2&gt;
&lt;p&gt;展开一个错误后，面板分成两侧：左边是浏览器原样报告的 &lt;strong&gt;raw stack&lt;/strong&gt;，也就是那段很难读的生产栈；右边是解析结果区域。选择一个 &lt;strong&gt;source map&lt;/strong&gt;，点击 &lt;strong&gt;Resolve&lt;/strong&gt;，Horizon 会解析 stack，并通过这份 map 映射 &lt;strong&gt;每一帧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每帧原始 &lt;strong&gt;&lt;code&gt;file:line:column&lt;/code&gt;&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;原始 &lt;strong&gt;symbol name&lt;/strong&gt;（如果 map 携带了）；&lt;/li&gt;
&lt;li&gt;出错行附近几行 &lt;strong&gt;原始源码&lt;/strong&gt;，命中行会高亮（如果 map 内嵌 &lt;code&gt;sourcesContent&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;map 覆盖不到的 frame 会明确标为 &lt;code&gt;unmapped&lt;/code&gt;。所以一条顶层 frame 为 &lt;code&gt;app.min.js:1:45&lt;/code&gt; 的 stack，可以还原成 &lt;code&gt;checkout.ts:2:20&lt;/code&gt; 上的 &lt;code&gt;computeCartTotal&lt;/code&gt;，并把 &lt;code&gt;checkout.ts&lt;/code&gt; 附近几行显示出来。真正抛错的 &lt;code&gt;cart.items.reduce(...)&lt;/code&gt; 就在面板里，不只是还原第一帧，而是从上到下还原整条 stack。&lt;/p&gt;
&lt;p&gt;这里有一些细节决定结果是否可信：浏览器 stack 的列号从 1 开始计数，source map 从 0 开始计数，所以解析器每次查找前都会做偏移；这条路径用真实 bundler 输出测试，而不是手写 fixture。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p08-browser-02-resolve.webp&#34; alt=&#34;图 2：展开后的错误。左侧是压缩后的原始 stack，右侧是解析后的 stack，每一帧显示原始 file:line:column、symbol 和高亮源码片段。&#34;&gt;
图 2：核心流程：把压缩后的 stack 匹配到正确 map，然后逐帧还原到你自己的源码。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;哪些错误可以用-source-map-解析&#34;&gt;哪些错误可以用 source map 解析&lt;/h2&gt;
&lt;p&gt;不是每类错误都有可还原内容。&lt;strong&gt;&lt;code&gt;JS&lt;/code&gt;&lt;/strong&gt;、&lt;strong&gt;&lt;code&gt;PROMISE&lt;/code&gt;&lt;/strong&gt; 和 &lt;strong&gt;&lt;code&gt;VUE&lt;/code&gt;&lt;/strong&gt; 是真实 JavaScript 错误，它们的 stack 指向 bundle，可以解析。&lt;strong&gt;&lt;code&gt;AJAX&lt;/code&gt;&lt;/strong&gt; 和 &lt;strong&gt;&lt;code&gt;RESOURCE&lt;/code&gt;&lt;/strong&gt; 是网络和加载失败；它们的“stack”是 HTTP status 或失败 URL，不是代码，所以 source map 没有东西可映射（Horizon 不会阻止它们，只是没有可映射的 JavaScript 位置）。没有 source map 的代码、&lt;code&gt;eval&lt;/code&gt; 或 inline scripts 里的 frame，也会保持 &lt;code&gt;unmapped&lt;/code&gt;。（&lt;code&gt;JS&lt;/code&gt; 也是唯一由浏览器上报顶层 &lt;code&gt;line:col&lt;/code&gt; 的 category；其他 category 的位置在 stack 字符串内部，由解析器提取。）&lt;/p&gt;
&lt;h2 id=&#34;提供-source-map上传或挂载&#34;&gt;提供 source map：上传或挂载&lt;/h2&gt;
&lt;p&gt;解析前必须让 map 可用。Horizon 提供两种方式，持久化策略有意不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Upload&lt;/strong&gt; 一个 &lt;code&gt;.map&lt;/code&gt;，直接从标签页上传。它只保存在服务端 &lt;strong&gt;内存&lt;/strong&gt; 里，没有后端存储，并且临时性是设计目标：它占用内存预算，在压力下按 least-recently-used 淘汰，&lt;strong&gt;服务重启后丢失&lt;/strong&gt;；多实例部署时，它也只存在于接收上传的那一个实例。这个路径适合临时排查：拖一个 map 进来，解析，处理完离开。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mount&lt;/strong&gt; &lt;code&gt;.map&lt;/code&gt; 文件到服务端 &lt;strong&gt;source-map 目录&lt;/strong&gt;（容器镜像中是 &lt;code&gt;/app/sourcemaps&lt;/code&gt;，可通过 &lt;code&gt;HORIZON_SOURCEMAPS_DIR&lt;/code&gt; 指定）。这些文件在启动时按 Source Map v3 校验，按需从磁盘读取（所以不占内存预算），重启后仍然存在，会自动重新加载，并且 &lt;strong&gt;不能从 UI 删除&lt;/strong&gt;。这是生产路径：把构建产物里的 map 文件放进镜像或挂载目录，它们就一直可用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;管理器会显示每个 map 的来源（&lt;em&gt;uploaded · temporary&lt;/em&gt; 还是 &lt;em&gt;mounted · durable&lt;/em&gt;），以及当前内存预算使用量。预算配置，包括单文件上限和常驻上传总量上限，默认 64 MiB 和 512 MiB，位于 &lt;code&gt;horizon.yaml&lt;/code&gt; 的 &lt;code&gt;sourceMaps&lt;/code&gt; 块。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p08-browser-03-sourcemap-manager.webp&#34; alt=&#34;图 3：source-map 管理器。Upload .map 控件、内存预算条（这里是 0 B / 512 MB，max 64 MB/file）和已加载 maps；图中 map 是 Mounted 且 durable，所以不能在这里删除，uploaded maps 会显示为 temporary。&#34;&gt;
图 3：两种提供 map 的方式：upload 用于快速排查，mount 用于生产环境长期使用。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;map-必须手动选择&#34;&gt;map 必须手动选择&lt;/h2&gt;
&lt;p&gt;Horizon 刻意 &lt;strong&gt;不猜测&lt;/strong&gt;。浏览器端 agent 会上报 app &lt;strong&gt;version&lt;/strong&gt;，但不会上报精确的构建指纹，所以没有安全方法自动把一个错误匹配到某份 map。用错构建的 map 会给出非常自信、但完全错误的行号，比没有答案更糟。所以这里由你选择：挑选和这次错误对应构建的 map，并按版本给 map 清晰命名。（还有一个必须直说的注意点：source map 的 &lt;code&gt;sourcesContent&lt;/code&gt; 会包含你的原始源码，所以无论上传还是挂载，都要把 map 当成敏感内容，只放在可信服务器上。）&lt;/p&gt;
&lt;p&gt;手动选择 map 这件事，也划清了 &lt;strong&gt;权限&lt;/strong&gt; 边界。查看错误、列出 maps、&lt;strong&gt;解析&lt;/strong&gt; stack 都是读操作，由 &lt;code&gt;browser-errors:read&lt;/code&gt; 控制；&lt;strong&gt;上传或删除&lt;/strong&gt; map 是写操作，由 &lt;code&gt;source-map:write&lt;/code&gt; 控制。所以只读用户可以反复解析 stack，但没有权限改变已加载的 map 集合。读就是读，修改 map 存储才是写。&lt;/p&gt;
&lt;h2 id=&#34;后续阅读&#34;&gt;后续阅读&lt;/h2&gt;
&lt;p&gt;字段参考，包括 categories、两种提供路径、预算，以及如何按构建版本匹配 map，可以看 &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/browser-source-maps/&#34;&gt;Browser Logs &amp;amp; Source Maps 文档&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下一篇进入 Profiling：五种 profiler（trace、async、eBPF、Go pprof、network）如何共用一套火焰图视图，以及为什么 network profiling 是例外。&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: Meet Horizon UI · 5/17: The 3D Infrastructure Map</title>
      <link>/blog/2026-06-22-horizon-ui-3d-infrastructure-map/</link>
      <pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/blog/2026-06-22-horizon-ui-3d-infrastructure-map/</guid>
      <description>
        
        
        &lt;p&gt;This is the fifth post in the &lt;a href=&#34;/blog/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; series. &lt;a href=&#34;/blog/2026-06-21-horizon-ui-topology-and-dependency/&#34;&gt;Part 3&lt;/a&gt; drew the map &lt;em&gt;between&lt;/em&gt; services and &lt;a href=&#34;/blog/2026-06-21-horizon-ui-deployment-and-banyandb/&#34;&gt;Part 4&lt;/a&gt; drew it &lt;em&gt;inside&lt;/em&gt; one service. This post zooms all the way out: a single &lt;strong&gt;WebGL&lt;/strong&gt; view of your &lt;em&gt;entire&lt;/em&gt; deployment at once — every SkyWalking layer&amp;rsquo;s services rendered as cubes, stacked in 3D, with live traffic, alarms, and the calls between them. It&amp;rsquo;s the &amp;ldquo;stand back and look at everything&amp;rdquo; companion to the per-layer dashboards.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s also genuinely interactive, so rather than describe it cold, here it is — the real map running on the demo&amp;rsquo;s sample data. Drag to rotate, scroll to zoom, click a cube:&lt;/p&gt;
&lt;figure class=&#34;map3d-stage&#34;&gt;
  &lt;a class=&#34;map3d-poster&#34; href=&#34;/3d-map-app/&#34; aria-label=&#34;Open the interactive 3D infrastructure map&#34;&gt;&lt;span class=&#34;map3d-badge&#34;&gt;Interactive · sample data&lt;/span&gt;
    &lt;img loading=&#34;lazy&#34; src=&#34;/images/home/horizon-3d-map.png&#34;
         alt=&#34;Apache SkyWalking topology rendered as an interactive 3D scene you can orbit, zoom and click&#34;&gt;
    &lt;span class=&#34;map3d-play&#34; aria-hidden=&#34;true&#34;&gt;
      &lt;span class=&#34;map3d-play-btn&#34;&gt;
        &lt;svg viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34;&gt;&lt;path d=&#34;M8 5v14l11-7-11-7z&#34; fill=&#34;currentColor&#34;/&gt;&lt;/svg&gt;
      &lt;/span&gt;
      &lt;span class=&#34;map3d-play-label&#34;&gt;Open the 3D map&lt;/span&gt;
    &lt;/span&gt;
  &lt;/a&gt;
&lt;/figure&gt;


&lt;h2 id=&#34;one-scene-for-the-whole-estate&#34;&gt;One scene for the whole estate&lt;/h2&gt;
&lt;p&gt;The 3D map is a standalone, full-screen view at &lt;code&gt;/3d/map&lt;/code&gt;, opened from the &lt;strong&gt;3D Infra&lt;/strong&gt; pill in the topbar. It deliberately drops the rest of the console — no sidebar, no topbar, no global time picker — so the scene gets the whole viewport. The SkyWalking mark sits bottom-left; the &lt;code&gt;×&lt;/code&gt; top-right returns you to Horizon. Everything in it is read live from the &lt;em&gt;same&lt;/em&gt; OAP the rest of Horizon talks to: the service roster, each layer&amp;rsquo;s topology, the per-service traffic, and the active alarms.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-01-overview.webp&#34; alt=&#34;Figure 1: The 3D Infrastructure Map — every layer&amp;rsquo;s services as cubes, grouped into brand-colored per-layer zones and stacked onto horizontal tiers, with a tier/layer panel on the right.&#34;&gt;
Figure 1: The whole deployment in one scene — services as cubes, layers as colored zones, roles as stacked tiers.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;tiers-are-the-spine&#34;&gt;Tiers are the spine&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;tier&lt;/strong&gt; is a horizontal plane that groups related layers by their role in the system. Tiers read top-to-bottom the way a request flows — from the apps a user touches down to the platform everything runs on. Horizon ships four bundled tiers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Apps&lt;/strong&gt; (top) — application surfaces and the dependencies as the app sees them: General agent services, Browser/RUM, mobile, and the &lt;code&gt;Virtual*&lt;/code&gt; targets (database, cache, MQ, gateway, GenAI).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Middleware&lt;/strong&gt; — the data and messaging services, gateways, and self-observability: MySQL, PostgreSQL, Redis, Kafka, RocketMQ, APISIX, Nginx, the SkyWalking SO11Y components, and cloud-managed data services.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Mesh&lt;/strong&gt; — the mesh that fronts the apps: Istio control/data plane, Cilium, the Envoy AI Gateway.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infra&lt;/strong&gt; (bottom) — the platform the rest runs on: Kubernetes, hosts, VMs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every layer OAP reports lands on exactly one tier. A layer Horizon hasn&amp;rsquo;t classified yet — a brand-new OAP layer, say — falls to the &lt;strong&gt;failover tier&lt;/strong&gt; (Middleware by default) with an &amp;ldquo;unclassified&amp;rdquo; mark, so it shows up and an operator can re-assign it rather than silently dropping off the map. The panel on the right mirrors the stack: click a tier row to fly the camera to it, use the eye toggle to show or hide a whole tier (or a single layer) at once, and read off how many of its services are currently visible.&lt;/p&gt;
&lt;h2 id=&#34;reading-the-map-cubes-zones-traffic&#34;&gt;Reading the map: cubes, zones, traffic&lt;/h2&gt;
&lt;p&gt;Each &lt;strong&gt;cube is one service&lt;/strong&gt;. Cubes are grouped into their layer&amp;rsquo;s &lt;strong&gt;zone&lt;/strong&gt; on the tier — a translucent rectangle painted in the layer&amp;rsquo;s brand color and stamped with the project&amp;rsquo;s logo (Istio&amp;rsquo;s sail, the Kubernetes helm, a database cylinder, a queue) so you can pick a zone out from any camera angle. Layers that ship a topology (General, Service Mesh, Kubernetes Service, Cilium) lay their cubes out &lt;strong&gt;by call dependency&lt;/strong&gt; — upstream callers on one side, downstream services on the other, the 3D analogue of the 2D service map. Layers without a topology pack their cubes into a tidy grid.&lt;/p&gt;
&lt;p&gt;A small &lt;strong&gt;traffic pill&lt;/strong&gt; under a cube shows that service&amp;rsquo;s live headline throughput — requests per minute for app and mesh services, queries or operations per second for data services, each with its own unit. The pills appear on cubes close enough to read and fade away as you zoom out to keep the scene clean, then return as you come back in; a selected cube always shows its number.&lt;/p&gt;
&lt;h2 id=&#34;alarms-and-beacon-mode-for-incidents&#34;&gt;Alarms, and Beacon mode for incidents&lt;/h2&gt;
&lt;p&gt;When a service has a &lt;strong&gt;currently-firing alarm&lt;/strong&gt; (Horizon polls the last 20 minutes and counts only service-scoped, still-firing ones), its cube &lt;strong&gt;pulses red&lt;/strong&gt; — a beacon you can spot from clear across the scene, while the alarm feed refreshes on its own.&lt;/p&gt;
&lt;p&gt;On a busy map, one more red cube among hundreds can still be hard to find — so there&amp;rsquo;s &lt;strong&gt;Beacon mode&lt;/strong&gt;. Toggle it from the toolbar and every &lt;em&gt;healthy&lt;/em&gt; cube dims to a dark wireframe ghost, leaving only the alarming services lit and glowing. The shape of the deployment stays legible, but the services that are actually on fire are the only thing with color. It turns the bird&amp;rsquo;s-eye view into an incident triage surface in one click.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-02-beacon-mode.webp&#34; alt=&#34;Figure 2: Beacon mode — every healthy cube dimmed to a wireframe ghost so only the services with a firing alarm stay lit and glowing red.&#34;&gt;
Figure 2: Beacon mode dims everything healthy to a ghost, so the firing services are the only thing you see.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-lines-between-things&#34;&gt;The lines between things&lt;/h2&gt;
&lt;p&gt;The map draws the call graph, not just the nodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;In-layer calls&lt;/strong&gt; — light cyan tubes between two services in the same layer, with packets animating along them. This is each layer&amp;rsquo;s internal call graph, always on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-layer calls&lt;/strong&gt; — soft amber arrows between services in &lt;em&gt;different&lt;/em&gt; layers on the same tier (a Browser app calling a Frontend, a Frontend calling a Virtual Database), pointing from caller to callee.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchy links&lt;/strong&gt; — and here&amp;rsquo;s the one that makes the 3D layout earn its keep. Select a cube and thicker gray tubes connect the &lt;strong&gt;different faces of the same logical service&lt;/strong&gt; across tiers — the service as its agent sees it, as the mesh sees it, as a Kubernetes service. These represent &lt;em&gt;identity&lt;/em&gt;, not traffic, so they stay hidden until you select a cube and then show just that cube&amp;rsquo;s relatives, climbing the stack from tier to tier. It&amp;rsquo;s the &lt;a href=&#34;/blog/2026-06-21-horizon-ui-topology-and-dependency/&#34;&gt;Smartscape&lt;/a&gt; idea from Part 3, drawn in the dimension it was always meant for.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-03-selected-hierarchy.webp&#34; alt=&#34;Figure 3: A selected service — its detail card beside the cube, and its cross-tier hierarchy links lit up, connecting the same logical service across the Apps, Service Mesh, and Infra tiers.&#34;&gt;
Figure 3: Select a cube and its identity links climb the tiers — one service, seen by its agent, the mesh, and Kubernetes.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;moving-around&#34;&gt;Moving around&lt;/h2&gt;
&lt;p&gt;Drag to rotate, scroll to zoom, and &lt;strong&gt;arrow keys&lt;/strong&gt; or &lt;strong&gt;WASD&lt;/strong&gt; pan the view (hold &lt;strong&gt;Shift&lt;/strong&gt; for a bigger step); a top-left toolbar offers the same gestures as buttons for trackpads. There&amp;rsquo;s one deliberate rule worth knowing: &lt;strong&gt;clicks inside the 3D scene never move the camera&lt;/strong&gt; — they only select. Click a cube and it highlights, a detail card appears beside it (the service&amp;rsquo;s name, its tier and layer, and an &lt;strong&gt;Open dashboard&lt;/strong&gt; button that jumps into that service&amp;rsquo;s layer dashboard in a new tab), and its hierarchy links light up. The camera-move surface is the &lt;em&gt;side panel&lt;/em&gt; and the &lt;em&gt;toolbar&lt;/em&gt; — click a layer row to glide the camera to its zone. Keeping those two jobs separate is what makes selecting a small cube feel reliable instead of having the cube slide out from under your cursor.&lt;/p&gt;
&lt;h2 id=&#34;how-it-builds&#34;&gt;How it builds&lt;/h2&gt;
&lt;p&gt;A whole deployment is too much to fetch in one request, so the map loads in stages, and a slim &lt;strong&gt;timeline strip&lt;/strong&gt; along the bottom shows the progress live: &lt;strong&gt;Services&lt;/strong&gt; (the roster and their layers) → &lt;strong&gt;Templates&lt;/strong&gt; (which layers carry a topology) → &lt;strong&gt;Topologies&lt;/strong&gt; (each topology-bearing layer&amp;rsquo;s call graph) → &lt;strong&gt;Hierarchy&lt;/strong&gt; (the cross-tier identity links) → &lt;strong&gt;Layout&lt;/strong&gt; (placing the cubes) → &lt;strong&gt;Metrics&lt;/strong&gt; (the per-service traffic, fetched in batches so the cubes light up progressively). Click any step for a drawer with its detail, or hit refresh to re-run the whole sequence.&lt;/p&gt;
&lt;p&gt;Two touches make refreshes cheap. The &lt;strong&gt;hierarchy&lt;/strong&gt; step is incremental — only services that are &lt;em&gt;new&lt;/em&gt; since the last run get probed, the rest reused from cache, so a steady deployment costs nothing there. And the scene is re-keyed on a per-layer structure hash, so an unchanged refresh keeps your camera exactly where it was; only a real roster or edge change rebuilds the layout. Under the hood it&amp;rsquo;s &lt;a href=&#34;https://threejs.org/&#34;&gt;Three.js&lt;/a&gt; with a thin Vue wrapper, every geometry and material shared across cubes of the same kind — the kind of detail that keeps a few hundred services rendering smoothly in a browser tab.&lt;/p&gt;
&lt;h2 id=&#34;configured-not-coded&#34;&gt;Configured, not coded&lt;/h2&gt;
&lt;p&gt;None of the above is a hard-coded &amp;ldquo;3D screen.&amp;rdquo; What the map shows is driven by a single configuration an administrator edits in a &lt;strong&gt;structured form editor&lt;/strong&gt; at &lt;code&gt;/admin/3d-map&lt;/code&gt; — tiers, layers, colors, and metrics through form controls, not raw JSON. From it you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Filter layers&lt;/strong&gt; with one global regex — anything it excludes drops off the map entirely.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arrange tiers&lt;/strong&gt; — rename them, reorder them top-to-bottom, and pin each layer to a tier (with a nominated failover tier so nothing falls off silently).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Group layers&lt;/strong&gt; — cluster several related layers (the SkyWalking self-observability components, say) into one labelled block, each member keeping its own color.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Color each layer&lt;/strong&gt; and &lt;strong&gt;choose its traffic metric&lt;/strong&gt; — the MQE expression, a label, and a unit, seeded by default from that layer&amp;rsquo;s dashboard template so most layers show a sensible number out of the box.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Horizon ships a bundled default, so the map is useful immediately; your edits live as a local draft until you &lt;strong&gt;Check diff &amp;amp; push&lt;/strong&gt; them to OAP — the same draft → preview → publish model behind every dashboard, and the same Export/Import for backup or moving a configuration between deployments. The map itself is a read-only &lt;strong&gt;observe&lt;/strong&gt; surface that runs against your current OAP; publishing the config that shapes it is part of the config-driven customization story a later post in this series covers end to end.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-04-config-editor.webp&#34; alt=&#34;Figure 4: The 3D-map config — a structured editor at /admin/3d-map for tiers, per-layer color and traffic metric, layer groups, and the global filter, with the usual draft → Check diff &amp;amp; push publish flow.&#34;&gt;
Figure 4: The map is configuration, not code — tiers, colors, and per-layer traffic metrics edited as a form, then published to OAP.&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;where-to-go-next&#34;&gt;Where to go next&lt;/h2&gt;
&lt;p&gt;The 3D map is the bird&amp;rsquo;s-eye summary; the 2D per-layer pages stay the authoritative service maps. Viewing it needs only read access (&lt;code&gt;infra-3d:read&lt;/code&gt;, held by the built-in viewer role and up); shaping it needs the same write permission as the dashboards. For the field reference — tiers, the config shape, the loading stages — see the &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/infra-3d-map/&#34;&gt;3D Infrastructure Map docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next up: &lt;strong&gt;the Trace Explorer&lt;/strong&gt; — from the bird&amp;rsquo;s-eye view of the whole deployment back down to a single request, drawn three different ways.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 认识 Horizon UI · 5/17：3D 基础设施地图</title>
      <link>/zh/2026-06-22-horizon-ui-3d-infrastructure-map/</link>
      <pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-06-22-horizon-ui-3d-infrastructure-map/</guid>
      <description>
        
        
        &lt;p&gt;&lt;em&gt;译自英文原文：&lt;a href=&#34;/blog/2026-06-22-horizon-ui-3d-infrastructure-map/&#34;&gt;Meet Horizon UI · 5/17: The 3D Infrastructure Map&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这是 &lt;a href=&#34;/zh/2026-06-21-skywalking-horizon-ui-introduction/&#34;&gt;Meet Horizon UI&lt;/a&gt; 系列的第五篇。&lt;a href=&#34;/zh/2026-06-21-horizon-ui-topology-and-dependency/&#34;&gt;第三篇&lt;/a&gt;画的是服务 &lt;em&gt;之间&lt;/em&gt; 的地图，&lt;a href=&#34;/zh/2026-06-21-horizon-ui-deployment-and-banyandb/&#34;&gt;第四篇&lt;/a&gt;画的是单个服务 &lt;em&gt;内部&lt;/em&gt; 的地图。这一篇把视角拉到最远：用一个 &lt;strong&gt;WebGL&lt;/strong&gt; 视图一次看完整个部署。每个 SkyWalking Layer 的服务都会渲染成立方体，堆叠到 3D 空间里，并显示实时流量、告警和它们之间的调用关系。它补上了按 Layer 仪表盘之外的全局视角：退后一步，看整个系统。&lt;/p&gt;
&lt;p&gt;而且它不是静态截图。所以这里直接放出来：下面就是运行在 demo 数据上的真实地图。拖动旋转，滚轮缩放，点击立方体：&lt;/p&gt;
&lt;figure class=&#34;map3d-stage&#34;&gt;
  &lt;a class=&#34;map3d-poster&#34; href=&#34;/3d-map-app/&#34; aria-label=&#34;Open the interactive 3D infrastructure map&#34;&gt;&lt;span class=&#34;map3d-badge&#34;&gt;交互演示 · 示例数据&lt;/span&gt;
    &lt;img loading=&#34;lazy&#34; src=&#34;/images/home/horizon-3d-map.png&#34;
         alt=&#34;Apache SkyWalking topology rendered as an interactive 3D scene you can orbit, zoom and click&#34;&gt;
    &lt;span class=&#34;map3d-play&#34; aria-hidden=&#34;true&#34;&gt;
      &lt;span class=&#34;map3d-play-btn&#34;&gt;
        &lt;svg viewBox=&#34;0 0 24 24&#34; fill=&#34;none&#34;&gt;&lt;path d=&#34;M8 5v14l11-7-11-7z&#34; fill=&#34;currentColor&#34;/&gt;&lt;/svg&gt;
      &lt;/span&gt;
      &lt;span class=&#34;map3d-play-label&#34;&gt;Open the 3D map&lt;/span&gt;
    &lt;/span&gt;
  &lt;/a&gt;
&lt;/figure&gt;


&lt;h2 id=&#34;一张-3d-图查看完整部署&#34;&gt;一张 3D 图查看完整部署&lt;/h2&gt;
&lt;p&gt;3D 地图是 &lt;code&gt;/3d/map&lt;/code&gt; 里的独立全屏页面，从顶栏里的 &lt;strong&gt;3D Infra&lt;/strong&gt; 入口打开。它刻意拿掉控制台其余部分：没有侧边栏，没有顶栏，没有全局时间选择器。整个浏览器视口都交给场景。SkyWalking 标识放在左下角，右上角的 &lt;code&gt;×&lt;/code&gt; 返回 Horizon。里面所有数据都来自 Horizon 其他页面访问的同一个 OAP：服务清单、每个 Layer 的拓扑、每个服务的流量，以及活跃告警。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-01-overview.webp&#34; alt=&#34;图 1：3D Infrastructure Map。每个 Layer 的服务渲染成立方体，按品牌色分组为每个 Layer 的 zone，并堆叠到横向 tier 上，右侧是 tier/layer 面板。&#34;&gt;
图 1：一张 3D 图看完整部署：服务是立方体，Layer 是带颜色的 zone，角色按 tier 堆叠。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;用-tier-组织系统层次&#34;&gt;用 tier 组织系统层次&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Tier&lt;/strong&gt; 是一层横向平面，用来把系统中职责相近的 Layer 放在一起。Tier 从上到下的阅读顺序，就是请求流动的大致方向：从用户直接访问的应用，一路到底层平台。Horizon 内置四个 tier：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Apps&lt;/strong&gt;（顶部）：应用界面和应用视角看到的依赖，包括 General agent services、Browser/RUM、mobile，以及 &lt;code&gt;Virtual*&lt;/code&gt; targets（database、cache、MQ、gateway、GenAI）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Middleware&lt;/strong&gt;：数据和消息服务、网关，以及自观测组件，包括 MySQL、PostgreSQL、Redis、Kafka、RocketMQ、APISIX、Nginx、SkyWalking SO11Y components 和云托管数据服务。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Mesh&lt;/strong&gt;：承载应用流量的 mesh，包括 Istio control/data plane、Cilium、Envoy AI Gateway。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Infra&lt;/strong&gt;（底部）：其余内容运行在其上的平台，包括 Kubernetes、hosts、VMs。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OAP 上报的每个 Layer 都会归入一个 tier。Horizon 还没分类的新 Layer，比如 OAP 新增的 Layer，会归入 &lt;strong&gt;failover tier&lt;/strong&gt;（默认 Middleware）并带 &amp;ldquo;unclassified&amp;rdquo; 标记。这样它会出现，运维人员也可以重新分配，而不是静默从地图上消失。右侧面板对应整个堆栈：点击 tier 行可以把镜头移到对应位置，用眼睛开关一次显示或隐藏整个 tier（或单个 Layer），并查看当前可见服务数量。&lt;/p&gt;
&lt;h2 id=&#34;地图元素立方体zone-和流量&#34;&gt;地图元素：立方体、zone 和流量&lt;/h2&gt;
&lt;p&gt;每个服务对应一个 &lt;strong&gt;立方体&lt;/strong&gt;。立方体会在 tier 上按所属 Layer 聚成一个 &lt;strong&gt;zone&lt;/strong&gt;：半透明矩形使用该 Layer 的品牌色，并盖上项目 logo（Istio 的帆、Kubernetes 舵轮、数据库圆柱、队列图标），所以从任何视角都能辨认出 zone。带拓扑的 Layer（General、Service Mesh、Kubernetes Service、Cilium）会按 &lt;strong&gt;调用依赖&lt;/strong&gt; 摆放立方体：上游 caller 在一侧，下游服务在另一侧，对应 2D 服务地图的 3D 版本。没有拓扑的 Layer 则把立方体排成整齐网格。&lt;/p&gt;
&lt;p&gt;立方体下方的小 &lt;strong&gt;traffic&lt;/strong&gt; 标签显示该服务的实时主吞吐：应用和 mesh 服务是 requests per minute，数据服务是 queries 或 operations per second，并保留各自单位。只有镜头足够近、文字可辨认时标签才出现；缩远后会淡出，保持场景干净；选中的立方体总会显示它的数字。&lt;/p&gt;
&lt;h2 id=&#34;用-beacon-mode-突出告警服务&#34;&gt;用 Beacon mode 突出告警服务&lt;/h2&gt;
&lt;p&gt;当一个服务有 &lt;strong&gt;当前正在触发的告警&lt;/strong&gt; 时（Horizon 轮询最近 20 分钟，并只统计 service 作用域下仍在触发的告警），它的立方体会 &lt;strong&gt;红色脉冲&lt;/strong&gt;。这就是一个信标，即使隔着整个场景也能看到，而告警列表会独立刷新。&lt;/p&gt;
&lt;p&gt;在繁忙地图里，几百个立方体中的一个红点仍然可能难找，所以有 &lt;strong&gt;Beacon mode&lt;/strong&gt;。从工具栏打开后，所有 &lt;em&gt;健康&lt;/em&gt; 立方体都会变成深色 wireframe，只留下正在告警的服务发光。部署结构仍然清楚，但真正出问题的服务才有颜色。这个模式可以把鸟瞰图快速切成 incident triage 视图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-02-beacon-mode.webp&#34; alt=&#34;图 2：Beacon mode。所有健康立方体变成 wireframe 幽影，只有正在触发告警的服务保持红色发光。&#34;&gt;
图 2：Beacon mode 会把健康对象变暗成幽影，所以你只会看到正在触发的服务。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;线表示调用和层级关系&#34;&gt;线表示调用和层级关系&lt;/h2&gt;
&lt;p&gt;地图不只画节点，也画调用图：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;In-layer calls&lt;/strong&gt;：同一个 Layer 内两个服务之间的浅青色管线，并带沿线运动的数据包动画。这是每个 Layer 自己的内部调用图，始终开启。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-layer calls&lt;/strong&gt;：同一个 tier 上不同 Layer 服务之间的柔和琥珀色箭头，比如 Browser app 调用 Frontend、Frontend 调用 Virtual Database，方向从 caller 指向 callee。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hierarchy links&lt;/strong&gt;：这是让 3D 布局真正发挥价值的一类线。选中一个立方体后，粗灰色管线会连接 &lt;strong&gt;同一个逻辑服务在不同 tier 上的不同形态&lt;/strong&gt;：agent 看到的服务、mesh 看到的服务、Kubernetes service 看到的服务。它们表示 &lt;em&gt;身份&lt;/em&gt;，不是流量，所以默认隐藏；选中立方体后只显示与它相关的对象，并沿着 tier 一层层爬上去。这就是&lt;a href=&#34;/zh/2026-06-21-horizon-ui-topology-and-dependency/&#34;&gt;第三篇&lt;/a&gt;里的 Smartscape，只是放到了更适合表达层级关系的 3D 视角里。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-03-selected-hierarchy.webp&#34; alt=&#34;图 3：选中一个服务后，详情卡片出现在立方体旁边，跨 tier hierarchy links 亮起，把 Apps、Service Mesh 和 Infra tier 中的同一个逻辑服务连起来。&#34;&gt;
图 3：选中立方体后，身份链接沿 tier 爬升：agent、mesh 和 Kubernetes 各自看到的同一个服务。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;视角移动与选择&#34;&gt;视角移动与选择&lt;/h2&gt;
&lt;p&gt;拖动旋转，滚轮缩放，&lt;strong&gt;方向键&lt;/strong&gt; 或 &lt;strong&gt;WASD&lt;/strong&gt; 平移视角（按住 &lt;strong&gt;Shift&lt;/strong&gt; 步长更大）；左上角工具栏也为触控板提供同样动作的按钮。有一条刻意设计的规则值得知道：&lt;strong&gt;3D 场景里的点击永远不会移动视角&lt;/strong&gt;，只负责选择。点击一个立方体，它会高亮，旁边出现详情卡片（服务名称、tier、Layer，以及会在新标签页打开该服务 Layer 仪表盘的 &lt;strong&gt;Open dashboard&lt;/strong&gt; 按钮），它的 hierarchy links 也会亮起。移动视角的交互入口是 &lt;em&gt;侧边面板&lt;/em&gt; 和 &lt;em&gt;工具栏&lt;/em&gt;；点击 Layer 行，镜头会滑到它的 zone。把“选择”和“移动视角”分开，点击小立方体才会可靠，不会刚点中它就让它从光标下滑走。&lt;/p&gt;
&lt;h2 id=&#34;地图如何分阶段加载&#34;&gt;地图如何分阶段加载&lt;/h2&gt;
&lt;p&gt;整个部署的数据太大，不适合一次请求拉完，所以地图分阶段加载，底部细长的 &lt;strong&gt;timeline strip&lt;/strong&gt; 会实时展示进度：&lt;strong&gt;Services&lt;/strong&gt;（服务清单和所属 Layer）→ &lt;strong&gt;Templates&lt;/strong&gt;（哪些 Layer 带拓扑）→ &lt;strong&gt;Topologies&lt;/strong&gt;（每个有拓扑 Layer 的调用图）→ &lt;strong&gt;Hierarchy&lt;/strong&gt;（跨 tier 身份链接）→ &lt;strong&gt;Layout&lt;/strong&gt;（摆放立方体）→ &lt;strong&gt;Metrics&lt;/strong&gt;（按批次获取每个服务的流量，让立方体逐步亮起来）。点击任意阶段可以打开抽屉查看详情，也可以点击 Refresh 重新跑完整流程。&lt;/p&gt;
&lt;p&gt;两个设计降低了刷新成本。&lt;strong&gt;Hierarchy&lt;/strong&gt; 阶段是增量的：只有上次之后新增的服务需要探测，其余从缓存复用，所以稳定部署在这一步没有额外代价。场景还会按每个 Layer 的结构 hash 重新生成 key；结构没变时刷新会保留你的视角位置，只有服务清单或边真的变化时才重建布局。底层使用 &lt;a href=&#34;https://threejs.org/&#34;&gt;Three.js&lt;/a&gt; 加一层很薄的 Vue 封装，同类立方体共享 geometry 和 material。正是这些细节，让几百个服务也能在浏览器标签页里平滑渲染。&lt;/p&gt;
&lt;h2 id=&#34;地图结构来自配置&#34;&gt;地图结构来自配置&lt;/h2&gt;
&lt;p&gt;上面这些不是一张写死的 &amp;ldquo;3D 页面&amp;rdquo;。地图展示什么，由管理员在 &lt;code&gt;/admin/3d-map&lt;/code&gt; 的 &lt;strong&gt;结构化表单编辑器&lt;/strong&gt; 里编辑：tier、Layer、颜色和指标都通过表单控制，而不是直接写 JSON。你可以在里面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;用一个全局正则过滤 Layer&lt;/strong&gt;：匹配排除的内容会完全从地图上消失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安排 tier&lt;/strong&gt;：重命名、从上到下重排，并把每个 Layer 固定到某个 tier 上，同时指定 failover tier，避免内容静默丢失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对 Layer 分组&lt;/strong&gt;：把多个相关 Layer，比如 SkyWalking 自观测组件，聚成一个带标签的 block，每个成员仍然保留自己的颜色。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为每个 Layer 配色并选择流量指标&lt;/strong&gt;：配置 MQE 表达式、label 和单位；默认值会从该 Layer 的仪表盘模板里初始化，所以大多数 Layer 一开始就能显示合理数字。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Horizon 自带一份默认配置，所以地图开箱就有用。你的修改会以本地 draft 保存，直到点击 &lt;strong&gt;Check diff &amp;amp; push&lt;/strong&gt; 发布到 OAP。它使用和仪表盘相同的 draft → preview → publish 模型，也支持同样的 Export/Import，用于备份或在部署之间迁移配置。地图本身是只读 &lt;strong&gt;observe&lt;/strong&gt; 界面，可以直接运行在当前 OAP 上；发布用于控制地图形态的配置，则属于后续文章会完整介绍的配置化定制。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/screenshots/horizon-0.7.0/p05-3dmap-04-config-editor.webp&#34; alt=&#34;图 4：3D-map 配置。/admin/3d-map 提供结构化编辑器，用于配置 tier、每个 Layer 的颜色和流量指标、Layer group 和全局过滤器，并使用常规 draft → Check diff &amp;amp; push 发布流程。&#34;&gt;
图 4：地图是配置，不是代码。tier、颜色和每个 Layer 的流量指标都以表单方式编辑，然后发布到 OAP。&lt;/br&gt;&lt;/p&gt;
&lt;h2 id=&#34;后续阅读&#34;&gt;后续阅读&lt;/h2&gt;
&lt;p&gt;3D 地图是全局入口；2D 的按 Layer 页面仍然是最完整的服务地图。查看它只需要读权限（&lt;code&gt;infra-3d:read&lt;/code&gt;，内置 viewer 角色及以上持有）；调整它需要和仪表盘相同的写权限。字段参考，包括 tier、配置结构和加载阶段，可以看 &lt;a href=&#34;https://skywalking.apache.org/docs/skywalking-horizon-ui/next/operate/infra-3d-map/&#34;&gt;3D Infrastructure Map 文档&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下一篇回到单个请求：Trace Explorer 会用分布图、瀑布图和调用树帮你定位慢调用。&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 基于 SkyWalking 10.4 的大模型应用监控：洞察 LLM 的性能与成本</title>
      <link>/zh/2026-04-05-virtual-genai-monitoring/</link>
      <pubDate>Sun, 05 Apr 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-04-05-virtual-genai-monitoring/</guid>
      <description>
        
        
        &lt;h1 id=&#34;问题当应用开始吞噬大模型监控却留下了盲区&#34;&gt;问题：当应用开始“吞噬”大模型，监控却留下了盲区&lt;/h1&gt;
&lt;p&gt;随着生成式 AI（GenAI）在企业业务中的深度渗透，开发者正面临一个尴尬的局面：我们在应用中通过&lt;code&gt;Spring AI&lt;/code&gt;或&lt;code&gt;OpenAI SDK&lt;/code&gt;快速集成了强大的大模型能力，但对于这些调用的实际表现却几乎一无所知。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;成本与性能的“黑盒”：昂贵的模型真的更具性价比吗？&lt;/strong&gt;&lt;br&gt;
面对高昂的大模型账单，我们往往只知道把钱交给了某个&lt;code&gt;Provider&lt;/code&gt;，却算不清这笔账在应用内部的“投入产出比”。
盲目的选型升级：为了追求更好的体验，你可能将业务默认切换到了成本更高的旗舰模型。但在具体的业务场景下，花费数倍的 Token 成本，它真的能在真实请求中带来更低的延迟和更快的 TTFT(Time to First Token) 吗？
缺乏真实的评估基准：脱离了真实的业务请求，单纯看官网的 Benchmark 意义不大，你需要知道在实际的 Prompt 长度和并发压力下，同一&lt;code&gt;Provider&lt;/code&gt;下的哪个模型能在“Token/Cost 消耗”与“响应速度”之间达到完美的平衡。如果没有应用侧的数据支撑，你根本无从判断哪款模型才是当前业务的最优解。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;消失的“黄金超时时间”&lt;/strong&gt;&lt;br&gt;
很多团队在代码里给 LLM 调用设置超时（Timeout）时，往往是拍脑袋决定（比如 30s 或 60s）。&lt;br&gt;
设太短：长文本生成或模型高峰期时，请求会被频繁强行中断，导致业务失败率飙升。&lt;br&gt;
设太长：如果下游供应商出现故障（卡死），大量的请求会堆积在应用内存中，阻塞执行线程，最终导致整个 Java 应用甚至微服务集群的瘫痪。
只有真正掌握了预估的整体调用延迟（P99/P95 Latency），你才能基于数据而非直觉，为不同模型设置最合理的超时策略。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;被忽视的体验杀手：TTFT&lt;/strong&gt;&lt;br&gt;
在 GenAI 场景下，用户对“快”的感知并不完全取决于整个对话结束的总耗时，而取决于**“第一行字什么时候跳出来”**。
一个总耗时 10 秒但 TTFT 仅 500ms 的流式响应，给用户的观感是“秒回”。
一个总耗时 5 秒但 TTFT 需要 4s 的非流式响应，给用户的观感却是“卡死”。
如果你的观测系统只能看到总耗时，你就会漏掉最核心的 UX 指标，无法解释为什么用户反馈“AI 很慢”即便总耗时看起来还行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;SkyWalking 10.4：应用视角的“数字仪表盘”&lt;/strong&gt;&lt;br&gt;
Apache SkyWalking 自 10.4 版本引入的 Virtual GenAI 能力，正是为了解决应用层侧的这种“观测真空”。它不依赖任何外部网关，直接通过应用侧探针（如 Java Agent）在客户端视角采集最真实的数据。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;精准的延迟分布（Latency Percentiles）：通过 P50、P90、P99 等多维指标，帮你勾勒出 LLM 调用的真实波动曲线，为设置“动态超时时间”提供科学依据。&lt;/li&gt;
&lt;li&gt;核心 UX 指标——TTFT 监控：原生支持流式（Streaming）调用的首字延迟统计。通过对比不同 Provider 或不同模型的 TTFT，你可以优化提示词（Prompt）策略或切换更快的模型，确保用户体验始终在线。&lt;/li&gt;
&lt;li&gt;多维度的模型“画像”分析：在 Provider 和 Model 两个维度上，将 Token 消耗、预估成本与性能指标深度对齐。这让你不再看供应商全网的“理想平均数”，而是看清你的应用在调用特定模型时的真实表现，从而在复杂的模型生态中选出最具性价比的选型方案。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;虚拟-genai-观测&#34;&gt;虚拟 GenAI 观测&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;虚拟 GenAI&lt;/strong&gt; 代表了由探针插件检测到的生成式 AI 服务节点。GenAI 操作的性能指标均基于 &lt;strong&gt;GenAI 客户端视角&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;例如，Java 探针中的 &lt;strong&gt;Spring AI 插件&lt;/strong&gt;可以检测一次对话补全（Chat Completion）请求的响应延迟。随后，SkyWalking 将在仪表盘中展示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;流量与成功率&lt;/strong&gt; (CPM &amp;amp; SLA)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;响应延迟&lt;/strong&gt; (Latency &amp;amp; TTFT)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token 消耗&lt;/strong&gt; (Input/Output)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;预估成本&lt;/strong&gt; (Estimated Cost)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如图：
&lt;img src=&#34;provider-dashboard-1.png&#34; alt=&#34;provider-dashboard-1.png&#34;&gt;
&lt;img src=&#34;provider-dashboard-2.png&#34; alt=&#34;provider-dashboard-2.png&#34;&gt;
&lt;img src=&#34;provider-dashboard-3.png&#34; alt=&#34;provider-dashboard-3.png&#34;&gt;
&lt;img src=&#34;model-dashboard-1.png&#34; alt=&#34;model-dashboard-1.png&#34;&gt;
&lt;img src=&#34;model-dashboard-2.png&#34; alt=&#34;model-dashboard-2.png&#34;&gt;
&lt;img src=&#34;model-dashboard-3.png&#34; alt=&#34;model-dashboard-3.png&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;原理&#34;&gt;原理&lt;/h1&gt;
&lt;p&gt;当 SkyWalking Java Agent 或 OTLP 探针拦截到主流 AI 框架（如 Spring AI、OpenAI SDK 等）的调用时，将Trace 数据上报至 SkyWalking OAP。
OAP会基于这些 Trace 自动完成数据的聚合与计算。最终会生成 Provider（服务商）与 Model（模型）两个维度的各类性能指标，并直接渲染填充至内置的 Virtual-GenAI 仪表盘中。&lt;/p&gt;
&lt;h1 id=&#34;安装配置&#34;&gt;安装配置&lt;/h1&gt;
&lt;h2 id=&#34;要求&#34;&gt;要求&lt;/h2&gt;
&lt;h3 id=&#34;版本要求&#34;&gt;版本要求&lt;/h3&gt;
&lt;p&gt;● SkyWalking Java Agent: &amp;gt;= 9.7
● SkyWalking Oap: &amp;gt;= 10.4&lt;/p&gt;
&lt;h3 id=&#34;语义规范与兼容性&#34;&gt;语义规范与兼容性&lt;/h3&gt;
&lt;p&gt;SkyWalking 虚拟 GenAI 遵循&lt;code&gt; OpenTelemetry GenAI&lt;/code&gt; 语义规范。OAP 将根据以下标准识别 GenAI 相关 Span：&lt;/p&gt;
&lt;h4 id=&#34;skywalking-java-agent&#34;&gt;SkyWalking Java Agent&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;上报的 Span 必须为 Exit 类型，其 SpanLayer 属性需设定为 GENAI,包含&lt;code&gt;gen_ai.response.model&lt;/code&gt; 标签。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;输出otlp--zipkin格式数据的探针&#34;&gt;输出OTLP / Zipkin格式数据的探针&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;上报的 Span 中包含 &lt;code&gt;gen_ai.response.model&lt;/code&gt; 标签。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;具体可以参考e2e配置&lt;br&gt;
&lt;a href=&#34;https://github.com/apache/skywalking/blob/master/test/e2e-v2/cases/virtual-genai/docker-compose.yml&#34;&gt;SkyWalking Java Agent上报数据&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://github.com/apache/skywalking/blob/master/test/e2e-v2/cases/otlp-virtual-genai/docker-compose.yml&#34;&gt;探针上报OTLP格式数据&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://github.com/apache/skywalking/blob/master/test/e2e-v2/cases/zipkin-virtual-genai/docker-compose.yml&#34;&gt;探针上报Zipkin格式数据&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;genai-预估成本配置&#34;&gt;GenAI 预估成本配置&lt;/h1&gt;
&lt;h2 id=&#34;概览&#34;&gt;概览&lt;/h2&gt;
&lt;p&gt;SkyWalking 提供了一个内置的&lt;a href=&#34;https://github.com/apache/skywalking/blob/master/oap-server/server-starter/src/main/resources/gen-ai-config.yml&#34;&gt;GenAI计费配置文件&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;该配置定义了SkyWalking 如何将 Trace 数据中的模型名称映射到对应的供应商，并估算每次 LLM 调用的 Token 成本。估算成本将与 Trace 和指标数据一起显示在 SkyWalking UI 中，帮助用户直观了解 GenAI 使用带来的 预估费用影响。
重要提示: 此文件中的定价仅用于成本估算，不得视为实际账单或发票金额。建议用户定期从供应商官方定价页面核实最新费率。&lt;/p&gt;
&lt;h2 id=&#34;配置结构&#34;&gt;配置结构&lt;/h2&gt;
&lt;h3 id=&#34;top-字段&#34;&gt;Top 字段&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;字段&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;类型&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;描述&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;last-updated&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;date&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;定价数据的最后更新日期。所有价格均基于该日期前各厂商官网公布的公开计费标准。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;providers&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;GenAI 厂商定义列表。每个厂商条目下包含匹配规则（matching rules）以及具体的模型计费信息（model pricing）。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;provider-定义&#34;&gt;provider 定义&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;providers&lt;/code&gt; 下的每个条目定义一个 GenAI 供应商：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;providers&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;provider&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&amp;lt;provider-name&amp;gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;prefix-match&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &amp;lt;prefix-1&amp;gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &amp;lt;prefix-2&amp;gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;models&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&amp;lt;model-name&amp;gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;aliases&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&amp;lt;alias-1&amp;gt;, &amp;lt;alias-2&amp;gt;]&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;input-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&amp;lt;cost&amp;gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;output-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&amp;lt;cost&amp;gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;字段 (Field)&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;类型 (Type)&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;必填 (Required)&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;描述 (Description)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;provider&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;是&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;供应商标识（如 &lt;code&gt;openai&lt;/code&gt;, &lt;code&gt;anthropic&lt;/code&gt;, &lt;code&gt;gemini&lt;/code&gt;）。在 SkyWalking 中作为虚拟 GenAI 服务名显示。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;prefix-match&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;list[string]&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;是&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;用于将模型名称匹配到该供应商的前缀列表。如果 Trace 数据中的模型名以其中任一前缀开头，则会被映射到该供应商。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;models&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;list[model]&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;否&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;包含定价信息的模型定义列表。如果省略，系统仍能识别供应商，但不会进行成本估算。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;model-定义&#34;&gt;model 定义&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;models&lt;/code&gt; 下的每个条目定义特定模型的定价：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;字段 (Field)&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;类型 (Type)&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;必填 (Required)&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;描述 (Description)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;是&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;用于匹配的标准模型名称。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;aliases&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;list[string]&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;否&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;应解析为同一计费条目的备选名称。当供应商使用不同的命名习惯时非常有用（参见“模型别名”部分）。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;input-estimated-cost-per-m&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;否&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;每 1,000,000（一百万）输入（Prompt）Token 的预估成本。默认单位为 USD。&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;output-estimated-cost-per-m&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;否&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;每 1,000,000（一百万）输出（Completion）Token 的预估成本。默认单位为 USD。&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;模型匹配机制&#34;&gt;模型匹配机制&lt;/h2&gt;
&lt;h3 id=&#34;供应商级前缀匹配&#34;&gt;供应商级前缀匹配&lt;/h3&gt;
&lt;p&gt;当 SkyWalking 接收到包含 GenAI 调用的 Trace 时，会按照以下优先级顺序来确定供应商（Provider）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;gen_ai.provider.name&lt;/code&gt; 标签：首先检索此标签。它是&lt;code&gt;OpenTelemetry&lt;/code&gt;最新的语义规范。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gen_ai.system&lt;/code&gt; 标签：如果缺少上述标签，系统将回退到此旧版（Legacy）标签。注意：此标签仅在处理 OTLP 或 Zipkin 协议的数据时会被解析，主要用于兼容旧版的 Python 自动仪表化等库。&lt;/li&gt;
&lt;li&gt;前缀匹配 (Prefix Matching)：若上述两个标签均不存在，&lt;code&gt;SkyWalking&lt;/code&gt; 会读取 &lt;code&gt;gen-ai-config.yml&lt;/code&gt; 中定义的 prefix-match 规则，通过匹配 模型名称 (Model Name) 来尝试识别供应商。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;provider&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;openai&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;prefix-match&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- gpt&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;任何以 gpt 开头的模型名称（如 gpt-4o, gpt-4.1-mini, gpt-5-nano）都会被映射到 openai 供应商。
一个供应商可以拥有多个前缀：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;provider&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;tencent&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;prefix-match&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- hunyuan&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- Tencent&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;模型级最长前缀匹配-model-level-longest-prefix-matching&#34;&gt;模型级最长前缀匹配 (Model-Level Longest-Prefix Matching)&lt;/h3&gt;
&lt;p&gt;一旦确定了供应商，SkyWalking 会使用基于前缀树 (Trie) 的最长前缀匹配算法来查找最佳的模型计费条目。这至关重要，因为 LLM 供应商在 API 响应中返回的模型名称通常包含版本号或时间戳，与配置中的基础模型名称有所不同。
示例： 假设 OpenAI 的配置条目如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;models&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gpt-4o&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;input-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;2.5&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;output-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;10.0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gpt-4o-mini&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;input-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0.15&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;output-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0.6&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其匹配行为如下表所示：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;Trace 中的模型名称&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;匹配的配置条目&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;原因&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;完全匹配&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o-2024-08-06&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;最长前缀为 &lt;code&gt;gpt-4o&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o-mini&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o-mini&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;完全匹配（比 &lt;code&gt;gpt-4o&lt;/code&gt; 更长的前缀优先）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o-mini-2024-07-18&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gpt-4o-mini&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;最长前缀为 &lt;code&gt;gpt-4o-mini&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这种机制确保了 API 返回的带有版本的模型名称能够被正确映射到相应的价格档位，而无需在配置文件中维护精确的全名。&lt;/p&gt;
&lt;h3 id=&#34;模型别名-model-aliases&#34;&gt;模型别名 (Model Aliases)&lt;/h3&gt;
&lt;p&gt;部分供应商在 API 响应和官方文档中会使用不同的命名规范。例如，Anthropic 的模型在 Trace 中可能显示为 claude-4-sonnet 或 claude-sonnet-4。通过 aliases 字段，可以让单个计费条目同时支持这两种配置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;claude-4-sonnet&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;aliases&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;claude-sonnet-4]&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;input-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;3.0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;output-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;15.0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在这种配置下，&lt;code&gt;claude-4-sonnet&lt;/code&gt; 和 &lt;code&gt;claude-sonnet-4&lt;/code&gt;（以及任何带有版本的变体，如 &lt;code&gt;claude-sonnet-4-20250514&lt;/code&gt;）都会解析为同一个计费条目。&lt;br&gt;
&lt;strong&gt;注意&lt;/strong&gt;： 别名同样参与最长前缀匹配。因此，&lt;code&gt;claude-sonnet-4-20250514&lt;/code&gt; 会匹配到别名 &lt;code&gt;claude-sonnet-4&lt;/code&gt;，进而解析到 &lt;code&gt;claude-4-sonnet&lt;/code&gt; 的定价信息。&lt;/p&gt;
&lt;h2 id=&#34;自定义配置&#34;&gt;自定义配置&lt;/h2&gt;
&lt;p&gt;添加新供应商 (Adding a New Provider)
要添加默认配置中未包含的供应商：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;providers&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# ... 现有供应商 ...&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;provider&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;ollama&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;prefix-match&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- mymodel&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;models&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;mymodel-large&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;input-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;1.0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;output-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;5.0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;mymodel-small&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;input-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0.1&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;output-estimated-cost-per-m&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0.5&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;针对OTLP/zipkin的数据，新增了单独的estimated tag, 可以在UI上看到这次GenAI调用消耗的cost。&lt;br&gt;
&lt;img src=&#34;otlp-estimated-tag.png&#34; alt=&#34;otlp-estimated-tag&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;主要指标&#34;&gt;主要指标&lt;/h1&gt;
&lt;h2 id=&#34;1-provider-level-服务商维度&#34;&gt;1. Provider Level (服务商维度)&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;指标 ID&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;描述&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;含义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_cpm&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Calls Per Minute&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;每分钟请求数 (吞吐量)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_sla&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Success Rate&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;请求成功率&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_resp_time&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Avg Response Time&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;平均响应耗时&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_latency_percentile&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Latency Percentiles&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;响应耗时百分位数 (P50, P75, P90, P95, P99)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_input_tokens_sum/avg&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Input Token Usage&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;输入 Token 的总和及平均值&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_output_tokens_sum/avg&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Output Token Usage&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;输出 Token 的总和及平均值&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_provider_total_estimated_cost/avg&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Estimated Cost&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;预估总成本及次均成本&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;2-model-level-模型维度&#34;&gt;2. Model Level (模型维度)&lt;/h2&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;指标 ID&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;描述&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;含义&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_call_cpm&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Calls Per Minute&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;该特定模型的每分钟请求数&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_sla&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Success Rate&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;模型请求成功率&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_latency_avg/percentile&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Latency&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;模型响应耗时的平均值及百分位数&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_ttft_avg/percentile&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;TTFT&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;首个token响应时间 (仅限流式传输 Streaming)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_input_tokens_sum/avg&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Input Token Usage&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;该模型的输入 Token 消耗详情&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_output_tokens_sum/avg&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Output Token Usage&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;该模型的输出 Token 消耗详情&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;gen_ai_model_total_estimated_cost/avg&lt;/code&gt;&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;Estimated Cost&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;该模型的预估总成本及次均成本&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;建议使用场景&#34;&gt;建议使用场景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;性能评估：利用 响应延迟（Latency） 和 首字响应时间（TTFT） 指标，分析模型推理效率及终端用户交互体验。&lt;/li&gt;
&lt;li&gt;Token 监控：实时监控 输入（Input）与输出（Output）Token 的消耗，用于分析不同业务场景下的资源占用情况。&lt;/li&gt;
&lt;li&gt;成本预警：支持基于 预估成本（Cost） 或 Token 消耗量 配置告警阈值，及时发现异常调用，防止成本超支。&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 用 Apache SkyWalking 监控 Envoy AI Gateway</title>
      <link>/zh/2026-04-02-envoy-ai-gateway-monitoring/</link>
      <pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-04-02-envoy-ai-gateway-monitoring/</guid>
      <description>
        
        
        &lt;h2 id=&#34;问题llm-流量缺乏统一观测&#34;&gt;问题：LLM 流量缺乏统一观测&lt;/h2&gt;
&lt;p&gt;LLM 流量正在成为生产基础设施中不可忽视的一部分。团队同时在调用 OpenAI、Anthropic、AWS Bedrock、Azure OpenAI、Google Gemini——往往还不止一个提供商。但大多数组织对这些流量缺乏统一的可见性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token 费用失控&lt;/strong&gt;，却不知道哪个团队、哪个模型、哪个提供商在烧钱。一个配置不当的 prompt 模板就可能在无人察觉的情况下烧掉几千美元。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提供商故障引发连锁反应。&lt;/strong&gt; OpenAI 出问题的那一小时，你的应用也跟着挂——而你既没有故障切换的可见性，也无法自动切换提供商。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺乏统一指标。&lt;/strong&gt; 延迟、首 Token 耗时（TTFT）、每 Token 输出耗时（TPOT）、Token 用量、错误率——每个提供商的报告方式都不一样，有些甚至不提供。没有一个统一的面板能做对比。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这和十年前微服务面临的可观测性困境如出一辙。当时的解法是服务网格和内置遥测的 API 网关。对 AI 工作负载来说，答案就是 AI 网关。&lt;/p&gt;
&lt;h2 id=&#34;为什么选择-ai-网关&#34;&gt;为什么选择 AI 网关&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://aigateway.envoyproxy.io/&#34;&gt;Envoy AI Gateway&lt;/a&gt; 是一个开源 AI 网关，构建在 &lt;a href=&#34;https://www.envoyproxy.io/&#34;&gt;Envoy Proxy&lt;/a&gt; 和 &lt;a href=&#34;https://gateway.envoyproxy.io/&#34;&gt;Envoy Gateway&lt;/a&gt; 之上。底层就是云原生世界里已经广泛部署的 Envoy，天然具备基础设施级的稳定性和性能。&lt;/p&gt;
&lt;p&gt;核心能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;多提供商路由&lt;/strong&gt; —— 支持 16+ AI 提供商（OpenAI、Anthropic、AWS Bedrock、Azure OpenAI、Google Gemini、Mistral、Cohere、DeepSeek 等），统一 API 接入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;基于 Token 的限流&lt;/strong&gt; —— 按 Token 消耗限流，而不只是按请求数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提供商故障切换&lt;/strong&gt; —— 某个提供商宕机或响应慢时自动切换。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;模型虚拟化&lt;/strong&gt; —— 抽象模型名称，让应用与具体提供商解耦。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;两层架构&lt;/strong&gt; —— 参考架构包含一个集中入口网关（Tier 1）负责认证和全局路由，以及每集群网关（Tier 2）负责推理优化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CNCF 生态原生&lt;/strong&gt; —— 运行在 Kubernetes 上，兼容现有的 Envoy Filter、WASM 插件和标准 Kubernetes Gateway API 资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Envoy AI Gateway 原生支持通过 OTLP 发送 GenAI 指标和访问日志，遵循 &lt;a href=&#34;https://opentelemetry.io/docs/specs/semconv/gen-ai/&#34;&gt;OpenTelemetry GenAI 语义约定&lt;/a&gt;，可以直接接入任何兼容 OpenTelemetry 的后端。&lt;/p&gt;
&lt;p&gt;从 SkyWalking 10.4.0 开始，OAP 原生接收和分析 Envoy AI Gateway 的 OTLP 指标和访问日志——中间不需要部署 OpenTelemetry Collector。&lt;/p&gt;
&lt;h2 id=&#34;数据流&#34;&gt;数据流&lt;/h2&gt;
&lt;p&gt;AI Gateway 通过 OTLP gRPC 直接将遥测数据推送到 SkyWalking：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;workflow.jpg&#34; alt=&#34;数据流&#34;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;应用&lt;/strong&gt; 通过 Envoy AI Gateway 发送 LLM API 请求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Envoy AI Gateway&lt;/strong&gt; 将请求路由到 AI 提供商（或 Ollama 这样的本地模型），同时记录 GenAI 指标（Token 用量、延迟、TTFT、TPOT）和访问日志。&lt;/li&gt;
&lt;li&gt;网关通过 &lt;strong&gt;OTLP gRPC&lt;/strong&gt; 直接将指标和日志推送到 &lt;strong&gt;SkyWalking OAP&lt;/strong&gt; 的 11800 端口。&lt;/li&gt;
&lt;li&gt;SkyWalking OAP 用 MAL 规则解析指标、用 LAL 规则解析访问日志，然后统一存储到 &lt;strong&gt;BanyanDB&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不需要 OpenTelemetry Collector。SkyWalking OAP 内置的 OTLP 接收器可以直接处理所有数据。&lt;/p&gt;
&lt;h2 id=&#34;本地体验&#34;&gt;本地体验&lt;/h2&gt;
&lt;p&gt;这个 Demo 使用 &lt;a href=&#34;https://ollama.com/&#34;&gt;Ollama&lt;/a&gt; 作为本地 LLM 后端，不需要任何 API Key 就能跑起来。&lt;a href=&#34;https://github.com/envoyproxy/ai-gateway/tree/main/cmd/aigw&#34;&gt;Envoy AI Gateway CLI&lt;/a&gt;（&lt;code&gt;aigw&lt;/code&gt;）提供独立运行模式，不依赖 Kubernetes，非常适合本地测试。&lt;/p&gt;
&lt;h3 id=&#34;前置条件&#34;&gt;前置条件&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Docker 和 Docker Compose&lt;/li&gt;
&lt;li&gt;主机上已安装 &lt;a href=&#34;https://ollama.com/&#34;&gt;Ollama&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;第一步启动-ollama&#34;&gt;第一步：启动 Ollama&lt;/h3&gt;
&lt;p&gt;让 Ollama 监听所有网络接口，以便 Docker 容器能访问到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#953800&#34;&gt;OLLAMA_HOST&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;0.0.0.0 ollama serve
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;拉取一个小模型用于测试：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ollama pull llama3.2:1b
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;第二步启动服务栈&#34;&gt;第二步：启动服务栈&lt;/h3&gt;
&lt;p&gt;创建 &lt;code&gt;docker-compose.yaml&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;services&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;banyandb&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;apache/skywalking-banyandb:0.10.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;container_name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;banyandb&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;17912:17912&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;standalone --stream-root-path /tmp/stream-data --measure-root-path /tmp/measure-data&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;healthcheck&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;CMD-SHELL&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;wget -qO- http://localhost:17913/api/healthz || exit 1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;interval&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;5s&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;timeout&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;3s&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;retries&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;10&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;oap&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;apache/skywalking-oap-server:10.4.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;container_name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;oap&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;depends_on&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;banyandb&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;condition&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;service_healthy&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;11800:11800&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;12800:12800&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;environment&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;SW_STORAGE&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;banyandb&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;SW_STORAGE_BANYANDB_TARGETS&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;banyandb:17912&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;healthcheck&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;test&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;CMD-SHELL&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;bash -c &amp;#39;echo &amp;gt; /dev/tcp/localhost/12800&amp;#39; || exit 1&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;interval&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;10s&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;timeout&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;5s&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;retries&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;30&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;start_period&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;60s&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ui&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;apache/skywalking-ui:10.4.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;container_name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;ui&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;depends_on&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;oap&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;condition&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;service_healthy&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;8080:8080&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;environment&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;SW_OAP_ADDRESS&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;http://oap:12800&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;aigw&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;envoyproxy/ai-gateway-cli:latest&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;container_name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;aigw&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;depends_on&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;oap&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;condition&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;service_healthy&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;environment&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OPENAI_BASE_URL=http://host.docker.internal:11434/v1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OPENAI_API_KEY=unused&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_SERVICE_NAME=my-ai-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_EXPORTER_OTLP_ENDPOINT=http://oap:11800&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_EXPORTER_OTLP_PROTOCOL=grpc&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_METRICS_EXPORTER=otlp&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_LOGS_EXPORTER=otlp&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_METRIC_EXPORT_INTERVAL=5000&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- OTEL_RESOURCE_ATTRIBUTES=job_name=envoy-ai-gateway,service.instance.id=aigw-1,service.layer=ENVOY_AI_GATEWAY&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;1975:1975&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;extra_hosts&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;host.docker.internal:host-gateway&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;run&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启动所有服务：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;等待所有服务变为健康状态（BanyanDB 先启动，然后是 OAP，最后是 UI 和 AI Gateway）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker compose ps
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;aigw&lt;/code&gt; 服务的关键 OTLP 配置：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;环境变量&lt;/th&gt;
          &lt;th&gt;值&lt;/th&gt;
          &lt;th&gt;用途&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;OTEL_SERVICE_NAME&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;my-ai-gateway&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;SkyWalking 中的服务名&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;http://oap:11800&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;SkyWalking OAP gRPC 端点&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;OTEL_EXPORTER_OTLP_PROTOCOL&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;grpc&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;OTLP 传输协议&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;OTEL_METRICS_EXPORTER&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;otlp&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;启用指标推送&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;OTEL_LOGS_EXPORTER&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;otlp&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;启用访问日志推送&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;OTEL_RESOURCE_ATTRIBUTES&lt;/code&gt; 必须包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;job_name=envoy-ai-gateway&lt;/code&gt; —— MAL/LAL 规则的路由标签&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service.instance.id=&amp;lt;id&amp;gt;&lt;/code&gt; —— 实例标识&lt;/li&gt;
&lt;li&gt;&lt;code&gt;service.layer=ENVOY_AI_GATEWAY&lt;/code&gt; —— 将日志路由到 AI Gateway LAL 规则&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MAL 和 LAL 规则在 SkyWalking OAP 中默认启用，不需要额外配置。&lt;/p&gt;
&lt;h3 id=&#34;第三步运行-demo-应用&#34;&gt;第三步：运行 Demo 应用&lt;/h3&gt;
&lt;p&gt;创建一个简单的 Python 应用，通过 AI Gateway 发送请求（&lt;code&gt;app.py&lt;/code&gt;）。
它混合了普通请求、流式请求（用于产生 TTFT/TPOT 指标）和错误请求（不存在的模型 → HTTP 404，始终会被 LAL 采样策略捕获）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;time&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;random&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#24292e&#34;&gt;requests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GATEWAY &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;http://localhost:1975&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;HEADERS &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Bearer unused&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;questions &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;What is Apache SkyWalking? Answer in one sentence.&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;What is Envoy Proxy used for? Answer in one sentence.&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;What are the benefits of an AI gateway? Answer in two sentences.&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;Explain observability in three sentences.&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;chat&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;model&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; question&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; stream&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;False&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    resp &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; requests&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;post&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;GATEWAY&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;/v1/chat/completions&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        json&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;model&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; model&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;messages&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;[{&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; question&lt;span style=&#34;color:#1f2328&#34;&gt;}],&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;stream&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt; stream&lt;span style=&#34;color:#1f2328&#34;&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        headers&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;HEADERS&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; timeout&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; stream&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;stream&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#cf222e&#34;&gt;if&lt;/span&gt; stream&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chunks &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#cf222e&#34;&gt;for&lt;/span&gt; line &lt;span style=&#34;color:#0550ae&#34;&gt;in&lt;/span&gt; resp&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;iter_lines&lt;span style=&#34;color:#1f2328&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#cf222e&#34;&gt;if&lt;/span&gt; line&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                chunks&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;append&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;line&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;decode&lt;span style=&#34;color:#1f2328&#34;&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt; resp&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;[streamed &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;len&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;chunks&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt; chunks]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#cf222e&#34;&gt;return&lt;/span&gt; resp&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;status_code&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; resp&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;json&lt;span style=&#34;color:#1f2328&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;True&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    r &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;random&lt;span style=&#34;color:#1f2328&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#cf222e&#34;&gt;if&lt;/span&gt; r &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;0.2&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57606a&#34;&gt;# Error request: non-existent model triggers 404&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        status&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; chat&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;non-existent-model&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6639ba&#34;&gt;print&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;[error] model=non-existent-model status=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;status&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#cf222e&#34;&gt;elif&lt;/span&gt; r &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;0.5&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57606a&#34;&gt;# Streaming request — generates TTFT and TPOT metrics&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        q &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;choice&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;questions&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        status&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; info &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; chat&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;llama3.2:1b&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; q&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; stream&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;True&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6639ba&#34;&gt;print&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;[stream] status=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;status&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;info&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#cf222e&#34;&gt;else&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#57606a&#34;&gt;# Normal non-streaming request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        q &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; random&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;choice&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;questions&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        status&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; body &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; chat&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;llama3.2:1b&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; q&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        answer &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; body&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;choices&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;[{}])[&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{})&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)[:&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;80&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        tokens &lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt; body&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;get&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;usage&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#1f2328&#34;&gt;{})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#6639ba&#34;&gt;print&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;f&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;[ok] status=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;status&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt; tokens=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;tokens&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt; answer=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;{&lt;/span&gt;answer&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;...&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    time&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;sleep&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;random&lt;span style=&#34;color:#0550ae&#34;&gt;.&lt;/span&gt;randint&lt;span style=&#34;color:#1f2328&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#0550ae&#34;&gt;30&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;运行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pip install requests
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python app.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;应用通过 1975 端口与 AI Gateway 通信，AI Gateway 再路由到 Ollama。每次请求都会产生 GenAI 指标（Token 用量、延迟、TTFT、TPOT）和访问日志，由网关通过 OTLP 推送到 SkyWalking。&lt;/p&gt;
&lt;p&gt;错误请求（不存在的模型 → HTTP 404）始终会被访问日志采样策略捕获，所以在 SkyWalking 的日志视图中一定能看到。&lt;/p&gt;
&lt;h3 id=&#34;第四步在-skywalking-ui-中查看&#34;&gt;第四步：在 SkyWalking UI 中查看&lt;/h3&gt;
&lt;p&gt;打开 &lt;a href=&#34;http://localhost:8080&#34;&gt;http://localhost:8080&lt;/a&gt;，选择 &lt;strong&gt;GenAI &amp;gt; Envoy AI Gateway&lt;/strong&gt; 菜单。&lt;/p&gt;
&lt;p&gt;服务列表显示 &lt;code&gt;my-ai-gateway&lt;/code&gt;，可以一览 CPM、延迟和 Token 速率：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen-1.png&#34; alt=&#34;服务列表&#34;&gt;&lt;/p&gt;
&lt;p&gt;点击进入服务详情，查看完整仪表盘——请求 CPM、延迟（平均值 + 百分位数）、输入/输出 Token 速率、TTFT 和 TPOT：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen-2.png&#34; alt=&#34;服务仪表盘&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Providers&lt;/strong&gt; 标签页按 AI 提供商维度展示指标：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen-3.png&#34; alt=&#34;提供商维度&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Models&lt;/strong&gt; 标签页展示每个模型的指标，包括 TTFT 和 TPOT（仅流式请求）。注意 &lt;code&gt;unknown&lt;/code&gt; 模型条目——这些就是使用不存在模型的错误请求：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen-4.png&#34; alt=&#34;模型维度&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log&lt;/strong&gt; 标签页展示访问日志。采样策略会丢弃正常的成功响应，但始终保留错误（HTTP 404）和高 Token 消耗的请求：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;screen-5.png&#34; alt=&#34;访问日志&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;清理&#34;&gt;清理&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker compose down
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;kubernetes-生产部署&#34;&gt;Kubernetes 生产部署&lt;/h2&gt;
&lt;p&gt;生产环境中，Envoy AI Gateway 作为完整的 Kubernetes 控制器运行，以 Envoy Gateway 作为控制面。详见 &lt;a href=&#34;https://aigateway.envoyproxy.io/docs/getting-started/&#34;&gt;Envoy AI Gateway 入门指南&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;OTLP 配置方式相同——在 AI Gateway 的 External Processor 上设置 &lt;code&gt;OTEL_*&lt;/code&gt; 环境变量，指向 SkyWalking OAP 的 gRPC 端口（11800）。详见 &lt;a href=&#34;https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-envoy-ai-gateway-monitoring/&#34;&gt;SkyWalking Envoy AI Gateway 监控文档&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;不用-ai-网关也能做-genai-可观测&#34;&gt;不用 AI 网关也能做 GenAI 可观测&lt;/h2&gt;
&lt;p&gt;并非所有场景都需要 AI 网关。如果你的应用直接调用 LLM 提供商，SkyWalking 10.4.0 也提供了基于 &lt;a href=&#34;https://skywalking.apache.org/docs/main/next/en/setup/service-agent/virtual-genai/&#34;&gt;Virtual GenAI&lt;/a&gt; 层的 GenAI 可观测方案。&lt;/p&gt;
&lt;p&gt;任何接入了 SkyWalking、OpenTelemetry 或 Zipkin 探针的应用都能使用这个功能。只要 Trace 中携带 &lt;code&gt;gen_ai.*&lt;/code&gt; 标签（遵循 &lt;a href=&#34;https://opentelemetry.io/docs/specs/semconv/gen-ai/&#34;&gt;OpenTelemetry GenAI 语义约定&lt;/a&gt;），SkyWalking 就能从客户端视角推导出每提供商、每模型的指标：延迟、Token 用量、成功率和预估费用。&lt;/p&gt;
&lt;p&gt;对于 Java 应用，SkyWalking Java Agent（9.7+）内置了 Spring AI 插件，自动为 13+ 提供商（OpenAI、Anthropic、AWS Bedrock、Google GenAI、DeepSeek、Mistral 等）的调用注入正确的 &lt;code&gt;gen_ai.*&lt;/code&gt; Span 标签——不需要改代码。&lt;/p&gt;
&lt;p&gt;这与上面介绍的 Envoy AI Gateway 监控是不同的使用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Envoy AI Gateway 层&lt;/strong&gt;：基础设施级可观测——网关视角，覆盖所有流量。适合负责集中 AI 路由的平台团队。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Virtual GenAI 层&lt;/strong&gt;：应用级可观测——每个应用自己看到的 LLM 调用情况。适合没有集中网关的团队，或者需要按应用维度跟踪费用的场景。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://aigateway.envoyproxy.io/&#34;&gt;Envoy AI Gateway&lt;/a&gt; —— 项目官网和文档&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/envoyproxy/ai-gateway/tree/main/cmd/aigw&#34;&gt;Envoy AI Gateway CLI&lt;/a&gt; —— 本地开发用的独立运行模式&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-envoy-ai-gateway-monitoring/&#34;&gt;SkyWalking Envoy AI Gateway 监控&lt;/a&gt; —— OAP 配置文档&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://skywalking.apache.org/docs/main/next/en/setup/service-agent/virtual-genai/&#34;&gt;SkyWalking Virtual GenAI&lt;/a&gt; —— 客户端侧 GenAI 可观测&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://opentelemetry.io/docs/specs/semconv/gen-ai/&#34;&gt;OpenTelemetry GenAI 语义约定&lt;/a&gt; —— 两个项目共同遵循的指标/属性标准&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: Agentic Vibe Coding in a Mature OSS Project: What Worked, What Didn&#39;t</title>
      <link>/blog/2026-03-08-agentic-vibe-coding/</link>
      <pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate>
      <guid>/blog/2026-03-08-agentic-vibe-coding/</guid>
      <description>
        
        
        &lt;p&gt;Most &amp;ldquo;vibe coding&amp;rdquo; stories start with a greenfield project. This one doesn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;Apache SkyWalking is a 9-year-old observability platform with hundreds of production deployments, a complex DSL stack, and an external API surface that users have built dashboards, alerting rules, and automation scripts against. When I decided to replace the core scripting engine — purging the Groovy runtime from four DSL compilers — the constraint wasn&amp;rsquo;t &amp;ldquo;can AI write the code?&amp;rdquo; It was: &amp;ldquo;can AI write the code without breaking anything for existing users?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The answer turned out to be yes — &lt;strong&gt;~77,000 lines changed across 10 major PRs in about 5 weeks&lt;/strong&gt; — but only because the AI was tightly guided by a human who understood the project&amp;rsquo;s architecture, its compatibility contracts, and its users. This post is about the methodology: what worked, what didn&amp;rsquo;t, and what mature open-source maintainers should know before handing their codebase to AI agents.&lt;/p&gt;
&lt;h2 id=&#34;the-project-in-brief&#34;&gt;The Project in Brief&lt;/h2&gt;
&lt;p&gt;The task was to replace SkyWalking&amp;rsquo;s Groovy-based scripting engines (MAL, LAL, Hierarchy) with a unified ANTLR4 + Javassist bytecode compilation pipeline, matching the architecture already proven by the OAL compiler. The internal tech stack was completely overhauled; the external interface had to remain identical.&lt;/p&gt;
&lt;p&gt;Beyond the compiler rewrites, the scope included a new queue infrastructure (threads dropped from 36 to 15), virtual thread support for JDK 25+, and E2E test modernization. By conventional estimates, this was 5-8 months of senior engineer work.&lt;/p&gt;
&lt;p&gt;For the full technical details on the compiler architecture, see the &lt;a href=&#34;https://github.com/apache/skywalking/discussions/13716&#34;&gt;Groovy elimination discussion&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;what-is-agentic-vibe-coding&#34;&gt;What is Agentic Vibe Coding?&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Vibe coding&amp;rdquo; — a term coined by Andrej Karpathy — describes a style of programming where you describe intent and let AI write the code. It&amp;rsquo;s powerful for prototyping, but on its own, it&amp;rsquo;s risky for production systems.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Agentic vibe coding&lt;/strong&gt; takes this further: instead of a single AI autocomplete, you orchestrate multiple AI agents — each with different strengths — under your architectural direction, with automated tests as the safety net. In my workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code (plan mode)&lt;/strong&gt;: Primary coding agent. Plan mode lets me review the approach before any code is generated. This is critical for architectural decisions — I steer the design, Claude handles the implementation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemini&lt;/strong&gt;: Code review, concurrency analysis, and verification reports. Gemini reviewed every major PR for thread-safety, feature parity, and edge cases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex&lt;/strong&gt;: Autonomous task execution for well-defined, bounded work items.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key insight: &lt;strong&gt;AI writes the code, but the architect owns the design.&lt;/strong&gt; Without deep domain knowledge of SkyWalking&amp;rsquo;s internals, no AI could have planned these changes. Without AI, I couldn&amp;rsquo;t have executed them in 5 weeks.&lt;/p&gt;
&lt;h2 id=&#34;how-tdd-made-ai-coding-safe&#34;&gt;How TDD Made AI Coding Safe&lt;/h2&gt;
&lt;p&gt;The reason I could move this fast without breaking things comes down to one principle: &lt;strong&gt;never let AI code without a test harness.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My workflow for each major change:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Plan mode first&lt;/strong&gt;: Describe the goal to Claude, review the plan, iterate on architecture before any code is written.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write the test contract&lt;/strong&gt;: Define what &amp;ldquo;correct&amp;rdquo; means — for the compiler rewrites, this meant cross-version comparison tests that run every expression through both the old and new engines, asserting identical results across 1,290+ expressions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Let AI implement&lt;/strong&gt;: With the test contract in place, Claude can write thousands of lines of implementation code. If it&amp;rsquo;s wrong, the tests catch it immediately.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E2E as the final gate&lt;/strong&gt;: Every PR must pass the full E2E test suite — Docker-based integration tests that boot the entire server with real storage backends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI code review&lt;/strong&gt;: Gemini reviewed each PR for concurrency issues, thread-safety, and feature parity — catching things that unit tests alone wouldn&amp;rsquo;t find.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the opposite of &amp;ldquo;hope it works&amp;rdquo; vibe coding. The AI writes fast, the tests verify fast, and I steer the architecture. The feedback loop is tight enough that I can iterate on complex compiler code in minutes instead of days.&lt;/p&gt;
&lt;h2 id=&#34;lessons-learned&#34;&gt;Lessons Learned&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AI is a force multiplier, not a replacement.&lt;/strong&gt; Before any AI agent wrote a single line, a human had to define the replacement solution: &lt;em&gt;what&lt;/em&gt; gets replaced, &lt;em&gt;how&lt;/em&gt; it gets replaced, and — critically — &lt;em&gt;where the boundaries are&lt;/em&gt;. Which APIs could break? The internal compilation pipeline was fair game for a complete overhaul. Which APIs must stay aligned? Every external-facing DSL syntax, every YAML configuration key, every metrics name and tag structure had to remain byte-for-byte identical — because hundreds of deployed dashboards, alerting rules, and user scripts depend on them. Drawing these boundaries required deep knowledge of the codebase and its users. AI executed the plan at extraordinary speed, but the plan itself — the scope, the invariants, the compatibility contract — had to come from a human who understood the blast radius of every change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plan mode is non-negotiable for architectural work.&lt;/strong&gt; Letting AI jump straight to code on a compiler rewrite would be a disaster. Plan mode&amp;rsquo;s strength is that it collects code context — scanning imports, tracing call chains, mapping class hierarchies — and uses that context to help me fill in implementation details I&amp;rsquo;d otherwise have to look up manually. But it can&amp;rsquo;t tell you the design principles. That direction had to come from me, stated clearly upfront, so the AI&amp;rsquo;s planning stayed on the right track instead of optimizing toward a locally reasonable but architecturally wrong solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Know when to hit ESC.&lt;/strong&gt; Claude has a clear tendency to dive deep into solution code writing once it starts — and it won&amp;rsquo;t stop on its own when it encounters something that conflicts with the original plan&amp;rsquo;s concept. Instead of pausing to flag the conflict, it will push forward, improvising around the obstacle in ways that silently violate the design intent. I had to learn to watch for this: when Claude&amp;rsquo;s output started drifting from the plan, I&amp;rsquo;d manually cancel the task (ESC), call it off, identify where the plan and reality diverged, adjust the plan, and restart. This interrupt-replan cycle was a regular part of the workflow, not an exception. The architect has to stay in the loop — not just at planning time, but during execution — because AI agents don&amp;rsquo;t yet know when to stop and ask.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spec-driven testing is necessary but not sufficient — the logic workflow matters more.&lt;/strong&gt; It&amp;rsquo;s tempting to think that if you define the input/output spec clearly enough, AI can fill in the implementation and tests will catch any mistakes. I tried this. It doesn&amp;rsquo;t work for anything non-trivial. During the expression compiler rewrite, Claude would sometimes change code in unreasonable ways just to make the spec tests pass — the inputs went in, the expected outputs came out, and everything looked green. But the internal logic was wrong: inconsistent with the design patterns the rest of the codebase relied on, impossible to extend, or solving the specific test case through a hack rather than a general mechanism. A spec only checks &lt;em&gt;what&lt;/em&gt; the code produces; it says nothing about &lt;em&gt;how&lt;/em&gt; the code produces it. For a mature project, the &amp;ldquo;how&amp;rdquo; matters enormously — the solution needs to be consistent with the existing architecture, widely adoptable by contributors, and maintainable long-term. That&amp;rsquo;s why I needed cross-version testing &lt;em&gt;and&lt;/em&gt; human review of the implementation path, not just the results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing at two levels kept the rewrite honest.&lt;/strong&gt; Cross-version testing was part of my design plan from the start — I architected the dual-path comparison framework so that every production DSL expression runs through both the old and new engines, asserting identical results across 1,290+ expressions. This gave me confidence no human review could match, and it was a deliberate planning decision: I knew AI-generated compiler code needed a mechanical proof of behavioral equivalence, not just eyeball review. On top of that, E2E tests served as the project&amp;rsquo;s existing infrastructure safety net — Docker-based integration tests that boot the entire server with real storage backends. Unit tests and cross-version tests verify logic in isolation; E2E tests verify the system actually works end-to-end. For infrastructure-level changes like queue replacement and thread model changes, E2E is the only gate that truly matters. Together, the two layers — designed-for-this-rewrite cross-version tests and pre-existing E2E infrastructure — caught different classes of bugs and made shipping with confidence possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multiple AIs have different strengths.&lt;/strong&gt; Claude excels at large-scale code generation with plan mode. Gemini is exceptional at logic review — it can mentally trace code branches with given input data, simulating execution without actually running the code. This is significant for reviewing AI-generated code: Gemini would walk through a generated compiler method step by step, flagging where a null check was missing or where a branch would produce wrong output for a specific edge case. Codex proved most valuable as a test reviewer and honesty checker. AI-generated code has a subtle failure mode: the coding agent can make wrong assumptions and then write tests that pass by setting expected values to match the wrong behavior — effectively bypassing the test safety net. Codex caught cases where Claude had set unreasonable expected values that happened to make tests green, masking logic errors that would have surfaced in production. Using all three as checks on each other was far more effective than relying on any single one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Mythical Man-Month still applies — and so does the Mythical Token-Month.&lt;/strong&gt; Brooks taught us that a task requiring 12 person-months does not mean 12 people can finish it in one month. The same law applies to AI: you cannot simply throw more tokens, more agents, or more parallel sessions at a problem and expect it to converge faster. Communication costs, coordination overhead, requirements analysis, and conceptual integrity — these software engineering fundamentals do not disappear just because your workforce is artificial. Worse, when the direction is wrong — when there&amp;rsquo;s a conceptual error in the design or an unreasonable architectural choice — AI will not recognize it. It will charge down the wrong path at extraordinary speed, burning tokens furiously while trapped in a vortex of self-justification: patching code to make failing tests pass, adjusting expected values to match wrong behavior, adding workarounds on top of workarounds — each iteration making the codebase look more &amp;ldquo;complete&amp;rdquo; while drifting further from correctness. AI vibe coding cannot break out of this spiral on its own. Only a human who understands the domain can recognize &amp;ldquo;this is fundamentally wrong, stop,&amp;rdquo; discard the work, and redirect. Speed without direction is just expensive chaos.&lt;/p&gt;
&lt;h2 id=&#34;the-bigger-picture&#34;&gt;The Bigger Picture&lt;/h2&gt;
&lt;p&gt;The agentic vibe coding approach worked because it combined AI&amp;rsquo;s speed with human architectural judgment and automated test discipline. It&amp;rsquo;s not magic — it&amp;rsquo;s engineering, accelerated.&lt;/p&gt;
&lt;p&gt;Brooks also gave us &amp;ldquo;No Silver Bullet,&amp;rdquo; and its core distinction matters more than ever: software complexity comes in two kinds. &lt;strong&gt;Essential complexity&lt;/strong&gt; comes from the problem itself — the domain semantics, the behavioral contracts, the concurrency invariants. No tool can eliminate this; it must be understood, modeled, and reasoned about by someone who knows the domain. &lt;strong&gt;Accidental complexity&lt;/strong&gt; comes from the tools and implementation — boilerplate code, manual refactoring across hundreds of files, the mechanical work of translating a design into compilable source. This is exactly where AI excels. What made this project work was recognizing which complexity was which: I owned the essential complexity (architecture, API boundaries, correctness invariants), and AI demolished the accidental complexity (generating 77K lines of implementation, scaffolding test harnesses, rewriting repetitive patterns across dozens of config files). Confuse the two — let AI make essential decisions, or waste human time on accidental work — and you get the worst of both worlds.&lt;/p&gt;
&lt;p&gt;Qian Xuesen(Tsien Hsue-shen)&amp;rsquo;s &lt;em&gt;Engineering Cybernetics&lt;/em&gt; offers another lens that proved surprisingly relevant. His core framework — &lt;strong&gt;feedback&lt;/strong&gt;, &lt;strong&gt;control&lt;/strong&gt;, &lt;strong&gt;optimization&lt;/strong&gt; — describes how to keep complex systems running toward their target. AI vibe coding at full speed is like a hypersonic missile: extraordinarily fast, but without a guidance system it just creates a bigger crater in the wrong place. The feedback loop in my workflow was the test harness — cross-version tests and E2E tests providing continuous signal on whether the system was still on course. Control was the human architect deciding when to intervene: reviewing plans before execution, hitting ESC when the direction drifted, choosing which AI to trust for which task. Optimization was iterative: each interrupt-replan cycle refined the approach, each Gemini review tightened the logic, each Codex audit caught assumptions the coding agent had smuggled past the tests. Without all three — feedback to detect deviation, control to correct course, optimization to converge — the speed of AI coding would be not an advantage but a liability. The faster the missile, the more precise the guidance must be.&lt;/p&gt;
&lt;p&gt;For more details or to share your own experience with agentic coding on production systems, feel free to reach me on &lt;a href=&#34;https://github.com/wu-sheng&#34;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 在成熟开源大型项目中实践 Agentic Vibe Coding：软件工程与工程控制论还在延续</title>
      <link>/zh/2026-03-08-agentic-vibe-coding/</link>
      <pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate>
      <guid>/zh/2026-03-08-agentic-vibe-coding/</guid>
      <description>
        
        
        &lt;p&gt;大多数&amp;quot;vibe coding&amp;quot;的故事都从一个全新项目开始，讲述一个快速构建原型或者可运行项目的过程，但这篇不是。&lt;/p&gt;
&lt;p&gt;Apache SkyWalking 是一个有 9 年历史的Apache顶级项目，线上数以千计的集群部署，内部有一套复杂的 DSL 编译栈，对外暴露的 API 上承载着用户构建的仪表盘、告警规则和自动化脚本。当我决定替换核心脚本引擎——从四个 DSL 编译器中彻底移除 Groovy 运行时——面临的问题不是&amp;quot;AI 能不能写出代码&amp;quot;，而是&amp;quot;也许只有AI能完成如此大规模的一致性迭代&amp;quot;，以及&amp;quot;AI 能不能在不破坏系统的前提下写出完整且高效的代码&amp;quot;。&lt;/p&gt;
&lt;p&gt;答案是可以——&lt;strong&gt;约 7.7 万行代码变更，10 个主要 PR，历时约 5 周&lt;/strong&gt;——但前提是 AI 始终在一个深刻理解项目架构、兼容性要求和用户场景的人的引导下工作。这篇文章分享了我在过去几个月的实践体验，以及成熟开源项目的维护者在把代码库交给 AI 智能体之前应该知道什么。&lt;/p&gt;
&lt;h2 id=&#34;项目概况&#34;&gt;项目概况&lt;/h2&gt;
&lt;p&gt;这次的任务是将 SkyWalking 基于 Groovy 的脚本引擎（MAL、LAL、Hierarchy）替换为统一的 ANTLR4 + Javassist 字节码编译管线，对齐 OAL 编译器已经验证过的架构。内部技术栈彻底重构，但对外接口必须保持完全一致。&lt;/p&gt;
&lt;p&gt;除了编译器重写，范围还包括新的线程管理策略（线程数从 36 降到 15）、JDK 25+ 虚拟线程支持，以及端到端测试的现代化改造。按传统估算，这是 5-8 个月的资深工程师（以我自己为例）工作量。&lt;/p&gt;
&lt;p&gt;编译器架构的完整技术细节，参见 &lt;a href=&#34;https://github.com/apache/skywalking/discussions/13716&#34;&gt;Groovy 移除讨论&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;什么是-agentic-vibe-coding&#34;&gt;什么是 Agentic Vibe Coding？&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Vibe coding&amp;rdquo;——Andrej Karpathy 提出的概念——描述的是一种你表达意图、让 AI 来写代码的编程风格。整个AI编程过程，一直以来都是用来做原型，效果强大且速度迅猛，但单独用于生产系统是有风险的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Agentic vibe coding&lt;/strong&gt; 更进一步：不是单一的 AI 自动补全，而是在你的架构指导下编排多个 AI 智能体——各有所长——以自动化测试作为安全网。我的工作流是这样的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code（plan 模式）&lt;/strong&gt;：主力编码智能体。Plan 模式让我在生成任何代码之前先审查方案。这对架构决策至关重要——我把控设计方向，Claude 负责实现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemini&lt;/strong&gt;：代码审查、并发分析和验证报告。每个主要 PR 都经过 Gemini 审查线程安全性、功能对等性和边界情况。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codex&lt;/strong&gt;：对定义明确、边界清晰的工作项进行自主任务执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;核心洞察：&lt;strong&gt;AI 写代码，但架构师掌控设计。&lt;/strong&gt; 没有对 SkyWalking 内部机制的深入领域知识，任何 AI 都无法规划这些变更。没有 AI，我也不可能在 5 周内完成执行。&lt;/p&gt;
&lt;h2 id=&#34;tdd-如何让-ai-编程变得安全&#34;&gt;TDD 如何让 AI 编程变得安全&lt;/h2&gt;
&lt;p&gt;我能以这样的速度推进而不搞砸，归结为一个原则：&lt;strong&gt;绝不让 AI 在没有测试保护的情况下写代码。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;每次重大变更的工作流：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先进 plan 模式&lt;/strong&gt;：向 Claude 描述目标，审查方案，在写任何代码之前先在架构层面迭代。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编写测试契约&lt;/strong&gt;：定义&amp;quot;正确&amp;quot;意味着什么——对于编译器重写，这意味着交叉版本对比测试，让每个表达式同时通过新旧两个引擎运行，在 1290+ 个表达式上断言结果完全一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;让 AI 实现&lt;/strong&gt;：有了测试契约，Claude 可以写出数千行实现代码。如果写错了，测试会立即捕获。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;端到端测试作为最终关卡&lt;/strong&gt;：每个 PR 都必须通过完整的端到端测试套件——基于 Docker 的集成测试，启动整个服务器并连接真实存储后端。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 代码审查&lt;/strong&gt;：Gemini 审查每个 PR 的并发问题、线程安全性和功能对等性——捕获单元测试无法发现的问题。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这和&amp;quot;写完祈祷能跑&amp;quot;的 vibe coding 完全相反。AI 写得快，测试验证得快，我把控架构方向。反馈循环足够紧凑，让我能在几分钟而不是几天内迭代复杂的编译器代码。&lt;/p&gt;
&lt;h2 id=&#34;经验教训&#34;&gt;经验教训&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AI 是力量倍增器，不是替代品。&lt;/strong&gt; 在任何 AI 智能体写下第一行代码之前，必须由人来定义替换方案：&lt;em&gt;替换什么&lt;/em&gt;、&lt;em&gt;怎么替换&lt;/em&gt;，以及——至关重要的——&lt;em&gt;边界在哪里&lt;/em&gt;。哪些 API 可以破坏性变更？内部编译管线可以彻底重构。哪些 API 必须保持对齐？每一个对外的 DSL 语法、每一个 YAML 配置键、每一个指标名称和标签结构都必须逐字节保持一致——因为数百个已部署的仪表盘、告警规则和用户脚本依赖于它们。划定这些边界需要对代码库及其用户的深入了解。AI 以惊人的速度执行了计划，但计划本身——范围、不变量、兼容性契约——必须来自一个理解每次变更影响半径的人。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;架构级工作，plan 模式不可妥协。&lt;/strong&gt; 让 AI 在编译器重写上直接跳到写代码，那是灾难。Plan 模式的价值在于它会收集代码上下文——扫描 import、追踪调用链、映射类继承关系——并利用这些上下文帮我补全那些我本来需要手动查找的实现细节。但它无法告诉你设计原则。方向必须由我在前期明确给出，这样 AI 的规划才能沿着正确的轨道走，而不是朝着一个局部合理但架构上错误的方案去优化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;要知道什么时候该按 ESC。&lt;/strong&gt; Claude 有一个明显的倾向：一旦开始写解决方案代码就会一头扎进去——当遇到与原始计划概念冲突的东西时，它不会自己停下来。它不会暂停来标记冲突，而是会继续推进，用即兴的方式绕过障碍，悄无声息地违背设计意图。我必须学会观察这个信号：当 Claude 的输出开始偏离计划时，我会手动取消任务（ESC），叫停它，找出计划和现实的分歧点，调整计划，然后重新开始。这种中断-重新规划的循环是工作流的常态，而非例外。架构师必须始终在环路中——不仅是在规划阶段，执行阶段也是——因为 AI 智能体还不知道什么时候该停下来问一句。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spec-Driven 更多的运用于测试，而非开发。它只是一个必要的但不充分条件，而逻辑工作流更重要。&lt;/strong&gt; 很容易产生一种想法：只要把输入/输出规格定义得足够清楚，AI 就能填充实现，测试会捕获任何错误。我试过。对于任何复杂的生产场景，这行不通。在表达式编译器重写过程中，Claude 有时会以不合理的方式修改代码，仅仅为了让规格测试通过——输入进去了，预期输出出来了，一切看起来都是正常的。但内部逻辑是错的：与代码库其他部分依赖的设计模式不一致，无法扩展，或者通过 hack （代码反射、字段名称静态比较等不可接受的工程方法）而非通用机制来解决特定测试用例。规格只检查代码&lt;em&gt;产出了什么&lt;/em&gt;；它对代码&lt;em&gt;如何产出&lt;/em&gt;一无所知。对于成熟项目，&amp;ldquo;如何&amp;quot;极其重要——解决方案需要与现有架构一致，能被贡献者广泛采用，并且长期可维护可扩展。这就是为什么我需要交叉版本测试&lt;em&gt;加上&lt;/em&gt;对实现路径的人工审查，而不仅仅是审查结果。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;两个层次的测试让重写的代码验证更有保障。&lt;/strong&gt; 交叉版本测试从一开始就是我设计方案的一部分——我架构了双路径对比框架，让每个生产环境的 DSL 表达式同时通过新旧两个引擎运行，在 1290+ 个表达式上断言结果完全一致。这给了我任何人工审查都无法匹敌的信心，而且这是一个刻意的规划决策：我知道 AI 生成的编译器代码需要行为等价性的机械证明，而不仅仅是肉眼审查。在此之上，端到端测试作为项目已有的基础设施安全网——基于 Docker/K8s 的集成测试，启动整个服务器并连接真实存储后端。单元测试和交叉版本测试在隔离环境中验证逻辑；端到端测试验证系统真正能端到端地工作。对于队列替换和线程模型变更这样的基础设施级变更，端到端测试是唯一真正重要的关卡。两个层次——为本次重写专门设计的交叉版本测试和预先存在的端到端基础设施——捕获了不同类别的 bug，使得有信心地发布成为可能。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;多个 AI 各有所长。&lt;/strong&gt; Claude 擅长配合 plan 模式进行大规模代码生成。Gemini 在逻辑审查方面表现出色——它能在给定输入数据的情况下在脑中追踪代码分支，模拟执行而无需实际运行代码。这对审查 AI 生成的代码意义重大：Gemini 会逐步走查一个编译器生成的方法，标记出哪里缺少空值检查，或者哪个分支在特定边界情况下会产生错误输出。Codex 作为测试审查者和诚实性检查者最有价值。AI 生成的代码有一种微妙的失败模式：编码智能体可能做出错误假设，然后编写测试时将期望值设置为匹配错误行为——实际上绕过了测试安全网。Codex 捕获了 Claude 设置不合理期望值使测试变绿的情况，掩盖了本会在生产环境中暴露的逻辑错误。将三者互相校验，远比依赖其中任何一个更有效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;人月神话依然适用——基于Token的AI月神话同样如此。&lt;/strong&gt; Brooks 告诉我们，一个需要 12 人月的任务不意味着 12 个人能在一个月内完成。同样的定律适用于 AI：你不能简单地投入更多 token、更多智能体或更多并行会话，就指望问题更快收敛。沟通成本、协调开销、需求分析和概念完整性——这些软件工程的基本规律不会因为你的劳动力是人工智能就消失。更糟糕的是，当方向错误时——当设计中存在概念性错误或不合理的架构选择时——AI 不会识别出来。它会以惊人的速度冲向错误的方向，疯狂消耗 token，同时陷入自我辩护的漩涡：修补代码让失败的测试通过，调整期望值去匹配错误行为，在变通方案上叠加变通方案——每次迭代都让代码库看起来更&amp;quot;完整&amp;rdquo;，实际上却离正确越来越远。AI vibe coding 无法自行跳出这个螺旋。只有理解领域的人才能认识到&amp;quot;这从根本上就是错的，停下来&amp;quot;，丢弃这些工作，重新引导方向。没有方向的速度，只是昂贵的混乱。&lt;/p&gt;
&lt;h2 id=&#34;更大的图景&#34;&gt;更大的图景&lt;/h2&gt;
&lt;p&gt;Agentic vibe coding 之所以有效，是因为它将 AI 的速度与人的架构判断力和自动化测试纪律结合在了一起。这不是魔法——这是被加速的工程。&lt;/p&gt;
&lt;p&gt;Brooks 还给了我们《没有银弹》，其核心区分在今天比以往任何时候都更重要：软件复杂性分为两种。&lt;strong&gt;本质复杂性&lt;/strong&gt;来自问题本身——领域语义、行为契约、并发不变量。没有任何工具能消除它；它必须由理解领域的人去理解、建模和推理。&lt;strong&gt;偶然复杂性&lt;/strong&gt;来自工具和实现——样板代码、跨数百个文件的手动重构、将设计翻译成可编译源码的机械工作。这恰恰是 AI 擅长的地方。这个项目之所以成功，在于认清了哪种复杂性是哪种：我掌控本质复杂性（架构、API 边界、正确性不变量），AI 消灭偶然复杂性（生成 7.7 万行实现代码、搭建测试框架、跨数十个配置文件重写重复模式）。搞混这两者——让 AI 做本质决策，或者让人浪费时间在偶然工作上——你会得到两个世界中最差的结果。&lt;/p&gt;
&lt;p&gt;钱学森的《工程控制论》提供了另一个视角，在实践中出人意料地切题。他的核心框架——&lt;strong&gt;反馈&lt;/strong&gt;、&lt;strong&gt;控制&lt;/strong&gt;、&lt;strong&gt;优化&lt;/strong&gt;——描述的是如何让复杂系统持续朝目标运行。全速运转的 AI vibe coding 就像一枚高超音速导弹：速度惊人，但没有制导系统只会在错误的地方炸出一个更大的坑。我工作流中的反馈回路是测试体系——交叉版本测试和端到端测试持续提供系统是否仍在航线上的信号。控制是人类架构师决定何时介入：在执行前审查方案，在方向偏移时按 ESC，选择哪个 AI 负责哪项任务。优化是迭代式的：每次中断-重新规划的循环都在精炼方法，每次 Gemini 审查都在收紧逻辑，每次 Codex 审计都在捕获编码智能体偷偷绕过测试的假设。缺少其中任何一个——检测偏差的反馈、纠正航向的控制、趋向收敛的优化——AI 编程的速度就不是优势而是负债。导弹越快，制导就必须越精确。&lt;/p&gt;
&lt;p&gt;AI Vibe Coding以及它的迭代，正在快速地走进每一个开发者，也正在广泛地融入开源和商业软件。我们都在见证这种新的开发模式，以及AI Vibe Coding和软件工程理论的融合。如果你想和我探讨更多的AI + OSS话题，欢迎在 &lt;a href=&#34;https://github.com/wu-sheng&#34;&gt;GitHub&lt;/a&gt; 上联系我。&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 使用 SkyWalking 监控 Flink</title>
      <link>/zh/2024-04-19-flink-monitoring-by-skywalking/</link>
      <pubDate>Fri, 25 Apr 2025 00:00:00 +0000</pubDate>
      <guid>/zh/2024-04-19-flink-monitoring-by-skywalking/</guid>
      <description>
        
        
        &lt;h1 id=&#34;背景介绍&#34;&gt;背景介绍&lt;/h1&gt;
&lt;p&gt;Apache Flink 是一个框架和分布式处理引擎，用于在无边界和有边界数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行，并能以内存速度和任意规模进行计算。
从SkyWalking OAP 10.3 版本开始，新增了对来自Flink的指标数据监控面板，本文将展示并介绍如何使用 SkyWalking来监控Flink。&lt;/p&gt;
&lt;h1 id=&#34;部署&#34;&gt;部署&lt;/h1&gt;
&lt;h2 id=&#34;准备&#34;&gt;准备&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking&#34;&gt;SkyWalking oap服务,v10.3 +&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/flink&#34;&gt;Flink v2.0-preview1 +&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/open-telemetry/opentelemetry-collector-contrib&#34;&gt;OpenTelemetry-collector v0.87+&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;启动流程&#34;&gt;启动流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;启动 &lt;code&gt;jobmanager&lt;/code&gt; 和 &lt;code&gt;taskmanager&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;启动 &lt;code&gt;skywalking oap&lt;/code&gt; 和 &lt;code&gt;ui&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;启动 &lt;code&gt;opentelmetry-collector&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;启动job&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;dataflow&#34;&gt;DataFlow:&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;data-flow.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;配置&#34;&gt;配置&lt;/h2&gt;
&lt;h3 id=&#34;docker-compose&#34;&gt;docker-compose&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;version: &amp;#34;3&amp;#34;

services:
  oap:
    extends:
      file: ../../script/docker-compose/base-compose.yml
      service: oap
    ports:
      - &amp;#34;12800:12800&amp;#34;
    networks:
      - e2e

  banyandb:
    extends:
      file: ../../script/docker-compose/base-compose.yml
      service: banyandb
    ports:
      - 17912

  jobmanager:
    image: flink:2.0-preview1
    environment:
      - |
        FLINK_PROPERTIES=
        jobmanager.rpc.address: jobmanager
        metrics.reporter.prom.factory.class: org.apache.flink.metrics.prometheus.PrometheusReporterFactory
        metrics.reporter.prom.port: 9260
    ports:
      - &amp;#34;8081:8081&amp;#34;
      - &amp;#34;9260:9260&amp;#34;
    command: jobmanager
    healthcheck:
      test: [&amp;#34;CMD&amp;#34;, &amp;#34;curl&amp;#34;, &amp;#34;-f&amp;#34;, &amp;#34;http://localhost:8081&amp;#34;]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - e2e

  taskmanager:
    image: flink:2.0-preview1
    environment:
      - |
        FLINK_PROPERTIES=
        jobmanager.rpc.address: jobmanager
        metrics.reporter.prom.factory.class: org.apache.flink.metrics.prometheus.PrometheusReporterFactory
        metrics.reporter.prom.port: 9261
    depends_on:
      jobmanager:
        condition: service_healthy
    ports:
      - &amp;#34;9261:9261&amp;#34;
    command: taskmanager
    healthcheck:
      test: [&amp;#34;CMD&amp;#34;, &amp;#34;curl&amp;#34;, &amp;#34;-f&amp;#34;, &amp;#34;http://localhost:9261/metrics&amp;#34;]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - e2e

  executeJob:
    image: flink:2.0-preview1
    depends_on:
      taskmanager:
        condition: service_healthy
    command: &amp;gt;
      bash -c &amp;#34;
      ./bin/flink run -m jobmanager:8081 examples/streaming/WindowJoin.jar&amp;#34;
    networks:
      - e2e

  otel-collector:
    image: otel/opentelemetry-collector:${OTEL_COLLECTOR_VERSION}
    networks:
      - e2e
    command: [ &amp;#34;--config=/etc/otel-collector-config.yaml&amp;#34; ]
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    expose:
      - 55678
    depends_on:
      oap:
        condition: service_healthy

networks:
  e2e:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如果是使用&lt;code&gt;pushGateWay&lt;/code&gt;模式来暴露metrics数据请&lt;a href=&#34;https://nightlies.apache.org/flink/flink-docs-release-2.0-preview1/docs/deployment/metric_reporters/#prometheuspushgateway&#34;&gt;参考&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;opentelemetry-collector&#34;&gt;OpenTelemetry-collector&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;receivers:
  prometheus:
    config:
      scrape_configs:
        - job_name: &amp;#34;flink-jobManager-monitoring&amp;#34;
          scrape_interval: 30s
          static_configs:
            - targets: [&amp;#39;jobmanager:9260&amp;#39;]
              labels:
                cluster: flink-cluster
          relabel_configs:
            - source_labels: [ __address__ ]
              target_label: jobManager_node
              replacement: $$1
          metric_relabel_configs:
            - source_labels: [ job_name ]
              action: replace
              target_label: flink_job_name
              replacement: $$1
            - source_labels: [ ]
              target_label: job_name
              replacement: flink-jobManager-monitoring

        - job_name: &amp;#34;flink-taskManager-monitoring&amp;#34;
          scrape_interval: 30s
          static_configs:
            - targets: [ &amp;#34;taskmanager:9261&amp;#34; ]
              labels:
                cluster: flink-cluster
          relabel_configs:
            - source_labels: [ __address__ ]
              regex: (.+)
              target_label: taskManager_node
              replacement: $$1
          metric_relabel_configs:
            - source_labels: [ job_name ]
              action: replace
              target_label: flink_job_name
              replacement: $$1
            - source_labels: [ ]
              target_label: job_name
              replacement: flink-taskManager-monitoring

exporters:
  otlp:
    endpoint: oap:11800
    tls:
      insecure: true

processors:
  batch:
service:
  pipelines:
    metrics:
      receivers:
        - prometheus
      processors:
        - batch
      exporters:
        - otlp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注意:&lt;br&gt;
&lt;code&gt;job_name&lt;/code&gt;的值请不要修改,否则 skyWalking 不会处理这部分数据。&lt;br&gt;
&lt;code&gt;oap&lt;/code&gt; 为 &lt;code&gt;skywalking oap&lt;/code&gt; 地址,请自行替换。&lt;br&gt;
因为原始&lt;code&gt;flink&lt;/code&gt;数据中含有&lt;code&gt;job_name&lt;/code&gt;标签，而skyWalking又根据&lt;code&gt;job_name&lt;/code&gt;标签来处理对应OTEL任务的数据，
为了避免冲突，使用&lt;code&gt;metric_relabel_configs&lt;/code&gt;替换原始数据中&lt;code&gt;job_name&lt;/code&gt;的标签为&lt;code&gt;flink_job_name&lt;/code&gt;。&lt;/p&gt;
&lt;h1 id=&#34;监控指标&#34;&gt;监控指标&lt;/h1&gt;
&lt;p&gt;指标分为三个维度,cluster,taskManager,job&lt;/p&gt;
&lt;h2 id=&#34;cluster-metrics&#34;&gt;Cluster Metrics&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;cluster-dashboard-1.png&#34; alt=&#34;&#34;&gt;
&lt;img src=&#34;cluster-dashboard-2.png&#34; alt=&#34;&#34;&gt;
&lt;img src=&#34;cluster-dashboard-3.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Cluster Metrics&lt;/code&gt;主要是站在集群的角度统计以及jobManager的jvm相关指标展示,比如&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Running Jobs&lt;/code&gt;：正在运行的任务数量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TaskManagers&lt;/code&gt;：taskManager数量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Task Managers Slots Total&lt;/code&gt;：taskManager slot数量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Task Managers Slots Available&lt;/code&gt;：taskManager可用slot数量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JVM CPU Load&lt;/code&gt;：jobManager的jvm占用cpu的负载&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;taskmanager-metrics&#34;&gt;TaskManager Metrics&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;broker-dashboard-1.png&#34; alt=&#34;&#34;&gt;
&lt;img src=&#34;broker-dashboard-2.png&#34; alt=&#34;&#34;&gt;
&lt;img src=&#34;broker-dashboard-3.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TaskManager Metrics&lt;/code&gt;主要是站在taskManager节点的角度来统计展示,比如&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;JVM Memory Heap Used&lt;/code&gt;：taskManager节点JVM已用内存大小。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;JVM Memory Heap Available&lt;/code&gt;：taskManager节点JVM可用内存大小。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NumRecordsIn&lt;/code&gt;：taskManager每分钟接受的数据数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NumBytesInPerSecond&lt;/code&gt;：taskManager每秒接受的Bytes数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IsBackPressured&lt;/code&gt;：该taskManager节点是否处在背压。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IdleTimeMsPerSecond&lt;/code&gt;：该taskManager节点每秒的闲置时长。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;job-metrics&#34;&gt;Job Metrics&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;topic-dashboard-1.png&#34; alt=&#34;&#34;&gt;
&lt;img src=&#34;topic-dashboard-2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Job Metrics&lt;/code&gt;主要是站在运行任务的角度来统计展示,比如&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Job RunningTime&lt;/code&gt;：该任务运行的时长。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Job Restart Number&lt;/code&gt;：该任务重启次数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Checkpoints Failed&lt;/code&gt;：失败的checkpoints数量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NumBytesInPerSecond&lt;/code&gt;：该任务每秒接受的Bytes数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;各个指标的含义可以在图标的 tip 上找到解释&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;tip.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h1 id=&#34;参考文档&#34;&gt;参考文档&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://nightlies.apache.org/flink/flink-docs-release-2.0-preview1/docs/deployment/metric_reporters/#prometheus&#34;&gt;Flink Prometheus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://skywalking.apache.org/docs/main/next/en/setup/backend/backend-flink-monitoring/&#34;&gt;SkyWalking Flink Monitoring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: How to run Apache SkyWalking on AWS EKS and RDS/Aurora</title>
      <link>/blog/2022-12-13-how-to-run-apache-skywalking-on-aws-eks-rds/</link>
      <pubDate>Tue, 13 Dec 2022 00:00:00 +0000</pubDate>
      <guid>/blog/2022-12-13-how-to-run-apache-skywalking-on-aws-eks-rds/</guid>
      <description>
        
        
        &lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Apache SkyWalking is an open source APM tool for monitoring and troubleshooting distributed systems,
especially designed for microservices, cloud native and container-based (Docker, Kubernetes, Mesos)
architectures. It provides distributed tracing, service mesh observability, metric aggregation and
visualization, and alarm.&lt;/p&gt;
&lt;p&gt;In this article, I will introduce how to quickly set up Apache SkyWalking on AWS EKS and RDS/Aurora,
as well as a couple of sample services, monitoring services to observe SkyWalking itself.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AWS account&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&#34;&gt;AWS CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.terraform.io/downloads.html&#34;&gt;Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kubernetes.io/docs/tasks/tools/#kubectl&#34;&gt;kubectl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can use the AWS web console or CLI to create all resources needed in this tutorial, but it can be
too tedious and hard to debug when something goes wrong. So in this artical I will use Terraform to
create all AWS resources, deploy SkyWalking, sample services, and load generator services (Locust).&lt;/p&gt;
&lt;h2 id=&#34;architecture&#34;&gt;Architecture&lt;/h2&gt;
&lt;p&gt;The demo architecture is as follows:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph AWS
        subgraph EKS
          subgraph istio-system namespace
              direction TB
              OAP[[SkyWalking OAP]]
              UI[[SkyWalking UI]]
            Istio[[istiod]]
          end
          subgraph sample namespace
              Service0[[Service0]]
              Service1[[Service1]]
              ServiceN[[Service ...]]
          end
          subgraph locust namespace
              LocustMaster[[Locust Master]]
              LocustWorkers0[[Locust Worker 0]]
              LocustWorkers1[[Locust Worker 1]]
              LocustWorkersN[[Locust Worker ...]]
          end
        end
        RDS[[RDS/Aurora]]
    end
    OAP --&amp;gt; RDS
    Service0 -. telemetry data -.-&amp;gt; OAP
    Service1 -. telemetry data -.-&amp;gt; OAP
    ServiceN -. telemetry data -.-&amp;gt; OAP
    UI --query--&amp;gt; OAP
    LocustWorkers0 -- traffic --&amp;gt; Service0
    LocustWorkers1 -- traffic --&amp;gt; Service0
    LocustWorkersN -- traffic --&amp;gt; Service0
    Service0 --&amp;gt; Service1 --&amp;gt; ServiceN
    LocustMaster --&amp;gt; LocustWorkers0
    LocustMaster --&amp;gt; LocustWorkers1
    LocustMaster --&amp;gt; LocustWorkersN
    User --&amp;gt; LocustMaster
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As shown in the architecture diagram, we need to create the following AWS resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EKS cluster&lt;/li&gt;
&lt;li&gt;RDS instance or Aurora cluster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sounds simple, but there are a lot of things behind the scenes, such as VPC, subnets, security groups, etc.
You have to configure them correctly to make sure the EKS cluster can connect to RDS instance/Aurora cluster
otherwise the SkyWalking won&amp;rsquo;t work. Luckily, Terraform can help us to create and destroy all these resources
automatically.&lt;/p&gt;
&lt;p&gt;I have created a Terraform module to create all AWS resources needed in this tutorial, you can find it in the
&lt;a href=&#34;https://github.com/kezhenxu94/oap-load-test/tree/main/aws&#34;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;create-aws-resources&#34;&gt;Create AWS resources&lt;/h2&gt;
&lt;p&gt;First, we need to clone the GitHub repository and &lt;code&gt;cd&lt;/code&gt; into the folder:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/kezhenxu94/oap-load-test.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then, we need to create a file named &lt;code&gt;terraform.tfvars&lt;/code&gt; to specify the AWS region and other variables:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &amp;gt; terraform.tfvars &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;aws_access_key = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;aws_secret_key = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;cluster_name   = &amp;#34;skywalking-on-aws&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;region         = &amp;#34;ap-east-1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;db_type        = &amp;#34;rds-postgresql&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have already configured the AWS CLI, you can skip the &lt;code&gt;aws_access_key&lt;/code&gt; and &lt;code&gt;aws_secret_key&lt;/code&gt; variables.
To install SkyWalking with RDS postgresql, set the &lt;code&gt;db_type&lt;/code&gt; to &lt;code&gt;rds-postgresql&lt;/code&gt;, to install SkyWalking with
Aurora postgresql, set the &lt;code&gt;db_type&lt;/code&gt; to &lt;code&gt;aurora-postgresql&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are a lot of other variables you can configure, such as tags, sample services count, replicas, etc.,
you can find them in the &lt;a href=&#34;https://github.com/kezhenxu94/oap-load-test/blob/main/aws/variables.tf&#34;&gt;variables.tf&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then, we can run the following commands to initialize the Terraform module and download the required providers,
then create all AWS resources:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform init
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform apply -var-file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;terraform.tfvars
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; to confirm the creation of all AWS resources, or add the &lt;code&gt;-auto-approve&lt;/code&gt; flag to the &lt;code&gt;terraform apply&lt;/code&gt;
to skip the confirmation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform apply -var-file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;terraform.tfvars -auto-approve
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now what you need to do is to wait for the creation of all AWS resources to complete, it may take a few minutes.
You can check the progress of the creation in the AWS web console, and check the deployment progress of the services
inside the EKS cluster.&lt;/p&gt;
&lt;h2 id=&#34;generate-traffic&#34;&gt;Generate traffic&lt;/h2&gt;
&lt;p&gt;Besides creating necessary AWS resources, the Terraform module also deploys SkyWalking, sample services, and Locust
load generator services to the EKS cluster.&lt;/p&gt;
&lt;p&gt;You can access the Locust web UI to generate traffic to the sample services:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;open http://&lt;span style=&#34;color:#cf222e&#34;&gt;$(&lt;/span&gt;kubectl get svc -n locust -l &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;locust-master -o &lt;span style=&#34;color:#953800&#34;&gt;jsonpath&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;{.items[0].status.loadBalancer.ingress[0].hostname}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;)&lt;/span&gt;:8089
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The command opens the browser to the Locust web UI, you can configure the number of users and hatch rate to generate
traffic.&lt;/p&gt;
&lt;h2 id=&#34;observe-skywalking&#34;&gt;Observe SkyWalking&lt;/h2&gt;
&lt;p&gt;You can access the SkyWalking web UI to observe the sample services.&lt;/p&gt;
&lt;p&gt;First you need to forward the SkyWalking UI port to local&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl -n istio-system port-forward &lt;span style=&#34;color:#cf222e&#34;&gt;$(&lt;/span&gt;kubectl -n istio-system get pod -l &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;skywalking -l &lt;span style=&#34;color:#953800&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;ui -o name&lt;span style=&#34;color:#cf222e&#34;&gt;)&lt;/span&gt; 8080:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then open the browser to http://localhost:8080 to access the SkyWalking web UI.&lt;/p&gt;
&lt;h2 id=&#34;observe-rdsaurora&#34;&gt;Observe RDS/Aurora&lt;/h2&gt;
&lt;p&gt;You can also access the RDS/Aurora web console to observe the performance of RDS/Aurora instance/Aurora cluste.&lt;/p&gt;
&lt;h2 id=&#34;test-results&#34;&gt;Test Results&lt;/h2&gt;
&lt;h3 id=&#34;test-1-skywalking-with-eks-and-rds-postgresql&#34;&gt;Test 1: SkyWalking with EKS and RDS PostgreSQL&lt;/h3&gt;
&lt;h4 id=&#34;service-traffic&#34;&gt;Service Traffic&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/postgresql/test1-cpm-locust.png&#34; alt=&#34;Service Traffic Locust&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-cpm.png&#34; alt=&#34;Service Traffic SW&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;rds-performance&#34;&gt;RDS Performance&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/postgresql/test1-postgresql-1.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-postgresql-2.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-postgresql-3.png&#34; alt=&#34;RDS Performance&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;skywalking-performance&#34;&gt;SkyWalking Performance&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/postgresql/test1-so11y-1.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-2.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-3.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-4.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-5.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;test-2-skywalking-with-eks-and-aurora-postgresql&#34;&gt;Test 2: SkyWalking with EKS and Aurora PostgreSQL&lt;/h3&gt;
&lt;h4 id=&#34;service-traffic-1&#34;&gt;Service Traffic&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/aurora/test1-cpm-locust.png&#34; alt=&#34;Service Traffic Locust&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-cpm-skywalking.png&#34; alt=&#34;Service Traffic SW&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;rds-performance-1&#34;&gt;RDS Performance&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/aurora/test1-postgresql-1.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-postgresql-2.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-postgresql-3.png&#34; alt=&#34;RDS Performance&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;skywalking-performance-1&#34;&gt;SkyWalking Performance&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/aurora/test1-so11y-1.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-2.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-3.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-4.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-5.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;clean-up&#34;&gt;Clean up&lt;/h2&gt;
&lt;p&gt;When you are done with the demo, you can run the following command to destroy all AWS resources:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform destroy -var-file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;terraform.tfvars -auto-approve
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
      </description>
    </item>
    
    <item>
      <title>Zh: 如何在 AWS EKS 和 RDS/Aurora 上运行 Apache SkyWalking</title>
      <link>/zh/2022-12-13-how-to-run-apache-skywalking-on-aws-eks-rds/</link>
      <pubDate>Tue, 13 Dec 2022 00:00:00 +0000</pubDate>
      <guid>/zh/2022-12-13-how-to-run-apache-skywalking-on-aws-eks-rds/</guid>
      <description>
        
        
        &lt;h2 id=&#34;介绍&#34;&gt;介绍&lt;/h2&gt;
&lt;p&gt;Apache SkyWalking 是一个开源的 APM 工具，用于监控分布式系统和排除故障，特别是为微服务、云原生和基于容器（Docker、Kubernetes、Mesos）的架构而设计。它提供分布式跟踪、服务网格可观测性、指标聚合和可视化以及警报。&lt;/p&gt;
&lt;p&gt;在本文中，我将介绍如何在 AWS EKS 和 RDS/Aurora 上快速设置 Apache SkyWalking，以及几个示例服务，监控服务以观察 SkyWalking 本身。&lt;/p&gt;
&lt;h2 id=&#34;先决条件&#34;&gt;先决条件&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AWS 账号&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&#34;&gt;AWS CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.terraform.io/downloads.html&#34;&gt;Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kubernetes.io/docs/tasks/tools/#kubectl&#34;&gt;kubectl&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以使用 AWS Web 控制台或 CLI 来创建本教程所需的所有资源，但是当出现问题时，它可能过于繁琐且难以调试。因此，在本文中，我将使用 Terraform 创建所有 AWS 资源、部署 SkyWalking、示例服务和负载生成器服务 (Locust)。&lt;/p&gt;
&lt;h2 id=&#34;架构&#34;&gt;架构&lt;/h2&gt;
&lt;p&gt;演示架构如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph AWS
        subgraph EKS
          subgraph istio-system namespace
              direction TB
              OAP[[SkyWalking OAP]]
              UI[[SkyWalking UI]]
            Istio[[istiod]]
          end
          subgraph sample namespace
              Service0[[Service0]]
              Service1[[Service1]]
              ServiceN[[Service ...]]
          end
          subgraph locust namespace
              LocustMaster[[Locust Master]]
              LocustWorkers0[[Locust Worker 0]]
              LocustWorkers1[[Locust Worker 1]]
              LocustWorkersN[[Locust Worker ...]]
          end
        end
        RDS[[RDS/Aurora]]
    end
    OAP --&amp;gt; RDS
    Service0 -. telemetry data -.-&amp;gt; OAP
    Service1 -. telemetry data -.-&amp;gt; OAP
    ServiceN -. telemetry data -.-&amp;gt; OAP
    UI --query--&amp;gt; OAP
    LocustWorkers0 -- traffic --&amp;gt; Service0
    LocustWorkers1 -- traffic --&amp;gt; Service0
    LocustWorkersN -- traffic --&amp;gt; Service0
    Service0 --&amp;gt; Service1 --&amp;gt; ServiceN
    LocustMaster --&amp;gt; LocustWorkers0
    LocustMaster --&amp;gt; LocustWorkers1
    LocustMaster --&amp;gt; LocustWorkersN
    User --&amp;gt; LocustMaster
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如架构图所示，我们需要创建以下 AWS 资源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EKS 集群&lt;/li&gt;
&lt;li&gt;RDS 实例或 Aurora 集群&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;听起来很简单，但背后有很多东西，比如 VPC、子网、安全组等。你必须正确配置它们以确保 EKS 集群可以连接到 RDS 实例 / Aurora 集群，否则 SkyWalking 不会不工作。幸运的是，Terraform 可以帮助我们自动创建和销毁所有这些资源。&lt;/p&gt;
&lt;p&gt;我创建了一个 Terraform 模块来创建本教程所需的所有 AWS 资源，您可以在 &lt;a href=&#34;https://github.com/kezhenxu94/oap-load-test/tree/main/aws&#34;&gt;GitHub 存储库&lt;/a&gt;中找到它。&lt;/p&gt;
&lt;h2 id=&#34;创建-aws-资源&#34;&gt;创建 AWS 资源&lt;/h2&gt;
&lt;p&gt;首先，我们需要将 GitHub 存储库克隆 &lt;code&gt;cd&lt;/code&gt; 到文件夹中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/kezhenxu94/oap-load-test.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后，我们需要创建一个文件 &lt;code&gt;terraform.tfvars&lt;/code&gt; 来指定 AWS 区域和其他变量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &amp;gt; terraform.tfvars &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;aws_access_key = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;aws_secret_key = &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;cluster_name   = &amp;#34;skywalking-on-aws&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;region         = &amp;#34;ap-east-1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;db_type        = &amp;#34;rds-postgresql&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果您已经配置了 AWS CLI，则可以跳过 &lt;code&gt;aws_access_key&lt;/code&gt; 和 &lt;code&gt;aws_secret_key&lt;/code&gt; 变量。要使用 RDS postgresql 安装 SkyWalking，请将 &lt;code&gt;db_type&lt;/code&gt; 设置为 &lt;code&gt;rds-postgresql&lt;/code&gt;，要使用 Aurora postgresql 安装 SkyWalking，请将 &lt;code&gt;db_type&lt;/code&gt; 设置为 &lt;code&gt;aurora-postgresql&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;您可以配置许多其他变量，例如标签、示例服务计数、副本等，您可以在 variables.tf 中找到&lt;a href=&#34;https://github.com/kezhenxu94/oap-load-test/blob/main/aws/variables.tf&#34;&gt;它们&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;然后，我们可以运行以下命令来初始化 Terraform 模块并下载所需的提供程序，然后创建所有 AWS 资源：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform init
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform apply -var-file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;terraform.tfvars
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;键入 &lt;code&gt;yes&lt;/code&gt; 以确认所有 AWS 资源的创建，或将标志 &lt;code&gt;-auto-approve&lt;/code&gt; 添加到 &lt;code&gt;terraform apply&lt;/code&gt; 以跳过确认：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform apply -var-file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;terraform.tfvars -auto-approve
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;现在你需要做的就是等待所有 AWS 资源的创建完成，这可能需要几分钟的时间。您可以在 AWS Web 控制台查看创建进度，也可以查看 EKS 集群内部服务的部署进度。&lt;/p&gt;
&lt;h2 id=&#34;产生流量&#34;&gt;产生流量&lt;/h2&gt;
&lt;p&gt;除了创建必要的 AWS 资源外，Terraform 模块还将 SkyWalking、示例服务和 Locust 负载生成器服务部署到 EKS 集群。&lt;/p&gt;
&lt;p&gt;您可以访问 Locust Web UI 以生成到示例服务的流量：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;open http://&lt;span style=&#34;color:#cf222e&#34;&gt;$(&lt;/span&gt;kubectl get svc -n locust -l &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;locust-master -o &lt;span style=&#34;color:#953800&#34;&gt;jsonpath&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#39;{.items[0].status.loadBalancer.ingress[0].hostname}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;)&lt;/span&gt;:8089
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;该命令将浏览器打开到 Locust web UI，您可以配置用户数量和孵化率以生成流量。&lt;/p&gt;
&lt;h2 id=&#34;观察-skywalking&#34;&gt;观察 SkyWalking&lt;/h2&gt;
&lt;p&gt;您可以访问 SkyWalking Web UI 来观察示例服务。&lt;/p&gt;
&lt;p&gt;首先需要将 SkyWalking UI 端口转发到本地：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl -n istio-system port-forward &lt;span style=&#34;color:#cf222e&#34;&gt;$(&lt;/span&gt;kubectl -n istio-system get pod -l &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;skywalking -l &lt;span style=&#34;color:#953800&#34;&gt;component&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;ui -o name&lt;span style=&#34;color:#cf222e&#34;&gt;)&lt;/span&gt; 8080:8080
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后在浏览器中打开 http://localhost:8080 访问 SkyWalking web UI。&lt;/p&gt;
&lt;h2 id=&#34;观察-rdsaurora&#34;&gt;观察 RDS/Aurora&lt;/h2&gt;
&lt;p&gt;您也可以访问 RDS/Aurora web 控制台，观察 RDS/Aurora 实例 / Aurora 集群的性能。&lt;/p&gt;
&lt;h2 id=&#34;试验结果&#34;&gt;试验结果&lt;/h2&gt;
&lt;h3 id=&#34;测试-1使用-eks-和-rds-postgresql-的-skywalking&#34;&gt;测试 1：使用 EKS 和 RDS PostgreSQL 的 SkyWalking&lt;/h3&gt;
&lt;h4 id=&#34;服务流量&#34;&gt;服务流量&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/postgresql/test1-cpm-locust.png&#34; alt=&#34;Service Traffic Locust&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-cpm.png&#34; alt=&#34;Service Traffic SW&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;rds-性能&#34;&gt;RDS 性能&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/postgresql/test1-postgresql-1.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-postgresql-2.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-postgresql-3.png&#34; alt=&#34;RDS Performance&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;skywalking-性能&#34;&gt;SkyWalking 性能&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/postgresql/test1-so11y-1.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-2.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-3.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-4.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/postgresql/test1-so11y-5.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;测试-2使用-eks-和-aurora-postgresql-的-skywalking&#34;&gt;测试 2：使用 EKS 和 Aurora PostgreSQL 的 SkyWalking&lt;/h3&gt;
&lt;h4 id=&#34;服务流量-1&#34;&gt;服务流量&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/aurora/test1-cpm-locust.png&#34; alt=&#34;Service Traffic Locust&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-cpm-skywalking.png&#34; alt=&#34;Service Traffic SW&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;rds-性能-1&#34;&gt;RDS 性能&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/aurora/test1-postgresql-1.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-postgresql-2.png&#34; alt=&#34;RDS Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-postgresql-3.png&#34; alt=&#34;RDS Performance&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;skywalking-性能-1&#34;&gt;SkyWalking 性能&lt;/h4&gt;
&lt;p&gt;&lt;img src=&#34;./outputs/aurora/test1-so11y-1.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-2.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-3.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-4.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;
&lt;img src=&#34;./outputs/aurora/test1-so11y-5.png&#34; alt=&#34;SkyWalking Performance&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;清理&#34;&gt;清理&lt;/h2&gt;
&lt;p&gt;完成演示后，您可以运行以下命令销毁所有 AWS 资源：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;terraform destroy -var-file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;terraform.tfvars -auto-approve
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
      </description>
    </item>
    
    <item>
      <title>Blog: [Video] Distributed tracing demo using Apache SkyWalking and Kong API Gateway</title>
      <link>/blog/2022-08-11-kongcast-20-distributed-tracing-using-skywalking-kong/</link>
      <pubDate>Thu, 11 Aug 2022 00:00:00 +0000</pubDate>
      <guid>/blog/2022-08-11-kongcast-20-distributed-tracing-using-skywalking-kong/</guid>
      <description>
        
        
        &lt;p&gt;Observability essential when working with distributed systems. Built on 3 pillars of metrics, logging and
tracing, having the right tools in place to quickly identify and determine the root cause of an issue in production
is imperative. In this Kongcast interview, we explore the benefits of having observability and demo the use of
Apache SkyWalking. We walk through the capabilities that SkyWalking offers out of the box and debug a common HTTP 500
error using the tool.&lt;/p&gt;
&lt;p&gt;Andrew Kew is interviewed by Viktor Gamov, a developer advocate at &lt;a href=&#34;https://konghq.com/&#34;&gt;Kong Inc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Andrew is a highly passionate technologist with over 16 valuable years experience in building server side and cloud
applications. Having spent the majority of his time in the Financial Services domain, his meritocratic rise to CTO of an
Algorithmic Trading firm allowed him to not only steer the business from a technology standpoint, but build robust and
scalable trading algorithms. His mantra is &amp;ldquo;right first time&amp;rdquo;, thus ensuring the projects or clients he is involved in
are left in a better place than they were before he arrived.&lt;/p&gt;
&lt;p&gt;He is the founder of a boutique software consultancy in the United Kingdom, &lt;a href=&#34;https://quadcorps.co.uk&#34;&gt;QuadCorps Ltd&lt;/a&gt;, working in the API and
Integration Ecosystem space and is currently on a residency programme at &lt;a href=&#34;https://konghq.com/&#34;&gt;Kong Inc&lt;/a&gt; as a senior field engineer and
technical account manager working across many of their enterprise strategic accounts.&lt;/p&gt;
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/r8e9ib0powM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;


      </description>
    </item>
    
    <item>
      <title>Blog: How to use the java agent injector?</title>
      <link>/blog/2022-04-19-how-to-use-the-java-agent-injector/</link>
      <pubDate>Tue, 19 Apr 2022 00:00:00 +0000</pubDate>
      <guid>/blog/2022-04-19-how-to-use-the-java-agent-injector/</guid>
      <description>
        
        
        &lt;h3 id=&#34;content&#34;&gt;content:&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;#1.-Introduction&#34;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#2.-Features&#34;&gt;Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#3.-Install-SWCK&#34;&gt;Install SWCK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#4.-Deploy-a-demo-application&#34;&gt;Deploy a demo application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#5.-Verify-the-injector&#34;&gt;Verify the injector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#6.-Concluding-remarks&#34;&gt;Concluding remarks&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;1-introduction&#34;&gt;1. Introduction&lt;/h2&gt;
&lt;h3 id=&#34;11-whats-swck&#34;&gt;1.1 What&amp;rsquo;s SWCK?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck&#34;&gt;SWCK&lt;/a&gt; is a platform for the SkyWalking user, provisions, upgrades, maintains SkyWalking relevant components, and makes them work natively on Kubernetes.&lt;/p&gt;
&lt;p&gt;In fact, SWCK is an operator developed based on &lt;a href=&#34;https://book.kubebuilder.io/introduction.html&#34;&gt;kubebuilder&lt;/a&gt;, providing users with Custom Resources ( CR ) and controllers for managing resources ( Controller ), all CustomResourceDefinitions（CRDs）are as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#javaagent&#34;&gt;JavaAgent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#oap&#34;&gt;OAP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#ui&#34;&gt;UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#storage&#34;&gt;Storage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#satellite&#34;&gt;Satellite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#fetcher&#34;&gt;Fetcher&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-whats-the-java-agent-injector&#34;&gt;1.2 What&amp;rsquo;s the java agent injector?&lt;/h3&gt;
&lt;p&gt;For a java application, users need to inject the java agent into the application to get metadata and send it to the SkyWalking backend. To make users use the java agent more natively, we propose the java agent injector to inject the java agent sidecar into a pod. The java agent injector is actually a &lt;a href=&#34;https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/&#34;&gt;Kubernetes Mutation Webhook Controller&lt;/a&gt;. The controller intercepts pod events and applies mutations to the pod if annotations exist within the request.&lt;/p&gt;
&lt;h2 id=&#34;2-features&#34;&gt;2. Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transparent&lt;/strong&gt;. User’s applications generally run in normal containers while the java agent runs in the init container, and both belong to the same pod. Each container in the pod mounts a shared memory volume that provides a storage path for the java agent. When the pod starts, the java agent in the init container will run before the application container, and the injector will store the java agent file in the shared memory volume. When the application container starts, the injector injects the agent file into the application by setting the JVM parameter. Users can inject the java agent in this way without rebuilding the container image containing the java agent.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configurability.&lt;/strong&gt; The injector provides two ways to configure the java agent: global configuration and custom configuration. The default global configuration is stored in the &lt;a href=&#34;https://kubernetes.io/docs/concepts/configuration/configmap/&#34;&gt;configmap&lt;/a&gt;, you can update it as your own global configuration, such as &lt;code&gt;backend_service&lt;/code&gt;. In addition, you can also set custom configuration for some applications via &lt;a href=&#34;https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/annotations/&#34;&gt;annotation&lt;/a&gt;, such as “service_name”. For more information, please see &lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/java-agent-injector.md&#34;&gt;java-agent-injector&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Observability.&lt;/strong&gt; For each injected java agent, we provide &lt;a href=&#34;https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/&#34;&gt;CustomDefinitionResources&lt;/a&gt; called &lt;code&gt;JavaAgent&lt;/code&gt; to observe the final agent configuration. Please refer to &lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/javaagent.md&#34;&gt;javaagent&lt;/a&gt; to get more details.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;3-install-swck&#34;&gt;3. Install SWCK&lt;/h2&gt;
&lt;p&gt;In the next steps, we will show how to build a stand-alone Kubernetes cluster and deploy the 0.6.1 version of SWCK on the platform.&lt;/p&gt;
&lt;h3 id=&#34;31-tool-preparation&#34;&gt;3.1 Tool Preparation&lt;/h3&gt;
&lt;p&gt;Firstly, you need to install some tools as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;http://kind.sigs.k8s.io&#34;&gt;kind&lt;/a&gt;, which is used to create a stand-alone Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kubernetes.io/docs/tasks/tools/&#34;&gt;kubectl&lt;/a&gt;, which is used to communicate with the Kubernetes cluster.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;32-install-stand-alone-kubernetes-cluster&#34;&gt;3.2 Install stand-alone Kubernetes cluster&lt;/h3&gt;
&lt;p&gt;After installing &lt;code&gt;kind&lt;/code&gt; , you could use the following command to create a stand-alone Kubernetes cluster.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice! If your terminal is configured with a proxy, you need to close it before the cluster is created to avoid some errors.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kind create cluster --image&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;kindest/node:v1.19.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After creating a cluster, you can get the pods as below.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get pod -A                          
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAMESPACE            NAME                                         READY   STATUS    RESTARTS   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          coredns-f9fd979d6-57xpc                      1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m16s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          coredns-f9fd979d6-8zj8h                      1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m16s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          etcd-kind-control-plane                      1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m23s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          kindnet-gc9gt                                1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m16s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          kube-apiserver-kind-control-plane            1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m23s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          kube-controller-manager-kind-control-plane   1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m23s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          kube-proxy-6zbtb                             1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m16s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kube-system          kube-scheduler-kind-control-plane            1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m23s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;local-path-storage   local-path-provisioner-78776bfc44-jwwcs      1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          7m16s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;33-install-certificates-mangercert-manger&#34;&gt;3.3 Install certificates manger(cert-manger)&lt;/h3&gt;
&lt;p&gt;The certificates of SWCK are distributed and verified by the certificate manager. You need to install the &lt;a href=&#34;https://cert-manager.io/docs/&#34;&gt;cert-manager&lt;/a&gt; through the following command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verify whether cert-manager is installed successfully.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get pod -n cert-manager
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                                       READY   STATUS    RESTARTS   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cert-manager-7dd5854bb4-slcmd              1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          73s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cert-manager-cainjector-64c949654c-tfmt2   1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          73s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cert-manager-webhook-6bdffc7c9d-h8cfv      1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          73s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;34-install-swck&#34;&gt;3.4 Install SWCK&lt;/h3&gt;
&lt;p&gt;The java agent injector is a component of the operator, so please follow the next steps to install the operator first.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the deployment yaml file of SWCK and deploy it.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ curl -Ls https://archive.apache.org/dist/skywalking/swck/0.6.1/skywalking-swck-0.6.1-bin.tgz &lt;span style=&#34;color:#1f2328&#34;&gt;|&lt;/span&gt; tar -zxf - -O ./config/operator-bundle.yaml &lt;span style=&#34;color:#1f2328&#34;&gt;|&lt;/span&gt; kubectl apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Check SWCK as below.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get pod -n skywalking-swck-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                                                  READY   STATUS    RESTARTS   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;skywalking-swck-controller-manager-7f64f996fc-qh8s9   2/2     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          94s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;35-install-skywalking-components--oapserver-and-ui&#34;&gt;3.5 Install Skywalking components — OAPServer and UI&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Deploy the OAPServer and UI in the &lt;code&gt;default&lt;/code&gt; namespace.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl apply -f https://raw.githubusercontent.com/apache/skywalking-swck/master/operator/config/samples/default.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Check the OAPServer.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get oapserver
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME      INSTANCES   RUNNING   ADDRESS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;default   &lt;span style=&#34;color:#0550ae&#34;&gt;1&lt;/span&gt;           &lt;span style=&#34;color:#0550ae&#34;&gt;1&lt;/span&gt;         default-oap.default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Check the UI.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get ui
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME      INSTANCES   RUNNING   INTERNALADDRESS      EXTERNALIPS   PORTS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;default   &lt;span style=&#34;color:#0550ae&#34;&gt;1&lt;/span&gt;           &lt;span style=&#34;color:#0550ae&#34;&gt;1&lt;/span&gt;         default-ui.default                 &lt;span style=&#34;color:#0550ae&#34;&gt;[&lt;/span&gt;80&lt;span style=&#34;color:#0550ae&#34;&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;4-deploy-a-demo-application&#34;&gt;4. Deploy a demo application&lt;/h2&gt;
&lt;p&gt;In the third step, we have installed SWCK and related Skywalking components. Next, we will show how to use the java agent injector in SWCK through two java application examples in two ways: global configuration and custom configuration.&lt;/p&gt;
&lt;h3 id=&#34;41-set-the-global-configuration&#34;&gt;4.1 Set the global configuration&lt;/h3&gt;
&lt;p&gt;When we have installed SWCK, the default configuration is the configmap in the system namespace, we can get it as follows.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$  kubectl get configmap skywalking-swck-java-agent-configmap -n skywalking-swck-system -oyaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apiVersion: v1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  agent.config: &lt;span style=&#34;color:#1f2328&#34;&gt;|&lt;/span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# The service name in UI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent.service_name&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;SW_AGENT_NAME&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;Your_ApplicationName&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# Backend service addresses.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    collector.backend_service&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;SW_AGENT_COLLECTOR_BACKEND_SERVICES&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;127&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;.0.0.1:&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;11800&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# Please refer to https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/configurations/#table-of-agent-configuration-properties to get more details.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the cluster created by &lt;code&gt;kind&lt;/code&gt;, the &lt;code&gt;backend_service&lt;/code&gt; may not be correct, we need to use the real OAPServer&amp;rsquo;s address &lt;code&gt;default-oap.default&lt;/code&gt; to replace the default &lt;code&gt;127.0.0.1&lt;/code&gt;, so we can edit the configmap as follow.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl edit configmap skywalking-swck-java-agent-configmap -n skywalking-swck-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;configmap/skywalking-swck-java-agent-configmap edited
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get configmap skywalking-swck-java-agent-configmap -n skywalking-swck-system -oyaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;apiVersion: v1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;data:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  agent.config: &lt;span style=&#34;color:#1f2328&#34;&gt;|&lt;/span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# The service name in UI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    agent.service_name&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;SW_AGENT_NAME&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;Your_ApplicationName&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# Backend service addresses.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    collector.backend_service&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;SW_AGENT_COLLECTOR_BACKEND_SERVICES&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;default&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;-oap.default:&lt;/span&gt;&lt;span style=&#34;color:#953800&#34;&gt;11800&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#57606a&#34;&gt;# Please refer to https://skywalking.apache.org/docs/skywalking-java/latest/en/setup/service-agent/java-agent/configurations/#table-of-agent-configuration-properties to get more details.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;42-set-the-custom-configuration&#34;&gt;4.2 Set the custom configuration&lt;/h3&gt;
&lt;p&gt;In some cases, we need to use the Skywalking component to monitor different java applications, so the agent configuration of different applications may be different, such as the name of the application, and the plugins that the application needs to use, etc. Next, we will take two simple java applications developed based on &lt;code&gt;spring boot&lt;/code&gt; and &lt;code&gt;spring cloud gateway&lt;/code&gt; as examples for a detailed description. You can use the &lt;a href=&#34;https://github.com/dashanji/swck-spring-cloud-k8s-demo&#34;&gt;source code&lt;/a&gt; to build the image.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# build the springboot and springcloudgateway image &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ git clone https://github.com/dashanji/swck-spring-cloud-k8s-demo 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#6639ba&#34;&gt;cd&lt;/span&gt; swck-spring-cloud-k8s-demo &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; make
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# check the image&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ docker images
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;REPOSITORY     TAG       IMAGE ID       CREATED          SIZE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gateway        v0.0.1    51d16251c1d5   &lt;span style=&#34;color:#0550ae&#34;&gt;48&lt;/span&gt; minutes ago   723MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app            v0.0.1    62f4dbcde2ed   &lt;span style=&#34;color:#0550ae&#34;&gt;48&lt;/span&gt; minutes ago   561MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# load the image into the cluster&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kind load docker-image app:v0.0.1 &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; kind load docker-image gateway:v0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;43-deploy-spring-boot-application&#34;&gt;4.3 deploy spring boot application&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create the &lt;code&gt;springboot-system&lt;/code&gt; namespace.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl create namespace springboot-system
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Label the &lt;code&gt;springboot-system&lt;/code&gt;namespace to enable the java agent injector.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl label namespace springboot-system swck-injection&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;enabled
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Deploy the corresponding deployment file &lt;code&gt;springboot.yaml&lt;/code&gt; for the spring boot application, which uses annotation to override the default agent configuration, such as &lt;code&gt;service_name&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice! Before using the annotation to override the agent configuration, you need to add &lt;code&gt;strategy.skywalking.apache.org/agent.Overlay: &amp;quot;true&amp;quot;&lt;/code&gt; to make the override take effect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-springboot&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;springboot-system&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;matchLabels&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-springboot&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;labels&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;swck-java-agent-injected&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# enable the java agent injector&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-springboot&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;annotations&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;strategy.skywalking.apache.org/agent.Overlay&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# enable the agent overlay&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;agent.skywalking.apache.org/agent.service_name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;backend-service&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;containers&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;springboot&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;imagePullPolicy&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;IfNotPresent&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;app:v0.0.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;args&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;-jar&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;/app.jar&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;---&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;v1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Service&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;springboot-system&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;ClusterIP&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;8085&lt;/span&gt;-tcp&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;port&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;8085&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;protocol&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;TCP&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;targetPort&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;8085&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-springboot&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Deploy a &lt;code&gt;spring boot&lt;/code&gt; application in the &lt;code&gt;springboot-system&lt;/code&gt; namespace.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl apply -f springboot.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;Check for deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get pod -n springboot-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                               READY   STATUS    RESTARTS   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;demo-springboot-7c89f79885-dvk8m   1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          11s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;Get the finnal injected java agent configuration through &lt;code&gt;JavaAgent&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get javaagent -n springboot-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                            PODSELECTOR           SERVICENAME       BACKENDSERVICE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app-demo-springboot-javaagent   &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;demo-springboot   backend-service   default-oap.default:11800
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;44-deploy-spring-cloud-gateway-application&#34;&gt;4.4 deploy spring cloud gateway application&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Create the &lt;code&gt;gateway-system&lt;/code&gt; namespace.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl create namespace gateway-system
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Label the &lt;code&gt;gateway-system&lt;/code&gt;namespace to enable the java agent injector.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl label namespace gateway-system swck-injection&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;enabled
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Deploy the corresponding deployment file &lt;code&gt;springgateway.yaml&lt;/code&gt; for the spring cloud gateway application, which uses annotation to override the default agent configuration, such as &lt;code&gt;service_name&lt;/code&gt;. In addition, when using &lt;code&gt;spring cloud gateway&lt;/code&gt;, we need to add the &lt;code&gt;spring cloud gateway&lt;/code&gt; plugin to the agent configuration.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice! Before using the annotation to override the agent configuration, you need to add &lt;code&gt;strategy.skywalking.apache.org/agent.Overlay: &amp;quot;true&amp;quot;&lt;/code&gt; to make the override take effect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;labels&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gateway-system&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;matchLabels&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;labels&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;swck-java-agent-injected&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;annotations&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;strategy.skywalking.apache.org/agent.Overlay&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;agent.skywalking.apache.org/agent.service_name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;gateway-service&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;     
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;optional.skywalking.apache.org&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;cloud-gateway-3.x&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# add spring cloud gateway plugin&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;containers&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gateway:v0.0.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;java&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;args&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;-jar&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;,&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;/gateway.jar&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#24292e&#34;&gt;---&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;v1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Service&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;service-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gateway-system&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;ClusterIP&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;ports&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;9999&lt;/span&gt;-tcp&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;port&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;9999&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;protocol&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;TCP&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;targetPort&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;9999&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;demo-gateway&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Deploy a &lt;code&gt;spring cloud gateway&lt;/code&gt; application in the &lt;code&gt;gateway-system&lt;/code&gt; namespace.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl apply -f springgateway.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;Check for deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get pod -n gateway-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                           READY   STATUS    RESTARTS   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;demo-gateway-5bb77f6d85-9j7c6   1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          15s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;Get the finnal injected java agent configuration through &lt;code&gt;JavaAgent&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get javaagent -n gateway-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME                         PODSELECTOR        SERVICENAME       BACKENDSERVICE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;app-demo-gateway-javaagent   &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;demo-gateway   gateway-service   default-oap.default:11800
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;5-verify-the-injector&#34;&gt;5. Verify the injector&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;After completing the above steps, we can view detailed state of the injected pod, like the injected &lt;code&gt;agent&lt;/code&gt; container.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# get all injected pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get pod -A -lswck-java-agent-injected&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAMESPACE           NAME                               READY   STATUS    RESTARTS   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gateway-system      demo-gateway-5bb77f6d85-lt4z7      1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          69s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;springboot-system   demo-springboot-7c89f79885-lkb5j   1/1     Running   &lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;          75s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# view detailed state of the injected pod [demo-springboot]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl describe pod -l &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;demo-springboot -n springboot-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Events:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Type   Reason   Age                From                           Message
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ----   ------  ----                ----                           -------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Created  91s  kubelet,kind-control-plane Created  container inject-skywalking-agent
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Started  91s  kubelet,kind-control-plane Started  container inject-skywalking-agent
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Created  90s  kubelet,kind-control-plane Created  container springboot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Started  90s  kubelet,kind-control-plane Started  container springboot
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# view detailed state of the injected pod [demo-gateway] &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl describe pod -l &lt;span style=&#34;color:#953800&#34;&gt;app&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;demo-gateway -n gateway-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Events:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Type   Reason   Age            From                         Message
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ----   ------  ----            ----                         -------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Created 2m20s kubelet,kind-control-plane Created container inject-skywalking-agent
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Started 2m20s kubelet,kind-control-plane Started container inject-skywalking-agent
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Created 2m20s kubelet,kind-control-plane Created container gateway
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Normal Started 2m20s kubelet,kind-control-plane Started container gateway
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Now we can expose the service and watch the data displayed on the web. First of all, we need to get the &lt;code&gt;gateway&lt;/code&gt; service and the &lt;code&gt;ui&lt;/code&gt; service as follows.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get service service-gateway -n gateway-system
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span style=&#34;color:#0550ae&#34;&gt;(&lt;/span&gt;S&lt;span style=&#34;color:#0550ae&#34;&gt;)&lt;/span&gt;    AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;service-gateway   ClusterIP   10.99.181.145   &amp;lt;none&amp;gt;        9999/TCP   9m19s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl get service default-ui
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT&lt;span style=&#34;color:#0550ae&#34;&gt;(&lt;/span&gt;S&lt;span style=&#34;color:#0550ae&#34;&gt;)&lt;/span&gt;   AGE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;default-ui   ClusterIP   10.111.39.250   &amp;lt;none&amp;gt;        80/TCP    82m
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;Then open two terminals to expose the service:  &lt;code&gt;service-gateway&lt;/code&gt;、&lt;code&gt;default-ui&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl port-forward service/service-gateway -n gateway-system 9999:9999
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Forwarding from 127.0.0.1:9999 -&amp;gt; &lt;span style=&#34;color:#0550ae&#34;&gt;9999&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Forwarding from &lt;span style=&#34;color:#0550ae&#34;&gt;[&lt;/span&gt;::1&lt;span style=&#34;color:#0550ae&#34;&gt;]&lt;/span&gt;:9999 -&amp;gt; &lt;span style=&#34;color:#0550ae&#34;&gt;9999&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ kubectl port-forward service/default-ui 8090:80                     
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Forwarding from 127.0.0.1:8090 -&amp;gt; &lt;span style=&#34;color:#0550ae&#34;&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Forwarding from &lt;span style=&#34;color:#0550ae&#34;&gt;[&lt;/span&gt;::1&lt;span style=&#34;color:#0550ae&#34;&gt;]&lt;/span&gt;:8090 -&amp;gt; &lt;span style=&#34;color:#0550ae&#34;&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;4&#34;&gt;
&lt;li&gt;Use the following commands to access the &lt;code&gt;spring boot&lt;/code&gt; demo 10 times through the &lt;code&gt;spring cloud gateway&lt;/code&gt; service.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#cf222e&#34;&gt;for&lt;/span&gt; i in &lt;span style=&#34;color:#0550ae&#34;&gt;{&lt;/span&gt;1..10&lt;span style=&#34;color:#0550ae&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;;&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;do&lt;/span&gt; curl http://127.0.0.1:9999/gateway/hello &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#6639ba&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;;&lt;/span&gt; &lt;span style=&#34;color:#cf222e&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Hello World!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start=&#34;5&#34;&gt;
&lt;li&gt;We can see the Dashboard by accessing &lt;code&gt;http://127.0.0.1:8090&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;dashboard.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ol start=&#34;6&#34;&gt;
&lt;li&gt;All services&amp;rsquo; topology is shown below.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;topology.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ol start=&#34;7&#34;&gt;
&lt;li&gt;We can see the trace information of &lt;code&gt;gateway-service&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;gateway.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ol start=&#34;8&#34;&gt;
&lt;li&gt;We can see the trace information of &lt;code&gt;backend-service&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;backend.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;6-concluding-remarks&#34;&gt;6. Concluding remarks&lt;/h2&gt;
&lt;p&gt;If your application is deployed in the Kubernetes platform and requires Skywalking to provide monitoring services, SWCK can help you deploy, upgrade and maintain the Skywalking components in the Kubernetes cluster. In addition to this blog, you can also view &lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/operator.md#operator-Usage-Guide&#34;&gt;swck document&lt;/a&gt; and &lt;a href=&#34;https://github.com/apache/skywalking-swck/blob/master/docs/java-agent-injector.md&#34;&gt;Java agent injector documentation&lt;/a&gt; for more information. If you find this project useful, please give &lt;a href=&#34;https://github.com/apache/skywalking-swck&#34;&gt;SWCK&lt;/a&gt; a star! If you have any questions, welcome to ask in &lt;a href=&#34;https://github.com/apache/skywalking/issues&#34;&gt;Issues&lt;/a&gt; or &lt;a href=&#34;https://github.com/apache/skywalking/discussions&#34;&gt;Discussions&lt;/a&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Zh: 将 Apache SkyWalking 与源代码集成</title>
      <link>/zh/2022-04-14-integrating-skywalking-with-source-code/</link>
      <pubDate>Thu, 14 Apr 2022 00:00:00 +0000</pubDate>
      <guid>/zh/2022-04-14-integrating-skywalking-with-source-code/</guid>
      <description>
        
        
        &lt;p&gt;&lt;sub&gt;Read this post in original language: &lt;a href=&#34;https://skywalking.apache.org/blog/2022-04-14-integrating-skywalking-with-source-code/&#34;&gt;English&lt;/a&gt;&lt;/sub&gt;&lt;/p&gt;
&lt;h2 id=&#34;介绍&#34;&gt;介绍&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;最具影响力的技术是那些消失的技术。他们交织在日常生活中，直到二者完全相融。 - 马克韦瑟&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;马克韦瑟在 1980 年代后期预言，影响最深远的技术是那些消失在空气中的技术。&lt;/p&gt;
&lt;p&gt;“当人们足够熟知它，就不会再意识到它。”&lt;/p&gt;
&lt;p&gt;正如韦瑟所说，这种消失的现象不只源于技术，更是人类的心理。
正是这种经验使我们能够摆脱对底层的考量，进入更高层次的思考。
一旦我们不再被平凡的细枝末节所阻碍，我们就可以自如地专注于新的目标。&lt;/p&gt;
&lt;p&gt;随着 APM(应用性能管理系统) 变得越来越普遍，这种认识变得更加重要。随着更多的应用程序开始使用 APM 部署，底层源代码抽象表示的数量也在同步增加。
虽然这为组织内的许多非开发角色提供了巨大的价值，但它确实也对开发人员提出了额外的挑战 -
他们必须将这些表示转化为可操作的概念（即源代码）。
对此，韦瑟相当简洁的总结道,“就像不应要求汽车机械师在不查看引擎的情况下工作一样，我们不应要求程序员在不访问源代码的情况下工作”。&lt;/p&gt;
&lt;p&gt;尽管如此，APM 收集更多信息只是为了产生充足的新抽象表示。
在本文中，我们将介绍开源实时编码平台 &lt;a href=&#34;https://github.com/sourceplusplus/live-platform&#34;&gt;Source++&lt;/a&gt; 中的一个新概念，旨在让开发人员更直观地监控生产应用程序。&lt;/p&gt;
&lt;h2 id=&#34;实时查看&#34;&gt;实时查看&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我们尚且不理解在收集了数百个指标之后，是什么让程序更容易理解、修改、重复使用或借用。
我不认为我们能够通过原理程序本身而到它们的抽象接口中找到答案。答案就在源代码之中。 - 马克韦瑟&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;随着 APM 从“有了更好”转变为“必须拥有”，有一个基本特性阻碍了它们的普及。
它们必须从意识中消失。作为开发人员，我们不应急于打开浏览器以更好地理解底层源代码，答案就在源代码中。
相反，我们应该改进我们的工具，以便源代码直观地告诉我们需要了解的内容。
想想如果失败的代码总是表明它是如何以及为什么失败的，生活会多么简单。这就是 Source++ 背后的理念。&lt;/p&gt;
&lt;p&gt;在我们的上一篇博客中，我们讨论了不间断断点 &lt;a href=&#34;https://skywalking.apache.org/blog/2021-12-06-extend-skywalking-with-nbb/&#34;&gt;Extending Apache SkyWalking&lt;/a&gt;。
我们介绍了一个名为 &lt;strong&gt;Live Instruments&lt;/strong&gt;(实时埋点) 的概念，开发人员可以使用它轻松调试实时生产应用程序，而无需离开他们的开发环境。
而今天，我们将讨论如何通过一个名为 &lt;strong&gt;Live Views&lt;/strong&gt;（实时查看）的新概念将现有部署的 SkyWalking 集成到您的 IDE 中。
与专为调试实时应用程序而设计的 Live Instruments (实时埋点) 不同，Live Views（实时查看）旨在提高对应用程序的理解和领悟。
这将通过输入到 Live Command Palette (实时命令面板) 中的各种命令来完成。&lt;/p&gt;
&lt;h3 id=&#34;实时命令面板&#34;&gt;实时命令面板&lt;/h3&gt;
&lt;p&gt;Live Command Palette (LCP) 是一个当前上下文场景下的命令行面板，这个组件包含在 &lt;a href=&#34;https://github.com/sourceplusplus/interface-jetbrains&#34;&gt;Source++ JetBrains 插件中&lt;/a&gt;，它允许开发人员从 IDE 中直接控制和对实时应用程序发起查询。&lt;/p&gt;
&lt;p&gt;LCP 通过键盘快捷键 (&lt;code&gt;Ctrl+Shift+S&lt;/code&gt;) 打开，允许开发人员轻松了解与他们当前正在查看的源代码相关的运行指标。&lt;/p&gt;
&lt;p&gt;目前 LCP 支持以下实时查看命令：&lt;/p&gt;
&lt;h4 id=&#34;命令viewoverviewactivitytraceslogs--查看-总览活动追踪日志&#34;&gt;命令：&lt;code&gt;view&lt;/code&gt;（overview/activity/traces/Logs）- 查看 总览/活动/追踪/日志&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;view&lt;/code&gt; 查看命令会展示一个与当前源码的实时运维数据关联的弹窗。
这些命令允许开发人员查看根据相关指标过滤的传统 SkyWalking 的运维数据。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;view_command.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;命令watch-log---实时监听日志&#34;&gt;命令：&lt;code&gt;watch log&lt;/code&gt; - 实时监听日志&lt;/h4&gt;
&lt;p&gt;本日志命令允许开发人员实时跟踪正在运行的应用程序的每一条日志。
通过此命令开发人员无需手动查阅大量日志就可以查找特定日志语句的实例。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;watch_log_command.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;命令showhide-quick-stats-显示隐藏快速统计&#34;&gt;命令：(show/hide) quick stats （显示/隐藏）快速统计&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;show quick stats&lt;/code&gt; 显示快速统计命令显示实时端点指标，以便快速了解端点的活动。
使用此命令，开发人员可以快速评估端点的状态并确定端点是否按预期正常运行。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;show_quick_stats_command.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;未来的工作&#34;&gt;未来的工作&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;好工具是无形的。我所指的无形，是指这个工具不会侵入你的意识；
你专注于任务，而不是工具。
眼镜就是很好的工具——你看的是世界，而不是眼镜。 - 马克韦瑟&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source++ 旨在扩展 SkyWalking，使 SkyWalking 本身变得无需感知。
为此，我们计划支持自定义的开发人员命令。
开发人员将能够构建自定义命令，以及与团队共享的命令。
这些命令将识别上下文、类型和条件，从而允许广泛的操作。
随着更多命令的添加，开发人员将能够洞悉 SkyWalking 所提供的所有功能，同时专注于最重要的源码。&lt;/p&gt;
&lt;p&gt;如果您觉得这些功能有用，请考虑尝试使用 Source++。
您可以通过 &lt;a href=&#34;https://plugins.jetbrains.com/plugin/12033-source-&#34;&gt;JetBrains Marketplace&lt;/a&gt;
或直接从您的 JetBrains IDE 安装插件。
如果您有任何疑问，&lt;a href=&#34;https://github.com/sourceplusplus/interface-jetbrains/issues&#34;&gt;请到这提 issue&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;欢迎随时反馈！&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: SourceMarker: Continuous Feedback for Developers</title>
      <link>/blog/2021-03-16-continuous-feedback/</link>
      <pubDate>Tue, 16 Mar 2021 00:00:00 +0000</pubDate>
      <guid>/blog/2021-03-16-continuous-feedback/</guid>
      <description>
        
        
        &lt;p&gt;&lt;img src=&#34;SM_IDE-APM.gif&#34; alt=&#34;Alt Text&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://sourcemarker.dev&#34;&gt;SourceMarker&lt;/a&gt; is an open-source continuous feedback IDE plugin built on top of Apache SkyWalking, a popular open-source APM system with monitoring, tracing, and diagnosing capabilities for distributed software systems. SkyWalking, a truly holistic system, provides the means for automatically producing, storing, and querying software operation metrics. It requires little to no code changes to implement and is lightweight enough to be used in production. By itself, SkyWalking is a formidable force in the realm of continuous monitoring technology.&lt;/p&gt;
&lt;p&gt;SourceMarker, leveraging the continuous monitoring functionality provided by SkyWalking, creates continuous feedback technology by automatically linking software operation metrics to source code and displaying feedback directly inside of the IDE. While currently only supporting JetBrains-based IDEs and JVM-based programming languages, SourceMarker may be extended to support any number of programming languages and IDEs. Using SourceMarker, software developers can understand and validate software operation inside of their IDE. Instead of charts that indicate the health of the application, software developers can view the health of individual source code components and interpret software operation metrics from a much more familiar perspective. Such capabilities improve productivity as time spent continuously context switching from development to monitoring would be eliminated.&lt;/p&gt;
&lt;h2 id=&#34;logging&#34;&gt;Logging&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;SM_Logging.gif&#34; alt=&#34;Logging&#34;&gt;&lt;/p&gt;
&lt;p&gt;The benefits of continuous feedback technology are immediately apparent with the ability to view and search logs directly from source code. Instead of tailing log files or viewing logs through the browser, SourceMarker allows software developers to navigate production logs just as easily as they navigate source code. By using the source code as the primary perspective for navigating logs, SourceMarker allows software developers to view logs specific to any package, class, method, or line directly from the context of the source code which resulted in those logs.&lt;/p&gt;
&lt;h2 id=&#34;tracing&#34;&gt;Tracing&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;SM_Tracing.gif&#34; alt=&#34;Tracing&#34;&gt;&lt;/p&gt;
&lt;p&gt;Furthermore, continuous feedback technology offers software developers a deeper understanding of software by explicitly tying the implicit software operation to source code. Instead of visualizing software traces as Gantt charts, SourceMarker allows software developers to step through trace stacks while automatically resolving trace tags and logs. With SourceMarker, software developers can navigate production software traces in much the same way one debugs local applications.&lt;/p&gt;
&lt;h2 id=&#34;alerting&#34;&gt;Alerting&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;SM_Alerting.gif&#34; alt=&#34;Alerting&#34;&gt;&lt;/p&gt;
&lt;p&gt;Most importantly, continuous feedback technology keeps software developers aware of production software operation. Armed with an APM-powered IDE, every software developer can keep track of the behavior of any method, class, package, and even the entire application itself. Moreover, this allows for source code to be the medium through which production bugs are made evident, thereby creating the feasibility of source code with the ability to self-diagnose and convey its own health.&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;download-sourcemarker&#34;&gt;Download SourceMarker&lt;/h1&gt;
&lt;p&gt;SourceMarker aims to bridge the theoretical and empirical practices of software development through continuous feedback. The goal is to make developing software with empirical data feel natural and intuitive, creating more complete software developers that understand the entire software development cycle.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/sourceplusplus/sourcemarker&#34;&gt;https://github.com/sourceplusplus/sourcemarker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This project is still early in its development, so if you think of any ways to improve SourceMarker, please let us know.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: [Design] The Verifier of NGE2E</title>
      <link>/blog/2021-02-01-e2e-verifier-design/</link>
      <pubDate>Mon, 01 Feb 2021 00:00:00 +0000</pubDate>
      <guid>/blog/2021-02-01-e2e-verifier-design/</guid>
      <description>
        
        
        &lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;The verifier is an important part of the next generation End-to-End Testing framework (NGE2E), which is responsible for verifying whether the actual output satisfies the expected template.&lt;/p&gt;
&lt;h2 id=&#34;design-thinking&#34;&gt;Design Thinking&lt;/h2&gt;
&lt;p&gt;We will implement the verifier with &lt;a href=&#34;https://golang.org/pkg/text/template/&#34;&gt;Go template&lt;/a&gt;, plus some enhancements. Firstly, users need to write a Go template file with provided functions and actions to describe how the expected data looks like. Then the verifer renders the template with the actual data object. Finally, the verifier compares the rendered output with the actual data. If the rendered output is not the same with the actual output, it means the actual data is inconsist with the expected data. Otherwise, it means the actual data match the expected data. On failure, the verifier will also print out what are different between expected and actual data.&lt;/p&gt;
&lt;h2 id=&#34;branches--actions&#34;&gt;Branches / Actions&lt;/h2&gt;
&lt;p&gt;The verifier inherits all the actions from the standard Go template, such as &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;range&lt;/code&gt;, etc. In addition, we also provide some custom actions to satisfy our own needs.&lt;/p&gt;
&lt;h3 id=&#34;list-elements-match&#34;&gt;List Elements Match&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;contains&lt;/code&gt; checks if the actual list contains elements that match the given template.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metrics&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{- contains .metrics }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;notEmpty .name }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;notEmpty .id }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gt .value 0 }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{- end }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It means that the list &lt;code&gt;metrics&lt;/code&gt; must contain an element whose &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; are not empty, and &lt;code&gt;value&lt;/code&gt; is greater than &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metrics&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{- contains .metrics }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;p95&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gt .value 0 }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;p99&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gt .value 0 }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{- end }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This means that the list &lt;code&gt;metrics&lt;/code&gt; must contain an element named &lt;code&gt;p95&lt;/code&gt; with a &lt;code&gt;value&lt;/code&gt; greater than 0, and an element named &lt;code&gt;p95&lt;/code&gt; with a &lt;code&gt;value&lt;/code&gt; greater than 0. Besides the two element, the list &lt;code&gt;metrics&lt;/code&gt; may or may not have other random elements.&lt;/p&gt;
&lt;h2 id=&#34;functions&#34;&gt;Functions&lt;/h2&gt;
&lt;p&gt;Users can use these provided functions in the template to describe the expected data.&lt;/p&gt;
&lt;h3 id=&#34;not-empty&#34;&gt;Not Empty&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;notEmpty&lt;/code&gt; checks if the string &lt;code&gt;s&lt;/code&gt; is empty.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;notEmpty .id }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;regexp-match&#34;&gt;Regexp match&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;regexp&lt;/code&gt; checks if string &lt;code&gt;s&lt;/code&gt; matches the regular expression pattern.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;label&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;regexp .label &amp;#34;ratings.*&amp;#34; }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;base64&#34;&gt;Base64&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;b64enc s&lt;/code&gt; returns the Base64 encoded string of s.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;User&amp;#34; }}.static-suffix&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# this evalutes the base64 encoded string of &amp;#34;User&amp;#34;, concatenated with a static suffix &amp;#34;.static-suffix&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Result:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;VXNlcg==.static-suffix&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;full-example&#34;&gt;Full Example&lt;/h2&gt;
&lt;p&gt;Here is an example of expected data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# expected.data.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;nodes&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;User&amp;#34; }}.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;User&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;USER&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;isReal&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;Your_ApplicationName&amp;#34; }}.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Your_ApplicationName&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Tomcat&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;isReal&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;$h2ID := (index .nodes 2).id }}{{ notEmpty $h2ID }}&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# We assert that nodes[2].id is not empty and save it to variable `h2ID` for later use&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;localhost:-1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;H2&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;isReal&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;calls&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;notEmpty (index .calls 0).id }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;Your_ApplicationName&amp;#34; }}.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;$h2ID }}&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# We use the previously assigned variable `h2Id` to asert that the `target` is equal to the `id` of the nodes[2]&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;detectPoints&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- CLIENT&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;User&amp;#34; }}.0-{{ b64enc &amp;#34;Your_ApplicationName&amp;#34; }}.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;User&amp;#34; }}.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;b64enc &amp;#34;Your_ApplicationName&amp;#34; }}.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;detectPoints&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- SERVER&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;will validate this data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# actual.data.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;nodes&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;VXNlcg==.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;User&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;USER&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;isReal&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;WW91cl9BcHBsaWNhdGlvbk5hbWU=.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Your_ApplicationName&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;Tomcat&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;isReal&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;bG9jYWxob3N0Oi0x.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;localhost:-1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;H2&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;isReal&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cf222e&#34;&gt;false&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;calls&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;WW91cl9BcHBsaWNhdGlvbk5hbWU=.1-bG9jYWxob3N0Oi0x.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;WW91cl9BcHBsaWNhdGlvbk5hbWU=.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;detectPoints&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- CLIENT&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;bG9jYWxob3N0Oi0x.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;VXNlcg==.0-WW91cl9BcHBsaWNhdGlvbk5hbWU=.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;source&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;VXNlcg==.0&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;detectPoints&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;- SERVER&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;WW91cl9BcHBsaWNhdGlvbk5hbWU=.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# expected.data.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metrics&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{- contains .metrics }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;notEmpty .name }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;notEmpty .id }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gt .value 0 }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{- end }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;will validate this data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# actual.data.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metrics&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;business-zone::projectA&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;YnVzaW5lc3Mtem9uZTo6cHJvamVjdEE=.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;system::load balancer1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;c3lzdGVtOjpsb2FkIGJhbGFuY2VyMQ==.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;system::load balancer2&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;c3lzdGVtOjpsb2FkIGJhbGFuY2VyMg==.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and will report an error when validating this data, because there is no element with a value greater than 0:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# actual.data.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metrics&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;business-zone::projectA&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;YnVzaW5lc3Mtem9uZTo6cHJvamVjdEE=.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;system::load balancer1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;c3lzdGVtOjpsb2FkIGJhbGFuY2VyMQ==.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;system::load balancer2&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;c3lzdGVtOjpsb2FkIGJhbGFuY2VyMg==.1&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;contains&lt;/code&gt; does an unordered list verification, in order to do list verifications including orders, you can simply use the basic ruls like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# expected.data.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;metrics&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;p99&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gt (index .metrics 0).value 0 }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;p95&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;gt (index .metrics 1).value 0 }}&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;which expects the actual &lt;code&gt;metrics&lt;/code&gt; list to be exactly ordered, with first element named &lt;code&gt;p99&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; greater 0, second element named &lt;code&gt;p95&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; greater 0.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: [Design] NGE2E - Next Generation End-to-End Testing Framework</title>
      <link>/blog/e2e-design/</link>
      <pubDate>Mon, 14 Dec 2020 00:00:00 +0000</pubDate>
      <guid>/blog/e2e-design/</guid>
      <description>
        
        
        &lt;p&gt;NGE2E is the next generation End-to-End Testing framework that aims to help developers to set up, debug, and verify E2E tests with ease. It&amp;rsquo;s built based on the lessons learnt from tens of hundreds of test cases in the SkyWalking main repo.&lt;/p&gt;
&lt;h1 id=&#34;goal&#34;&gt;Goal&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Keep the feature parity with the existing E2E framework in SkyWalking main repo;&lt;/li&gt;
&lt;li&gt;Support both &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;docker-compose&lt;/a&gt; and &lt;a href=&#34;https://kind.sigs.k8s.io&#34;&gt;KinD&lt;/a&gt; to orchestrate the tested services under different environments;&lt;/li&gt;
&lt;li&gt;Get rid of the heavy &lt;code&gt;Java/Maven&lt;/code&gt; stack, which exists in the current E2E; be language independent as much as possible, users only need to configure YAMLs and run commands, without writing codes;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;non-goal&#34;&gt;Non-Goal&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;This framework is not involved with the build process, i.e. it won&amp;rsquo;t do something like &lt;code&gt;mvn package&lt;/code&gt; or &lt;code&gt;docker build&lt;/code&gt;, the artifacts (&lt;code&gt;.tar&lt;/code&gt;, docker images) should be ready in an earlier process before this;&lt;/li&gt;
&lt;li&gt;This project doesn&amp;rsquo;t take the plugin tests into account, at least for now;&lt;/li&gt;
&lt;li&gt;This project doesn&amp;rsquo;t mean to add/remove any new/existing test case to/from the main repo;&lt;/li&gt;
&lt;li&gt;This documentation won&amp;rsquo;t cover too much technical details of how to implement the framework, that should go into an individual documentation;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;design&#34;&gt;Design&lt;/h1&gt;
&lt;p&gt;Before diving into the design details, let&amp;rsquo;s take a quick look at how the end user might use NGE2E.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All the following commands are mock, and are open to debate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To run a test case in a directory &lt;code&gt;/path/to/the/case/directory&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e run /path/to/the/case/directory
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;cd&lt;/span&gt; /path/to/the/case/directory &lt;span style=&#34;color:#0550ae&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; e2e run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will run the test case in the specified directory, this command is a wrapper that glues all the following commands, which can be executed separately, for example, to debug the case:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: because all the options can be loaded from a configuration file, so as long as a configuration file (say &lt;code&gt;e2e.yaml&lt;/code&gt;) is given in the directory, every command should be able to run in bare mode (without any option explicitly specified in the command line);&lt;/p&gt;
&lt;h3 id=&#34;set-up&#34;&gt;Set Up&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e setup --env&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;compose --file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;docker-compose.yaml --wait-for&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;service/health
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e setup --env&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;kind --file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;kind.yaml --manifests&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;bookinfo.yaml,gateway.yaml --wait-for&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;pod/ready
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e setup &lt;span style=&#34;color:#57606a&#34;&gt;# If configuration file e2e.yaml is present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--env&lt;/code&gt;: the environment, may be &lt;code&gt;compose&lt;/code&gt; or &lt;code&gt;kind&lt;/code&gt;, represents docker-compose and KinD respectively;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--file&lt;/code&gt;: the &lt;code&gt;docker-compose.yaml&lt;/code&gt; or &lt;code&gt;kind.yaml&lt;/code&gt; file that declares how to set up the environment;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--manifests&lt;/code&gt;: for KinD, the resources files/directories to apply (using &lt;code&gt;kubectl apply -f&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--command&lt;/code&gt;: a command to run after the environment is started, this may be useful when users need to install some extra tools or apply resources from command line, like &lt;code&gt;istioctl install --profile=demo&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--wait-for&lt;/code&gt;: can be specified multiple times to give a list of conditions to be met; wait until the given conditions are met; the most frequently-used strategy should be &lt;code&gt;--wait-for=service/health&lt;/code&gt;, &lt;code&gt;--wait-for=deployments/available&lt;/code&gt;, etc. that make the &lt;code&gt;e2e setup&lt;/code&gt; command to wait for all conditions to be met; other possible strategies may be something like &lt;code&gt;--wait-for=&amp;quot;log:Started Successfully&amp;quot;&lt;/code&gt;, &lt;code&gt;--wait-for=&amp;quot;http:localhost:8080/healthcheck&amp;quot;&lt;/code&gt;, etc. if really needed;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;trigger-inputs&#34;&gt;Trigger Inputs&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e trigger --interval&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;3s --times&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt; --action&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;http --url&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;localhost:8080/users&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e trigger --interval&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;3s --times&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt; --action&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;cmd --cmd&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;curl localhost:8080/users&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e trigger &lt;span style=&#34;color:#57606a&#34;&gt;# If configuration file e2e.yaml is present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--interval=3s&lt;/code&gt;: trigger the action every 3 seconds;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--times=0&lt;/code&gt;: how many times to trigger the action, &lt;code&gt;0=infinite&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--action=http&lt;/code&gt;: the action of the trigger, i.e. &amp;ldquo;perform an http request as an input&amp;rdquo;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--action=cmd&lt;/code&gt;: the action of the trigger, i.e. &amp;ldquo;execute the &lt;code&gt;cmd&lt;/code&gt; as an input&amp;rdquo;;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;query-output&#34;&gt;Query Output&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;swctl service ls
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;this is a project-specific step, different project may use different tools to query the actual output, for SkyWalking, it uses &lt;code&gt;swctl&lt;/code&gt; to query the actual output.&lt;/p&gt;
&lt;h3 id=&#34;verify&#34;&gt;Verify&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e verify --actual&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;actual.data.yaml --expected&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;expected.data.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e verify --query&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#0a3069&#34;&gt;&amp;#34;swctl service ls&amp;#34;&lt;/span&gt; --expected&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;expected.data.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e verify &lt;span style=&#34;color:#57606a&#34;&gt;# If configuration file e2e.yaml is present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--actual&lt;/code&gt;: the actual data file, only YAML file format is supported;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--expected&lt;/code&gt;: the expected data file, only YAML file format is supported;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--query&lt;/code&gt;: the query to get the actual data, the query result must have the same format as &lt;code&gt;--actual&lt;/code&gt; and &lt;code&gt;--expected&lt;/code&gt;;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;--query&lt;/code&gt; option will get the output into a temporary file and use the &lt;code&gt;--actual&lt;/code&gt; under the hood;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;cleanup&#34;&gt;Cleanup&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e cleanup --env&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;compose --file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;docker-compose.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e cleanup --env&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;kind --file&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;kind.yaml --resources&lt;span style=&#34;color:#0550ae&#34;&gt;=&lt;/span&gt;bookinfo.yaml,gateway.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e cleanup &lt;span style=&#34;color:#57606a&#34;&gt;# If configuration file e2e.yaml is present&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This step requires the same options in the setup step so that it can clean up all things necessarily.&lt;/p&gt;
&lt;h3 id=&#34;summarize&#34;&gt;Summarize&lt;/h3&gt;
&lt;p&gt;To summarize, the directory structure of a test case might be&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;case-name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── agent-service        # optional, an arbitrary project that is used in the docker-compose.yaml if needed
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── pom.xml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── src
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── docker-compose.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── e2e.yaml             # see a sample below
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── testdata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── expected.endpoints.service1.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── expected.endpoints.service2.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── expected.services.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;or&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;case-name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── kind.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── bookinfo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── bookinfo.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── bookinfo-gateway.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── e2e.yaml             # see a sample below
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── testdata
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── expected.endpoints.service1.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ├── expected.endpoints.service2.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── expected.services.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;a sample of &lt;code&gt;e2e.yaml&lt;/code&gt; may be&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;setup&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;env&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;kind&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;file&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;kind.yaml&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;manifests&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;bookinfo.yaml&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;wait&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# you can have multiple conditions to wait&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;bookinfo&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;label-selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;app=product&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;for&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;deployment/available&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;reviews&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;label-selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;app=product&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;for&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;deployment/available&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;ratings&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;label-selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;app=product&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;for&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;deployment/available&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;run&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;command&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# it can be a shell script or anything executable&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;istioctl install --profile=demo -y&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;kubectl label namespace default istio-injection=enabled&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;      &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;wait&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;        &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;namespace&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;istio-system&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;label-selector&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;app=istiod&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;          &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;for&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;deployment/available&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# OR&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# env: compose&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#57606a&#34;&gt;# file: docker-compose.yaml&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;trigger&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;action&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;http&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;interval&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;3s&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;times&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;url&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;localhost:9090/users&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;verify&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;query&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;swctl service ls&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;expected&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;expected.services.yaml&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#0550ae&#34;&gt;query&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;swctl endpoint ls --service=&amp;#34;YnVzaW5lc3Mtem9uZTo6cHJvamVjdEM=.1&amp;#34;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;expected&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;expected.projectC.endpoints.yaml&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;then a single command should do the trick.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e run
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id=&#34;modules&#34;&gt;Modules&lt;/h1&gt;
&lt;p&gt;This project is divided into the following modules.&lt;/p&gt;
&lt;h2 id=&#34;controller&#34;&gt;Controller&lt;/h2&gt;
&lt;p&gt;A controller command (&lt;code&gt;e2e run&lt;/code&gt;) composes all the steps declared in the &lt;code&gt;e2e.yaml&lt;/code&gt;, it should be progressive and clearly display which step is currently running. If it failed in a step, the error message should be as much comprehensive as possible. An example of the output might be&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e run
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Started Kind Cluster - Cluster Name
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Checked Pods Readiness - All pods are ready
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;? Generating Traffic - http localhost:9090/users (progress spinner)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Verified Output - service ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(progress spinner) Verifying Output - endpoint ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✘ Failed to Verify Output Data - endpoint ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;the diff content&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;✔ Clean Up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Compared with running the steps one by one, the controller is also responsible for cleaning up env (by executing &lt;code&gt;cleanup&lt;/code&gt; command) no mater what status other commands are, even if they are failed, the controller has the following semantics in terms of &lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;cleanup&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// Java
try {
    setup();
    // trigger step
    // verify step
    // ...
} finally {
    cleanup();
}

// GoLang
func run() {
    setup();
    defer cleanup();
    // trigger step
    // verify step
    // ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;initializer&#34;&gt;Initializer&lt;/h2&gt;
&lt;p&gt;The initializer is responsible for&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;When &lt;code&gt;env==compose&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start the &lt;code&gt;docker-compose&lt;/code&gt; services;&lt;/li&gt;
&lt;li&gt;Check the services&amp;rsquo; healthiness;&lt;/li&gt;
&lt;li&gt;Wait until all services are ready according to the &lt;code&gt;interval&lt;/code&gt;, etc.;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When &lt;code&gt;env==kind&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start the KinD cluster according to the config files;&lt;/li&gt;
&lt;li&gt;Apply the resources files (&lt;code&gt;--manifests&lt;/code&gt;) or/and run the custom init command (&lt;code&gt;--commands&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;Check the pods&amp;rsquo; readiness;&lt;/li&gt;
&lt;li&gt;Wait until all pods are ready according to the &lt;code&gt;interval&lt;/code&gt;, etc.;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;verifier&#34;&gt;Verifier&lt;/h2&gt;
&lt;p&gt;According to scenarios we have at the moment, the must-have features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Matchers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Exact match&lt;/li&gt;
&lt;li&gt;Not null&lt;/li&gt;
&lt;li&gt;Not empty&lt;/li&gt;
&lt;li&gt;Greater than 0&lt;/li&gt;
&lt;li&gt;Regexp match&lt;/li&gt;
&lt;li&gt;At least one of list element match&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Functions&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Base64 encode/decode&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;in order to help to identify simple bugs from the GitHub Actions workflow, there are some &amp;ldquo;nice to have&amp;rdquo; features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Printing the diff content when verification failed is a super helpful bonus proved in the Python agent repo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;logging&#34;&gt;Logging&lt;/h1&gt;
&lt;p&gt;When a test case failed, all the necessary logs should be collected into a dedicated directory, which could be uploaded to the GitHub Artifacts for downloading and analysis;&lt;/p&gt;
&lt;p&gt;Logs through the entire process of a test case are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;KinD clusters logs;&lt;/li&gt;
&lt;li&gt;Containers/pods logs;&lt;/li&gt;
&lt;li&gt;The logs from the NGE2E itself;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;more-planned&#34;&gt;More Planned&lt;/h1&gt;
&lt;h2 id=&#34;debugging&#34;&gt;Debugging&lt;/h2&gt;
&lt;p&gt;Debugging the E2E locally has been a strong requirement and time killer that we haven&amp;rsquo;t solve up to date, though we have enhancements like &lt;a href=&#34;https://github.com/apache/skywalking/pull/5198&#34;&gt;https://github.com/apache/skywalking/pull/5198&lt;/a&gt; , but in this framework, we will adopt a new method to &amp;ldquo;really&amp;rdquo; support debugging locally.&lt;/p&gt;
&lt;p&gt;The most common case when debugging is to run the E2E tests, with one or more services forwarded into the host machine, where the services are run in the IDE or in debug mode.&lt;/p&gt;
&lt;p&gt;For example, you may run the SkyWalking OAP server in an IDE and run &lt;code&gt;e2e run&lt;/code&gt;, expecting the other services (e.g. agent services, SkyWalking WebUI, etc.) inside the containers to connect to your local OAP, instead of the one declared in &lt;code&gt;docker-compose.yaml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For Docker Desktop Mac/Windows, we can access the services running on the host machine inside containers via &lt;code&gt;host.docker.internal&lt;/code&gt;, for Linux, it&amp;rsquo;s &lt;code&gt;172.17.0.1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;One possible solution is to add an option &lt;code&gt;--debug-services=oap,other-service-name&lt;/code&gt; that rewrites all the router rules inside the containers from &lt;code&gt;oap&lt;/code&gt; to &lt;code&gt;host.docker.internal&lt;/code&gt;/&lt;code&gt;172.17.0.1&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;codegen&#34;&gt;CodeGen&lt;/h2&gt;
&lt;p&gt;When adding new test case, a code generator would be of great value to eliminate the repeated labor and copy-pasting issues.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;e2e new &amp;lt;&lt;span style=&#34;color:#cf222e&#34;&gt;case&lt;/span&gt;-name&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
      </description>
    </item>
    
    <item>
      <title>Blog: The first design of Satellite 0.1.0</title>
      <link>/blog/2020-11-25-skywalking-satellite-0.1.0-design/</link>
      <pubDate>Wed, 25 Nov 2020 00:00:00 +0000</pubDate>
      <guid>/blog/2020-11-25-skywalking-satellite-0.1.0-design/</guid>
      <description>
        
        
        &lt;p&gt;&lt;img src=&#34;Satellite.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Author: &lt;a href=&#34;https://github.com/evanljp&#34;&gt;Jiapeng Liu&lt;/a&gt;. Baidu.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/apache/skywalking-satellite&#34;&gt;skywalking-satellite&lt;/a&gt;: The Sidecar Project of Apache SkyWalking&lt;/li&gt;
&lt;li&gt;Nov. 25th, 2020&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A lightweight collector/sidecar which can be deployed close to the target monitored system, to collect metrics, traces, and logs. It also provides advanced features, such as local cache, format transformation, and sampling.&lt;/p&gt;
&lt;h2 id=&#34;design-thinking&#34;&gt;Design Thinking&lt;/h2&gt;
&lt;p&gt;Satellite is a 2 level system to collect observability data from other core systems. So, the core element of the design is to guarantee data stability during Pod startup all the way to Pod shutdown avoiding alarm loss. All modules are designed as plugins, and if you have other ideas, you can add them yourself.&lt;/p&gt;
&lt;h2 id=&#34;slo&#34;&gt;SLO&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Single gatherer supports &amp;gt; 1000 ops (Based 0.5 Core,50M)&lt;/li&gt;
&lt;li&gt;At least once delivery.(Optional)&lt;/li&gt;
&lt;li&gt;Data stability: 99.999%.(Optional)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Because they are influenced by the choice of plugins, some items in SLO are optional.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;role&#34;&gt;Role&lt;/h2&gt;
&lt;p&gt;Satellite would be running as a &lt;strong&gt;Sidecar&lt;/strong&gt;. Although Daemonset mode would take up fewer resources, it will cause more troubles to the forwarding of agents. So we also want to use Sidecar mode by reducing the costs. But Daemonset mode would be also supported in the future plan.&lt;/p&gt;
&lt;h2 id=&#34;core-modules&#34;&gt;Core Modules&lt;/h2&gt;
&lt;p&gt;The Satellite has 3 core modules which are Gatherer, Processor, and Sender.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Gatherer module is responsible for fetching or receiving data and pushing the data to Queue.&lt;/li&gt;
&lt;li&gt;The Processor module is responsible for reading data from the queue and processing data by a series of filter chains.&lt;/li&gt;
&lt;li&gt;The Sender module is responsible for async processing and forwarding the data to the external services in the batch mode. After sending success, Sender would also acknowledge the offset of Queue in Gatherer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;core-modules.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;detailed-structure&#34;&gt;Detailed Structure&lt;/h2&gt;
&lt;p&gt;The overall design is shown in detail in the figure below. We will explain the specific components one by one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;detail-modules.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;gatherer&#34;&gt;Gatherer&lt;/h3&gt;
&lt;h4 id=&#34;concepts&#34;&gt;Concepts&lt;/h4&gt;
&lt;p&gt;The Gatherer has 4 components to support the data collection, which are Input, Collector, Worker, and Queue. There are 2 roles in the Worker, which are Fetcher and Receiver.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Input is an abstraction of the input source, which is usually mapped to a configuration file.&lt;/li&gt;
&lt;li&gt;The Collector is created by the Source, but many collectors could be created by the same Source. For example, when a log path has been configured as the /var/*.log in an Input, the number of collectors is the same as the file number in this path.&lt;/li&gt;
&lt;li&gt;The Fetcher and Receiver is the real worker to collect data. The receiver interface is an abstraction, which has multiple implementations, such as gRPC receiver  and HTTP receiver.Here are some specific use cases:
&lt;ul&gt;
&lt;li&gt;Trace Receiver is a gRPC server for receiving trace data created by Skywalking agents.&lt;/li&gt;
&lt;li&gt;Log Receiver is also a gRPC server for receiving log data which is collected by Skywalking agents. (In the future we want Skywalking Agent to support log sending, and RPC-based log sending is more efficient and needs fewer resources than file reading. For example, the way of file reading will bring IO pressure and performance cost under multi-line splicing.)&lt;/li&gt;
&lt;li&gt;Log Fetcher is like Filebeat, which fits the common log collection scenario. This fetcher will have more responsibility than any other workers because it needs to record the offset and process the multi-line splicing. This feature will be implemented in the future.&lt;/li&gt;
&lt;li&gt;Prometheus Fetcher supports a new way to fetch Prometheus data and push the data to the upstream.&lt;/li&gt;
&lt;li&gt;&amp;hellip;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The Queue is a buffer module to decouple collection and transmission. In the 1st release version, we will use persistent storage to ensure data stability. But the implementation is a plug-in design that can support pure memory queues later.
&lt;img src=&#34;gatherer.jpg&#34; alt=&#34;&#34;&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;the-data-flow&#34;&gt;The data flow&lt;/h4&gt;
&lt;p&gt;We use the Trace Receiver as an example to introduce the data flow.
&lt;img src=&#34;DataFlow.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;queue&#34;&gt;Queue&lt;/h4&gt;
&lt;h5 id=&#34;mmapqueue&#34;&gt;MmapQueue&lt;/h5&gt;
&lt;p&gt;We have simplified the design of MmapQueue to reduce the resources cost on the memory and disk.&lt;/p&gt;
&lt;h6 id=&#34;concepts-1&#34;&gt;Concepts&lt;/h6&gt;
&lt;p&gt;There are 2 core concepts in MmapQueue.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Segment: Segment is the real data store center, that provides large-space storage and does not reduce read and write performance as much as possible by using mmap. And we will avoid deleting files by reusing them.&lt;/li&gt;
&lt;li&gt;Meta: The purpose of meta is to find the data that the consumer needs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h6 id=&#34;segment&#34;&gt;Segment&lt;/h6&gt;
&lt;p&gt;One MmapQueue has a directory to store the whole data. The Queue directory is made up with many segments and 1 meta file. The number of the segments would be computed by 2 params, which are the max cost of the Queue and the cost of each segment. For example, If the max cost is 512M and each segment cost is 256K, the directory can hold up to 2000 files. Once capacity is exceeded, an coverage policy is adopted that means the 2000th would override the first file.&lt;/p&gt;
&lt;p&gt;Each segment in Queue will be N times the size of the page cache and will be read and written in an appended sequence rather than randomly. These would improve the performance of Queue. For example, each Segment is a 128k file, as shown in the figure below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;segments.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h6 id=&#34;meta&#34;&gt;Meta&lt;/h6&gt;
&lt;p&gt;The Meta is a mmap file that only contains 56Bit. There are 5 concepts in the Meta.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Version: A version flag.&lt;/li&gt;
&lt;li&gt;Watermark Offset: Point to the current writing space.
&lt;ul&gt;
&lt;li&gt;ID: SegmentID&lt;/li&gt;
&lt;li&gt;Offset: The offset in Segment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Writed Offset: Point to the latest refreshed data, that would be overridden by the write offset after period refresh.
&lt;ul&gt;
&lt;li&gt;ID: SegmentID&lt;/li&gt;
&lt;li&gt;Offset: The offset in Segment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Reading Offset: Point to the current reading space.
&lt;ul&gt;
&lt;li&gt;ID: SegmentID&lt;/li&gt;
&lt;li&gt;Offset: The offset in Segment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Committed Offset: Point to the latest committed offset , that is equal to the latest acked offset plus one.
&lt;ul&gt;
&lt;li&gt;ID: SegmentID&lt;/li&gt;
&lt;li&gt;Offset: The offset in Segment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;meta.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The following diagram illustrates the transformation process.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The publisher receives data and wants to write to Queue.
&lt;ul&gt;
&lt;li&gt;The publisher would read Writing Offset to find a space and do plus one.&lt;/li&gt;
&lt;li&gt;After this, the publisher will write the data to the space.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The consumer wants to read the data from Queue.
&lt;ul&gt;
&lt;li&gt;The consumer would read Reading Offset to find the current read offset and do plus one.&lt;/li&gt;
&lt;li&gt;After this, the consumer will read the data from the space.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On period flush, the flusher would override Watermark Offset by using Writing Offset.&lt;/li&gt;
&lt;li&gt;When the ack operation is triggered, Committed Offset would plus the batch size in the ack batch.&lt;/li&gt;
&lt;li&gt;When facing crash, Writing Offset and Reading Offset would be overridden by Watermark Offset and Committed Offset. That is because the Reading Offset and Writing Offset cannot guarantee at least once delivery.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;offset-convert.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h6 id=&#34;mmap-performance-test&#34;&gt;Mmap Performance Test&lt;/h6&gt;
&lt;p&gt;The test is to verify the efficiency of mmap in low memory cost.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The rate of data generation: 7.5K/item 1043 item/s (Based on Aifanfan online pod.)&lt;/li&gt;
&lt;li&gt;The test structure is based on &lt;a href=&#34;https://github.com/grandecola/bigqueue&#34;&gt;Bigqueue&lt;/a&gt; because of similar structure.&lt;/li&gt;
&lt;li&gt;Test tool: Go Benchmark Test&lt;/li&gt;
&lt;li&gt;Command: go test -bench BenchmarkEnqueue  -run=none -cpu=1&lt;/li&gt;
&lt;li&gt;Result On Mac(15-inch, 2018,16 GB 2400 MHz DDR4, 2.2 GHz Intel Core i7 SSD):
&lt;ul&gt;
&lt;li&gt;BenchmarkEnqueue/ArenaSize-128KB/MessageSize-8KB/MaxMem-384KB              66501             21606 ns/op              68 B/op          1 allocs/op&lt;/li&gt;
&lt;li&gt;BenchmarkEnqueue/ArenaSize-128KB/MessageSize-8KB/MaxMem-1.25MB             72348             16649 ns/op              67 B/op          1 allocs/op&lt;/li&gt;
&lt;li&gt;BenchmarkEnqueue/ArenaSize-128KB/MessageSize-16KB/MaxMem-1.25MB            39996             33199 ns/op             103 B/op          1 allocs/op&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Result On Linux(INTEL Xeon E5-2450 V2 8C 2.5GHZ&lt;em&gt;2,INVENTEC PC3L-10600 16G&lt;/em&gt;8,INVENTEC SATA 4T 7.2K*8):
&lt;ul&gt;
&lt;li&gt;BenchmarkEnqueue/ArenaSize-128KB/MessageSize-8KB/MaxMem-384KB         	  126662	     12070 ns/op	      62 B/op	       1 allocs/op&lt;/li&gt;
&lt;li&gt;BenchmarkEnqueue/ArenaSize-128KB/MessageSize-8KB/MaxMem-1.25MB        	  127393	     12097 ns/op	      62 B/op	       1 allocs/op&lt;/li&gt;
&lt;li&gt;BenchmarkEnqueue/ArenaSize-128KB/MessageSize-16KB/MaxMem-1.25MB       	   63292	     23806 ns/op	      92 B/op	       1 allocs/op&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conclusion: Based on the above tests, mmap is both satisfied at the write speed and at little memory with very low consumption when running as a sidecar.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;processor&#34;&gt;Processor&lt;/h3&gt;
&lt;p&gt;The Processor has 3 core components, which are Consumer, Filter, and Context.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Consumer is created by the downstream Queue. The consumer has its own read offset and committed offset, which is similar to the offset concept of Spark Streaming.&lt;/li&gt;
&lt;li&gt;Due to the particularity of APM data preprocessing, Context is a unique concept in the Satellite filter chain, which supports storing the intermediate event because the intermediate state event also needs to be sent in sometimes.&lt;/li&gt;
&lt;li&gt;The Filter is the core data processing part, which is similar to the processor of &lt;a href=&#34;https://github.com/elastic/beats&#34;&gt;beats&lt;/a&gt;. Due to the context, the upstream/downstream filters would be logically coupling.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;Processor.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;sender&#34;&gt;Sender&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;BatchConverter decouples the Processor and Sender by staging the Buffer structure, providing parallelization. But if BatchBuffer is full, the downstream processors would be blocked.&lt;/li&gt;
&lt;li&gt;Follower is a real send worker that has a client, such as a gRPC client or Kafka client, and a fallback strategy. Fallback strategy is an interface, we can add more strategies to resolve the abnormal conditions, such as Instability in the network, upgrade the oap cluster.&lt;/li&gt;
&lt;li&gt;When sent success, Committed Offset in Queue would plus the number of this batch.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;Sender.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;high-performance&#34;&gt;High Performance&lt;/h3&gt;
&lt;p&gt;The scenario using Satellite is to collect a lot of APM data collection. We guarantee high performance by the following ways.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Shorten transmission path, that means only join 2 components,which are Queue and Processor, between receiving and forwarding.&lt;/li&gt;
&lt;li&gt;High Performance Queue. MmapQueue provides a big, fast and persistent queue based on memory mapped file and ring structure.&lt;/li&gt;
&lt;li&gt;Processor maintains a linear design, that could be functional processed in one go-routine to avoid too much goroutines switching.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;stability&#34;&gt;Stability&lt;/h2&gt;
&lt;p&gt;Stability is a core point in Satellite. Stability can be considered in many ways, such as stable resources cost, stable running and crash recovery.&lt;/p&gt;
&lt;h3 id=&#34;stable-resource-cost&#34;&gt;Stable resource cost&lt;/h3&gt;
&lt;p&gt;In terms of resource cost,  Memory and CPU should be a concern.&lt;/p&gt;
&lt;p&gt;In the aspect of the CPU, we keep a sequence structure to avoid a large number of retries occurring when facing network congestion. And Satellite avoids keep pulling when the Queue is empty based on the offset design of Queue.&lt;/p&gt;
&lt;p&gt;In the aspect of the Memory, we have guaranteed only one data caching in Satellite, that is Queue. For the queue structure, we also keep the size fixed based on the ring structure to maintain stable Memory cost. Also, MmapQueue is designed for minimizing memory consumption and providing persistence while keeping speed as fast as possible. Maybe supports some strategy to dynamically control the size of MmapQueue to process more extreme conditions in the future.&lt;/p&gt;
&lt;h3 id=&#34;stable-running&#34;&gt;Stable running&lt;/h3&gt;
&lt;p&gt;There are many cases of network congestion, such as the network problem on the host node, OAP cluster is under upgrating, and Kafka cluster is unstable. When facing the above cases, Follower would process fallback strategy and block the downstream processes. Once the failure strategy is finished, such that send success or give up this batch, the Follower would process the next batch.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;Sender.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;crash-recovery&#34;&gt;Crash Recovery&lt;/h3&gt;
&lt;p&gt;The crash recovery only works when the user selects MmapQueue in Gatherer because of persistent file system design. When facing a crash, Reading Offset would be overridden by Committed Offset that ensure the at least once delivery. And Writed Offset would override Writing Offset that ensures the consumer always works properly and avoid encountering uncrossable defective data blocks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;offset-convert.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;buffer-pool&#34;&gt;Buffer pool&lt;/h2&gt;
&lt;p&gt;The Queue is to store fixed structure objects, object buffer pool would be efficient to reuse memory to avoid GC.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ackChan&lt;/li&gt;
&lt;li&gt;batch convertor&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;some-metrics&#34;&gt;Some metrics&lt;/h2&gt;
&lt;p&gt;In Satellite, we should also collect its own monitoring metrics. The following metrics are necessary for Satellite.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cpu&lt;/li&gt;
&lt;li&gt;memory&lt;/li&gt;
&lt;li&gt;go routine number&lt;/li&gt;
&lt;li&gt;gatherer_writing_offset&lt;/li&gt;
&lt;li&gt;gatherer_watermark_offset&lt;/li&gt;
&lt;li&gt;processor_reading_count&lt;/li&gt;
&lt;li&gt;sender_committed_offset&lt;/li&gt;
&lt;li&gt;sender_abandoned_count&lt;/li&gt;
&lt;li&gt;sender_retry_count&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;input-and-output&#34;&gt;Input and Output&lt;/h2&gt;
&lt;p&gt;We will reuse this diagram to explain the input and output.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Input
&lt;ul&gt;
&lt;li&gt;Because the push-pull mode is both supported, Queue is a core component.&lt;/li&gt;
&lt;li&gt;Queue is designed to be a ring-shaped fixed capacity, that means the oldest data would be overridden by the latest data. If users find data loss, users should raise the ceiling of memory Queue. MmapQueue generally doesn&amp;rsquo;t face this problem unless the Sender transport is congested.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Ouput
&lt;ul&gt;
&lt;li&gt;If the BatchBuffer is full, the processor would be blocked.&lt;/li&gt;
&lt;li&gt;If the Channel is full, the downstream components would be blocked, such as BatchConvertor and Processor.&lt;/li&gt;
&lt;li&gt;When SenderWorker sends failure, the batch data would do a failure strategy that would block pulling data from the Channel. The strategy is a part of Sender,the operation mode is synchronous.&lt;/li&gt;
&lt;li&gt;Once the failure strategy is finished, such that send success or give up this batch, the Sendworker would keep pulling data from the Channel.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;detail-modules.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;questions&#34;&gt;Questions&lt;/h2&gt;
&lt;h3 id=&#34;how-to-avoid-keep-pulling-when-the-queue-is-empty&#34;&gt;How to avoid keep pulling when the Queue is empty?&lt;/h3&gt;
&lt;p&gt;If Watermark Offset is less than or equal to Reading Offset, a signal would be sent to the consumer to avoid keep pulling.&lt;/p&gt;
&lt;h3 id=&#34;why-reusing-files-in-queue&#34;&gt;Why reusing files in Queue?&lt;/h3&gt;
&lt;p&gt;The unified model  is a ring in Queue, that limits fixed resources cost  in memory or disk.In Mmap Queue, reusing files turns the delete operations into an overwrite operations, effectively reducing the creation and deletion behavior in files.&lt;/p&gt;
&lt;h3 id=&#34;what-are-the-strategies-for-file-creation-and-deletion-in-mmapqueue&#34;&gt;What are the strategies for file creation and deletion in MmapQueue?&lt;/h3&gt;
&lt;p&gt;As Satellite running, the number of the files in MmapQueue would keep growing until up to the maximum capacity. After this, the old files will be overridden by the new data to avoid file deletion. When the Pod died, all resources were recycled.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: SkyWalking alarm webhook sharing</title>
      <link>/blog/2019-09-25-alarm-webhook-share/</link>
      <pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate>
      <guid>/blog/2019-09-25-alarm-webhook-share/</guid>
      <description>
        
        
        &lt;ul&gt;
&lt;li&gt;Author: Wei Qiang&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/weiqiang333&#34;&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;SkyWalking backend provides the alarm function, we can define some Alarm rules, call webhook after the rule is triggered. I share my implementation&lt;/p&gt;
&lt;h2 id=&#34;demonstration&#34;&gt;Demonstration&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;skywalking-UI-alarm.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;SkyWalking alarm UI&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;skywalking-dingding-notify.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p align=&#34;center&#34;&gt;dingtalk message body&lt;/p&gt;
&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;install&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;go get -u github.com/weiqiang333/infra-skywalking-webhook
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6639ba&#34;&gt;cd&lt;/span&gt; &lt;span style=&#34;color:#953800&#34;&gt;$GOPATH&lt;/span&gt;/src/github.com/weiqiang333/infra-skywalking-webhook/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bash build/build.sh
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./bin/infra-skywalking-webhook &lt;span style=&#34;color:#6639ba&#34;&gt;help&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;main configs file&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;configs/production.yml&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;dingtalk&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;p3&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt; &lt;/span&gt;token...&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Example&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./bin/infra-skywalking-webhook --config configs/production.yml --address 0.0.0.0:8000
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;SkyWalking backend alarm settings&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#f7f7f7;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#0550ae&#34;&gt;webhooks&lt;/span&gt;&lt;span style=&#34;color:#1f2328&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#fff&#34;&gt;  &lt;/span&gt;- http://127.0.0.1:8000/dingtalk&lt;span style=&#34;color:#fff&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;collaboration&#34;&gt;Collaboration&lt;/h2&gt;
&lt;p&gt;Hope that we can improve together &lt;a href=&#34;https://github.com/weiqiang333/infra-skywalking-webhook&#34;&gt;webhook&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;SkyWalking alarm rules may add more metric names (eg priority name), we can send different channels by locating different levels of alerts (dingtalk / SMS / phone)&lt;/p&gt;
&lt;p&gt;Thanks.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Blog: SkyWalking performance in Service Mesh scenario</title>
      <link>/blog/2019-01-25-mesh-loadtest/</link>
      <pubDate>Fri, 25 Jan 2019 00:00:00 +0000</pubDate>
      <guid>/blog/2019-01-25-mesh-loadtest/</guid>
      <description>
        
        
        &lt;ul&gt;
&lt;li&gt;Author: Hongtao Gao, Apache SkyWalking &amp;amp; ShardingShpere PMC&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/hanahmily&#34;&gt;GitHub&lt;/a&gt;, &lt;a href=&#34;https://twitter.com/hanahmily&#34;&gt;Twitter&lt;/a&gt;, &lt;a href=&#34;https://www.linkedin.com/in/gao-hongtao-47b835168/&#34;&gt;Linkedin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Service mesh receiver was first introduced in Apache SkyWalking 6.0.0-beta. It is designed to provide a common entrance for receiving telemetry data from service mesh framework, for instance, Istio, Linkerd, Envoy etc. What’s the service mesh? According to Istio’s explain:&lt;/p&gt;
&lt;p&gt;The term service mesh is used to describe the network of microservices that make up such applications and the interactions between them.&lt;/p&gt;
&lt;p&gt;As a PMC member of Apache SkyWalking, I tested trace receiver and well understood the performance of collectors in trace scenario. I also would like to figure out the performance of service mesh receiver.&lt;/p&gt;
&lt;h2 id=&#34;different-between-trace-and-service-mesh&#34;&gt;Different between trace and service mesh&lt;/h2&gt;
&lt;p&gt;Following chart presents a typical trace map:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;image5.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;You could find a variety of elements in it just like web service, local method, database, cache, MQ and so on. But service mesh only collect service network telemetry data that contains the entrance and exit data of a service for now(more elements will be imported soon, just like Database). A smaller quantity of data is sent to the service mesh receiver than the trace.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;image1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;But using sidecar is a little different.The client requesting “A” that will send a segment to service mesh receiver from “A”’s sidecar. If “A” depends on “B”,  another segment will be sent from “A”’s sidecar. But for a trace system, only one segment is received by the collector. The sidecar model splits one segment into small segments, that will increase service mesh receiver network overhead.&lt;/p&gt;
&lt;h2 id=&#34;deployment-architecture&#34;&gt;Deployment Architecture&lt;/h2&gt;
&lt;p&gt;In this test, I will pick two different backend deployment. One is called mini unit, consist of one collector and one elasticsearch instance. Another is a standard production cluster, contains three collectors and three elasticsearch instances.&lt;/p&gt;
&lt;p&gt;Mini unit is a suitable architecture for dev or test environment. It saves your time and VM resources, speeds up depolyment process.&lt;/p&gt;
&lt;p&gt;The standard cluster provides good performance and HA for a production scenario. Though you will pay more money and take care of the cluster carefully, the reliability of the cluster will be a good reward to you.&lt;/p&gt;
&lt;p&gt;I pick 8 CPU and 16GB VM to set up the test environment. This test targets the performance of normal usage scenarios, so that choice is reasonable. The cluster is built on Google Kubernetes Engine(GKE), and every node links each other with a VPC network. For running collector is a CPU intensive task, the resource request of collector deployment should be 8 CPU, which means every collector instance occupy a VM node.&lt;/p&gt;
&lt;h2 id=&#34;testing-process&#34;&gt;Testing Process&lt;/h2&gt;
&lt;p&gt;Receiving mesh fragments per second(MPS) depends on the following variables.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ingress query per second(QPS)&lt;/li&gt;
&lt;li&gt;The topology of a microservice cluster&lt;/li&gt;
&lt;li&gt;Service mesh mode(proxy or sidecar)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this test, I use Bookinfo app as a demo cluster.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;image6.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;So every request will touch max 4 nodes. Plus picking the sidecar mode(every request will send two telemetry data),  the MPS will be QPS * 4 *2.&lt;/p&gt;
&lt;p&gt;There are also some important metrics that should be explained&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client Query Latency: GraphQL API query response time heatmap.&lt;/li&gt;
&lt;li&gt;Client Mesh Sender: Send mesh segments per second. The total line represents total send amount and the error line is the total number of failed send.&lt;/li&gt;
&lt;li&gt;Mesh telemetry latency: service mesh receiver handling data heatmap.&lt;/li&gt;
&lt;li&gt;Mesh telemetry received: received mesh telemetry data per second.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;mini-unit&#34;&gt;Mini Unit&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;image3.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;You could find collector can process up to &lt;strong&gt;25k&lt;/strong&gt; data per second. The CPU usage is about 4 cores. Most of the query latency is less than 50ms. After login the VM on which collector instance running, I know that system load is reaching the limit(max is 8).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;image2.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;According to the previous formula, a single collector instance could process &lt;strong&gt;3k&lt;/strong&gt; QPS of Bookinfo traffic.&lt;/p&gt;
&lt;h3 id=&#34;standard-cluster&#34;&gt;Standard Cluster&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;image4.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Compare to the mini-unit, cluster’s throughput increases linearly. Three instances provide total 80k per second processing power. Query latency increases slightly, but it’s also very small(less than 500ms). I also checked every collector instance system load that all reached the limit. 10k QPS of BookInfo telemetry data could be processed by the cluster.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Let’s wrap them up. There are some important things you could get from this test.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;QPS varies by the there variables. The test results in this blog are not important. The user should pick property value according to his system.&lt;/li&gt;
&lt;li&gt;Collector cluster’s processing power could scale out.&lt;/li&gt;
&lt;li&gt;The collector is CPU intensive application. So you should provide sufficient CPU resource to it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This blog gives people a common method to evaluate the throughput of Service Mesh Receiver. Users could use this to design their Apache Skywalking backend deployment architecture.&lt;/p&gt;

      </description>
    </item>
    
  </channel>
</rss>
