This is an automated email from the ASF dual-hosted git repository.
wu-sheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-website.git
The following commit(s) were added to refs/heads/master by this push:
new 2a1919eb5cf docs(blog): add Meet Horizon UI 15-17 (final three) (#883)
2a1919eb5cf is described below
commit 2a1919eb5cf58e5c2cf63841cae3ff82238c3cf0
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Tue Jun 30 14:33:21 2026 +0800
docs(blog): add Meet Horizon UI 15-17 (final three) (#883)
* docs(blog): add Meet Horizon UI 15-17 (final three) — Customization,
Localization, Getting Started
Closes out the Meet Horizon UI series (Act 5 — make it yours & adopt):
- 15/17 Customization: Config-Driven Layer Templates — the local-draft →
preview → publish model, the widget canvas, add-a-layer, and export/import
portability (4 figures).
- 16/17 Localization in Eight Languages — server-side per-locale overlays,
click-to-translate, the 8-language locale chip, and a localized dashboard (3
figures).
- 17/17 Getting Started & Migration — single env-driven container, the OAP
10.x vs 11 compatibility matrix, and drop-in migration from an existing UI
(code blocks + table, no figures).
* docs(blog): add Chinese versions of Meet Horizon UI 15-17
Chinese translations for the final three posts (Customization,
Localization, Getting Started & Migration), under content/zh/ with the same
slugs and figures as the English originals.
---
.../index.md | 49 ++++++++++++
.../index.md | 81 ++++++++++++++++++++
.../index.md | 42 +++++++++++
.../index.md | 51 +++++++++++++
.../index.md | 83 +++++++++++++++++++++
.../index.md | 44 +++++++++++
.../p15-templates-01-layer-admin.webp | Bin 0 -> 134742 bytes
.../p15-templates-02-widget-editor.webp | Bin 0 -> 153620 bytes
.../horizon-0.7.0/p15-templates-03-diff-push.webp | Bin 0 -> 131084 bytes
.../horizon-0.7.0/p15-templates-04-overview.webp | Bin 0 -> 148214 bytes
.../horizon-0.7.0/p16-i18n-01-translations.webp | Bin 0 -> 88782 bytes
.../horizon-0.7.0/p16-i18n-02-locale-chip.webp | Bin 0 -> 34406 bytes
.../horizon-0.7.0/p16-i18n-03-localized.webp | Bin 0 -> 149850 bytes
13 files changed, 350 insertions(+)
diff --git
a/content/blog/2026-06-30-horizon-ui-customization-and-templates/index.md
b/content/blog/2026-06-30-horizon-ui-customization-and-templates/index.md
new file mode 100644
index 00000000000..55fb761736c
--- /dev/null
+++ b/content/blog/2026-06-30-horizon-ui-customization-and-templates/index.md
@@ -0,0 +1,49 @@
+---
+title: "Meet Horizon UI · 15/17: Customization — Config-Driven Layer Templates"
+date: 2026-06-30
+author: Sheng Wu
+description: "Part 15 of the Meet Horizon UI series: the whole console is
driven by templates you can edit. Open any layer or overview as a template,
change its widgets, components, and labels in a local draft, preview it, and
publish it to OAP for the whole org — with diff-before-push and export/import."
+tags:
+ - Engineering
+ - Cloud Native
+---
+
+This is the fifteenth post in the [Meet Horizon
UI](/blog/2026-06-21-skywalking-horizon-ui-introduction/) series, and it opens
**Act 5 — make it yours & adopt**. Everything you've seen across this series —
the per-layer dashboards, the overviews, the topology, the 3D map — is **not
hard-coded**. It's all driven by **templates**, and Horizon ships the editor
for them. This post is that editor: change any dashboard in a local draft,
preview it, and publish it to OAP so the whole org sees it.
+
+## Everything is a template
+
+Open **Admin → Layer dashboards** and every layer the backend reports is there
to configure. The editing model is the important part, and it's the same
everywhere: **Save (local)** keeps your edits *in this browser only* — it never
touches the server; the **live, shared version** lives on OAP; and the
**bundled** JSON Horizon ships is just the seed and a read-only fallback. A
status badge tells you where each layer stands — **synced**, **diverged** (the
bundle differs from what's live on [...]
+
+A layer's setup is more than charts: you choose **which sub-views it exposes**
(Service, Instances, Topology, Deployment, Traces, Logs, profiling…), its
display **alias**, and even the **menu nouns** — the Istio mesh layer below
renames "Instances" to *Sidecars*.
+
+
+Figure 1: The Layer dashboards admin — every layer is a template. The header
carries the whole model: **Save (local)** in your browser, **Reset to** /
**Preview** the Bundled or Remote version, and **Check diff & push** to
publish. Below, the layer chooses which sub-views it exposes, its alias, and
renamed menu nouns (here *Sidecars* for instances).</br>
+
+## Edit the widgets
+
+Inside each scope, the dashboard is a **12-column grid** you edit directly —
**drag a header to reorder**, **drag a corner to resize**, **+ Add widget** to
add one. Click any widget and its editor drawer opens: the **MQE
expression(s)** that feed it, the widget **type** (line / top / table / card…),
**title**, **unit**, and a **Visible when** gate that hides the widget unless
an expression has a value or an entity attribute matches.
+
+
+Figure 2: The widget canvas — a 12-column grid you drag to reorder and resize.
Click a widget and its drawer opens: the **MQE expression(s)**, type, title,
unit, and a **Visible when** gate.</br>
+
+## Publish, safely
+
+Nothing you do reaches other users until you publish — and publishing shows
you exactly what will change first. **Check diff & push** opens a side-by-side
diff (the live **remote** on the left, **your local** draft on the right); only
then, on **Confirm push**, does the draft replace the live version for
everyone. The button is enabled only when your local actually differs from
remote.
+
+
+Figure 3: Check diff & push — before a draft goes live for everyone, a
side-by-side diff (remote left, your local right) shows exactly what changes.
Here a widget title is renamed; nothing publishes until you **Confirm
push**.</br>
+
+This is also how you **add a layer**. A layer the backend reports but Horizon
ships no template for opens on a blank default — configure its components and
widgets, **Save**, and that first push publishes the template to OAP. No
per-layer JSON has to be shipped for a layer to be fully configurable.
+
+## Overviews, and portability
+
+The **Overview templates** editor is the same model on a 12-column canvas that
mirrors the live grid (with mock data — the real page uses real data). **+ New
dashboard** writes a local draft; **Delete** is a soft-disable (OAP has no hard
delete). And every template admin page — layer dashboards, overviews, the
3D-map config, translations — carries **Export** and **Import**: Export
downloads the *in-use* version (what users actually render) as a JSON file for
backup, sharing, or moving a [...]
+
+
+Figure 4: Overview templates are the same model — a 12-column canvas with mock
data, **+ New dashboard** as a local draft, and **Export / Import** to move a
dashboard between OAP backends.</br>
+
+## Where it runs
+
+Editing and previewing are entirely **browser-local** — no OAP call happens
until you publish. Publishing writes the template to OAP's **ui-template
store** through the admin host, which arrives with OAP 11; the bundled JSON is
a seed and read-only fallback, and the OAP-published version always wins at
render time. Access is role-gated: publishing layer dashboards needs
`dashboard:write`, overviews need `overview:write`. For the field reference —
the template shapes, the widget types, an [...]
+
+Next up: **Localization** — how those same templates speak eight languages.
diff --git
a/content/blog/2026-06-30-horizon-ui-getting-started-and-migration/index.md
b/content/blog/2026-06-30-horizon-ui-getting-started-and-migration/index.md
new file mode 100644
index 00000000000..3842061bdfc
--- /dev/null
+++ b/content/blog/2026-06-30-horizon-ui-getting-started-and-migration/index.md
@@ -0,0 +1,81 @@
+---
+title: "Meet Horizon UI · 17/17: Getting Started & Migration"
+date: 2026-06-30
+author: Sheng Wu
+description: "The finale of the Meet Horizon UI series: install Horizon as a
single env-driven container, point it at your OAP, and — if you're on the old
UI — swap it in drop-in. Plus the OAP 10.x vs 11.x compatibility matrix: what
works on the query port, and what needs OAP 11's admin host."
+tags:
+ - Release
+ - Community
+---
+
+This is the seventeenth and final post in the [Meet Horizon
UI](/blog/2026-06-21-skywalking-horizon-ui-introduction/) series, closing **Act
5 — make it yours & adopt**. Sixteen posts toured what Horizon shows, does,
governs, and lets you customize. This one is the practical close: how to get it
running, what it needs from your backend, and how to swap it in for an existing
UI.
+
+## One image, one config
+
+Horizon ships as a **single container image** — the Vue UI and its Fastify BFF
in one artifact — published to GHCR at `ghcr.io/apache/skywalking-horizon-ui`.
There's one configuration file, `horizon.yaml`, and its defining trait is that
**every field is an environment-variable token** (`${HORIZON_X:default}`)
expanded *before* the YAML is parsed. So you can run the image with nothing
mounted and set only the env vars you care about, or copy the file, edit it,
and mount it.
+
+```yaml
+# horizon.yaml — every field is an env token: ${HORIZON_X:default},
+# expanded before YAML parsing. Run image-native with env vars, or mount this
file.
+server:
+ host: "${HORIZON_SERVER_HOST:127.0.0.1}" # the image sets 0.0.0.0
+ port: ${HORIZON_SERVER_PORT:8081}
+
+oap:
+ queryUrl: "${HORIZON_OAP_QUERY_URL:http://127.0.0.1:12800}" #
GraphQL — required
+ adminUrl: "${HORIZON_OAP_ADMIN_URL:http://127.0.0.1:17128}" #
admin REST — operate features
+ zipkinUrl: "${HORIZON_OAP_ZIPKIN_URL:http://127.0.0.1:9412/zipkin}" #
optional
+
+auth:
+ backend: "${HORIZON_AUTH_BACKEND:local}" # local | ldap
+ local:
+ users: ${HORIZON_AUTH_LOCAL_USERS:[]} # JSON; hash with: pnpm
--filter bff cli:hash
+
+session:
+ ttlMinutes: ${HORIZON_SESSION_TTL_MINUTES:60}
+ cookieSecure: ${HORIZON_SESSION_COOKIE_SECURE:false} # set true behind
HTTPS
+```
+
+Pointing it at an existing OAP is three URLs. A container run is then just env
vars over the image:
+
+```bash
+docker run -d --name horizon -p 8081:8081 \
+ -e HORIZON_SERVER_HOST=0.0.0.0 \
+ -e HORIZON_OAP_QUERY_URL=http://oap:12800 \
+ -e HORIZON_OAP_ADMIN_URL=http://oap:17128 \
+ -e
HORIZON_AUTH_LOCAL_USERS='[{"username":"admin","passwordHash":"$argon2id$...","roles":["admin"]}]'
\
+ ghcr.io/apache/skywalking-horizon-ui:<version>
+```
+
+Two things worth knowing on the first boot. There is **no default
admin/admin** — with the `local` backend and no users (or `ldap` with no group
mappings) the BFF starts but no one can sign in until you configure it; you
generate password hashes with `pnpm --filter bff cli:hash`. And for
reproducible deploys, **pin to a specific release tag or commit SHA** rather
than a rolling tag.
+
+## What works on which OAP
+
+Horizon is built **natively against OAP 11.x**, and it **partially supports
OAP 10.x**. The split is clean and maps to the two halves of this series: the
**observe** data-plane runs against OAP's query port and works on both lines;
the **operate** surfaces live on OAP's admin port, which only 11.x runs.
+
+| Surface | OAP 10.x | OAP 11.x | Port |
+|---|:---:|:---:|---|
+| Layer dashboards, overviews, topology | ✓ | ✓ | query `:12800` |
+| Traces (native + Zipkin), logs, alarms (read), profiling | ✓ | ✓ | query
`:12800` (+ `:9412` for Zipkin) |
+| Inspect, DSL Management, Live Debugger, Alarm-rule editor | — | ✓ | admin
`:17128` |
+| Cluster Status → Admin pane, template & translation publishing | — | ✓ |
admin `:17128` |
+
+Crucially, Horizon **never reads the OAP version number** — it detects each
capability by probing for the module and GraphQL fields it needs, hides the
sidebar entries it can't back, and falls admin pages back to read-only when the
admin port is dark. So if you only need triage (dashboards, alarms, traces,
logs), a 10.x backend is enough; anything in the operate half needs 11.x with
its admin modules (`admin-server`, `receiver-runtime-rule`, `dsl-debugging`,
`inspect`) enabled.
+
+## Swapping in for an existing UI
+
+If you run the previous-generation UI today, the migration is **drop-in**.
Horizon speaks the **same OAP GraphQL query protocol and the same MQE
language**, so there are **no agent changes and no backend changes** — you
point Horizon at the OAP you already run. The clean cutover is to run both side
by side, let people use Horizon against live data, and retire the old UI when
you're ready. Everything Horizon adds on top — its
[governance](/blog/2026-06-30-horizon-ui-access-control-and-sec [...]
+
+## Verify and operate
+
+Confirm the connection from the [Cluster
Status](/blog/2026-06-30-horizon-ui-platform-introspection/) page: the topbar
carries the OAP build-version chip, and the Query pane shows version, timezone,
and a health score; the Admin and Zipkin panes light up to match what your
backend exposes. A few operational notes for production:
+
+- **Persist state.** Admin-edited templates land under
`/app/bundled_templates` and the audit, setup, and alarm files under `/data`;
mount durable volumes there or those edits are ephemeral and vanish with the
container.
+- **Sessions are per-BFF.** They live in each node's memory with no shared
store, so multiple replicas need sticky sessions (otherwise a failover means
re-login).
+- **Probes and TLS.** `/api/health` is public with no OAP dependency — wire it
to your container probes — and set `session.cookieSecure: true` behind HTTPS.
+
+For the full reference — the [container
image](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/setup/container-image/),
the
[`horizon.yaml`](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/setup/horizon-yaml/)
field-by-field, and the [OAP compatibility
matrix](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/compatibility/oap-version/)
— see the docs.
+
+## That's the series
+
+Seventeen posts, five acts: we **oriented** in the console, **observed**
services across layers — metrics, traces, logs, topology, profiling —
**operated** on the backend with runtime rules, live debugging, and inspection,
**governed** it with access control, and finally made it **ours** with
templates, eight languages, and a clean install. The best next step is to stop
reading and start clicking: the public demo at
[demo.skywalking.apache.org](https://demo.skywalking.apache.org) runs Ho [...]
diff --git a/content/blog/2026-06-30-horizon-ui-localization-i18n/index.md
b/content/blog/2026-06-30-horizon-ui-localization-i18n/index.md
new file mode 100644
index 00000000000..26830d6f7fc
--- /dev/null
+++ b/content/blog/2026-06-30-horizon-ui-localization-i18n/index.md
@@ -0,0 +1,42 @@
+---
+title: "Meet Horizon UI · 16/17: Localization in Eight Languages"
+date: 2026-06-30
+author: Sheng Wu
+description: "Part 16 of the Meet Horizon UI series: Horizon speaks eight
languages, not by re-translating on every render but as server-side overlays
merged onto the same templates. Pick a language from the topbar, or click any
widget on the Translations admin to translate it — while every value OAP
supplies stays verbatim."
+tags:
+ - Engineering
+ - Community
+---
+
+This is the sixteenth post in the [Meet Horizon
UI](/blog/2026-06-21-skywalking-horizon-ui-introduction/) series, still in
**Act 5 — make it yours & adopt**. The [previous
post](/blog/2026-06-30-horizon-ui-customization-and-templates/) showed that the
whole console is driven by templates. Localization builds straight on that:
Horizon speaks **eight languages**, and a translation is just an **overlay on
the same template** — not a fork, and not a re-translation on every render.
+
+## Translations are overlays, resolved once on the server
+
+Each non-English locale is an **overlay catalog** layered onto the English
**source template**. When the BFF serves a template, it reads your chosen
locale (from a request header), **merges the overlay onto the source once**,
and returns the localized template — translation is resolved a single time on
the server, not per chart at mount. The merge is forgiving: it deep-merges onto
the source, and any leaf the overlay doesn't translate **falls back to
English**, so a half-translated catal [...]
+
+There's a deliberate line about *what* gets translated. The **chrome** does —
widget titles, tips, labels, menu aliases. But **data OAP supplies never
does**: service, instance, and endpoint names, tags, log lines, alarm-rule
names, and span operations render exactly as the backend reports them. Product
and protocol names (SkyWalking, Kubernetes, Envoy), MQE expressions, and codes
like RPM / P95 / SLA stay verbatim too.
+
+## Click to translate
+
+You don't edit JSON to localize. **Admin → Translations** gives you a live
preview in your target language: pick the template and the language, **click
any widget**, and a panel opens showing each translatable field — the English
source above, your translation below. A progress counter tracks how far along
each language is, and the same draft model from the templates post applies:
**Stage local** keeps it in your browser, **Check diff & push** publishes the
overlay to OAP, and **Export / [...]
+
+
+Figure 1: The Translations admin — pick a language (here Français), click any
widget in the live preview, and type the translation. The title and tip
translate; codes like `RPM` / `P95` / `SLA` stay verbatim — and so does every
value OAP supplies.</br>
+
+## Eight languages, picked per device
+
+The set is eight first-class locales: **English** (the source) plus **Deutsch,
Español, Français, 日本語, 한국어, Português, 中文 (简体)**. You switch from the
**language chip** in the topbar — on every page, including the pre-auth login —
and the choice persists per device.
+
+
+Figure 2: The topbar language chip — eight first-class languages (English plus
Deutsch, Español, Français, 日本語, 한국어, Português, 中文), picked per device on
every page including the login.</br>
+
+Switch the language and the whole console follows. Here is the General Service
dashboard in Chinese — the sidebar, tabs, and widget titles are localized,
while the API paths, service names, and metric values stay exactly as OAP
reports them. Notice that a few widget titles are still in English: those
leaves simply aren't translated yet, so they fall back to the source — exactly
the graceful degradation the overlay model is built for.
+
+
+Figure 3: The same General Service dashboard in Chinese — sidebar, tabs, and
widget titles localized, while service names, API paths, and metric values stay
exactly as OAP reports them. Untranslated leaves fall back to English.</br>
+
+## Where it runs
+
+Locale resolution happens **BFF-side**, so it works on any OAP. Publishing a
translation writes a sibling overlay to OAP's template store through the admin
host (OAP 11), gated on `overview:write`; the eight UI-chrome message catalogs
are bundled and switch synchronously with no network fetch. A CI gate
(`i18n:validate`) keeps every source template paired with an overlay per locale
so nothing drifts. For the field reference — the translatable-field rules and
the add-a-language recipe — s [...]
+
+Next, the series closes with **Getting Started & Migration** — installing
Horizon and swapping it in for an existing UI.
diff --git
a/content/zh/2026-06-30-horizon-ui-customization-and-templates/index.md
b/content/zh/2026-06-30-horizon-ui-customization-and-templates/index.md
new file mode 100644
index 00000000000..c7529967e81
--- /dev/null
+++ b/content/zh/2026-06-30-horizon-ui-customization-and-templates/index.md
@@ -0,0 +1,51 @@
+---
+title: "认识 Horizon UI · 15/17:用模板定制控制台"
+date: 2026-06-30
+author: 吴晟
+description: "Horizon UI 系列第十五篇:整个控制台都由可编辑模板驱动。你可以把任意 layer 或 overview
打开成模板,在本地草稿里调整组件、widget 和文案,预览后发布到 OAP 给整个组织使用,并在发布前查看差异,也可以导出和导入。"
+tags:
+ - Engineering
+ - Cloud Native
+---
+
+*译自英文原文:[Meet Horizon UI · 15/17: Customization — Config-Driven Layer
Templates](/blog/2026-06-30-horizon-ui-customization-and-templates/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第十五篇,也开启第五幕 **make it yours & adopt**。这个系列里你看到的 per-layer
dashboard、overview、topology、3D map,都**不是写死在代码里**。它们由一套套 **template** 驱动,Horizon
也把编辑这些 template 的工具做进了控制台。这篇讲的就是这个编辑器:在本地草稿里修改任意 dashboard,先预览,再发布到
OAP,让整个组织都看到同一个版本。
+
+## 一切都是模板
+
+打开 **Admin → Layer dashboards**,后端上报的每个 layer
都可以配置。这里最重要的是编辑模型,而且所有模板编辑器都遵循同一套模型:**Save (local)**
只把修改保存在当前浏览器里,不会碰服务端;真正被所有人看到的**共享版本**保存在 OAP;Horizon 随版本发布的 **bundled** JSON
只是种子模板和只读 fallback。状态 badge 会告诉你每个 layer 当前处在哪种状态:**synced**
表示本地和远端一致;**diverged** 表示 bundled 和 OAP 上的 live 版本不同,渲染时以 OAP 为准;**local**
表示当前浏览器里有未发布的编辑。**Reset to ▾** 可以把 Bundled 或 Remote 重新加载到编辑器;**Preview ▾** 则会用
Local、Bundled 或 Remote 版本打开真实页面预览。
+
+一个 layer 的配置不只是图表。你还可以选择它暴露哪些 **sub-view**,比如
Service、Instances、Topology、Deployment、Traces、Logs、profiling;可以设置展示用的
**alias**,甚至可以改菜单里的名词。下面的 Istio mesh layer 就把 “Instances” 改成了 *Sidecars*。
+
+
+图 1:Layer dashboards 管理页:每个 layer 都是一个 template。顶部操作区体现了完整模型:**Save (local)**
保存在浏览器里,**Reset to** / **Preview** Bundled 或 Remote 版本,最后用 **Check diff &
push** 发布。下面可以选择这个 layer 暴露哪些 sub-view、设置 alias,并重命名菜单名词,比如把 instances 叫作
*Sidecars*。</br>
+
+## 编辑 Widget
+
+每个 scope 里的 dashboard 都是一个可以直接编辑的 **12 列网格**:拖动 header 可以调整顺序,拖动角落可以改变大小,点 **+
Add widget** 可以新增 widget。点击任意 widget,会打开它的编辑抽屉:这里可以改驱动它的 **MQE
expression**、widget **type**(line / top / table / card
等)、**title**、**unit**,还可以设置 **Visible when** 条件,让某个表达式有值或者某个实体属性匹配时才显示这个 widget。
+
+
+图 2:Widget 画布:一个可以拖拽排序、调整大小的 12 列网格。点中 widget 后会打开抽屉,编辑 **MQE
expression**、type、title、unit 和 **Visible when** 条件。</br>
+
+## 发布前先看差异
+
+在你发布之前,本地修改不会影响其他用户;而发布之前,Horizon 会先让你看清楚到底改了什么。点击 **Check diff & push**
会打开左右对照的 diff:左侧是当前 live 的 **remote** 版本,右侧是**你的 local** 草稿。只有继续点 **Confirm
push**,本地草稿才会替换 OAP 上的 live 版本,所有用户才会看到变化。只有 local 和 remote 真的不一样时,这个按钮才会启用。
+
+
+图 3:Check diff & push:草稿发布给所有人之前,先用左右对照 diff 展示变化(remote 在左,本地草稿在右)。这里改了一个
widget title;只有点 **Confirm push** 才会真正发布。</br>
+
+新增 layer 也走这套流程。后端已经上报、但 Horizon 没有内置模板的 layer,会先打开一个空白默认模板;你配置它的 components 和
widgets,**Save** 后第一次 push 就会把模板发布到 OAP。一个 layer 不需要先随版本带上 JSON 文件,才能变成完整可配置的页面。
+
+## Overview 与迁移复用
+
+**Overview templates** 编辑器也使用同样的模型:一个 12 列画布,布局和线上页面一致,只是编辑器里使用 mock
data,真实页面会使用真实数据。**+ New dashboard** 会写出一个本地草稿;**Delete** 是软禁用,因为 OAP 目前没有 hard
delete。所有模板管理页也都有 **Export** 和 **Import**,包括 layer dashboards、overviews、3D map
config 和 translations:Export 会把当前实际使用的版本下载成 JSON 文件,便于备份、共享,或者把一个 dashboard
搬到另一个 OAP;Import 读取 JSON 文件、校验,然后作为**本地草稿**加载进来,供你预览和发布。Import 不会直接写 OAP。
+
+
+图 4:Overview templates 使用同一套模型:12 列画布、mock data、作为本地草稿创建的 **+ New
dashboard**,以及用 **Export / Import** 在不同 OAP 后端之间迁移 dashboard。</br>
+
+## 它在哪里运行
+
+编辑和预览完全发生在**浏览器本地**;只有发布时才会调用 OAP。发布会通过 OAP 11 提供的 admin host,把 template 写入
OAP 的 **ui-template store**。Bundled JSON 是种子模板和只读 fallback;只要 OAP 上发布过版本,渲染时就以
OAP 版本为准。权限也按角色控制:发布 layer dashboards 需要 `dashboard:write`,发布 overviews 需要
`overview:write`。字段参考,包括 template 结构、widget 类型和新增 layer 的流程,可以看 [Layer
templates](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/customization/layer-templates/)、[Overview
templates](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/cus
[...]
+
+下一篇:[八种语言的本地化](/zh/2026-06-30-horizon-ui-localization-i18n/):同一套 template
如何支持八种语言。
diff --git
a/content/zh/2026-06-30-horizon-ui-getting-started-and-migration/index.md
b/content/zh/2026-06-30-horizon-ui-getting-started-and-migration/index.md
new file mode 100644
index 00000000000..915f590dc9e
--- /dev/null
+++ b/content/zh/2026-06-30-horizon-ui-getting-started-and-migration/index.md
@@ -0,0 +1,83 @@
+---
+title: "认识 Horizon UI · 17/17:安装与迁移"
+date: 2026-06-30
+author: 吴晟
+description: "Meet Horizon UI 系列收官篇:用一个环境变量驱动的容器启动 Horizon,指向已有 OAP;如果你还在使用上一代
UI,也可以把 Horizon 平滑替换进去。文中还说明 OAP 10.x 和 11.x 的兼容边界:哪些功能只需要 query port,哪些需要 OAP
11 的 admin host。"
+tags:
+ - Release
+ - Community
+---
+
+*译自英文原文:[Meet Horizon UI · 17/17: Getting Started &
Migration](/blog/2026-06-30-horizon-ui-getting-started-and-migration/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第十七篇,也是最后一篇,为第五幕 **make it yours & adopt** 收尾。前十六篇已经讲过 Horizon
能展示什么、能操作什么、如何治理,以及如何定制。这篇回到最实际的问题:怎样把它跑起来,它对后端有什么要求,以及怎样替换掉现有 UI。
+
+## 一个镜像,一份配置
+
+Horizon 以**单个容器镜像**发布:Vue UI 和 Fastify BFF 打在同一个 artifact 里,镜像位于
GHCR:`ghcr.io/apache/skywalking-horizon-ui`。配置文件只有一个:`horizon.yaml`。它最重要的特点是,**每个字段都是环境变量
token**(`${HORIZON_X:default}`),并且会在 YAML
解析之前展开。所以你可以不挂载任何文件,只用环境变量启动镜像;也可以复制这份文件,修改后挂载进去。
+
+```yaml
+# horizon.yaml — 每个字段都是 env token: ${HORIZON_X:default}
+# 会在 YAML 解析前展开。可以直接用环境变量启动镜像,也可以挂载这份文件。
+server:
+ host: "${HORIZON_SERVER_HOST:127.0.0.1}" # 镜像内默认设置为 0.0.0.0
+ port: ${HORIZON_SERVER_PORT:8081}
+
+oap:
+ queryUrl: "${HORIZON_OAP_QUERY_URL:http://127.0.0.1:12800}" #
GraphQL,必需
+ adminUrl: "${HORIZON_OAP_ADMIN_URL:http://127.0.0.1:17128}" #
admin REST,operate 功能使用
+ zipkinUrl: "${HORIZON_OAP_ZIPKIN_URL:http://127.0.0.1:9412/zipkin}" # 可选
+
+auth:
+ backend: "${HORIZON_AUTH_BACKEND:local}" # local | ldap
+ local:
+ users: ${HORIZON_AUTH_LOCAL_USERS:[]} # JSON;用 pnpm --filter bff
cli:hash 生成 hash
+
+session:
+ ttlMinutes: ${HORIZON_SESSION_TTL_MINUTES:60}
+ cookieSecure: ${HORIZON_SESSION_COOKIE_SECURE:false} # HTTPS 后面应设为 true
+```
+
+把 Horizon 指向已有 OAP,只需要三个 URL。容器启动命令就是给镜像加上环境变量:
+
+```bash
+docker run -d --name horizon -p 8081:8081 \
+ -e HORIZON_SERVER_HOST=0.0.0.0 \
+ -e HORIZON_OAP_QUERY_URL=http://oap:12800 \
+ -e HORIZON_OAP_ADMIN_URL=http://oap:17128 \
+ -e
HORIZON_AUTH_LOCAL_USERS='[{"username":"admin","passwordHash":"$argon2id$...","roles":["admin"]}]'
\
+ ghcr.io/apache/skywalking-horizon-ui:<version>
+```
+
+第一次启动时有两点需要注意。第一,Horizon **没有默认的 admin/admin**。使用 `local` 后端但没有配置用户,或者使用
`ldap` 但没有配置 group mapping 时,BFF 会启动,但没人能登录;密码哈希可以用 `pnpm --filter bff
cli:hash` 生成。第二,为了让部署可复现,生产环境应该固定到明确的 release tag 或 commit SHA,而不是使用滚动标签。
+
+## 哪些功能需要哪个 OAP
+
+Horizon **主要面向 OAP 11.x 构建**,同时**部分支持 OAP 10.x**。边界很清楚,也对应这个系列的两半:**observe**
数据面走 OAP query port,两条版本线都能用;**operate** 页面依赖 OAP admin port,只有 11.x 提供。
+
+| 功能 | OAP 10.x | OAP 11.x | 端口 |
+|---|:---:|:---:|---|
+| Layer dashboards、overviews、topology | ✓ | ✓ | query `:12800` |
+| Traces(native + Zipkin)、logs、alarms(read)、profiling | ✓ | ✓ | query
`:12800`(Zipkin 使用 `:9412`) |
+| Inspect、DSL Management、Live Debugger、Alarm-rule editor | — | ✓ | admin
`:17128` |
+| Cluster Status → Admin pane、template 和 translation 发布 | — | ✓ | admin
`:17128` |
+
+关键点是,Horizon **不会靠读取 OAP 版本号来判断能力**。它会探测所需 module 和 GraphQL
field:能支撑的功能才显示侧边栏入口;admin port 不可用时,admin 页面会降级成只读或显示明确提示。所以如果你只需要 triage,也就是
dashboard、alarm、trace、log,OAP 10.x 就够了;operate 这半边需要 OAP 11.x,并启用对应 admin
modules(`admin-server`、`receiver-runtime-rule`、`dsl-debugging`、`inspect`)。
+
+## 替换现有 UI
+
+如果你现在运行的是上一代 UI,迁移到 Horizon 可以做成**平滑替换**。Horizon 使用**同一套 OAP GraphQL query
protocol 和同一套 MQE 语言**,所以**不需要改 agent,也不需要改后端**;只要把 Horizon 指向正在运行的 OAP
即可。更稳妥的切换方式是先让两个 UI 并行运行,让大家用 Horizon 访问 live data,等准备好了再下线旧 UI。Horizon
额外提供的东西,比如
[governance](/zh/2026-06-30-horizon-ui-access-control-and-security/)(RBAC、auth、audit、themes)和
[config-driven
templates](/zh/2026-06-30-horizon-ui-customization-and-templates/),都是 Horizon
自己在 BFF 里叠加的能力,和 OAP 本身相互独立。
+
+## 验证与生产运行
+
+可以从 [Cluster Status](/zh/2026-06-30-horizon-ui-platform-introspection/)
页面确认连接情况:topbar 会显示 OAP build-version chip,Query 面板会展示版本、时区和健康评分;Admin 和 Zipkin
面板则会根据后端实际暴露的能力亮起。生产环境还要注意几件事:
+
+- **持久化状态。** 管理员编辑的 template 会落在 `/app/bundled_templates`,audit、setup 和 alarm
文件会落在 `/data`;这两个路径应该挂载持久化 volume,否则容器重建后这些修改会丢失。
+- **Session 按 BFF 节点保存。** Session 保存在每个节点内存里,没有共享存储;多副本部署需要 sticky
sessions,否则故障切换后用户要重新登录。
+- **探针和 TLS。** `/api/health` 是公开接口,不依赖 OAP,适合作为容器探针;如果部署在 HTTPS 后面,记得把
`session.cookieSecure` 设为 `true`。
+
+完整参考可以看文档:[container
image](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/setup/container-image/)、[`horizon.yaml`](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/setup/horizon-yaml/)
字段说明,以及 [OAP compatibility
matrix](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/compatibility/oap-version/)。
+
+## 系列到这里结束
+
+十七篇,五幕:我们先在控制台里**建立方向感**,然后跨 layer **观察**服务的 metrics、traces、logs、topology 和
profiling;接着用 runtime rules、live debugging 和 inspect **操作**后端;再通过 access
control **治理**控制台;最后用 templates、八种语言和清晰的安装路径,把它变成自己的工具。下一步最好不是继续读,而是直接点开试试:公开
demo [demo.skywalking.apache.org](https://demo.skywalking.apache.org) 已经把
Horizon 接到了一套 live SkyWalking 后端上。谢谢一路读到这里。
diff --git a/content/zh/2026-06-30-horizon-ui-localization-i18n/index.md
b/content/zh/2026-06-30-horizon-ui-localization-i18n/index.md
new file mode 100644
index 00000000000..5305dad9f8e
--- /dev/null
+++ b/content/zh/2026-06-30-horizon-ui-localization-i18n/index.md
@@ -0,0 +1,44 @@
+---
+title: "认识 Horizon UI · 16/17:八种语言的本地化"
+date: 2026-06-30
+author: 吴晟
+description: "Horizon UI 系列第十六篇:Horizon 支持八种语言。本地化不是每次渲染时重新翻译,而是在服务端把翻译
overlay 合并到同一套 template 上;用户可以从顶栏切换语言,也可以在 Translations 管理页点选任意 widget 直接补翻译,而
OAP 返回的数据始终保持原样。"
+tags:
+ - Engineering
+ - Community
+---
+
+*译自英文原文:[Meet Horizon UI · 16/17: Localization in Eight
Languages](/blog/2026-06-30-horizon-ui-localization-i18n/)。*
+
+这是 [Meet Horizon UI](/zh/2026-06-21-skywalking-horizon-ui-introduction/)
系列的第十六篇,仍然属于第五幕 **make it yours & adopt**。上一篇讲到,整个控制台都由 template
驱动。本地化也建立在这个基础上:Horizon 支持**八种语言**,每种翻译都是覆盖在同一套 template 上的一层 **overlay**。它不是
fork 出一份 dashboard,也不是每次渲染图表时重新翻译。
+
+## 翻译是 overlay,在服务端合并一次
+
+每个非英语 locale 都是一份 **overlay catalog**,覆盖在英语 **source template** 上。BFF 返回
template 时,会根据请求头里的语言选择,把 overlay **合并到 source 上一次**,再返回本地化后的
template。翻译只在服务端解析一次,不会在每个图表加载时再处理。合并逻辑也比较宽容:overlay 会 deep-merge 到 source
上;没有翻译到的 leaf 会**回退到英语**,所以一份只翻了一半的 catalog 也是有效的。新增或修正语言,本质上就是编辑 overlay,不需要改
source dashboard。
+
+这里有一条刻意划清的边界:哪些内容会翻译,哪些不会。会翻译的是界面 **chrome**,例如 widget title、tip、label、menu
alias。但 **OAP 提供的数据不会翻译**:service、instance、endpoint 名称,tags,log line,alarm rule
name,span operation,都会按后端返回的原样展示。SkyWalking、Kubernetes、Envoy 这类产品或协议名,MQE
表达式,以及 RPM / P95 / SLA 这样的代码和缩写,也保持原样。
+
+## 点一下就能翻译
+
+本地化不需要手写 JSON。**Admin → Translations** 会给出目标语言的 live preview:选择 template 和
language,**点击任意 widget**,右侧面板会列出可以翻译的字段:上方是英语
source,下方是你的翻译。每种语言都有进度计数,模板那篇提到的草稿模型也同样适用:**Stage local** 把改动保存在浏览器里,**Check
diff & push** 把 overlay 发布到 OAP,**Export / Import** 可以把某种语言的翻译作为 JSON 文件迁移。
+
+
+图 1:Translations 管理页:选择语言(这里是 Français),在 live preview 中点击任意
widget,就可以输入翻译。title 和 tip 会翻译;`RPM` / `P95` / `SLA` 这类代码保持原样,OAP
提供的所有值也保持原样。</br>
+
+## 八种语言,按设备选择
+
+Horizon 正式支持八种 locale:**English** 是 source,另外还有
**Deutsch、Español、Français、日本語、한국어、Português、中文(简体)**。你可以从顶栏的 **language chip**
切换语言;所有页面都支持,包括登录前的 login 页面。选择会按当前设备持久化。
+
+
+图 2:顶栏 language chip:八种正式支持的语言,English 加
Deutsch、Español、Français、日本語、한국어、Português、中文。每个设备可以独立选择,登录页也能切换。</br>
+
+切换语言后,整个控制台都会跟着变。下面是中文里的 General Service dashboard:sidebar、tabs 和 widget title
都已经本地化,而 API path、service name 和 metric value 仍然按 OAP 返回值展示。你也会看到少数 widget
title 还是英语:那几个 leaf 还没有翻译,所以自然回退到 source。这正是 overlay 模型想要的降级方式。
+
+
+图 3:同一个 General Service dashboard 的中文版本:sidebar、tabs 和 widget title
会本地化;service name、API path 和 metric value 仍然按 OAP 返回值展示。未翻译的 leaf 会回退到英语。</br>
+
+## 它在哪里运行
+
+Locale 解析发生在 **BFF 侧**,所以它兼容任意 OAP。发布翻译时,会通过 OAP 11 的 admin host,把 sibling
overlay 写入 OAP 的 template store;权限由 `overview:write` 控制。八种 UI chrome message
catalog 是随包内置的,切换时同步生效,不需要额外网络请求。CI 里的 `i18n:validate` 会检查每个 source template
都有对应 locale 的 overlay,避免 source 和翻译脱节。字段参考,包括哪些字段可翻译、如何新增语言,可以看 [i18n
文档](https://skywalking.apache.org/docs/skywalking-horizon-ui/next/customization/i18n/)。
+
+下一篇,也就是这个系列的收官篇:[安装与迁移](/zh/2026-06-30-horizon-ui-getting-started-and-migration/):安装
Horizon,并把它替换到现有 UI 的位置。
diff --git a/static/screenshots/horizon-0.7.0/p15-templates-01-layer-admin.webp
b/static/screenshots/horizon-0.7.0/p15-templates-01-layer-admin.webp
new file mode 100644
index 00000000000..0ed924fd492
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p15-templates-01-layer-admin.webp differ
diff --git
a/static/screenshots/horizon-0.7.0/p15-templates-02-widget-editor.webp
b/static/screenshots/horizon-0.7.0/p15-templates-02-widget-editor.webp
new file mode 100644
index 00000000000..19fb755489a
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p15-templates-02-widget-editor.webp differ
diff --git a/static/screenshots/horizon-0.7.0/p15-templates-03-diff-push.webp
b/static/screenshots/horizon-0.7.0/p15-templates-03-diff-push.webp
new file mode 100644
index 00000000000..d76ab766c9f
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p15-templates-03-diff-push.webp differ
diff --git a/static/screenshots/horizon-0.7.0/p15-templates-04-overview.webp
b/static/screenshots/horizon-0.7.0/p15-templates-04-overview.webp
new file mode 100644
index 00000000000..b9bf36cb8e6
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p15-templates-04-overview.webp differ
diff --git a/static/screenshots/horizon-0.7.0/p16-i18n-01-translations.webp
b/static/screenshots/horizon-0.7.0/p16-i18n-01-translations.webp
new file mode 100644
index 00000000000..5f1be16286d
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p16-i18n-01-translations.webp differ
diff --git a/static/screenshots/horizon-0.7.0/p16-i18n-02-locale-chip.webp
b/static/screenshots/horizon-0.7.0/p16-i18n-02-locale-chip.webp
new file mode 100644
index 00000000000..f9f55bafadb
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p16-i18n-02-locale-chip.webp differ
diff --git a/static/screenshots/horizon-0.7.0/p16-i18n-03-localized.webp
b/static/screenshots/horizon-0.7.0/p16-i18n-03-localized.webp
new file mode 100644
index 00000000000..91573310937
Binary files /dev/null and
b/static/screenshots/horizon-0.7.0/p16-i18n-03-localized.webp differ