You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2026/10/16 21:38:53 UTC
(doris-website) branch master updated: [doc](dogstack) split deployment into Docker Compose and Kubernetes pages (#4171)
This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-website.git
The following commit(s) were added to refs/heads/master by this push:
new e0a2e1c4c9d [doc](dogstack) split deployment into Docker Compose and Kubernetes pages (#4171)
e0a2e1c4c9d is described below
commit e0a2e1c4c9d94779cba5c42f95dc4169a13053a5
Author: Berkin <bi...@icloud.com>
AuthorDate: Sat Oct 17 05:38:47 2026 +0800
[doc](dogstack) split deployment into Docker Compose and Kubernetes pages (#4171)
## Summary
Split DOG Stack deployment into Docker Compose and Kubernetes pages.
Preserve old deployment URLs through the existing redirect index and
group the new pages under Deployment, with a Chinese category label.
The dev and 4.x English/Chinese tutorials pin AI Observe Stack commit
`2c1fbee831701178c7dddba857e88130ef292949` (Helm chart 0.2.0). They
cover existing or chart-deployed Doris, collector installation,
verification, parsing, production settings, upgrades, uninstall and
troubleshooting. The original 3.x author changes remain in the PR; this
audit only updates dev and 4.x.
The audit corrects gateway PVC prerequisites even with external Doris,
schema/load/query permissions, shell placeholder quoting, OTLP/HTTP
versus gRPC ingress routing, explicit SDK protocol selection and
unsupported exactly-once claims. It also documents current timestamp
rewriting and bounded export retries. Step 2 creates its namespace even
when backend installation is skipped; Option B keeps dog-values.yaml for
both installation and upgrades.
## Validation
Both Docker Compose files pass config parsing. Both Helm charts pass
lint, and dev/prod/external/collector modes render with matching object
names. Eight dev/4.x pages compile with Docusaurus preprocessing;
changed-page links have no errors; sidebar/locale JSON and all six old
deployment redirects pass. No containers or Kubernetes cluster were
started during this audit. The original author reported prior end-to-end
deployment tests; those were not rerun here.
---------
Co-authored-by: morningman <mo...@apache.org>
---
docs/observability/dogstack/deployment.md | 172 -------
docs/observability/dogstack/docker.md | 116 +++++
docs/observability/dogstack/kubernetes.md | 503 +++++++++++++++++++++
docusaurus.config.js | 11 +
.../docusaurus-plugin-content-docs/current.json | 4 +
.../current/observability/dogstack/deployment.md | 172 -------
.../current/observability/dogstack/docker.md | 116 +++++
.../current/observability/dogstack/kubernetes.md | 503 +++++++++++++++++++++
.../version-3.x.json | 4 +
.../observability/dogstack/deployment.md | 172 -------
.../version-3.x/observability/dogstack/docker.md | 112 +++++
.../observability/dogstack/kubernetes.md | 485 ++++++++++++++++++++
.../version-4.x.json | 4 +
.../observability/dogstack/deployment.md | 172 -------
.../version-4.x/observability/dogstack/docker.md | 116 +++++
.../observability/dogstack/kubernetes.md | 503 +++++++++++++++++++++
sidebars.ts | 9 +-
.../observability/dogstack/deployment.md | 172 -------
.../version-3.x/observability/dogstack/docker.md | 112 +++++
.../observability/dogstack/kubernetes.md | 485 ++++++++++++++++++++
.../observability/dogstack/deployment.md | 172 -------
.../version-4.x/observability/dogstack/docker.md | 116 +++++
.../observability/dogstack/kubernetes.md | 503 +++++++++++++++++++++
versioned_sidebars/version-3.x-sidebars.json | 9 +-
versioned_sidebars/version-4.x-sidebars.json | 9 +-
25 files changed, 3717 insertions(+), 1035 deletions(-)
diff --git a/docs/observability/dogstack/deployment.md b/docs/observability/dogstack/deployment.md
deleted file mode 100644
index c313ea2e4af..00000000000
--- a/docs/observability/dogstack/deployment.md
+++ /dev/null
@@ -1,172 +0,0 @@
----
-{
- "title": "Deployment",
- "sidebar_label": "Deployment",
- "language": "en-US",
- "description": "How to deploy DOG Stack in production? This article introduces deployment options and selection guidance for the three core components: OpenTelemetry Collector, Apache Doris, and Grafana with the Doris App plugin.",
- "keywords": [
- "DOG Stack deployment",
- "OpenTelemetry Collector",
- "Apache Doris deployment",
- "Grafana deployment",
- "Doris App plugin",
- "Docker Compose",
- "Kubernetes",
- "observability"
- ]
-}
----
-
-<!-- Knowledge type: operational steps -->
-<!-- Applicable scenario: observability platform / DOG Stack production deployment -->
-
-As shown in the architecture diagram, DOG Stack consists of four components: the data collection tools, the OpenTelemetry Collector, Doris, and Grafana with the Doris App plugin. This deployment document focuses on the latter three components; the data collection tools are covered in the data collection document.
-
-DOG Stack offers multiple deployment options for different environments and purposes.
-
-
-
-## Docker Compose
-
-Best suited for local testing, development, and PoC.
-
-### Prerequisites
-
-- Docker Engine (v20.10+)
-- Docker Compose (v2.0+)
-
-
-
-### Deployment
-
-1. Clone the repository:
-
- `git clone https://github.com/ai-observe/ai-observe-stack.git cd ai-observe-stack/docker`
-
-
-
-2. Start all services:
-
- `docker compose up -d`
-
-
-
-3. Verify that services are running:
-
- `docker compose ps`
-
- All services should show the `running` status.
-
-
-
-4. Access Grafana at http://localhost:3000 and log in with the default credentials `admin` / `admin`.
-
-
-
-### Service endpoints
-
-| Service | Endpoint | Credentials |
-| --------- | --------------------- | ------------- |
-| Grafana | http://localhost:3000 | admin / admin |
-| OTel gRPC | localhost:4317 | - |
-| OTel HTTP | localhost:4318 | - |
-
-### Stop and clean up
-
-Stop services while keeping data:
-
-```Bash
-docker compose down
-```
-
-Stop services and remove all data:
-
-```Bash
-docker compose down -v
-```
-
-
-
-## Kubernetes
-
-Best suited for production deployment, development environments, and scalable setups.
-
-### Prerequisites
-
-- Kubernetes cluster (v1.20+)
-- Helm (v3.0+)
-- kubectl configured to access your cluster
-- PersistentVolume provisioner (for data persistence)
-
-### Deployment
-
-1. Add the DOG Stack and Doris Helm repositories:
-
- `helm repo add dogstack https://charts.velodb.io helm repo update`
-
-
-
-2. Create a namespace for DOG Stack:
-
- `kubectl create namespace dogstack`
-
-
-
-3. Install DOG Stack:
-
- If you already have a Doris cluster, you can specify how to connect to it.
-
- `helm install my-dogstack dogstack/dogstack -n dogstack \ --set doris.mode=external \ --set doris.external.host=<DORIS_FE_HOST> \ --set doris.external.port=9030 \ --set doris.external.feHttpPort=8030 \ --set doris.internal.operator.enabled=false`
-
-
-
- If you want to deploy a new Doris cluster, just install directly with no extra options.
-
- `helm install my-dogstack dogstack/dogstack -n dogstack`
-
-
-
-4. Verify that all pods are running:
-
- `kubectl get pods -n dogstack`
-
- Wait until all pods show the `Running` status.
-
-
-
-5. Access Grafana:
-
- `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack`
-
- Open http://localhost:3000 and log in with `admin` / `admin`.
-
-
-
-### Service endpoints
-
-| Service | Port-forward command |
-| ----------- | ------------------------------------------------------------ |
-| Grafana | `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack` |
-| Doris FE UI | `kubectl port-forward svc/my-dogstack-doris-fe 8030:8030 -n dogstack` |
-| Doris MySQL | `kubectl port-forward svc/my-dogstack-doris-fe 9030:9030 -n dogstack` |
-
-### Uninstall
-
-```Bash
-helm uninstall my-dogstack -n dogstack
-kubectl delete namespace dogstack
-```
-
-
-
-## Manual deployment
-
-1. Deploy a Doris cluster by following the Doris [deployment documentation](https://doris.apache.org/docs/4.x/install/preparation/env-checking). Skip this step if you already have a Doris cluster.
-
-1. Deploy the OpenTelemetry Collector by following the [OpenTelemetry deployment documentation](https://opentelemetry.io/docs/collector/install/).
-
-1. Deploy Grafana by following the [Grafana deployment documentation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/).
-
-1. Open the DOG Stack web UI in your browser.
-
-Visit http://localhost:3000 to access the Grafana UI inside DOG Stack.
diff --git a/docs/observability/dogstack/docker.md b/docs/observability/dogstack/docker.md
new file mode 100644
index 00000000000..664de884d7c
--- /dev/null
+++ b/docs/observability/dogstack/docker.md
@@ -0,0 +1,116 @@
+---
+{
+ "title": "Deploy with Docker Compose",
+ "sidebar_label": "Deploy with Docker Compose",
+ "language": "en-US",
+ "description": "How to deploy DOG Stack on a single host with Docker Compose? This article describes how to start the OpenTelemetry Collector, Apache Doris, and Grafana with the Doris App plugin, either with a built-in Doris or with an existing Doris cluster, and how to verify, access, and clean up the deployment.",
+ "keywords": [
+ "DOG Stack deployment",
+ "Docker Compose",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Doris App plugin",
+ "observability"
+ ]
+}
+---
+
+<!-- Knowledge type: operational steps -->
+<!-- Applicable scenario: observability platform / DOG Stack single-host deployment -->
+
+This article describes how to deploy DOG Stack on a single host with Docker Compose. The deployment starts the OpenTelemetry Collector, Grafana with the Doris App plugin, and optionally an all-in-one Doris. It is suited for local testing, development, and PoC. For production or Kubernetes environments, see [Deploy on Kubernetes](./kubernetes.md).
+
+:::tip
+To deploy the components without Docker, follow the [Doris deployment documentation](https://doris.apache.org/docs/4.x/install/preparation/env-checking), the [OpenTelemetry Collector installation documentation](https://opentelemetry.io/docs/collector/install/), and the [Grafana installation documentation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/).
+:::
+
+This guide is checked against AI Observe Stack commit [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949). The checkout below pins this revision so later main-branch changes do not alter the parameters. Its built-in Doris image is `4.0.3-all-slim`, intended for stack connectivity checks.
+
+## Prerequisites
+
+- Docker Engine 20.10 or later
+- Docker Compose 2.0 or later
+
+## Deployment
+
+1. Clone the repository:
+
+ ```Bash
+ git clone https://github.com/ai-observe/ai-observe-stack.git
+ cd ai-observe-stack
+ git checkout 2c1fbee831701178c7dddba857e88130ef292949
+ cd docker
+ ```
+
+2. Start the services. Choose one of the following modes.
+
+ - **Built-in Doris**: starts an all-in-one Doris together with the OpenTelemetry Collector and Grafana.
+
+ ```Bash
+ docker compose up -d
+ ```
+
+ - **Existing Doris**: starts only the OpenTelemetry Collector and Grafana, and writes data to your Doris cluster. Create the configuration file first:
+
+ ```Bash
+ cp .env.example .env
+ ```
+
+ Edit the Doris connection settings in `.env`:
+
+ ```Bash
+ DORIS_FE_HTTP_ENDPOINT=http://<DORIS_FE_HOST>:8030
+ DORIS_FE_MYSQL_ENDPOINT=<DORIS_FE_HOST>:9030
+ DORIS_USERNAME=root
+ DORIS_PASSWORD=
+ ```
+
+ Then start the services:
+
+ ```Bash
+ docker compose -f docker-compose-without-doris.yaml up -d
+ ```
+
+ The account must be able to create the target database and tables, load data, and query the tables used by Grafana. The OpenTelemetry Collector creates the database `otel` and its tables automatically.
+
+ The OpenTelemetry Collector writes data through Stream Load: FE redirects each request to a BE at the address the BE registered in Doris (shown by `SHOW BACKENDS`). Therefore the BE addresses and the BE HTTP port (8040 by default) must also be reachable from the OpenTelemetry Collector container, not only the FE.
+
+3. Verify that the services are running:
+
+ ```Bash
+ docker compose ps
+ ```
+
+ In the existing Doris mode, add `-f docker-compose-without-doris.yaml`. The `STATUS` column of every service should show `Up`. With the built-in Doris, the OpenTelemetry Collector starts only after Doris passes its health check.
+
+4. Open http://localhost:3000 and log in to Grafana with `admin` / `admin`.
+
+## Service Endpoints
+
+| Service | Endpoint | Credentials |
+| --- | --- | --- |
+| Grafana | http://localhost:3000 | admin / admin |
+| OTLP gRPC | localhost:4317 | - |
+| OTLP HTTP | localhost:4318 | - |
+| OpenTelemetry Collector health check | http://localhost:13133 | - |
+| Doris FE Web UI (built-in Doris only) | http://localhost:8030 | root / empty password |
+| Doris MySQL protocol (built-in Doris only) | localhost:9030 | root / empty password |
+
+Applications instrumented with an OpenTelemetry SDK send data to `localhost:4317` (gRPC) or `localhost:4318` (HTTP).
+
+## Stop and Clean Up
+
+Stop the services and keep the data:
+
+```Bash
+docker compose down
+```
+
+Stop the services and remove all data:
+
+```Bash
+docker compose down -v
+```
+
+In the existing Doris mode, add `-f docker-compose-without-doris.yaml` to both commands. The data in your Doris cluster is not removed.
diff --git a/docs/observability/dogstack/kubernetes.md b/docs/observability/dogstack/kubernetes.md
new file mode 100644
index 00000000000..06eb74bc147
--- /dev/null
+++ b/docs/observability/dogstack/kubernetes.md
@@ -0,0 +1,503 @@
+---
+{
+ "title": "Deploy on Kubernetes",
+ "sidebar_label": "Deploy on Kubernetes",
+ "language": "en-US",
+ "description": "How to deploy DOG Stack on Kubernetes and collect container logs, metrics, and Events of a cluster? This article uses Helm to install the DOG Stack backend (OpenTelemetry gateway, Apache Doris, Grafana) and the Kubernetes collector, and covers verification, log parsing rules, production settings, and troubleshooting.",
+ "keywords": [
+ "DOG Stack",
+ "Kubernetes",
+ "Helm",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Kubernetes log collection",
+ "observability"
+ ]
+}
+---
+
+<!-- Knowledge type: operational steps -->
+<!-- Applicable scenario: observability platform / DOG Stack Kubernetes deployment and cluster collection -->
+
+This article describes how to deploy DOG Stack on Kubernetes with Helm and collect container logs, kubelet, node, and cluster metrics, and Kubernetes Events of a cluster into it. After completing the steps, you can query the data in Doris and view it in the Grafana dashboards. To deploy on a single host instead, see [Deploy with Docker Compose](./docker.md).
+
+DOG Stack on Kubernetes consists of two independent Helm charts:
+
+| Chart | Role | Workloads |
+| --- | --- | --- |
+| `ai-observe-stack` | The DOG Stack backend. Installed once. It receives OTLP data, writes it into Doris, and displays it in Grafana. It collects nothing by itself. | OpenTelemetry gateway (StatefulSet), Grafana with the Doris App plugin (Deployment), and optionally a Doris cluster managed by the Doris Operator |
+| `dog-k8s-collector` | The Kubernetes collector. Installed once in every cluster to be collected, and pointed at the gateway. | agent (DaemonSet, one per node): container logs, kubelet and node metrics, node-local OTLP entry point; cluster (Deployment, one per cluster): cluster metrics and Kubernetes Events |
+
+The data flow is:
+
+```text
+applications (OTLP SDK) ───────┐
+ │ OTLP
+dog-k8s-collector ─────────────┤
+ agent (DaemonSet) │
+ cluster (Deployment) ▼
+ai-observe-stack
+ gateway OpenTelemetry Collector receives OTLP, writes Doris
+ doris Apache Doris stores logs, metrics, traces
+ grafana Grafana + Doris App shows them
+```
+
+There is no Helm dependency between the two charts: they are installed and upgraded separately. Several Kubernetes clusters can send to one DOG Stack, told apart by the cluster name.
+
+:::tip
+If a DOG Stack is already running (installed with this chart, deployed another way, or in another cluster), skip to [Step 2: Install the Kubernetes Collector](#step-2-install-the-kubernetes-collector). You only need the OTLP gRPC address of its gateway.
+:::
+
+This guide is checked against AI Observe Stack commit [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949). The checkout below pins this revision so later main-branch changes do not alter the parameters. Its Helm chart version is `0.2.0` and the default Doris images use `4.0.3`.
+
+## Prerequisites
+
+- Kubernetes 1.24 or later, Helm 3.8 or later, and `kubectl` configured to access the cluster.
+- Cluster-level RBAC permissions: the collector creates a ClusterRole and a ClusterRoleBinding.
+- A PersistentVolume provisioner for the default gateway sending queues, including when Doris is external. Alternatively, disable `gateway.persistence.enabled` for a disposable trial; queued data is then lost when a pod is replaced.
+- Nodes can pull the images `otel/opentelemetry-collector-contrib`, `otel/opentelemetry-collector-k8s`, `grafana/grafana`, `velodb/doris-app-plugin`, and `busybox`. For a private registry, set `global.imagePullSecrets` in `ai-observe-stack` and `imagePullSecrets` in `dog-k8s-collector`, and point the image parameters at your mirror: `gateway.image.repository`, `grafana.image.repository`, `dorisPlugin.image.repository`, and `global.helperImages.busybox` / `global.helperImages.curl` in `ai [...]
+- Doris, one of the following:
+ - An existing Doris cluster whose FE ports 9030 (MySQL protocol) and 8030 (HTTP) are reachable from the Kubernetes cluster, and an account that can create the target database and tables, load data, and query the Grafana tables. The gateway writes data through Stream Load: FE redirects each request to a BE at the address the BE registered in Doris (shown by `SHOW BACKENDS`), so these BE addresses and the BE HTTP port (8040 by default) must also be reachable from the gateway pods.
+ - Or let the chart deploy Doris: a PersistentVolume provisioner, and at least 2 CPU cores and 4 GiB of memory each for FE and BE.
+
+The commands in this article use the release name `dog` and the namespace `dog`. The object names below are derived from them; if you use other names, replace them accordingly.
+
+| Object | Name |
+| --- | --- |
+| Gateway StatefulSet and Service | `dog-ai-observe-stack-otel-gateway` |
+| Grafana Deployment and Service | `dog-ai-observe-stack-grafana` |
+| Grafana admin Secret | `dog-ai-observe-stack-grafana-admin` |
+| DorisCluster (when the chart deploys Doris) | `dog-ai-observe-stack-doris` |
+| Collector agent DaemonSet | `dog-k8s-collector-agent` |
+| Collector cluster Deployment | `dog-k8s-collector-cluster` |
+
+## Get the Charts
+
+Clone the repository and download the chart dependency. The Doris Operator subchart is declared in the chart's dependencies, so this step is required even if you connect to an existing Doris. Use `helm dependency update`, which does not require `helm repo add`.
+
+```Bash
+git clone https://github.com/ai-observe/ai-observe-stack.git
+cd ai-observe-stack
+git checkout 2c1fbee831701178c7dddba857e88130ef292949
+cd helm-charts
+helm dependency update ./ai-observe-stack
+```
+
+All following commands run in the `helm-charts` directory.
+
+## Step 1: Install the DOG Stack Backend
+
+Choose where Doris runs:
+
+| | Option A: use an existing Doris | Option B: the chart deploys Doris |
+| --- | --- | --- |
+| Suited for | An existing Doris or SelectDB cluster; production | Trials and development when no Doris is available |
+| Data location | Your Doris cluster | PVCs inside the Kubernetes cluster |
+
+### Option A: Use an Existing Doris
+
+1. Create the namespace and store the Doris account in a Secret. The Secret must contain the keys `username` and `password`.
+
+ ```Bash
+ kubectl create namespace dog
+ kubectl create secret generic doris-credentials -n dog \
+ --from-literal=username='<DORIS_USER>' --from-literal=password='<DORIS_PASSWORD>'
+ ```
+
+2. Create a values file `dog-values.yaml`:
+
+ ```yaml
+ doris:
+ mode: external
+ database: otel # the gateway creates this database and its tables
+ external:
+ host: <DORIS_FE_HOST> # FE address reachable from the cluster
+ port: 9030 # MySQL protocol port
+ feHttpPort: 8030 # HTTP port, used by Stream Load
+ existingSecret: doris-credentials
+ internal:
+ operator:
+ enabled: false # do not install the Doris Operator
+ ```
+
+ The same file is available as `examples/ai-observe-stack/minimal-external-doris.yaml` in the repository.
+
+3. Install:
+
+ ```Bash
+ helm install dog ./ai-observe-stack -n dog -f dog-values.yaml
+ ```
+
+### Option B: The Chart Deploys Doris
+
+The repository provides two sizes:
+
+- `examples/ai-observe-stack/dev.yaml`: one FE and one BE, no persistence, one gateway, debug output on. For trials only.
+- `examples/ai-observe-stack/prod.yaml`: three FE and three BE, persistence, three gateways, and Ingress. A starting point for production. Before using it, adapt the environment-specific settings: `storageClass` (set to the AWS `gp3`), the Ingress class, hosts, and TLS issuer, and the Grafana password (`CHANGE_ME`).
+
+```Bash
+cp examples/ai-observe-stack/dev.yaml dog-values.yaml
+helm install dog ./ai-observe-stack -n dog --create-namespace -f dog-values.yaml
+```
+
+Keep `dog-values.yaml` for later upgrades. For production, copy `prod.yaml` instead and adapt the environment-specific settings listed above before installing.
+
+Doris FE and BE take two to five minutes to pull images and initialize. The gateway waits until FE ports 9030 and 8030 are reachable and at least one BE is online before starting.
+
+:::caution
+The Doris Operator is cluster-scoped, and only one can run in a Kubernetes cluster. When installing a second DOG Stack in the same cluster, set `doris.internal.operator.enabled: false`.
+:::
+
+### Verify the Backend
+
+When the installation finishes, Helm prints NOTES, which include the OTLP gRPC address of the gateway: `dog-ai-observe-stack-otel-gateway.dog.svc:4317`. Record it for Step 2.
+
+1. Check that the pods are running. You should see the gateway pods (`dog-ai-observe-stack-otel-gateway-0` and `-1` with the default two replicas; `dev.yaml` runs one), the Grafana pod, and, with Option B, the Doris FE and BE pods, all in the `Running` status.
+
+ ```Bash
+ kubectl get pods -n dog
+ ```
+
+2. Run the built-in pipeline test. It sends a log record through the gateway and checks that it arrives in Doris.
+
+ ```Bash
+ helm test dog -n dog --logs
+ ```
+
+ The output ends with `OK: record found in Doris`.
+
+3. Get the Grafana admin password (default `admin`):
+
+ ```Bash
+ kubectl get secret -n dog dog-ai-observe-stack-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d; echo
+ ```
+
+4. Access Grafana:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-grafana 3000:3000
+ ```
+
+ Open http://localhost:3000 and log in as `admin` with the password above. The dashboards are empty at this point, because nothing sends data to the gateway yet.
+
+## Step 2: Install the Kubernetes Collector {#step-2-install-the-kubernetes-collector}
+
+The backend installed in Step 1 only receives data. The collector gathers data from the Kubernetes cluster and sends it to the gateway: on every node, the agent reads the stdout and stderr of all containers and collects kubelet and node metrics; the cluster collector collects cluster-level metrics and Kubernetes Events. Every record carries the pod, namespace, workload, and cluster name. Without the collector, only applications that send OTLP to the gateway directly reach DOG Stack, and [...]
+
+Prepare the namespace in the cluster being collected. Run this idempotent command even if you skipped Step 1:
+
+```Bash
+kubectl create namespace dog --dry-run=client -o yaml | kubectl apply -f -
+```
+
+1. Confirm that the gateway is reachable from the cluster to be collected. Replace the address with yours.
+
+ ```Bash
+ kubectl run -it --rm otlp-check --image=busybox:1.36 --restart=Never -- \
+ sh -c 'sleep 2; nc -zv dog-ai-observe-stack-otel-gateway.dog.svc 4317'
+ ```
+
+ `open` in the output means the gateway is reachable. The gateway address depends on where the DOG Stack runs:
+
+ | DOG Stack location | Gateway address (`host:port`, without `http://`) |
+ | --- | --- |
+ | Installed by this chart in the same cluster | `<release>-ai-observe-stack-otel-gateway.<namespace>.svc:4317` |
+ | Your own OpenTelemetry Collector with the Doris exporter | Its OTLP gRPC Service, usually `<service>.<namespace>.svc:4317` |
+ | Another Kubernetes cluster | The gateway's gRPC address exposed by a LoadBalancer (`gateway.service.type: LoadBalancer`) or a separately configured gRPC ingress. This chart's Ingress exposes OTLP/HTTP, not OTLP/gRPC. The chosen port must be reachable between clusters |
+
+2. The agent runs as root and mounts the node's `/var/log/pods`, `/` (read-only), and `/var/lib/otelcol`. If the namespace enforces the `restricted` Pod Security Standard, relax it:
+
+ ```Bash
+ kubectl label namespace dog pod-security.kubernetes.io/enforce=privileged --overwrite
+ ```
+
+3. Create a values file `collector-values.yaml`:
+
+ ```yaml
+ gateway:
+ endpoint: dog-ai-observe-stack-otel-gateway.dog.svc:4317
+ clusterName: my-cluster
+ platform: generic
+ ```
+
+ | Parameter | Required | Default | Description |
+ | --- | --- | --- | --- |
+ | `gateway.endpoint` | Yes | None | OTLP gRPC address of the gateway, in the form `host:port`. |
+ | `clusterName` | No | Release name | Written to every record as `k8s.cluster.name`, to tell clusters apart. |
+ | `platform` | No | `generic` | One of `generic`, `k3s`, `eks`, `gke`, `aks`, `ack`, `openshift`. Selects cloud resource detectors and journald unit names. Use `generic` if unsure. |
+ | `timezone` | No | `UTC` | IANA time zone assumed for log lines whose timestamp has no time zone. A parsing rule can override it with `timestamp.timezone`. |
+
+4. Install:
+
+ ```Bash
+ helm install dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+ ```
+
+The collector enables the following collection presets by default:
+
+| Preset | Default | Collects | Runs in |
+| --- | --- | --- | --- |
+| `logsCollection` | On | stdout and stderr of every container in every namespace | agent |
+| `kubeletMetrics` | On | CPU, memory, network, and filesystem of nodes, pods, containers, and volumes, with limit utilization | agent |
+| `hostMetrics` | On | CPU, memory, load, disk, filesystem, and network of the node | agent |
+| `otlp` | On | Node-local OTLP entry point for application SDKs | agent |
+| `clusterMetrics` | On | Deployment availability, node conditions, pod phases, quotas | cluster |
+| `kubernetesEvents` | On | Kubernetes Events, stored as logs | cluster |
+| `prometheusScrape` | Off | Pods annotated with `prometheus.io/scrape` | agent |
+| `journald` | Off | Node journal. Requires an image that contains `journalctl` | agent |
+
+:::caution
+`examples/ai-observe-stack/dev.yaml` turns on the gateway's debug output, which prints sampled records to the gateway log. When the collector runs in the same cluster, exclude the gateway's log files, otherwise every record is collected again:
+
+```yaml
+logs:
+ excludePaths:
+ - /var/log/pods/dog_dog-ai-observe-stack-otel-gateway-*/*/*.log
+```
+:::
+
+## Step 3: Verify Collection
+
+1. Check the collector pods. There is one agent pod per node and one cluster pod:
+
+ ```Bash
+ kubectl get pods -n dog -l app.kubernetes.io/instance=dog-k8s-collector -o wide
+ ```
+
+ ```text
+ NAME READY STATUS NODE
+ dog-k8s-collector-agent-7x2kq 1/1 Running node-1
+ dog-k8s-collector-agent-p9m4c 1/1 Running node-2
+ dog-k8s-collector-cluster-6f5d9c8b7d-hq2xn 1/1 Running node-1
+ ```
+
+2. Check that the agent reports no errors. The following command should print nothing:
+
+ ```Bash
+ kubectl logs -n dog ds/dog-k8s-collector-agent | grep '"level":"error"'
+ ```
+
+3. Query the data in Doris. With Option A, connect a MySQL client to port 9030 of your Doris FE. With Option B, forward the FE port first and connect with a MySQL client:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-doris-fe-service 9030:9030
+ mysql -h127.0.0.1 -P9030 -uroot
+ ```
+
+ ```sql
+ SELECT service_name, count(*) AS records
+ FROM otel.otel_logs
+ WHERE timestamp > now() - interval 5 minute
+ AND cast(resource_attributes['k8s.cluster.name'] as string) = 'my-cluster'
+ GROUP BY 1 ORDER BY 2 DESC;
+ ```
+
+ The first records arrive within 30 seconds after the agent starts. `service_name` is the name of the workload that owns the pod, looked up in the order Deployment, StatefulSet, DaemonSet, CronJob, and Job, or the pod name for a standalone pod. Kubernetes Events appear as `kubernetes-events`. Only log lines written after the installation are collected; existing log files on the nodes are not read again.
+
+4. Open Grafana and check the dashboards:
+
+ - **Logs Explorer**: filter logs by namespace, service, severity, and text.
+ - **Kubernetes Events**: Warning counts, top reasons, and the event table.
+ - **K8s Observability**: CPU and memory of nodes and pods.
+ - **Collector Self-Monitoring**: queue length, failures, and refusals of the agent, the cluster collector, and the gateway.
+
+### End-to-End Example
+
+The following pod prints one JSON log line per second, which verifies the whole path from container logs to Doris:
+
+```Bash
+kubectl create namespace demo
+kubectl run json-app -n demo --image=busybox:1.36 --restart=Never -- sh -c \
+ 'i=0; while true; do i=$((i+1)); echo "{\"time\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"level\":\"info\",\"msg\":\"tick $i\"}"; sleep 1; done'
+```
+
+After about 30 seconds, query Doris:
+
+```sql
+SELECT timestamp, severity_text, body, cast(resource_attributes['k8s.pod.name'] as string) AS pod
+FROM otel.otel_logs
+WHERE cast(resource_attributes['k8s.namespace.name'] as string) = 'demo'
+ORDER BY timestamp DESC LIMIT 5;
+```
+
+Rows with `severity_text` equal to `INFO` and `body` like `tick 42` show that the container log was collected, the JSON line was parsed, and Kubernetes metadata was attached. Clean up with `kubectl delete namespace demo`.
+
+## Configure Collection
+
+To change the collection, edit `collector-values.yaml` and run:
+
+```Bash
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+Only the affected agent or cluster collector restarts. With `agent.storage.enabled=true`, file offsets are retained on the same node and storage path. This reduces rereading but does not guarantee exactly-once delivery: retries can duplicate records, and node/storage loss or queue exhaustion can lose data.
+
+### Select Namespaces and Containers
+
+```yaml
+logs:
+ namespaces:
+ include: ["*"] # or a list, such as [shop, payment]
+ exclude: [kube-system, monitoring]
+ containers:
+ exclude: [istio-proxy, linkerd-proxy] # container names, excluded in every namespace
+```
+
+Exclusions take precedence over inclusions. The collector's own pods are never collected.
+
+### Parse Logs
+
+- **JSON logs**: lines starting with `{` are parsed automatically. `level` or `severity` becomes `severity_text`, `message` or `msg` becomes `body`, and other keys are stored in `log_attributes`. For other field names, set `logs.json.severityFields` and `logs.json.messageFields`.
+- **Common text formats**: use a built-in preset in a rule. `selector.container` matches the container name, not the pod or Deployment name.
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-api
+ selector: {namespace: "^shop$", container: "^api$"}
+ preset: java-spring
+ ```
+
+ | Preset | Format |
+ | --- | --- |
+ | `java-spring` | Spring Boot 3 default console format, with stack traces joined |
+ | `python-logging` | Python `logging.basicConfig` default format |
+ | `go-zap-console` | zap console encoder |
+ | `glog` | C++ and Go glog |
+ | `nginx-access`, `nginx-error` | nginx combined access log and error log |
+ | `mysql`, `postgres`, `redis` | Defaults of the official images |
+ | `doris-fe`, `doris-fe-audit`, `doris-be` | Apache Doris FE, FE audit, and BE logs |
+ | `json-generic` | Single-line JSON |
+
+ Doris FE writes runtime logs and audit logs to the same container. To parse both, add two rules with the same selector, one with `preset: doris-fe` and one with `preset: doris-fe-audit`.
+
+- **Custom formats**: write a regular expression with named groups `ts` (timestamp), `level` (severity), and `msg` (message):
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-worker
+ selector: {namespace: "^shop$", container: "^worker$"}
+ regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<level>[a-z]+)\s+(?P<msg>(?s:.*))$'
+ timestamp: {layout: "%Y-%m-%dT%H:%M:%S.%L%z"}
+ severity: {field: level}
+ multiline: {firstLinePattern: '^\d{4}-\d{2}-\d{2}T'} # stack trace lines join the previous record
+ drop: ['GET /healthz'] # dropped on the node
+ ```
+
+ If the timestamp in the log line has no time zone and the container does not run in UTC, add `timezone` to `timestamp`, for example `timestamp: {layout: "...", timezone: Asia/Shanghai}`. Rules are matched in order and the first matching rule wins. The attribute `dog.log.rule` of each record shows which rule parsed it.
+
+ Before deploying a rule, you can test it against sample logs with `hack/test-rule.sh` in the repository, which requires helm, yq, and Docker:
+
+ ```Bash
+ kubectl logs -n shop -l app=worker -c worker --tail=30 > worker.log
+ hack/test-rule.sh -f collector-values.yaml -n shop -c worker worker.log
+ ```
+
+### Receive OTLP from Applications
+
+Applications instrumented with an OpenTelemetry SDK can send traces, metrics, and logs to the agent on their node, which adds pod, namespace, and workload metadata. Expose the agent ports on the node:
+
+```yaml
+presets:
+ otlp:
+ hostPortHttp: 4318
+ hostPortGrpc: 4317
+```
+
+Then set the endpoint in the application's pod template:
+
+```yaml
+env:
+ - name: HOST_IP
+ valueFrom: {fieldRef: {fieldPath: status.hostIP}}
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://$(HOST_IP):4318
+ - name: OTEL_EXPORTER_OTLP_PROTOCOL
+ value: http/protobuf
+ - name: OTEL_SERVICE_NAME
+ value: checkout
+```
+
+### Scrape Prometheus Metrics
+
+Set `presets.prometheusScrape.enabled: true`, then annotate the application pods with `prometheus.io/scrape: "true"`, `prometheus.io/port`, and `prometheus.io/path`. The metrics are written to the `otel_metrics_*` tables, and `service_name` is the workload name.
+
+### Collect Multiple Clusters
+
+Install one collector release in each cluster. Point `gateway.endpoint` at the same gateway (use a LoadBalancer or separately configured gRPC ingress across clusters) and give each cluster a different `clusterName`. Queries and dashboards filter by `k8s.cluster.name`.
+
+## Production Recommendations
+
+| Item | Parameter | Description |
+| --- | --- | --- |
+| Doris replicas and disks | `doris.internal.cluster.fe.replicas`, `doris.internal.cluster.be.replicas`, `doris.internal.cluster.persistence` | At least three FE and three BE, with `gateway.dorisExporter.replicationNum: 3`. |
+| Gateway replicas and queue | `gateway.replicas`, `gateway.persistence.size`, `gateway.dorisExporter.sendingQueue` | Each gateway replica keeps its sending queue on its own PVC. If writes lag, watch `otelcol_exporter_queue_size` on the Collector Self-Monitoring dashboard. |
+| Data retention | `gateway.dorisExporter.historyDays` | 7 days by default. |
+| Grafana password | `grafana.adminPassword` or `grafana.existingSecret` | The default password is `admin`. |
+| External access | `ingress`, `gateway.service.type` | Ingress for Grafana; applications outside the cluster use the OTLP/HTTP Ingress path or a LoadBalancer. |
+| Debug output | `gateway.debug.enabled: false` | Turned on in `dev.yaml`. |
+| Collection scope | `logs.namespaces.exclude` | Usually excludes `kube-system`. |
+| Collector resources | `agent.resources`, `agent.queueSize` | Each agent requests 100m CPU and 128 MiB of memory, and is limited to 1 CPU and 512 MiB. While the gateway is unreachable, each node buffers up to `agent.queueSize` records (20,000 by default) on local disk. |
+
+`examples/ai-observe-stack/prod.yaml` in the repository is a production values file to start from.
+
+By default, `gateway.timeGuard.enabled=true` restamps records outside the writable partition window with receive time. Logs retain the original in `log.original_time_unix_nano`, traces in `span.original_start_time_unix_nano`, and metric timestamps are rewritten directly. For historical backfill, review `gateway.dorisExporter.createHistoryDays` and time-guard settings together. The default Doris export retry limit is 10 minutes (`gateway.dorisExporter.retryMaxElapsedTime`); a batch still [...]
+
+## Upgrade and Uninstall
+
+Upgrade or change configuration:
+
+```Bash
+helm upgrade dog ./ai-observe-stack -n dog -f dog-values.yaml
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+Uninstall:
+
+```Bash
+helm uninstall dog-k8s-collector -n dog
+helm uninstall dog -n dog
+```
+
+Uninstalling the collector does not affect the DOG Stack or the data in Doris. The file offsets under `/var/lib/otelcol` on the nodes are kept, and a reinstall continues from the last position. With Option A, uninstalling the DOG Stack does not delete the database in your Doris.
+
+Helm does not delete the PersistentVolumeClaims of the gateway, the PersistentVolumeClaims of Doris (Option B with persistence enabled), or the completed `helm test` pod. To remove them:
+
+```Bash
+kubectl delete pvc -n dog -l app.kubernetes.io/instance=dog
+kubectl delete pvc -n dog -l 'app.doris.ownerreference/name in (dog-ai-observe-stack-doris-fe,dog-ai-observe-stack-doris-be)'
+kubectl delete pod -n dog dog-ai-observe-stack-test-pipeline --ignore-not-found
+```
+
+With Option B, Helm also keeps the CustomResourceDefinition `dorisclusters.doris.selectdb.com` installed with the Doris Operator, because Helm never deletes CRDs. Deleting the CRD deletes every DorisCluster in the Kubernetes cluster, so delete it only when no other Doris cluster is managed by the Doris Operator:
+
+```Bash
+kubectl delete crd dorisclusters.doris.selectdb.com
+```
+
+## Troubleshooting
+
+| Symptom | Cause and solution |
+| --- | --- |
+| `helm install` reports `missing in charts/ directory: doris-operator` | The chart dependency is not downloaded. Run `helm dependency update ./ai-observe-stack`. |
+| `helm dependency build` reports `no repository definition for https://charts.selectdb.com` | `helm dependency build` only uses repositories added with `helm repo add`. Run `helm dependency update ./ai-observe-stack` instead. |
+| `helm install` reports `gateway.endpoint is required` | Set `gateway.endpoint` of the collector to the gateway address. |
+| `helm install` reports `additional properties 'xxx' not allowed` | A top-level key in the values file is misspelled. |
+| `helm test` fails, or the gateway log reports Doris errors | Check that Doris FE ports 9030 and 8030 are reachable and that the account can create the target database/tables, load data and query them. View the gateway log with `kubectl logs -n dog dog-ai-observe-stack-otel-gateway-0`. |
+| Agent pod reports `CreateContainerConfigError`, is rejected by Pod Security, or logs `permission denied` on `/var/log/pods` | The namespace enforces `restricted`. Add the `privileged` label, see Step 2. |
+| Agent log reports `connection refused` or `Unavailable` | The gateway address is wrong, the gateway is down, or a network policy blocks port 4317. Records are buffered on the node and sent after the problem is fixed. |
+| Agent pod stays in `ContainerCreating`, and its events report `MountVolume.SetUp failed for volume "zoneinfo"` | The node has no time zone database (some minimal operating system images). Set `agent.tzdata.hostPath: ""`, and keep `timezone` and the `timestamp.timezone` of every rule at UTC. |
+| A namespace has no logs | It is excluded by `logs.namespaces.exclude` or `logs.containers.exclude`, or no new lines were written after the installation. |
+| Logs arrive but `service_name` is empty | The agent cannot read pod metadata. Check the agent log for RBAC `forbidden` errors. |
+| A parsing rule has no effect (`dog.log.rule` is empty) | `selector.container` contains a pod name instead of a container name, or an earlier rule matched first. Verify with `hack/test-rule.sh`. |
+| Log times are off by hours | The log line has no time zone, the container does not run in UTC, and `timestamp.timezone` is not set. |
+
+To view the configuration that the agent actually runs:
+
+```Bash
+kubectl get cm -n dog dog-k8s-collector-agent-config -o jsonpath='{.data.config\.yaml}'
+```
+
+For all parameters of the two charts, see the `USER_GUIDE.md` and `values.yaml` of each chart under [helm-charts](https://github.com/ai-observe/ai-observe-stack/tree/main/helm-charts) in the repository.
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 3979b5e4dc6..903490c7a09 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -167,6 +167,17 @@ function buildRedirectIndex() {
add(to, from);
}
+ // The DOG Stack deployment page was split into Docker Compose and
+ // Kubernetes pages; its old URL lands on the Docker Compose page.
+ for (const version of ['dev', '4.x', '3.x']) {
+ for (const locale of ['', '/zh-CN']) {
+ add(
+ `${locale}/docs/${version}/observability/dogstack/docker`,
+ `${locale}/docs/${version}/observability/dogstack/deployment`,
+ );
+ }
+ }
+
// Old 4.x slugs that have no 1:1 counterpart in the new 4.x tree
// (which was reseeded from Dev). Generated by
// scripts/migrate-4.x/build-redirects.py from the diff between
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
index 2fff80f92e4..0f383b44844 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current.json
@@ -619,6 +619,10 @@
"message": "性能测试",
"description": "The label for category Benchmark in sidebar docs"
},
+ "sidebar.docs.category.Deployment": {
+ "message": "部署",
+ "description": "The label for category Deployment in sidebar docs"
+ },
"sidebar.docs.category.Observability": {
"message": "可观测性",
"description": "Observability"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/deployment.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/deployment.md
deleted file mode 100644
index 10a32733139..00000000000
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/deployment.md
+++ /dev/null
@@ -1,172 +0,0 @@
----
-{
- "title": "部署",
- "sidebar_label": "部署",
- "language": "zh-CN",
- "description": "如何在生产环境部署 DOG Stack?本文介绍 OpenTelemetry Collector、Apache Doris 与集成 Doris App 插件的 Grafana 三大核心组件的部署方式与选型建议。",
- "keywords": [
- "DOG Stack 部署",
- "OpenTelemetry Collector",
- "Apache Doris 部署",
- "Grafana 部署",
- "Doris App 插件",
- "Docker Compose",
- "Kubernetes",
- "可观测性建设"
- ]
-}
----
-
-<!-- 知识类型: 操作步骤 -->
-<!-- 适用场景: 可观测性建设 / DOG Stack 生产部署 -->
-
-如架构图所示,DOG Stack 由四个组件组成:数据采集工具、OpenTelemetry Collector、Doris 和 集成 Doris应用的Grafana。本部署文档将重点介绍后三个组件,而数据采集文档将重点介绍第一个组件。
-
-DOG Stack 为不同的环境和目的提供多种部署选项。
-
-
-
-## Docker Compose
-
-最适合本地测试、开发和 PoC。
-
-### 依赖项
-
-- Docker Engine (v20.10+)
-- Docker Compose (v2.0+)
-
-
-
-### 部署
-
-1. 克隆仓库:
-
- `git clone https://github.com/ai-observe/ai-observe-stack.git cd ai-observe-stack/docker`
-
-
-
-2. 启动所有服务:
-
- `docker compose up -d`
-
-
-
-3. 验证服务是否正在运行:
-
- `docker compose ps`
-
- 所有服务应显示`running`状态。
-
-
-
-4. 在 http://localhost:3000 访问 Grafana,使用默认用户密码`admin` / `admin`登录。
-
-
-
-### 服务端点
-
-| Service | Endpoint | Credentials |
-| --------- | --------------------- | ------------- |
-| Grafana | http://localhost:3000 | admin / admin |
-| OTel gRPC | localhost:4317 | - |
-| OTel HTTP | localhost:4318 | - |
-
-### 停止和清理
-
-在保留数据的同时停止服务:
-
-```Bash
-docker compose down
-```
-
-停止服务并删除所有数据:
-
-```Bash
-docker compose down -v
-```
-
-
-
-## Kubernetes
-
-最适合生产部署、开发环境和可扩展的设置。
-
-### 依赖项
-
-- Kubernetes cluster (v1.20+)
-- Helm (v3.0+)
-- kubectl configured to access your cluster
-- PersistentVolume provisioner (for data persistence)
-
-### 部署
-
-1. 添加DOG Stack 和Doris Helm仓库:
-
- `helm repo add dogstack https://charts.velodb.io helm repo update`
-
-
-
-2. 为DOG Stack 创建一个命名空间 堆栈:
-
- `kubectl create namespace dogstack`
-
-
-
-3. 安装 DOG Stack:
-
- 如果您已有 Doris 集群,可以指定已有 Doris 集群的连接方式。
-
- `helm install my-dogstack dogstack/dogstack -n dogstack \ --set doris.mode=external \ --set doris.external.host=<DORIS_FE_HOST> \ --set doris.external.port=9030 \ --set doris.external.feHttpPort=8030 \ --set doris.internal.operator.enabled=false`
-
-
-
- 如果您想使用新的 Doris 集群进行部署,只需直接安装,无需额外选项。
-
- `helm install my-dogstack dogstack/dogstack -n dogstack`
-
-
-
-4. 验证所有 Pod 都在运行:
-
- `kubectl get pods -n dogstack`
-
- 等待所有 Pod 显示`Running`状态。
-
-
-
-5. 访问Grafana:
-
- `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack`
-
- 打开 http://localhost:3000 并使用`admin` / `admin`登录。
-
-
-
-### 服务端点
-
-| Service | Port-forward command |
-| ----------- | ------------------------------------------------------------ |
-| Grafana | `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack` |
-| Doris FE UI | `kubectl port-forward svc/my-dogstack-doris-fe 8030:8030 -n dogstack` |
-| Doris MySQL | `kubectl port-forward svc/my-dogstack-doris-fe 9030:9030 -n dogstack` |
-
-### 卸载
-
-```Bash
-helm uninstall my-dogstack -n dogstack
-kubectl delete namespace dogstack
-```
-
-
-
-## 手动部署
-
-1. 按照 Doris [部署文档](https://doris.apache.org/docs/4.x/install/preparation/env-checking)部署 Doris 集群。如果已有 Doris 集群,则可跳过此步骤。
-
-1. 按照 [OpenTelemetry部署文档](https://opentelemetry.io/docs/collector/install/) 部署 OpenTelemetry Collector。
-
-1. 按照[ Grafana部署文档](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) 部署 Grafana UI。
-
-1. 浏览器打开 DOG Stack Web UI
-
-访问 http://localhost:3000 以访问 DOG Stack 内的 Grafana UI。
\ No newline at end of file
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/docker.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/docker.md
new file mode 100644
index 00000000000..91b41c91fb8
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/docker.md
@@ -0,0 +1,116 @@
+---
+{
+ "title": "Docker Compose 部署",
+ "sidebar_label": "Docker Compose 部署",
+ "language": "zh-CN",
+ "description": "如何使用 Docker Compose 在单机上部署 DOG Stack?本文介绍如何启动 OpenTelemetry Collector、Apache Doris 与集成 Doris App 插件的 Grafana,支持内置 Doris 和接入已有 Doris 集群两种方式,并介绍验证、访问与清理方法。",
+ "keywords": [
+ "DOG Stack 部署",
+ "Docker Compose",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Doris App 插件",
+ "可观测性建设"
+ ]
+}
+---
+
+<!-- 知识类型: 操作步骤 -->
+<!-- 适用场景: 可观测性建设 / DOG Stack 单机部署 -->
+
+本文介绍如何使用 Docker Compose 在单机上部署 DOG Stack。部署会启动 OpenTelemetry Collector、集成 Doris App 插件的 Grafana,以及可选的一体化 Doris,适用于本地测试、开发和 PoC。生产环境或 Kubernetes 环境请参见 [Kubernetes 部署](./kubernetes.md)。
+
+:::tip
+如果不使用 Docker 部署各组件,请参见 [Doris 部署文档](https://doris.apache.org/zh-CN/docs/4.x/install/preparation/env-checking)、[OpenTelemetry Collector 安装文档](https://opentelemetry.io/docs/collector/install/) 和 [Grafana 安装文档](https://grafana.com/docs/grafana/latest/setup-grafana/installation/)。
+:::
+
+本文按 AI Observe Stack 源码提交 [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949) 核对。下方检出命令固定该提交,避免主分支更新导致参数变化。该提交的内置 Doris 镜像为 `4.0.3-all-slim`,用于部署栈的连通性验证。
+
+## 前提条件
+
+- Docker Engine 20.10 及以上版本
+- Docker Compose 2.0 及以上版本
+
+## 部署
+
+1. 克隆仓库:
+
+ ```Bash
+ git clone https://github.com/ai-observe/ai-observe-stack.git
+ cd ai-observe-stack
+ git checkout 2c1fbee831701178c7dddba857e88130ef292949
+ cd docker
+ ```
+
+2. 启动服务,以下两种方式二选一。
+
+ - **内置 Doris**:同时启动一体化 Doris、OpenTelemetry Collector 和 Grafana。
+
+ ```Bash
+ docker compose up -d
+ ```
+
+ - **接入已有 Doris**:只启动 OpenTelemetry Collector 和 Grafana,数据写入已有的 Doris 集群。先创建配置文件:
+
+ ```Bash
+ cp .env.example .env
+ ```
+
+ 在 `.env` 中修改 Doris 连接配置:
+
+ ```Bash
+ DORIS_FE_HTTP_ENDPOINT=http://<DORIS_FE_HOST>:8030
+ DORIS_FE_MYSQL_ENDPOINT=<DORIS_FE_HOST>:9030
+ DORIS_USERNAME=root
+ DORIS_PASSWORD=
+ ```
+
+ 然后启动服务:
+
+ ```Bash
+ docker compose -f docker-compose-without-doris.yaml up -d
+ ```
+
+ 该账号需要具备创建目标数据库和表、导入数据,以及查询 Grafana 所用表的权限。OpenTelemetry Collector 会自动创建 `otel` 数据库及其中的表。
+
+ OpenTelemetry Collector 通过 Stream Load 写入数据:FE 会将每个请求重定向到 BE 在 Doris 中注册的地址(可通过 `SHOW BACKENDS` 查看)。因此,除了 FE 之外,OpenTelemetry Collector 容器还需要能够访问这些 BE 地址及 BE 的 HTTP 端口(默认 8040)。
+
+3. 验证服务是否正在运行:
+
+ ```Bash
+ docker compose ps
+ ```
+
+ 接入已有 Doris 时,需要加上 `-f docker-compose-without-doris.yaml`。所有服务的 `STATUS` 列应显示 `Up`。使用内置 Doris 时,OpenTelemetry Collector 会在 Doris 通过健康检查后才启动。
+
+4. 打开 <http://localhost:3000>,使用 `admin` / `admin` 登录 Grafana。
+
+## 服务端点
+
+| 服务 | 地址 | 账号 |
+| --- | --- | --- |
+| Grafana | http://localhost:3000 | admin / admin |
+| OTLP gRPC | localhost:4317 | - |
+| OTLP HTTP | localhost:4318 | - |
+| OpenTelemetry Collector 健康检查 | http://localhost:13133 | - |
+| Doris FE Web UI(仅内置 Doris) | http://localhost:8030 | root / 空密码 |
+| Doris MySQL 协议(仅内置 Doris) | localhost:9030 | root / 空密码 |
+
+接入 OpenTelemetry SDK 的应用将数据发送到 `localhost:4317`(gRPC)或 `localhost:4318`(HTTP)。
+
+## 停止和清理
+
+停止服务并保留数据:
+
+```Bash
+docker compose down
+```
+
+停止服务并删除所有数据:
+
+```Bash
+docker compose down -v
+```
+
+接入已有 Doris 时,以上两条命令都需要加上 `-f docker-compose-without-doris.yaml`。已有 Doris 集群中的数据不会被删除。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/kubernetes.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/kubernetes.md
new file mode 100644
index 00000000000..11aa32f351a
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/observability/dogstack/kubernetes.md
@@ -0,0 +1,503 @@
+---
+{
+ "title": "Kubernetes 部署",
+ "sidebar_label": "Kubernetes 部署",
+ "language": "zh-CN",
+ "description": "如何在 Kubernetes 上部署 DOG Stack 并采集集群的容器日志、指标和 Events?本文使用 Helm 安装 DOG Stack 后端(OpenTelemetry 网关、Apache Doris、Grafana)和 Kubernetes 采集器,并介绍验证方法、日志解析规则、生产配置与问题排查。",
+ "keywords": [
+ "DOG Stack",
+ "Kubernetes",
+ "Helm",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Kubernetes 日志采集",
+ "可观测性建设"
+ ]
+}
+---
+
+<!-- 知识类型: 操作步骤 -->
+<!-- 适用场景: 可观测性建设 / DOG Stack Kubernetes 部署与集群采集 -->
+
+本文介绍如何使用 Helm 在 Kubernetes 上部署 DOG Stack,并将集群的容器日志、kubelet / 节点 / 集群指标以及 Kubernetes Events 采集进来。完成后,可以在 Doris 中查询这些数据,并在 Grafana 仪表盘中查看。如果需要在单机上部署,请参见 [Docker Compose 部署](./docker.md)。
+
+Kubernetes 上的 DOG Stack 由两个相互独立的 Helm Chart 组成:
+
+| Chart | 作用 | 工作负载 |
+| --- | --- | --- |
+| `ai-observe-stack` | DOG Stack 后端,只需安装一次。接收 OTLP 数据、写入 Doris,并通过 Grafana 展示。它本身不采集任何数据。 | OpenTelemetry 网关(StatefulSet)、集成 Doris App 插件的 Grafana(Deployment),以及可选的由 Doris Operator 管理的 Doris 集群 |
+| `dog-k8s-collector` | Kubernetes 采集器。在每个需要采集的集群中安装一次,指向网关。 | agent(DaemonSet,每个节点一个):容器日志、kubelet 与节点指标、节点本地 OTLP 入口;cluster(Deployment,每个集群一个):集群指标与 Kubernetes Events |
+
+数据流如下:
+
+```text
+applications (OTLP SDK) ───────┐
+ │ OTLP
+dog-k8s-collector ─────────────┤
+ agent (DaemonSet) │
+ cluster (Deployment) ▼
+ai-observe-stack
+ gateway OpenTelemetry Collector receives OTLP, writes Doris
+ doris Apache Doris stores logs, metrics, traces
+ grafana Grafana + Doris App shows them
+```
+
+两个 Chart 之间没有 Helm 依赖关系,分别安装、分别升级。多个 Kubernetes 集群可以写入同一个 DOG Stack,通过集群名称区分。
+
+:::tip
+如果已经有运行中的 DOG Stack(使用本 Chart 安装、以其他方式部署或位于其他集群),可以直接跳到[步骤 2:安装 Kubernetes 采集器](#step-2-install-the-kubernetes-collector),只需要知道其网关的 OTLP gRPC 地址。
+:::
+
+本文按 AI Observe Stack 源码提交 [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949) 核对。下方检出命令固定该提交,避免主分支更新导致参数变化。该提交的 Helm Chart 版本为 `0.2.0`,默认 Doris 镜像为 `4.0.3`。
+
+## 前提条件
+
+- Kubernetes 1.24 及以上版本、Helm 3.8 及以上版本,并已配置 `kubectl` 访问集群。
+- 集群级 RBAC 权限:采集器会创建 ClusterRole 和 ClusterRoleBinding。
+- 默认网关发送队列需要 PersistentVolume 供应器,连接外部 Doris 时也一样。临时试用可设置 `gateway.persistence.enabled: false`,但 Pod 替换会丢失内存中的队列数据。
+- 节点能够拉取镜像 `otel/opentelemetry-collector-contrib`、`otel/opentelemetry-collector-k8s`、`grafana/grafana`、`velodb/doris-app-plugin` 和 `busybox`。使用私有镜像仓库时,在 `ai-observe-stack` 中设置 `global.imagePullSecrets`,在 `dog-k8s-collector` 中设置 `imagePullSecrets`,并将镜像参数指向镜像源:`ai-observe-stack` 中的 `gateway.image.repository`、`grafana.image.repository`、`dorisPlugin.image.repository` 以及 `global.helperImages.busybox` / `global.helperImages.curl`;`dog-k8s-collector` 中的 `agent.image.repository` 和 `cluster.image [...]
+- Doris,以下二选一:
+ - 已有 Doris 集群:FE 的 9030(MySQL 协议)和 8030(HTTP)端口可以从 Kubernetes 集群访问,并准备一个能创建目标数据库和表、导入数据及查询 Grafana 所用表的账号。网关通过 Stream Load 写入数据:FE 会将每个请求重定向到 BE 在 Doris 中注册的地址(可通过 `SHOW BACKENDS` 查看),因此网关 Pod 还需要能够访问这些 BE 地址及 BE 的 HTTP 端口(默认 8040)。
+ - 由 Chart 部署 Doris:集群中有 PersistentVolume 供应器,且 FE 和 BE 各自至少有 2 核 CPU 和 4 GiB 内存。
+
+本文命令使用 Release 名称 `dog` 和命名空间 `dog`,下表中的对象名称由此生成。如果使用其他名称,请相应替换。
+
+| 对象 | 名称 |
+| --- | --- |
+| 网关 StatefulSet 与 Service | `dog-ai-observe-stack-otel-gateway` |
+| Grafana Deployment 与 Service | `dog-ai-observe-stack-grafana` |
+| Grafana 管理员 Secret | `dog-ai-observe-stack-grafana-admin` |
+| DorisCluster(由 Chart 部署 Doris 时) | `dog-ai-observe-stack-doris` |
+| 采集器 agent DaemonSet | `dog-k8s-collector-agent` |
+| 采集器 cluster Deployment | `dog-k8s-collector-cluster` |
+
+## 获取 Chart
+
+克隆仓库并下载 Chart 依赖。Doris Operator 子 Chart 声明在 Chart 的依赖中,因此即使连接已有 Doris,也需要执行这一步。请使用 `helm dependency update`,它不需要预先执行 `helm repo add`。
+
+```Bash
+git clone https://github.com/ai-observe/ai-observe-stack.git
+cd ai-observe-stack
+git checkout 2c1fbee831701178c7dddba857e88130ef292949
+cd helm-charts
+helm dependency update ./ai-observe-stack
+```
+
+后续命令均在 `helm-charts` 目录下执行。
+
+## 步骤 1:安装 DOG Stack 后端
+
+选择 Doris 的部署方式:
+
+| | 方式 A:使用已有 Doris | 方式 B:由 Chart 部署 Doris |
+| --- | --- | --- |
+| 适用场景 | 已有 Doris 或 SelectDB 集群;生产环境 | 没有可用 Doris 时的试用和开发 |
+| 数据位置 | 已有的 Doris 集群 | Kubernetes 集群内的 PVC |
+
+### 方式 A:使用已有 Doris
+
+1. 创建命名空间,并将 Doris 账号保存到 Secret 中。Secret 必须包含 `username` 和 `password` 两个键。
+
+ ```Bash
+ kubectl create namespace dog
+ kubectl create secret generic doris-credentials -n dog \
+ --from-literal=username='<DORIS_USER>' --from-literal=password='<DORIS_PASSWORD>'
+ ```
+
+2. 创建 values 文件 `dog-values.yaml`:
+
+ ```yaml
+ doris:
+ mode: external
+ database: otel # 网关会自动创建该数据库及其中的表
+ external:
+ host: <DORIS_FE_HOST> # 集群内可访问的 FE 地址
+ port: 9030 # MySQL 协议端口
+ feHttpPort: 8030 # HTTP 端口,用于 Stream Load
+ existingSecret: doris-credentials
+ internal:
+ operator:
+ enabled: false # 不安装 Doris Operator
+ ```
+
+ 仓库中的 `examples/ai-observe-stack/minimal-external-doris.yaml` 即为该文件。
+
+3. 安装:
+
+ ```Bash
+ helm install dog ./ai-observe-stack -n dog -f dog-values.yaml
+ ```
+
+### 方式 B:由 Chart 部署 Doris
+
+仓库提供两种规格:
+
+- `examples/ai-observe-stack/dev.yaml`:FE 和 BE 各一个,不开启持久化,一个网关,开启调试输出。仅用于试用。
+- `examples/ai-observe-stack/prod.yaml`:FE 和 BE 各三个,开启持久化,三个网关,并配置 Ingress。可作为生产配置的起点。使用前需要修改其中与环境相关的配置:`storageClass`(示例为 AWS 的 `gp3`)、Ingress 的 class、域名和 TLS 证书签发者,以及 Grafana 密码(`CHANGE_ME`)。
+
+```Bash
+cp examples/ai-observe-stack/dev.yaml dog-values.yaml
+helm install dog ./ai-observe-stack -n dog --create-namespace -f dog-values.yaml
+```
+
+后续升级继续使用 `dog-values.yaml`。部署生产环境时,改为复制 `prod.yaml`,完成上面列出的环境配置后再安装。
+
+Doris FE 和 BE 拉取镜像并完成初始化需要 2 到 5 分钟。网关会等待 FE 的 9030 和 8030 端口可访问、且至少有一个 BE 在线后再启动。
+
+:::caution
+Doris Operator 是集群级组件,一个 Kubernetes 集群中只能运行一个。在同一集群中安装第二个 DOG Stack 时,需要设置 `doris.internal.operator.enabled: false`。
+:::
+
+### 验证后端
+
+安装完成后,Helm 会输出 NOTES,其中包含网关的 OTLP gRPC 地址 `dog-ai-observe-stack-otel-gateway.dog.svc:4317`,请记录下来,步骤 2 中需要使用。
+
+1. 检查 Pod 是否运行。应能看到网关 Pod(默认两个副本,即 `dog-ai-observe-stack-otel-gateway-0` 和 `-1`;`dev.yaml` 为一个副本)、Grafana Pod,以及方式 B 下的 Doris FE 和 BE Pod,状态均为 `Running`。
+
+ ```Bash
+ kubectl get pods -n dog
+ ```
+
+2. 运行内置的链路测试。该测试通过网关发送一条日志,并检查其是否写入 Doris。
+
+ ```Bash
+ helm test dog -n dog --logs
+ ```
+
+ 输出以 `OK: record found in Doris` 结尾。
+
+3. 获取 Grafana 管理员密码(默认为 `admin`):
+
+ ```Bash
+ kubectl get secret -n dog dog-ai-observe-stack-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d; echo
+ ```
+
+4. 访问 Grafana:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-grafana 3000:3000
+ ```
+
+ 打开 <http://localhost:3000>,使用 `admin` 和上一步获取的密码登录。此时还没有数据写入网关,仪表盘为空。
+
+## 步骤 2:安装 Kubernetes 采集器 {#step-2-install-the-kubernetes-collector}
+
+步骤 1 安装的后端只负责接收数据。采集器从 Kubernetes 集群中采集数据并发送给网关:agent 在每个节点上读取所有容器的 stdout 和 stderr,并采集 kubelet 与节点指标;cluster 采集器采集集群级指标和 Kubernetes Events。每条记录都会附带 Pod、命名空间、工作负载和集群名称。如果不安装采集器,只有直接向网关发送 OTLP 数据的应用能够接入 DOG Stack,不会有容器日志、节点与集群指标以及 Events。每个需要观测的 Kubernetes 集群中都需要安装一个采集器 Release。
+
+在被采集集群中准备命名空间。即使跳过步骤 1,也要执行以下幂等命令:
+
+```Bash
+kubectl create namespace dog --dry-run=client -o yaml | kubectl apply -f -
+```
+
+1. 确认在待采集的集群中可以访问网关。请将地址替换为实际地址。
+
+ ```Bash
+ kubectl run -it --rm otlp-check --image=busybox:1.36 --restart=Never -- \
+ sh -c 'sleep 2; nc -zv dog-ai-observe-stack-otel-gateway.dog.svc 4317'
+ ```
+
+ 输出中出现 `open` 表示网关可访问。网关地址取决于 DOG Stack 的部署位置:
+
+ | DOG Stack 部署位置 | 网关地址(`host:port`,不带 `http://`) |
+ | --- | --- |
+ | 使用本 Chart 安装在同一集群 | `<release>-ai-observe-stack-otel-gateway.<namespace>.svc:4317` |
+ | 自建的带 Doris exporter 的 OpenTelemetry Collector | 其 OTLP gRPC Service,通常为 `<service>.<namespace>.svc:4317` |
+ | 其他 Kubernetes 集群 | 通过 LoadBalancer(`gateway.service.type: LoadBalancer`)或单独配置的 gRPC Ingress 暴露网关地址。本 Chart 的 Ingress 仅暴露 OTLP/HTTP,不暴露 OTLP/gRPC。集群之间需要开放所选端口 |
+
+2. agent 以 root 身份运行,并挂载节点的 `/var/log/pods`、`/`(只读)和 `/var/lib/otelcol`。如果命名空间启用了 `restricted` 级别的 Pod Security Standard,需要放宽:
+
+ ```Bash
+ kubectl label namespace dog pod-security.kubernetes.io/enforce=privileged --overwrite
+ ```
+
+3. 创建 values 文件 `collector-values.yaml`:
+
+ ```yaml
+ gateway:
+ endpoint: dog-ai-observe-stack-otel-gateway.dog.svc:4317
+ clusterName: my-cluster
+ platform: generic
+ ```
+
+ | 参数 | 是否必填 | 默认值 | 说明 |
+ | --- | --- | --- | --- |
+ | `gateway.endpoint` | 是 | 无 | 网关的 OTLP gRPC 地址,格式为 `host:port`。 |
+ | `clusterName` | 否 | Release 名称 | 以 `k8s.cluster.name` 写入每条记录,用于区分集群。 |
+ | `platform` | 否 | `generic` | 可选 `generic`、`k3s`、`eks`、`gke`、`aks`、`ack`、`openshift`。决定云资源探测器和 journald 单元名称。不确定时使用 `generic`。 |
+ | `timezone` | 否 | `UTC` | 日志时间戳不带时区时所采用的 IANA 时区。解析规则可以通过 `timestamp.timezone` 覆盖。 |
+
+4. 安装:
+
+ ```Bash
+ helm install dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+ ```
+
+采集器默认开启以下采集预设:
+
+| 预设 | 默认 | 采集内容 | 运行位置 |
+| --- | --- | --- | --- |
+| `logsCollection` | 开启 | 所有命名空间中所有容器的 stdout 和 stderr | agent |
+| `kubeletMetrics` | 开启 | 节点、Pod、容器和卷的 CPU、内存、网络和文件系统,以及相对 limit 的使用率 | agent |
+| `hostMetrics` | 开启 | 节点自身的 CPU、内存、负载、磁盘、文件系统和网络 | agent |
+| `otlp` | 开启 | 供应用 SDK 使用的节点本地 OTLP 入口 | agent |
+| `clusterMetrics` | 开启 | Deployment 可用性、节点状态、Pod 阶段、配额等 | cluster |
+| `kubernetesEvents` | 开启 | Kubernetes Events,以日志形式存储 | cluster |
+| `prometheusScrape` | 关闭 | 带有 `prometheus.io/scrape` 注解的 Pod | agent |
+| `journald` | 关闭 | 节点 journal 日志,需要包含 `journalctl` 的镜像 | agent |
+
+:::caution
+`examples/ai-observe-stack/dev.yaml` 开启了网关的调试输出,会将采样数据打印到网关日志中。当采集器与网关位于同一集群时,需要排除网关的日志文件,否则每条记录都会被重复采集:
+
+```yaml
+logs:
+ excludePaths:
+ - /var/log/pods/dog_dog-ai-observe-stack-otel-gateway-*/*/*.log
+```
+:::
+
+## 步骤 3:验证采集
+
+1. 检查采集器 Pod。每个节点一个 agent Pod,另有一个 cluster Pod:
+
+ ```Bash
+ kubectl get pods -n dog -l app.kubernetes.io/instance=dog-k8s-collector -o wide
+ ```
+
+ ```text
+ NAME READY STATUS NODE
+ dog-k8s-collector-agent-7x2kq 1/1 Running node-1
+ dog-k8s-collector-agent-p9m4c 1/1 Running node-2
+ dog-k8s-collector-cluster-6f5d9c8b7d-hq2xn 1/1 Running node-1
+ ```
+
+2. 检查 agent 没有报错。以下命令应当没有任何输出:
+
+ ```Bash
+ kubectl logs -n dog ds/dog-k8s-collector-agent | grep '"level":"error"'
+ ```
+
+3. 在 Doris 中查询数据。使用方式 A 时,用 MySQL 客户端连接已有 Doris FE 的 9030 端口;使用方式 B 时,先转发 FE 端口,再用 MySQL 客户端连接:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-doris-fe-service 9030:9030
+ mysql -h127.0.0.1 -P9030 -uroot
+ ```
+
+ ```sql
+ SELECT service_name, count(*) AS records
+ FROM otel.otel_logs
+ WHERE timestamp > now() - interval 5 minute
+ AND cast(resource_attributes['k8s.cluster.name'] as string) = 'my-cluster'
+ GROUP BY 1 ORDER BY 2 DESC;
+ ```
+
+ agent 启动后 30 秒内会写入第一批数据。`service_name` 为 Pod 所属工作负载的名称,按 Deployment、StatefulSet、DaemonSet、CronJob、Job 的顺序查找;独立运行的 Pod 则为 Pod 名称。Kubernetes Events 显示为 `kubernetes-events`。只采集安装之后写入的日志行,节点上已有的日志文件不会重新读取。
+
+4. 打开 Grafana,检查以下仪表盘:
+
+ - **Logs Explorer**:按命名空间、服务、日志级别和文本过滤日志。
+ - **Kubernetes Events**:Warning 数量、主要原因和事件列表。
+ - **K8s Observability**:节点和 Pod 的 CPU 与内存。
+ - **Collector Self-Monitoring**:agent、cluster 采集器和网关的队列长度、失败和拒绝情况。
+
+### 端到端示例
+
+以下 Pod 每秒输出一行 JSON 日志,可以验证从容器日志到 Doris 的完整链路:
+
+```Bash
+kubectl create namespace demo
+kubectl run json-app -n demo --image=busybox:1.36 --restart=Never -- sh -c \
+ 'i=0; while true; do i=$((i+1)); echo "{\"time\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"level\":\"info\",\"msg\":\"tick $i\"}"; sleep 1; done'
+```
+
+约 30 秒后,在 Doris 中查询:
+
+```sql
+SELECT timestamp, severity_text, body, cast(resource_attributes['k8s.pod.name'] as string) AS pod
+FROM otel.otel_logs
+WHERE cast(resource_attributes['k8s.namespace.name'] as string) = 'demo'
+ORDER BY timestamp DESC LIMIT 5;
+```
+
+如果返回 `severity_text` 为 `INFO`、`body` 类似 `tick 42` 的记录,说明容器日志已被采集、JSON 已被解析,并附加了 Kubernetes 元数据。验证完成后执行 `kubectl delete namespace demo` 清理。
+
+## 配置采集
+
+修改采集配置时,编辑 `collector-values.yaml` 后执行:
+
+```Bash
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+只有受影响的 agent 或 cluster 采集器会重启。`agent.storage.enabled=true` 时,同一节点和存储路径上的读取位置会保留,减少重复读取,但不保证恰好一次交付:重试可能产生重复记录,节点/存储丢失或队列耗尽也可能丢失数据。
+
+### 选择命名空间和容器
+
+```yaml
+logs:
+ namespaces:
+ include: ["*"] # 或列表,例如 [shop, payment]
+ exclude: [kube-system, monitoring]
+ containers:
+ exclude: [istio-proxy, linkerd-proxy] # 容器名称,在所有命名空间中排除
+```
+
+排除优先于包含。采集器自身的 Pod 不会被采集。
+
+### 解析日志
+
+- **JSON 日志**:以 `{` 开头的行会被自动解析。`level` 或 `severity` 写入 `severity_text`,`message` 或 `msg` 写入 `body`,其余字段写入 `log_attributes`。字段名不同时,可设置 `logs.json.severityFields` 和 `logs.json.messageFields`。
+- **常见文本格式**:在规则中使用内置预设。`selector.container` 匹配的是容器名称,而不是 Pod 或 Deployment 名称。
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-api
+ selector: {namespace: "^shop$", container: "^api$"}
+ preset: java-spring
+ ```
+
+ | 预设 | 适用格式 |
+ | --- | --- |
+ | `java-spring` | Spring Boot 3 默认控制台格式,自动合并异常堆栈 |
+ | `python-logging` | Python `logging.basicConfig` 默认格式 |
+ | `go-zap-console` | zap console encoder |
+ | `glog` | C++ 和 Go glog |
+ | `nginx-access`、`nginx-error` | nginx combined 访问日志和错误日志 |
+ | `mysql`、`postgres`、`redis` | 官方镜像的默认格式 |
+ | `doris-fe`、`doris-fe-audit`、`doris-be` | Apache Doris FE、FE 审计和 BE 日志 |
+ | `json-generic` | 单行 JSON |
+
+ Doris FE 的运行日志和审计日志输出在同一个容器中。如果需要同时解析,请使用相同的 selector 添加两条规则,分别设置 `preset: doris-fe` 和 `preset: doris-fe-audit`。
+
+- **自定义格式**:编写带命名分组的正则表达式,分组 `ts` 为时间戳,`level` 为日志级别,`msg` 为消息:
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-worker
+ selector: {namespace: "^shop$", container: "^worker$"}
+ regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<level>[a-z]+)\s+(?P<msg>(?s:.*))$'
+ timestamp: {layout: "%Y-%m-%dT%H:%M:%S.%L%z"}
+ severity: {field: level}
+ multiline: {firstLinePattern: '^\d{4}-\d{2}-\d{2}T'} # 堆栈行合并到上一条记录
+ drop: ['GET /healthz'] # 在节点上直接丢弃
+ ```
+
+ 如果日志时间戳不带时区,且容器不在 UTC 时区运行,需要在 `timestamp` 中增加 `timezone`,例如 `timestamp: {layout: "...", timezone: Asia/Shanghai}`。规则按顺序匹配,第一个匹配的规则生效。每条记录的 `dog.log.rule` 属性标明了解析它的规则。
+
+ 部署规则之前,可以使用仓库中的 `hack/test-rule.sh` 对样例日志进行测试,该脚本依赖 helm、yq 和 Docker:
+
+ ```Bash
+ kubectl logs -n shop -l app=worker -c worker --tail=30 > worker.log
+ hack/test-rule.sh -f collector-values.yaml -n shop -c worker worker.log
+ ```
+
+### 接收应用的 OTLP 数据
+
+接入 OpenTelemetry SDK 的应用可以将 Trace、指标和日志发送到所在节点的 agent,agent 会附加 Pod、命名空间和工作负载元数据。先在节点上开放 agent 端口:
+
+```yaml
+presets:
+ otlp:
+ hostPortHttp: 4318
+ hostPortGrpc: 4317
+```
+
+然后在应用的 Pod 模板中设置地址:
+
+```yaml
+env:
+ - name: HOST_IP
+ valueFrom: {fieldRef: {fieldPath: status.hostIP}}
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://$(HOST_IP):4318
+ - name: OTEL_EXPORTER_OTLP_PROTOCOL
+ value: http/protobuf
+ - name: OTEL_SERVICE_NAME
+ value: checkout
+```
+
+### 抓取 Prometheus 指标
+
+设置 `presets.prometheusScrape.enabled: true`,然后为应用 Pod 添加注解 `prometheus.io/scrape: "true"`、`prometheus.io/port` 和 `prometheus.io/path`。指标写入 `otel_metrics_*` 表,`service_name` 为工作负载名称。
+
+### 采集多个集群
+
+在每个集群中各安装一个采集器 Release,`gateway.endpoint` 指向同一个网关(跨集群时使用 LoadBalancer 或单独配置的 gRPC Ingress 地址),并为每个集群设置不同的 `clusterName`。查询和仪表盘通过 `k8s.cluster.name` 过滤。
+
+## 生产环境建议
+
+| 项目 | 参数 | 说明 |
+| --- | --- | --- |
+| Doris 副本和磁盘 | `doris.internal.cluster.fe.replicas`、`doris.internal.cluster.be.replicas`、`doris.internal.cluster.persistence` | FE 和 BE 各至少三个,并设置 `gateway.dorisExporter.replicationNum: 3`。 |
+| 网关副本和队列 | `gateway.replicas`、`gateway.persistence.size`、`gateway.dorisExporter.sendingQueue` | 每个网关副本的发送队列保存在各自的 PVC 上。写入出现延迟时,在 Collector Self-Monitoring 仪表盘中观察 `otelcol_exporter_queue_size`。 |
+| 数据保留 | `gateway.dorisExporter.historyDays` | 默认 7 天。 |
+| Grafana 密码 | `grafana.adminPassword` 或 `grafana.existingSecret` | 默认密码为 `admin`。 |
+| 外部访问 | `ingress`、`gateway.service.type` | 为 Grafana 配置 Ingress;集群外的应用通过 OTLP/HTTP Ingress 路径或 LoadBalancer 写入。 |
+| 调试输出 | `gateway.debug.enabled: false` | `dev.yaml` 中为开启状态。 |
+| 采集范围 | `logs.namespaces.exclude` | 通常排除 `kube-system`。 |
+| 采集器资源 | `agent.resources`、`agent.queueSize` | 每个 agent 默认申请 100m CPU 和 128 MiB 内存,上限为 1 CPU 和 512 MiB。网关不可达期间,每个节点最多在本地磁盘缓存 `agent.queueSize` 条记录(默认 20,000 条)。 |
+
+仓库中的 `examples/ai-observe-stack/prod.yaml` 可作为生产 values 文件的起点。
+
+默认 `gateway.timeGuard.enabled=true` 会将超出可写分区时间窗口的记录时间改为接收时间:日志在 `log.original_time_unix_nano`、Trace 在 `span.original_start_time_unix_nano` 保留原时间,指标时间直接重写。需要回填历史数据时,应同时调整 `gateway.dorisExporter.createHistoryDays` 和时间保护配置。网关向 Doris 发送失败的默认重试时限为 10 分钟(`gateway.dorisExporter.retryMaxElapsedTime`),超时批次会记录错误并丢弃。
+
+## 升级与卸载
+
+升级或修改配置:
+
+```Bash
+helm upgrade dog ./ai-observe-stack -n dog -f dog-values.yaml
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+卸载:
+
+```Bash
+helm uninstall dog-k8s-collector -n dog
+helm uninstall dog -n dog
+```
+
+卸载采集器不会影响 DOG Stack 以及 Doris 中的数据。节点上 `/var/lib/otelcol` 中的文件读取位置会保留,重新安装后从上次的位置继续采集。使用方式 A 时,卸载 DOG Stack 不会删除已有 Doris 中的数据库。
+
+Helm 不会删除网关的 PersistentVolumeClaim、Doris 的 PersistentVolumeClaim(方式 B 开启持久化时)以及已完成的 `helm test` Pod。如需删除:
+
+```Bash
+kubectl delete pvc -n dog -l app.kubernetes.io/instance=dog
+kubectl delete pvc -n dog -l 'app.doris.ownerreference/name in (dog-ai-observe-stack-doris-fe,dog-ai-observe-stack-doris-be)'
+kubectl delete pod -n dog dog-ai-observe-stack-test-pipeline --ignore-not-found
+```
+
+使用方式 B 时,Helm 也不会删除随 Doris Operator 安装的 CustomResourceDefinition `dorisclusters.doris.selectdb.com`,因为 Helm 从不删除 CRD。删除该 CRD 会同时删除 Kubernetes 集群中所有的 DorisCluster,因此仅在集群中没有其他由 Doris Operator 管理的 Doris 集群时才删除:
+
+```Bash
+kubectl delete crd dorisclusters.doris.selectdb.com
+```
+
+## 常见问题
+
+| 现象 | 原因与解决方法 |
+| --- | --- |
+| `helm install` 报错 `missing in charts/ directory: doris-operator` | 未下载 Chart 依赖。执行 `helm dependency update ./ai-observe-stack`。 |
+| `helm dependency build` 报错 `no repository definition for https://charts.selectdb.com` | `helm dependency build` 只使用通过 `helm repo add` 添加的仓库。改为执行 `helm dependency update ./ai-observe-stack`。 |
+| `helm install` 报错 `gateway.endpoint is required` | 将采集器的 `gateway.endpoint` 设置为网关地址。 |
+| `helm install` 报错 `additional properties 'xxx' not allowed` | values 文件中有拼写错误的顶层键。 |
+| `helm test` 失败,或网关日志中出现 Doris 相关错误 | 检查 Doris FE 的 9030 和 8030 端口是否可访问,以及账号是否能创建目标数据库/表、导入和查询数据。使用 `kubectl logs -n dog dog-ai-observe-stack-otel-gateway-0` 查看网关日志。 |
+| agent Pod 报 `CreateContainerConfigError`、被 Pod Security 拒绝,或日志中出现 `/var/log/pods` 的 `permission denied` | 命名空间启用了 `restricted`。添加 `privileged` 标签,参见步骤 2。 |
+| agent 日志出现 `connection refused` 或 `Unavailable` | 网关地址错误、网关未运行,或网络策略阻止了 4317 端口。问题修复前,记录会缓存在节点上,修复后自动发送。 |
+| agent Pod 一直处于 `ContainerCreating` 状态,事件显示 `MountVolume.SetUp failed for volume "zoneinfo"` | 节点上没有时区数据库(部分精简操作系统镜像)。设置 `agent.tzdata.hostPath: ""`,并将 `timezone` 以及所有规则的 `timestamp.timezone` 保持为 UTC。 |
+| 某个命名空间没有日志 | 被 `logs.namespaces.exclude` 或 `logs.containers.exclude` 排除,或安装后没有写入新的日志行。 |
+| 有日志但 `service_name` 为空 | agent 无法读取 Pod 元数据。检查 agent 日志中是否有 RBAC `forbidden` 错误。 |
+| 解析规则不生效(`dog.log.rule` 为空) | `selector.container` 填写的是 Pod 名称而不是容器名称,或更靠前的规则先匹配。使用 `hack/test-rule.sh` 验证。 |
+| 日志时间相差若干小时 | 日志行不带时区、容器不在 UTC 时区运行,且未设置 `timestamp.timezone`。 |
+
+查看 agent 实际运行的配置:
+
+```Bash
+kubectl get cm -n dog dog-k8s-collector-agent-config -o jsonpath='{.data.config\.yaml}'
+```
+
+两个 Chart 的完整参数说明,请参见仓库 [helm-charts](https://github.com/ai-observe/ai-observe-stack/tree/main/helm-charts) 目录下各 Chart 的 `USER_GUIDE_zh.md` 和 `values.yaml`。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x.json
index 74b3fcde0f7..0258d95aaf2 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x.json
@@ -755,6 +755,10 @@
"message": "外部表",
"description": "The label for category Lakehouse.External Table in sidebar docs"
},
+ "sidebar.docs.category.Deployment": {
+ "message": "部署",
+ "description": "The label for category Deployment in sidebar docs"
+ },
"sidebar.docs.category.Observability": {
"message": "可观测性",
"description": "Observability"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/deployment.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/deployment.md
deleted file mode 100644
index 10a32733139..00000000000
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/deployment.md
+++ /dev/null
@@ -1,172 +0,0 @@
----
-{
- "title": "部署",
- "sidebar_label": "部署",
- "language": "zh-CN",
- "description": "如何在生产环境部署 DOG Stack?本文介绍 OpenTelemetry Collector、Apache Doris 与集成 Doris App 插件的 Grafana 三大核心组件的部署方式与选型建议。",
- "keywords": [
- "DOG Stack 部署",
- "OpenTelemetry Collector",
- "Apache Doris 部署",
- "Grafana 部署",
- "Doris App 插件",
- "Docker Compose",
- "Kubernetes",
- "可观测性建设"
- ]
-}
----
-
-<!-- 知识类型: 操作步骤 -->
-<!-- 适用场景: 可观测性建设 / DOG Stack 生产部署 -->
-
-如架构图所示,DOG Stack 由四个组件组成:数据采集工具、OpenTelemetry Collector、Doris 和 集成 Doris应用的Grafana。本部署文档将重点介绍后三个组件,而数据采集文档将重点介绍第一个组件。
-
-DOG Stack 为不同的环境和目的提供多种部署选项。
-
-
-
-## Docker Compose
-
-最适合本地测试、开发和 PoC。
-
-### 依赖项
-
-- Docker Engine (v20.10+)
-- Docker Compose (v2.0+)
-
-
-
-### 部署
-
-1. 克隆仓库:
-
- `git clone https://github.com/ai-observe/ai-observe-stack.git cd ai-observe-stack/docker`
-
-
-
-2. 启动所有服务:
-
- `docker compose up -d`
-
-
-
-3. 验证服务是否正在运行:
-
- `docker compose ps`
-
- 所有服务应显示`running`状态。
-
-
-
-4. 在 http://localhost:3000 访问 Grafana,使用默认用户密码`admin` / `admin`登录。
-
-
-
-### 服务端点
-
-| Service | Endpoint | Credentials |
-| --------- | --------------------- | ------------- |
-| Grafana | http://localhost:3000 | admin / admin |
-| OTel gRPC | localhost:4317 | - |
-| OTel HTTP | localhost:4318 | - |
-
-### 停止和清理
-
-在保留数据的同时停止服务:
-
-```Bash
-docker compose down
-```
-
-停止服务并删除所有数据:
-
-```Bash
-docker compose down -v
-```
-
-
-
-## Kubernetes
-
-最适合生产部署、开发环境和可扩展的设置。
-
-### 依赖项
-
-- Kubernetes cluster (v1.20+)
-- Helm (v3.0+)
-- kubectl configured to access your cluster
-- PersistentVolume provisioner (for data persistence)
-
-### 部署
-
-1. 添加DOG Stack 和Doris Helm仓库:
-
- `helm repo add dogstack https://charts.velodb.io helm repo update`
-
-
-
-2. 为DOG Stack 创建一个命名空间 堆栈:
-
- `kubectl create namespace dogstack`
-
-
-
-3. 安装 DOG Stack:
-
- 如果您已有 Doris 集群,可以指定已有 Doris 集群的连接方式。
-
- `helm install my-dogstack dogstack/dogstack -n dogstack \ --set doris.mode=external \ --set doris.external.host=<DORIS_FE_HOST> \ --set doris.external.port=9030 \ --set doris.external.feHttpPort=8030 \ --set doris.internal.operator.enabled=false`
-
-
-
- 如果您想使用新的 Doris 集群进行部署,只需直接安装,无需额外选项。
-
- `helm install my-dogstack dogstack/dogstack -n dogstack`
-
-
-
-4. 验证所有 Pod 都在运行:
-
- `kubectl get pods -n dogstack`
-
- 等待所有 Pod 显示`Running`状态。
-
-
-
-5. 访问Grafana:
-
- `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack`
-
- 打开 http://localhost:3000 并使用`admin` / `admin`登录。
-
-
-
-### 服务端点
-
-| Service | Port-forward command |
-| ----------- | ------------------------------------------------------------ |
-| Grafana | `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack` |
-| Doris FE UI | `kubectl port-forward svc/my-dogstack-doris-fe 8030:8030 -n dogstack` |
-| Doris MySQL | `kubectl port-forward svc/my-dogstack-doris-fe 9030:9030 -n dogstack` |
-
-### 卸载
-
-```Bash
-helm uninstall my-dogstack -n dogstack
-kubectl delete namespace dogstack
-```
-
-
-
-## 手动部署
-
-1. 按照 Doris [部署文档](https://doris.apache.org/docs/4.x/install/preparation/env-checking)部署 Doris 集群。如果已有 Doris 集群,则可跳过此步骤。
-
-1. 按照 [OpenTelemetry部署文档](https://opentelemetry.io/docs/collector/install/) 部署 OpenTelemetry Collector。
-
-1. 按照[ Grafana部署文档](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) 部署 Grafana UI。
-
-1. 浏览器打开 DOG Stack Web UI
-
-访问 http://localhost:3000 以访问 DOG Stack 内的 Grafana UI。
\ No newline at end of file
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/docker.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/docker.md
new file mode 100644
index 00000000000..bb5fbc9fbdd
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/docker.md
@@ -0,0 +1,112 @@
+---
+{
+ "title": "Docker Compose 部署",
+ "sidebar_label": "Docker Compose 部署",
+ "language": "zh-CN",
+ "description": "如何使用 Docker Compose 在单机上部署 DOG Stack?本文介绍如何启动 OpenTelemetry Collector、Apache Doris 与集成 Doris App 插件的 Grafana,支持内置 Doris 和接入已有 Doris 集群两种方式,并介绍验证、访问与清理方法。",
+ "keywords": [
+ "DOG Stack 部署",
+ "Docker Compose",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Doris App 插件",
+ "可观测性建设"
+ ]
+}
+---
+
+<!-- 知识类型: 操作步骤 -->
+<!-- 适用场景: 可观测性建设 / DOG Stack 单机部署 -->
+
+本文介绍如何使用 Docker Compose 在单机上部署 DOG Stack。部署会启动 OpenTelemetry Collector、集成 Doris App 插件的 Grafana,以及可选的一体化 Doris,适用于本地测试、开发和 PoC。生产环境或 Kubernetes 环境请参见 [Kubernetes 部署](./kubernetes.md)。
+
+:::tip
+如果不使用 Docker 部署各组件,请参见 [Doris 部署文档](https://doris.apache.org/zh-CN/docs/4.x/install/preparation/env-checking)、[OpenTelemetry Collector 安装文档](https://opentelemetry.io/docs/collector/install/) 和 [Grafana 安装文档](https://grafana.com/docs/grafana/latest/setup-grafana/installation/)。
+:::
+
+## 前提条件
+
+- Docker Engine 20.10 及以上版本
+- Docker Compose 2.0 及以上版本
+
+## 部署
+
+1. 克隆仓库:
+
+ ```Bash
+ git clone https://github.com/ai-observe/ai-observe-stack.git
+ cd ai-observe-stack/docker
+ ```
+
+2. 启动服务,以下两种方式二选一。
+
+ - **内置 Doris**:同时启动一体化 Doris、OpenTelemetry Collector 和 Grafana。
+
+ ```Bash
+ docker compose up -d
+ ```
+
+ - **接入已有 Doris**:只启动 OpenTelemetry Collector 和 Grafana,数据写入已有的 Doris 集群。先创建配置文件:
+
+ ```Bash
+ cp .env.example .env
+ ```
+
+ 在 `.env` 中修改 Doris 连接配置:
+
+ ```Bash
+ DORIS_FE_HTTP_ENDPOINT=http://<DORIS_FE_HOST>:8030
+ DORIS_FE_MYSQL_ENDPOINT=<DORIS_FE_HOST>:9030
+ DORIS_USERNAME=root
+ DORIS_PASSWORD=
+ ```
+
+ 然后启动服务:
+
+ ```Bash
+ docker compose -f docker-compose-without-doris.yaml up -d
+ ```
+
+ 该账号需要具有 `CREATE DATABASE` 权限。OpenTelemetry Collector 会自动创建 `otel` 数据库及其中的表。
+
+ OpenTelemetry Collector 通过 Stream Load 写入数据:FE 会将每个请求重定向到 BE 在 Doris 中注册的地址(可通过 `SHOW BACKENDS` 查看)。因此,除了 FE 之外,OpenTelemetry Collector 容器还需要能够访问这些 BE 地址及 BE 的 HTTP 端口(默认 8040)。
+
+3. 验证服务是否正在运行:
+
+ ```Bash
+ docker compose ps
+ ```
+
+ 接入已有 Doris 时,需要加上 `-f docker-compose-without-doris.yaml`。所有服务的 `STATUS` 列应显示 `Up`。使用内置 Doris 时,OpenTelemetry Collector 会在 Doris 通过健康检查后才启动。
+
+4. 打开 <http://localhost:3000>,使用 `admin` / `admin` 登录 Grafana。
+
+## 服务端点
+
+| 服务 | 地址 | 账号 |
+| --- | --- | --- |
+| Grafana | http://localhost:3000 | admin / admin |
+| OTLP gRPC | localhost:4317 | - |
+| OTLP HTTP | localhost:4318 | - |
+| OpenTelemetry Collector 健康检查 | http://localhost:13133 | - |
+| Doris FE Web UI(仅内置 Doris) | http://localhost:8030 | root / 空密码 |
+| Doris MySQL 协议(仅内置 Doris) | localhost:9030 | root / 空密码 |
+
+接入 OpenTelemetry SDK 的应用将数据发送到 `localhost:4317`(gRPC)或 `localhost:4318`(HTTP)。
+
+## 停止和清理
+
+停止服务并保留数据:
+
+```Bash
+docker compose down
+```
+
+停止服务并删除所有数据:
+
+```Bash
+docker compose down -v
+```
+
+接入已有 Doris 时,以上两条命令都需要加上 `-f docker-compose-without-doris.yaml`。已有 Doris 集群中的数据不会被删除。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/kubernetes.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/kubernetes.md
new file mode 100644
index 00000000000..897ac49c32c
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-3.x/observability/dogstack/kubernetes.md
@@ -0,0 +1,485 @@
+---
+{
+ "title": "Kubernetes 部署",
+ "sidebar_label": "Kubernetes 部署",
+ "language": "zh-CN",
+ "description": "如何在 Kubernetes 上部署 DOG Stack 并采集集群的容器日志、指标和 Events?本文使用 Helm 安装 DOG Stack 后端(OpenTelemetry 网关、Apache Doris、Grafana)和 Kubernetes 采集器,并介绍验证方法、日志解析规则、生产配置与问题排查。",
+ "keywords": [
+ "DOG Stack",
+ "Kubernetes",
+ "Helm",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Kubernetes 日志采集",
+ "可观测性建设"
+ ]
+}
+---
+
+<!-- 知识类型: 操作步骤 -->
+<!-- 适用场景: 可观测性建设 / DOG Stack Kubernetes 部署与集群采集 -->
+
+本文介绍如何使用 Helm 在 Kubernetes 上部署 DOG Stack,并将集群的容器日志、kubelet / 节点 / 集群指标以及 Kubernetes Events 采集进来。完成后,可以在 Doris 中查询这些数据,并在 Grafana 仪表盘中查看。如果需要在单机上部署,请参见 [Docker Compose 部署](./docker.md)。
+
+Kubernetes 上的 DOG Stack 由两个相互独立的 Helm Chart 组成:
+
+| Chart | 作用 | 工作负载 |
+| --- | --- | --- |
+| `ai-observe-stack` | DOG Stack 后端,只需安装一次。接收 OTLP 数据、写入 Doris,并通过 Grafana 展示。它本身不采集任何数据。 | OpenTelemetry 网关(StatefulSet)、集成 Doris App 插件的 Grafana(Deployment),以及可选的由 Doris Operator 管理的 Doris 集群 |
+| `dog-k8s-collector` | Kubernetes 采集器。在每个需要采集的集群中安装一次,指向网关。 | agent(DaemonSet,每个节点一个):容器日志、kubelet 与节点指标、节点本地 OTLP 入口;cluster(Deployment,每个集群一个):集群指标与 Kubernetes Events |
+
+数据流如下:
+
+```text
+applications (OTLP SDK) ───────┐
+ │ OTLP
+dog-k8s-collector ─────────────┤
+ agent (DaemonSet) │
+ cluster (Deployment) ▼
+ai-observe-stack
+ gateway OpenTelemetry Collector receives OTLP, writes Doris
+ doris Apache Doris stores logs, metrics, traces
+ grafana Grafana + Doris App shows them
+```
+
+两个 Chart 之间没有 Helm 依赖关系,分别安装、分别升级。多个 Kubernetes 集群可以写入同一个 DOG Stack,通过集群名称区分。
+
+:::tip
+如果已经有运行中的 DOG Stack(使用本 Chart 安装、以其他方式部署或位于其他集群),可以直接跳到[步骤 2:安装 Kubernetes 采集器](#step-2-install-the-kubernetes-collector),只需要知道其网关的 OTLP gRPC 地址。
+:::
+
+## 前提条件
+
+- Kubernetes 1.24 及以上版本、Helm 3.8 及以上版本,并已配置 `kubectl` 访问集群。
+- 集群级 RBAC 权限:采集器会创建 ClusterRole 和 ClusterRoleBinding。
+- 节点能够拉取镜像 `otel/opentelemetry-collector-contrib`、`otel/opentelemetry-collector-k8s`、`grafana/grafana`、`velodb/doris-app-plugin` 和 `busybox`。使用私有镜像仓库时,在 `ai-observe-stack` 中设置 `global.imagePullSecrets`,在 `dog-k8s-collector` 中设置 `imagePullSecrets`,并将镜像参数指向镜像源:`ai-observe-stack` 中的 `gateway.image.repository`、`grafana.image.repository`、`dorisPlugin.image.repository` 以及 `global.helperImages.busybox` / `global.helperImages.curl`;`dog-k8s-collector` 中的 `agent.image.repository` 和 `cluster.image [...]
+- Doris,以下二选一:
+ - 已有 Doris 集群:FE 的 9030(MySQL 协议)和 8030(HTTP)端口可以从 Kubernetes 集群访问,并准备一个具有 `CREATE DATABASE` 权限的账号。网关通过 Stream Load 写入数据:FE 会将每个请求重定向到 BE 在 Doris 中注册的地址(可通过 `SHOW BACKENDS` 查看),因此网关 Pod 还需要能够访问这些 BE 地址及 BE 的 HTTP 端口(默认 8040)。
+ - 由 Chart 部署 Doris:集群中有 PersistentVolume 供应器,且 FE 和 BE 各自至少有 2 核 CPU 和 4 GiB 内存。
+
+本文命令使用 Release 名称 `dog` 和命名空间 `dog`,下表中的对象名称由此生成。如果使用其他名称,请相应替换。
+
+| 对象 | 名称 |
+| --- | --- |
+| 网关 StatefulSet 与 Service | `dog-ai-observe-stack-otel-gateway` |
+| Grafana Deployment 与 Service | `dog-ai-observe-stack-grafana` |
+| Grafana 管理员 Secret | `dog-ai-observe-stack-grafana-admin` |
+| DorisCluster(由 Chart 部署 Doris 时) | `dog-ai-observe-stack-doris` |
+| 采集器 agent DaemonSet | `dog-k8s-collector-agent` |
+| 采集器 cluster Deployment | `dog-k8s-collector-cluster` |
+
+## 获取 Chart
+
+克隆仓库并下载 Chart 依赖。Doris Operator 子 Chart 声明在 Chart 的依赖中,因此即使连接已有 Doris,也需要执行这一步。请使用 `helm dependency update`,它不需要预先执行 `helm repo add`。
+
+```Bash
+git clone https://github.com/ai-observe/ai-observe-stack.git
+cd ai-observe-stack/helm-charts
+helm dependency update ./ai-observe-stack
+```
+
+后续命令均在 `helm-charts` 目录下执行。
+
+## 步骤 1:安装 DOG Stack 后端
+
+选择 Doris 的部署方式:
+
+| | 方式 A:使用已有 Doris | 方式 B:由 Chart 部署 Doris |
+| --- | --- | --- |
+| 适用场景 | 已有 Doris 或 SelectDB 集群;生产环境 | 没有可用 Doris 时的试用和开发 |
+| 数据位置 | 已有的 Doris 集群 | Kubernetes 集群内的 PVC |
+
+### 方式 A:使用已有 Doris
+
+1. 创建命名空间,并将 Doris 账号保存到 Secret 中。Secret 必须包含 `username` 和 `password` 两个键。
+
+ ```Bash
+ kubectl create namespace dog
+ kubectl create secret generic doris-credentials -n dog \
+ --from-literal=username=<DORIS_USER> --from-literal=password='<DORIS_PASSWORD>'
+ ```
+
+2. 创建 values 文件 `dog-values.yaml`:
+
+ ```yaml
+ doris:
+ mode: external
+ database: otel # 网关会自动创建该数据库及其中的表
+ external:
+ host: <DORIS_FE_HOST> # 集群内可访问的 FE 地址
+ port: 9030 # MySQL 协议端口
+ feHttpPort: 8030 # HTTP 端口,用于 Stream Load
+ existingSecret: doris-credentials
+ internal:
+ operator:
+ enabled: false # 不安装 Doris Operator
+ ```
+
+ 仓库中的 `examples/ai-observe-stack/minimal-external-doris.yaml` 即为该文件。
+
+3. 安装:
+
+ ```Bash
+ helm install dog ./ai-observe-stack -n dog -f dog-values.yaml
+ ```
+
+### 方式 B:由 Chart 部署 Doris
+
+仓库提供两种规格:
+
+- `examples/ai-observe-stack/dev.yaml`:FE 和 BE 各一个,不开启持久化,一个网关,开启调试输出。仅用于试用。
+- `examples/ai-observe-stack/prod.yaml`:FE 和 BE 各三个,开启持久化,三个网关,并配置 Ingress。可作为生产配置的起点。使用前需要修改其中与环境相关的配置:`storageClass`(示例为 AWS 的 `gp3`)、Ingress 的 class、域名和 TLS 证书签发者,以及 Grafana 密码(`CHANGE_ME`)。
+
+```Bash
+helm install dog ./ai-observe-stack -n dog --create-namespace -f examples/ai-observe-stack/dev.yaml
+```
+
+Doris FE 和 BE 拉取镜像并完成初始化需要 2 到 5 分钟。网关会等待 FE 的 9030 和 8030 端口可访问、且至少有一个 BE 在线后再启动。
+
+:::caution
+Doris Operator 是集群级组件,一个 Kubernetes 集群中只能运行一个。在同一集群中安装第二个 DOG Stack 时,需要设置 `doris.internal.operator.enabled: false`。
+:::
+
+### 验证后端
+
+安装完成后,Helm 会输出 NOTES,其中包含网关的 OTLP gRPC 地址 `dog-ai-observe-stack-otel-gateway.dog.svc:4317`,请记录下来,步骤 2 中需要使用。
+
+1. 检查 Pod 是否运行。应能看到网关 Pod(默认两个副本,即 `dog-ai-observe-stack-otel-gateway-0` 和 `-1`;`dev.yaml` 为一个副本)、Grafana Pod,以及方式 B 下的 Doris FE 和 BE Pod,状态均为 `Running`。
+
+ ```Bash
+ kubectl get pods -n dog
+ ```
+
+2. 运行内置的链路测试。该测试通过网关发送一条日志,并检查其是否写入 Doris。
+
+ ```Bash
+ helm test dog -n dog --logs
+ ```
+
+ 输出以 `OK: record found in Doris` 结尾。
+
+3. 获取 Grafana 管理员密码(默认为 `admin`):
+
+ ```Bash
+ kubectl get secret -n dog dog-ai-observe-stack-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d; echo
+ ```
+
+4. 访问 Grafana:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-grafana 3000:3000
+ ```
+
+ 打开 <http://localhost:3000>,使用 `admin` 和上一步获取的密码登录。此时还没有数据写入网关,仪表盘为空。
+
+## 步骤 2:安装 Kubernetes 采集器 {#step-2-install-the-kubernetes-collector}
+
+步骤 1 安装的后端只负责接收数据。采集器从 Kubernetes 集群中采集数据并发送给网关:agent 在每个节点上读取所有容器的 stdout 和 stderr,并采集 kubelet 与节点指标;cluster 采集器采集集群级指标和 Kubernetes Events。每条记录都会附带 Pod、命名空间、工作负载和集群名称。如果不安装采集器,只有直接向网关发送 OTLP 数据的应用能够接入 DOG Stack,不会有容器日志、节点与集群指标以及 Events。每个需要观测的 Kubernetes 集群中都需要安装一个采集器 Release。
+
+1. 确认在待采集的集群中可以访问网关。请将地址替换为实际地址。
+
+ ```Bash
+ kubectl run -it --rm otlp-check --image=busybox:1.36 --restart=Never -- \
+ sh -c 'sleep 2; nc -zv dog-ai-observe-stack-otel-gateway.dog.svc 4317'
+ ```
+
+ 输出中出现 `open` 表示网关可访问。网关地址取决于 DOG Stack 的部署位置:
+
+ | DOG Stack 部署位置 | 网关地址(`host:port`,不带 `http://`) |
+ | --- | --- |
+ | 使用本 Chart 安装在同一集群 | `<release>-ai-observe-stack-otel-gateway.<namespace>.svc:4317` |
+ | 自建的带 Doris exporter 的 OpenTelemetry Collector | 其 OTLP gRPC Service,通常为 `<service>.<namespace>.svc:4317` |
+ | 其他 Kubernetes 集群 | 网关的外部地址:LoadBalancer IP(`gateway.service.type: LoadBalancer`)或 Ingress 域名。集群之间需要开放 4317 端口 |
+
+2. agent 以 root 身份运行,并挂载节点的 `/var/log/pods`、`/`(只读)和 `/var/lib/otelcol`。如果命名空间启用了 `restricted` 级别的 Pod Security Standard,需要放宽:
+
+ ```Bash
+ kubectl label namespace dog pod-security.kubernetes.io/enforce=privileged --overwrite
+ ```
+
+3. 创建 values 文件 `collector-values.yaml`:
+
+ ```yaml
+ gateway:
+ endpoint: dog-ai-observe-stack-otel-gateway.dog.svc:4317
+ clusterName: my-cluster
+ platform: generic
+ ```
+
+ | 参数 | 是否必填 | 默认值 | 说明 |
+ | --- | --- | --- | --- |
+ | `gateway.endpoint` | 是 | 无 | 网关的 OTLP gRPC 地址,格式为 `host:port`。 |
+ | `clusterName` | 否 | Release 名称 | 以 `k8s.cluster.name` 写入每条记录,用于区分集群。 |
+ | `platform` | 否 | `generic` | 可选 `generic`、`k3s`、`eks`、`gke`、`aks`、`ack`、`openshift`。决定云资源探测器和 journald 单元名称。不确定时使用 `generic`。 |
+ | `timezone` | 否 | `UTC` | 日志时间戳不带时区时所采用的 IANA 时区。解析规则可以通过 `timestamp.timezone` 覆盖。 |
+
+4. 安装:
+
+ ```Bash
+ helm install dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+ ```
+
+采集器默认开启以下采集预设:
+
+| 预设 | 默认 | 采集内容 | 运行位置 |
+| --- | --- | --- | --- |
+| `logsCollection` | 开启 | 所有命名空间中所有容器的 stdout 和 stderr | agent |
+| `kubeletMetrics` | 开启 | 节点、Pod、容器和卷的 CPU、内存、网络和文件系统,以及相对 limit 的使用率 | agent |
+| `hostMetrics` | 开启 | 节点自身的 CPU、内存、负载、磁盘、文件系统和网络 | agent |
+| `otlp` | 开启 | 供应用 SDK 使用的节点本地 OTLP 入口 | agent |
+| `clusterMetrics` | 开启 | Deployment 可用性、节点状态、Pod 阶段、配额等 | cluster |
+| `kubernetesEvents` | 开启 | Kubernetes Events,以日志形式存储 | cluster |
+| `prometheusScrape` | 关闭 | 带有 `prometheus.io/scrape` 注解的 Pod | agent |
+| `journald` | 关闭 | 节点 journal 日志,需要包含 `journalctl` 的镜像 | agent |
+
+:::caution
+`examples/ai-observe-stack/dev.yaml` 开启了网关的调试输出,会将采样数据打印到网关日志中。当采集器与网关位于同一集群时,需要排除网关的日志文件,否则每条记录都会被重复采集:
+
+```yaml
+logs:
+ excludePaths:
+ - /var/log/pods/dog_dog-ai-observe-stack-otel-gateway-*/*/*.log
+```
+:::
+
+## 步骤 3:验证采集
+
+1. 检查采集器 Pod。每个节点一个 agent Pod,另有一个 cluster Pod:
+
+ ```Bash
+ kubectl get pods -n dog -l app.kubernetes.io/instance=dog-k8s-collector -o wide
+ ```
+
+ ```text
+ NAME READY STATUS NODE
+ dog-k8s-collector-agent-7x2kq 1/1 Running node-1
+ dog-k8s-collector-agent-p9m4c 1/1 Running node-2
+ dog-k8s-collector-cluster-6f5d9c8b7d-hq2xn 1/1 Running node-1
+ ```
+
+2. 检查 agent 没有报错。以下命令应当没有任何输出:
+
+ ```Bash
+ kubectl logs -n dog ds/dog-k8s-collector-agent | grep '"level":"error"'
+ ```
+
+3. 在 Doris 中查询数据。使用方式 A 时,用 MySQL 客户端连接已有 Doris FE 的 9030 端口;使用方式 B 时,先转发 FE 端口,再用 MySQL 客户端连接:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-doris-fe-service 9030:9030
+ mysql -h127.0.0.1 -P9030 -uroot
+ ```
+
+ ```sql
+ SELECT service_name, count(*) AS records
+ FROM otel.otel_logs
+ WHERE timestamp > now() - interval 5 minute
+ AND cast(resource_attributes['k8s.cluster.name'] as string) = 'my-cluster'
+ GROUP BY 1 ORDER BY 2 DESC;
+ ```
+
+ agent 启动后 30 秒内会写入第一批数据。`service_name` 为 Pod 所属工作负载的名称,按 Deployment、StatefulSet、DaemonSet、CronJob、Job 的顺序查找;独立运行的 Pod 则为 Pod 名称。Kubernetes Events 显示为 `kubernetes-events`。只采集安装之后写入的日志行,节点上已有的日志文件不会重新读取。
+
+4. 打开 Grafana,检查以下仪表盘:
+
+ - **Logs Explorer**:按命名空间、服务、日志级别和文本过滤日志。
+ - **Kubernetes Events**:Warning 数量、主要原因和事件列表。
+ - **K8s Observability**:节点和 Pod 的 CPU 与内存。
+ - **Collector Self-Monitoring**:agent、cluster 采集器和网关的队列长度、失败和拒绝情况。
+
+### 端到端示例
+
+以下 Pod 每秒输出一行 JSON 日志,可以验证从容器日志到 Doris 的完整链路:
+
+```Bash
+kubectl create namespace demo
+kubectl run json-app -n demo --image=busybox:1.36 --restart=Never -- sh -c \
+ 'i=0; while true; do i=$((i+1)); echo "{\"time\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"level\":\"info\",\"msg\":\"tick $i\"}"; sleep 1; done'
+```
+
+约 30 秒后,在 Doris 中查询:
+
+```sql
+SELECT timestamp, severity_text, body, cast(resource_attributes['k8s.pod.name'] as string) AS pod
+FROM otel.otel_logs
+WHERE cast(resource_attributes['k8s.namespace.name'] as string) = 'demo'
+ORDER BY timestamp DESC LIMIT 5;
+```
+
+如果返回 `severity_text` 为 `INFO`、`body` 类似 `tick 42` 的记录,说明容器日志已被采集、JSON 已被解析,并附加了 Kubernetes 元数据。验证完成后执行 `kubectl delete namespace demo` 清理。
+
+## 配置采集
+
+修改采集配置时,编辑 `collector-values.yaml` 后执行:
+
+```Bash
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+只有受影响的 agent 或 cluster 采集器会重启。节点上的文件读取位置会保留,不会重复采集日志。
+
+### 选择命名空间和容器
+
+```yaml
+logs:
+ namespaces:
+ include: ["*"] # 或列表,例如 [shop, payment]
+ exclude: [kube-system, monitoring]
+ containers:
+ exclude: [istio-proxy, linkerd-proxy] # 容器名称,在所有命名空间中排除
+```
+
+排除优先于包含。采集器自身的 Pod 不会被采集。
+
+### 解析日志
+
+- **JSON 日志**:以 `{` 开头的行会被自动解析。`level` 或 `severity` 写入 `severity_text`,`message` 或 `msg` 写入 `body`,其余字段写入 `log_attributes`。字段名不同时,可设置 `logs.json.severityFields` 和 `logs.json.messageFields`。
+- **常见文本格式**:在规则中使用内置预设。`selector.container` 匹配的是容器名称,而不是 Pod 或 Deployment 名称。
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-api
+ selector: {namespace: "^shop$", container: "^api$"}
+ preset: java-spring
+ ```
+
+ | 预设 | 适用格式 |
+ | --- | --- |
+ | `java-spring` | Spring Boot 3 默认控制台格式,自动合并异常堆栈 |
+ | `python-logging` | Python `logging.basicConfig` 默认格式 |
+ | `go-zap-console` | zap console encoder |
+ | `glog` | C++ 和 Go glog |
+ | `nginx-access`、`nginx-error` | nginx combined 访问日志和错误日志 |
+ | `mysql`、`postgres`、`redis` | 官方镜像的默认格式 |
+ | `doris-fe`、`doris-fe-audit`、`doris-be` | Apache Doris FE、FE 审计和 BE 日志 |
+ | `json-generic` | 单行 JSON |
+
+ Doris FE 的运行日志和审计日志输出在同一个容器中。如果需要同时解析,请使用相同的 selector 添加两条规则,分别设置 `preset: doris-fe` 和 `preset: doris-fe-audit`。
+
+- **自定义格式**:编写带命名分组的正则表达式,分组 `ts` 为时间戳,`level` 为日志级别,`msg` 为消息:
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-worker
+ selector: {namespace: "^shop$", container: "^worker$"}
+ regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<level>[a-z]+)\s+(?P<msg>(?s:.*))$'
+ timestamp: {layout: "%Y-%m-%dT%H:%M:%S.%L%z"}
+ severity: {field: level}
+ multiline: {firstLinePattern: '^\d{4}-\d{2}-\d{2}T'} # 堆栈行合并到上一条记录
+ drop: ['GET /healthz'] # 在节点上直接丢弃
+ ```
+
+ 如果日志时间戳不带时区,且容器不在 UTC 时区运行,需要在 `timestamp` 中增加 `timezone`,例如 `timestamp: {layout: "...", timezone: Asia/Shanghai}`。规则按顺序匹配,第一个匹配的规则生效。每条记录的 `dog.log.rule` 属性标明了解析它的规则。
+
+ 部署规则之前,可以使用仓库中的 `hack/test-rule.sh` 对样例日志进行测试,该脚本依赖 helm、yq 和 Docker:
+
+ ```Bash
+ kubectl logs -n shop -l app=worker -c worker --tail=30 > worker.log
+ hack/test-rule.sh -f collector-values.yaml -n shop -c worker worker.log
+ ```
+
+### 接收应用的 OTLP 数据
+
+接入 OpenTelemetry SDK 的应用可以将 Trace、指标和日志发送到所在节点的 agent,agent 会附加 Pod、命名空间和工作负载元数据。先在节点上开放 agent 端口:
+
+```yaml
+presets:
+ otlp:
+ hostPortHttp: 4318
+ hostPortGrpc: 4317
+```
+
+然后在应用的 Pod 模板中设置地址:
+
+```yaml
+env:
+ - name: HOST_IP
+ valueFrom: {fieldRef: {fieldPath: status.hostIP}}
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://$(HOST_IP):4318
+ - name: OTEL_SERVICE_NAME
+ value: checkout
+```
+
+### 抓取 Prometheus 指标
+
+设置 `presets.prometheusScrape.enabled: true`,然后为应用 Pod 添加注解 `prometheus.io/scrape: "true"`、`prometheus.io/port` 和 `prometheus.io/path`。指标写入 `otel_metrics_*` 表,`service_name` 为工作负载名称。
+
+### 采集多个集群
+
+在每个集群中各安装一个采集器 Release,`gateway.endpoint` 指向同一个网关(跨集群时使用其 LoadBalancer 或 Ingress 地址),并为每个集群设置不同的 `clusterName`。查询和仪表盘通过 `k8s.cluster.name` 过滤。
+
+## 生产环境建议
+
+| 项目 | 参数 | 说明 |
+| --- | --- | --- |
+| Doris 副本和磁盘 | `doris.internal.cluster.fe.replicas`、`doris.internal.cluster.be.replicas`、`doris.internal.cluster.persistence` | FE 和 BE 各至少三个,并设置 `gateway.dorisExporter.replicationNum: 3`。 |
+| 网关副本和队列 | `gateway.replicas`、`gateway.persistence.size`、`gateway.dorisExporter.sendingQueue` | 每个网关副本的发送队列保存在各自的 PVC 上。写入出现延迟时,在 Collector Self-Monitoring 仪表盘中观察 `otelcol_exporter_queue_size`。 |
+| 数据保留 | `gateway.dorisExporter.historyDays` | 默认 7 天。 |
+| Grafana 密码 | `grafana.adminPassword` 或 `grafana.existingSecret` | 默认密码为 `admin`。 |
+| 外部访问 | `ingress`、`gateway.service.type` | 为 Grafana 配置 Ingress;集群外的应用通过 OTLP/HTTP Ingress 路径或 LoadBalancer 写入。 |
+| 调试输出 | `gateway.debug.enabled: false` | `dev.yaml` 中为开启状态。 |
+| 采集范围 | `logs.namespaces.exclude` | 通常排除 `kube-system`。 |
+| 采集器资源 | `agent.resources`、`agent.queueSize` | 每个 agent 默认申请 100m CPU 和 128 MiB 内存,上限为 1 CPU 和 512 MiB。网关不可达期间,每个节点最多在本地磁盘缓存 `agent.queueSize` 条记录(默认 20,000 条)。 |
+
+仓库中的 `examples/ai-observe-stack/prod.yaml` 可作为生产 values 文件的起点。
+
+## 升级与卸载
+
+升级或修改配置:
+
+```Bash
+helm upgrade dog ./ai-observe-stack -n dog -f dog-values.yaml
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+卸载:
+
+```Bash
+helm uninstall dog-k8s-collector -n dog
+helm uninstall dog -n dog
+```
+
+卸载采集器不会影响 DOG Stack 以及 Doris 中的数据。节点上 `/var/lib/otelcol` 中的文件读取位置会保留,重新安装后从上次的位置继续采集。使用方式 A 时,卸载 DOG Stack 不会删除已有 Doris 中的数据库。
+
+Helm 不会删除网关的 PersistentVolumeClaim、Doris 的 PersistentVolumeClaim(方式 B 开启持久化时)以及已完成的 `helm test` Pod。如需删除:
+
+```Bash
+kubectl delete pvc -n dog -l app.kubernetes.io/instance=dog
+kubectl delete pvc -n dog -l 'app.doris.ownerreference/name in (dog-ai-observe-stack-doris-fe,dog-ai-observe-stack-doris-be)'
+kubectl delete pod -n dog dog-ai-observe-stack-test-pipeline --ignore-not-found
+```
+
+使用方式 B 时,Helm 也不会删除随 Doris Operator 安装的 CustomResourceDefinition `dorisclusters.doris.selectdb.com`,因为 Helm 从不删除 CRD。删除该 CRD 会同时删除 Kubernetes 集群中所有的 DorisCluster,因此仅在集群中没有其他由 Doris Operator 管理的 Doris 集群时才删除:
+
+```Bash
+kubectl delete crd dorisclusters.doris.selectdb.com
+```
+
+## 常见问题
+
+| 现象 | 原因与解决方法 |
+| --- | --- |
+| `helm install` 报错 `missing in charts/ directory: doris-operator` | 未下载 Chart 依赖。执行 `helm dependency update ./ai-observe-stack`。 |
+| `helm dependency build` 报错 `no repository definition for https://charts.selectdb.com` | `helm dependency build` 只使用通过 `helm repo add` 添加的仓库。改为执行 `helm dependency update ./ai-observe-stack`。 |
+| `helm install` 报错 `gateway.endpoint is required` | 将采集器的 `gateway.endpoint` 设置为网关地址。 |
+| `helm install` 报错 `additional properties 'xxx' not allowed` | values 文件中有拼写错误的顶层键。 |
+| `helm test` 失败,或网关日志中出现 Doris 相关错误 | 检查 Doris FE 的 9030 和 8030 端口是否可访问,以及账号是否具有 `CREATE DATABASE` 权限。使用 `kubectl logs -n dog dog-ai-observe-stack-otel-gateway-0` 查看网关日志。 |
+| agent Pod 报 `CreateContainerConfigError`、被 Pod Security 拒绝,或日志中出现 `/var/log/pods` 的 `permission denied` | 命名空间启用了 `restricted`。添加 `privileged` 标签,参见步骤 2。 |
+| agent 日志出现 `connection refused` 或 `Unavailable` | 网关地址错误、网关未运行,或网络策略阻止了 4317 端口。问题修复前,记录会缓存在节点上,修复后自动发送。 |
+| agent Pod 一直处于 `ContainerCreating` 状态,事件显示 `MountVolume.SetUp failed for volume "zoneinfo"` | 节点上没有时区数据库(部分精简操作系统镜像)。设置 `agent.tzdata.hostPath: ""`,并将 `timezone` 以及所有规则的 `timestamp.timezone` 保持为 UTC。 |
+| 某个命名空间没有日志 | 被 `logs.namespaces.exclude` 或 `logs.containers.exclude` 排除,或安装后没有写入新的日志行。 |
+| 有日志但 `service_name` 为空 | agent 无法读取 Pod 元数据。检查 agent 日志中是否有 RBAC `forbidden` 错误。 |
+| 解析规则不生效(`dog.log.rule` 为空) | `selector.container` 填写的是 Pod 名称而不是容器名称,或更靠前的规则先匹配。使用 `hack/test-rule.sh` 验证。 |
+| 日志时间相差若干小时 | 日志行不带时区、容器不在 UTC 时区运行,且未设置 `timestamp.timezone`。 |
+
+查看 agent 实际运行的配置:
+
+```Bash
+kubectl get cm -n dog dog-k8s-collector-agent-config -o jsonpath='{.data.config\.yaml}'
+```
+
+两个 Chart 的完整参数说明,请参见仓库 [helm-charts](https://github.com/ai-observe/ai-observe-stack/tree/main/helm-charts) 目录下各 Chart 的 `USER_GUIDE_zh.md` 和 `values.yaml`。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x.json b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x.json
index 51507339ffd..5b60d221132 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x.json
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x.json
@@ -607,6 +607,10 @@
"message": "性能测试",
"description": "The label for category Benchmark in sidebar docs"
},
+ "sidebar.docs.category.Deployment": {
+ "message": "部署",
+ "description": "The label for category Deployment in sidebar docs"
+ },
"sidebar.docs.category.Observability": {
"message": "可观测性",
"description": "Observability"
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/deployment.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/deployment.md
deleted file mode 100644
index 10a32733139..00000000000
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/deployment.md
+++ /dev/null
@@ -1,172 +0,0 @@
----
-{
- "title": "部署",
- "sidebar_label": "部署",
- "language": "zh-CN",
- "description": "如何在生产环境部署 DOG Stack?本文介绍 OpenTelemetry Collector、Apache Doris 与集成 Doris App 插件的 Grafana 三大核心组件的部署方式与选型建议。",
- "keywords": [
- "DOG Stack 部署",
- "OpenTelemetry Collector",
- "Apache Doris 部署",
- "Grafana 部署",
- "Doris App 插件",
- "Docker Compose",
- "Kubernetes",
- "可观测性建设"
- ]
-}
----
-
-<!-- 知识类型: 操作步骤 -->
-<!-- 适用场景: 可观测性建设 / DOG Stack 生产部署 -->
-
-如架构图所示,DOG Stack 由四个组件组成:数据采集工具、OpenTelemetry Collector、Doris 和 集成 Doris应用的Grafana。本部署文档将重点介绍后三个组件,而数据采集文档将重点介绍第一个组件。
-
-DOG Stack 为不同的环境和目的提供多种部署选项。
-
-
-
-## Docker Compose
-
-最适合本地测试、开发和 PoC。
-
-### 依赖项
-
-- Docker Engine (v20.10+)
-- Docker Compose (v2.0+)
-
-
-
-### 部署
-
-1. 克隆仓库:
-
- `git clone https://github.com/ai-observe/ai-observe-stack.git cd ai-observe-stack/docker`
-
-
-
-2. 启动所有服务:
-
- `docker compose up -d`
-
-
-
-3. 验证服务是否正在运行:
-
- `docker compose ps`
-
- 所有服务应显示`running`状态。
-
-
-
-4. 在 http://localhost:3000 访问 Grafana,使用默认用户密码`admin` / `admin`登录。
-
-
-
-### 服务端点
-
-| Service | Endpoint | Credentials |
-| --------- | --------------------- | ------------- |
-| Grafana | http://localhost:3000 | admin / admin |
-| OTel gRPC | localhost:4317 | - |
-| OTel HTTP | localhost:4318 | - |
-
-### 停止和清理
-
-在保留数据的同时停止服务:
-
-```Bash
-docker compose down
-```
-
-停止服务并删除所有数据:
-
-```Bash
-docker compose down -v
-```
-
-
-
-## Kubernetes
-
-最适合生产部署、开发环境和可扩展的设置。
-
-### 依赖项
-
-- Kubernetes cluster (v1.20+)
-- Helm (v3.0+)
-- kubectl configured to access your cluster
-- PersistentVolume provisioner (for data persistence)
-
-### 部署
-
-1. 添加DOG Stack 和Doris Helm仓库:
-
- `helm repo add dogstack https://charts.velodb.io helm repo update`
-
-
-
-2. 为DOG Stack 创建一个命名空间 堆栈:
-
- `kubectl create namespace dogstack`
-
-
-
-3. 安装 DOG Stack:
-
- 如果您已有 Doris 集群,可以指定已有 Doris 集群的连接方式。
-
- `helm install my-dogstack dogstack/dogstack -n dogstack \ --set doris.mode=external \ --set doris.external.host=<DORIS_FE_HOST> \ --set doris.external.port=9030 \ --set doris.external.feHttpPort=8030 \ --set doris.internal.operator.enabled=false`
-
-
-
- 如果您想使用新的 Doris 集群进行部署,只需直接安装,无需额外选项。
-
- `helm install my-dogstack dogstack/dogstack -n dogstack`
-
-
-
-4. 验证所有 Pod 都在运行:
-
- `kubectl get pods -n dogstack`
-
- 等待所有 Pod 显示`Running`状态。
-
-
-
-5. 访问Grafana:
-
- `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack`
-
- 打开 http://localhost:3000 并使用`admin` / `admin`登录。
-
-
-
-### 服务端点
-
-| Service | Port-forward command |
-| ----------- | ------------------------------------------------------------ |
-| Grafana | `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack` |
-| Doris FE UI | `kubectl port-forward svc/my-dogstack-doris-fe 8030:8030 -n dogstack` |
-| Doris MySQL | `kubectl port-forward svc/my-dogstack-doris-fe 9030:9030 -n dogstack` |
-
-### 卸载
-
-```Bash
-helm uninstall my-dogstack -n dogstack
-kubectl delete namespace dogstack
-```
-
-
-
-## 手动部署
-
-1. 按照 Doris [部署文档](https://doris.apache.org/docs/4.x/install/preparation/env-checking)部署 Doris 集群。如果已有 Doris 集群,则可跳过此步骤。
-
-1. 按照 [OpenTelemetry部署文档](https://opentelemetry.io/docs/collector/install/) 部署 OpenTelemetry Collector。
-
-1. 按照[ Grafana部署文档](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) 部署 Grafana UI。
-
-1. 浏览器打开 DOG Stack Web UI
-
-访问 http://localhost:3000 以访问 DOG Stack 内的 Grafana UI。
\ No newline at end of file
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/docker.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/docker.md
new file mode 100644
index 00000000000..91b41c91fb8
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/docker.md
@@ -0,0 +1,116 @@
+---
+{
+ "title": "Docker Compose 部署",
+ "sidebar_label": "Docker Compose 部署",
+ "language": "zh-CN",
+ "description": "如何使用 Docker Compose 在单机上部署 DOG Stack?本文介绍如何启动 OpenTelemetry Collector、Apache Doris 与集成 Doris App 插件的 Grafana,支持内置 Doris 和接入已有 Doris 集群两种方式,并介绍验证、访问与清理方法。",
+ "keywords": [
+ "DOG Stack 部署",
+ "Docker Compose",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Doris App 插件",
+ "可观测性建设"
+ ]
+}
+---
+
+<!-- 知识类型: 操作步骤 -->
+<!-- 适用场景: 可观测性建设 / DOG Stack 单机部署 -->
+
+本文介绍如何使用 Docker Compose 在单机上部署 DOG Stack。部署会启动 OpenTelemetry Collector、集成 Doris App 插件的 Grafana,以及可选的一体化 Doris,适用于本地测试、开发和 PoC。生产环境或 Kubernetes 环境请参见 [Kubernetes 部署](./kubernetes.md)。
+
+:::tip
+如果不使用 Docker 部署各组件,请参见 [Doris 部署文档](https://doris.apache.org/zh-CN/docs/4.x/install/preparation/env-checking)、[OpenTelemetry Collector 安装文档](https://opentelemetry.io/docs/collector/install/) 和 [Grafana 安装文档](https://grafana.com/docs/grafana/latest/setup-grafana/installation/)。
+:::
+
+本文按 AI Observe Stack 源码提交 [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949) 核对。下方检出命令固定该提交,避免主分支更新导致参数变化。该提交的内置 Doris 镜像为 `4.0.3-all-slim`,用于部署栈的连通性验证。
+
+## 前提条件
+
+- Docker Engine 20.10 及以上版本
+- Docker Compose 2.0 及以上版本
+
+## 部署
+
+1. 克隆仓库:
+
+ ```Bash
+ git clone https://github.com/ai-observe/ai-observe-stack.git
+ cd ai-observe-stack
+ git checkout 2c1fbee831701178c7dddba857e88130ef292949
+ cd docker
+ ```
+
+2. 启动服务,以下两种方式二选一。
+
+ - **内置 Doris**:同时启动一体化 Doris、OpenTelemetry Collector 和 Grafana。
+
+ ```Bash
+ docker compose up -d
+ ```
+
+ - **接入已有 Doris**:只启动 OpenTelemetry Collector 和 Grafana,数据写入已有的 Doris 集群。先创建配置文件:
+
+ ```Bash
+ cp .env.example .env
+ ```
+
+ 在 `.env` 中修改 Doris 连接配置:
+
+ ```Bash
+ DORIS_FE_HTTP_ENDPOINT=http://<DORIS_FE_HOST>:8030
+ DORIS_FE_MYSQL_ENDPOINT=<DORIS_FE_HOST>:9030
+ DORIS_USERNAME=root
+ DORIS_PASSWORD=
+ ```
+
+ 然后启动服务:
+
+ ```Bash
+ docker compose -f docker-compose-without-doris.yaml up -d
+ ```
+
+ 该账号需要具备创建目标数据库和表、导入数据,以及查询 Grafana 所用表的权限。OpenTelemetry Collector 会自动创建 `otel` 数据库及其中的表。
+
+ OpenTelemetry Collector 通过 Stream Load 写入数据:FE 会将每个请求重定向到 BE 在 Doris 中注册的地址(可通过 `SHOW BACKENDS` 查看)。因此,除了 FE 之外,OpenTelemetry Collector 容器还需要能够访问这些 BE 地址及 BE 的 HTTP 端口(默认 8040)。
+
+3. 验证服务是否正在运行:
+
+ ```Bash
+ docker compose ps
+ ```
+
+ 接入已有 Doris 时,需要加上 `-f docker-compose-without-doris.yaml`。所有服务的 `STATUS` 列应显示 `Up`。使用内置 Doris 时,OpenTelemetry Collector 会在 Doris 通过健康检查后才启动。
+
+4. 打开 <http://localhost:3000>,使用 `admin` / `admin` 登录 Grafana。
+
+## 服务端点
+
+| 服务 | 地址 | 账号 |
+| --- | --- | --- |
+| Grafana | http://localhost:3000 | admin / admin |
+| OTLP gRPC | localhost:4317 | - |
+| OTLP HTTP | localhost:4318 | - |
+| OpenTelemetry Collector 健康检查 | http://localhost:13133 | - |
+| Doris FE Web UI(仅内置 Doris) | http://localhost:8030 | root / 空密码 |
+| Doris MySQL 协议(仅内置 Doris) | localhost:9030 | root / 空密码 |
+
+接入 OpenTelemetry SDK 的应用将数据发送到 `localhost:4317`(gRPC)或 `localhost:4318`(HTTP)。
+
+## 停止和清理
+
+停止服务并保留数据:
+
+```Bash
+docker compose down
+```
+
+停止服务并删除所有数据:
+
+```Bash
+docker compose down -v
+```
+
+接入已有 Doris 时,以上两条命令都需要加上 `-f docker-compose-without-doris.yaml`。已有 Doris 集群中的数据不会被删除。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/kubernetes.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/kubernetes.md
new file mode 100644
index 00000000000..11aa32f351a
--- /dev/null
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-4.x/observability/dogstack/kubernetes.md
@@ -0,0 +1,503 @@
+---
+{
+ "title": "Kubernetes 部署",
+ "sidebar_label": "Kubernetes 部署",
+ "language": "zh-CN",
+ "description": "如何在 Kubernetes 上部署 DOG Stack 并采集集群的容器日志、指标和 Events?本文使用 Helm 安装 DOG Stack 后端(OpenTelemetry 网关、Apache Doris、Grafana)和 Kubernetes 采集器,并介绍验证方法、日志解析规则、生产配置与问题排查。",
+ "keywords": [
+ "DOG Stack",
+ "Kubernetes",
+ "Helm",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Kubernetes 日志采集",
+ "可观测性建设"
+ ]
+}
+---
+
+<!-- 知识类型: 操作步骤 -->
+<!-- 适用场景: 可观测性建设 / DOG Stack Kubernetes 部署与集群采集 -->
+
+本文介绍如何使用 Helm 在 Kubernetes 上部署 DOG Stack,并将集群的容器日志、kubelet / 节点 / 集群指标以及 Kubernetes Events 采集进来。完成后,可以在 Doris 中查询这些数据,并在 Grafana 仪表盘中查看。如果需要在单机上部署,请参见 [Docker Compose 部署](./docker.md)。
+
+Kubernetes 上的 DOG Stack 由两个相互独立的 Helm Chart 组成:
+
+| Chart | 作用 | 工作负载 |
+| --- | --- | --- |
+| `ai-observe-stack` | DOG Stack 后端,只需安装一次。接收 OTLP 数据、写入 Doris,并通过 Grafana 展示。它本身不采集任何数据。 | OpenTelemetry 网关(StatefulSet)、集成 Doris App 插件的 Grafana(Deployment),以及可选的由 Doris Operator 管理的 Doris 集群 |
+| `dog-k8s-collector` | Kubernetes 采集器。在每个需要采集的集群中安装一次,指向网关。 | agent(DaemonSet,每个节点一个):容器日志、kubelet 与节点指标、节点本地 OTLP 入口;cluster(Deployment,每个集群一个):集群指标与 Kubernetes Events |
+
+数据流如下:
+
+```text
+applications (OTLP SDK) ───────┐
+ │ OTLP
+dog-k8s-collector ─────────────┤
+ agent (DaemonSet) │
+ cluster (Deployment) ▼
+ai-observe-stack
+ gateway OpenTelemetry Collector receives OTLP, writes Doris
+ doris Apache Doris stores logs, metrics, traces
+ grafana Grafana + Doris App shows them
+```
+
+两个 Chart 之间没有 Helm 依赖关系,分别安装、分别升级。多个 Kubernetes 集群可以写入同一个 DOG Stack,通过集群名称区分。
+
+:::tip
+如果已经有运行中的 DOG Stack(使用本 Chart 安装、以其他方式部署或位于其他集群),可以直接跳到[步骤 2:安装 Kubernetes 采集器](#step-2-install-the-kubernetes-collector),只需要知道其网关的 OTLP gRPC 地址。
+:::
+
+本文按 AI Observe Stack 源码提交 [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949) 核对。下方检出命令固定该提交,避免主分支更新导致参数变化。该提交的 Helm Chart 版本为 `0.2.0`,默认 Doris 镜像为 `4.0.3`。
+
+## 前提条件
+
+- Kubernetes 1.24 及以上版本、Helm 3.8 及以上版本,并已配置 `kubectl` 访问集群。
+- 集群级 RBAC 权限:采集器会创建 ClusterRole 和 ClusterRoleBinding。
+- 默认网关发送队列需要 PersistentVolume 供应器,连接外部 Doris 时也一样。临时试用可设置 `gateway.persistence.enabled: false`,但 Pod 替换会丢失内存中的队列数据。
+- 节点能够拉取镜像 `otel/opentelemetry-collector-contrib`、`otel/opentelemetry-collector-k8s`、`grafana/grafana`、`velodb/doris-app-plugin` 和 `busybox`。使用私有镜像仓库时,在 `ai-observe-stack` 中设置 `global.imagePullSecrets`,在 `dog-k8s-collector` 中设置 `imagePullSecrets`,并将镜像参数指向镜像源:`ai-observe-stack` 中的 `gateway.image.repository`、`grafana.image.repository`、`dorisPlugin.image.repository` 以及 `global.helperImages.busybox` / `global.helperImages.curl`;`dog-k8s-collector` 中的 `agent.image.repository` 和 `cluster.image [...]
+- Doris,以下二选一:
+ - 已有 Doris 集群:FE 的 9030(MySQL 协议)和 8030(HTTP)端口可以从 Kubernetes 集群访问,并准备一个能创建目标数据库和表、导入数据及查询 Grafana 所用表的账号。网关通过 Stream Load 写入数据:FE 会将每个请求重定向到 BE 在 Doris 中注册的地址(可通过 `SHOW BACKENDS` 查看),因此网关 Pod 还需要能够访问这些 BE 地址及 BE 的 HTTP 端口(默认 8040)。
+ - 由 Chart 部署 Doris:集群中有 PersistentVolume 供应器,且 FE 和 BE 各自至少有 2 核 CPU 和 4 GiB 内存。
+
+本文命令使用 Release 名称 `dog` 和命名空间 `dog`,下表中的对象名称由此生成。如果使用其他名称,请相应替换。
+
+| 对象 | 名称 |
+| --- | --- |
+| 网关 StatefulSet 与 Service | `dog-ai-observe-stack-otel-gateway` |
+| Grafana Deployment 与 Service | `dog-ai-observe-stack-grafana` |
+| Grafana 管理员 Secret | `dog-ai-observe-stack-grafana-admin` |
+| DorisCluster(由 Chart 部署 Doris 时) | `dog-ai-observe-stack-doris` |
+| 采集器 agent DaemonSet | `dog-k8s-collector-agent` |
+| 采集器 cluster Deployment | `dog-k8s-collector-cluster` |
+
+## 获取 Chart
+
+克隆仓库并下载 Chart 依赖。Doris Operator 子 Chart 声明在 Chart 的依赖中,因此即使连接已有 Doris,也需要执行这一步。请使用 `helm dependency update`,它不需要预先执行 `helm repo add`。
+
+```Bash
+git clone https://github.com/ai-observe/ai-observe-stack.git
+cd ai-observe-stack
+git checkout 2c1fbee831701178c7dddba857e88130ef292949
+cd helm-charts
+helm dependency update ./ai-observe-stack
+```
+
+后续命令均在 `helm-charts` 目录下执行。
+
+## 步骤 1:安装 DOG Stack 后端
+
+选择 Doris 的部署方式:
+
+| | 方式 A:使用已有 Doris | 方式 B:由 Chart 部署 Doris |
+| --- | --- | --- |
+| 适用场景 | 已有 Doris 或 SelectDB 集群;生产环境 | 没有可用 Doris 时的试用和开发 |
+| 数据位置 | 已有的 Doris 集群 | Kubernetes 集群内的 PVC |
+
+### 方式 A:使用已有 Doris
+
+1. 创建命名空间,并将 Doris 账号保存到 Secret 中。Secret 必须包含 `username` 和 `password` 两个键。
+
+ ```Bash
+ kubectl create namespace dog
+ kubectl create secret generic doris-credentials -n dog \
+ --from-literal=username='<DORIS_USER>' --from-literal=password='<DORIS_PASSWORD>'
+ ```
+
+2. 创建 values 文件 `dog-values.yaml`:
+
+ ```yaml
+ doris:
+ mode: external
+ database: otel # 网关会自动创建该数据库及其中的表
+ external:
+ host: <DORIS_FE_HOST> # 集群内可访问的 FE 地址
+ port: 9030 # MySQL 协议端口
+ feHttpPort: 8030 # HTTP 端口,用于 Stream Load
+ existingSecret: doris-credentials
+ internal:
+ operator:
+ enabled: false # 不安装 Doris Operator
+ ```
+
+ 仓库中的 `examples/ai-observe-stack/minimal-external-doris.yaml` 即为该文件。
+
+3. 安装:
+
+ ```Bash
+ helm install dog ./ai-observe-stack -n dog -f dog-values.yaml
+ ```
+
+### 方式 B:由 Chart 部署 Doris
+
+仓库提供两种规格:
+
+- `examples/ai-observe-stack/dev.yaml`:FE 和 BE 各一个,不开启持久化,一个网关,开启调试输出。仅用于试用。
+- `examples/ai-observe-stack/prod.yaml`:FE 和 BE 各三个,开启持久化,三个网关,并配置 Ingress。可作为生产配置的起点。使用前需要修改其中与环境相关的配置:`storageClass`(示例为 AWS 的 `gp3`)、Ingress 的 class、域名和 TLS 证书签发者,以及 Grafana 密码(`CHANGE_ME`)。
+
+```Bash
+cp examples/ai-observe-stack/dev.yaml dog-values.yaml
+helm install dog ./ai-observe-stack -n dog --create-namespace -f dog-values.yaml
+```
+
+后续升级继续使用 `dog-values.yaml`。部署生产环境时,改为复制 `prod.yaml`,完成上面列出的环境配置后再安装。
+
+Doris FE 和 BE 拉取镜像并完成初始化需要 2 到 5 分钟。网关会等待 FE 的 9030 和 8030 端口可访问、且至少有一个 BE 在线后再启动。
+
+:::caution
+Doris Operator 是集群级组件,一个 Kubernetes 集群中只能运行一个。在同一集群中安装第二个 DOG Stack 时,需要设置 `doris.internal.operator.enabled: false`。
+:::
+
+### 验证后端
+
+安装完成后,Helm 会输出 NOTES,其中包含网关的 OTLP gRPC 地址 `dog-ai-observe-stack-otel-gateway.dog.svc:4317`,请记录下来,步骤 2 中需要使用。
+
+1. 检查 Pod 是否运行。应能看到网关 Pod(默认两个副本,即 `dog-ai-observe-stack-otel-gateway-0` 和 `-1`;`dev.yaml` 为一个副本)、Grafana Pod,以及方式 B 下的 Doris FE 和 BE Pod,状态均为 `Running`。
+
+ ```Bash
+ kubectl get pods -n dog
+ ```
+
+2. 运行内置的链路测试。该测试通过网关发送一条日志,并检查其是否写入 Doris。
+
+ ```Bash
+ helm test dog -n dog --logs
+ ```
+
+ 输出以 `OK: record found in Doris` 结尾。
+
+3. 获取 Grafana 管理员密码(默认为 `admin`):
+
+ ```Bash
+ kubectl get secret -n dog dog-ai-observe-stack-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d; echo
+ ```
+
+4. 访问 Grafana:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-grafana 3000:3000
+ ```
+
+ 打开 <http://localhost:3000>,使用 `admin` 和上一步获取的密码登录。此时还没有数据写入网关,仪表盘为空。
+
+## 步骤 2:安装 Kubernetes 采集器 {#step-2-install-the-kubernetes-collector}
+
+步骤 1 安装的后端只负责接收数据。采集器从 Kubernetes 集群中采集数据并发送给网关:agent 在每个节点上读取所有容器的 stdout 和 stderr,并采集 kubelet 与节点指标;cluster 采集器采集集群级指标和 Kubernetes Events。每条记录都会附带 Pod、命名空间、工作负载和集群名称。如果不安装采集器,只有直接向网关发送 OTLP 数据的应用能够接入 DOG Stack,不会有容器日志、节点与集群指标以及 Events。每个需要观测的 Kubernetes 集群中都需要安装一个采集器 Release。
+
+在被采集集群中准备命名空间。即使跳过步骤 1,也要执行以下幂等命令:
+
+```Bash
+kubectl create namespace dog --dry-run=client -o yaml | kubectl apply -f -
+```
+
+1. 确认在待采集的集群中可以访问网关。请将地址替换为实际地址。
+
+ ```Bash
+ kubectl run -it --rm otlp-check --image=busybox:1.36 --restart=Never -- \
+ sh -c 'sleep 2; nc -zv dog-ai-observe-stack-otel-gateway.dog.svc 4317'
+ ```
+
+ 输出中出现 `open` 表示网关可访问。网关地址取决于 DOG Stack 的部署位置:
+
+ | DOG Stack 部署位置 | 网关地址(`host:port`,不带 `http://`) |
+ | --- | --- |
+ | 使用本 Chart 安装在同一集群 | `<release>-ai-observe-stack-otel-gateway.<namespace>.svc:4317` |
+ | 自建的带 Doris exporter 的 OpenTelemetry Collector | 其 OTLP gRPC Service,通常为 `<service>.<namespace>.svc:4317` |
+ | 其他 Kubernetes 集群 | 通过 LoadBalancer(`gateway.service.type: LoadBalancer`)或单独配置的 gRPC Ingress 暴露网关地址。本 Chart 的 Ingress 仅暴露 OTLP/HTTP,不暴露 OTLP/gRPC。集群之间需要开放所选端口 |
+
+2. agent 以 root 身份运行,并挂载节点的 `/var/log/pods`、`/`(只读)和 `/var/lib/otelcol`。如果命名空间启用了 `restricted` 级别的 Pod Security Standard,需要放宽:
+
+ ```Bash
+ kubectl label namespace dog pod-security.kubernetes.io/enforce=privileged --overwrite
+ ```
+
+3. 创建 values 文件 `collector-values.yaml`:
+
+ ```yaml
+ gateway:
+ endpoint: dog-ai-observe-stack-otel-gateway.dog.svc:4317
+ clusterName: my-cluster
+ platform: generic
+ ```
+
+ | 参数 | 是否必填 | 默认值 | 说明 |
+ | --- | --- | --- | --- |
+ | `gateway.endpoint` | 是 | 无 | 网关的 OTLP gRPC 地址,格式为 `host:port`。 |
+ | `clusterName` | 否 | Release 名称 | 以 `k8s.cluster.name` 写入每条记录,用于区分集群。 |
+ | `platform` | 否 | `generic` | 可选 `generic`、`k3s`、`eks`、`gke`、`aks`、`ack`、`openshift`。决定云资源探测器和 journald 单元名称。不确定时使用 `generic`。 |
+ | `timezone` | 否 | `UTC` | 日志时间戳不带时区时所采用的 IANA 时区。解析规则可以通过 `timestamp.timezone` 覆盖。 |
+
+4. 安装:
+
+ ```Bash
+ helm install dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+ ```
+
+采集器默认开启以下采集预设:
+
+| 预设 | 默认 | 采集内容 | 运行位置 |
+| --- | --- | --- | --- |
+| `logsCollection` | 开启 | 所有命名空间中所有容器的 stdout 和 stderr | agent |
+| `kubeletMetrics` | 开启 | 节点、Pod、容器和卷的 CPU、内存、网络和文件系统,以及相对 limit 的使用率 | agent |
+| `hostMetrics` | 开启 | 节点自身的 CPU、内存、负载、磁盘、文件系统和网络 | agent |
+| `otlp` | 开启 | 供应用 SDK 使用的节点本地 OTLP 入口 | agent |
+| `clusterMetrics` | 开启 | Deployment 可用性、节点状态、Pod 阶段、配额等 | cluster |
+| `kubernetesEvents` | 开启 | Kubernetes Events,以日志形式存储 | cluster |
+| `prometheusScrape` | 关闭 | 带有 `prometheus.io/scrape` 注解的 Pod | agent |
+| `journald` | 关闭 | 节点 journal 日志,需要包含 `journalctl` 的镜像 | agent |
+
+:::caution
+`examples/ai-observe-stack/dev.yaml` 开启了网关的调试输出,会将采样数据打印到网关日志中。当采集器与网关位于同一集群时,需要排除网关的日志文件,否则每条记录都会被重复采集:
+
+```yaml
+logs:
+ excludePaths:
+ - /var/log/pods/dog_dog-ai-observe-stack-otel-gateway-*/*/*.log
+```
+:::
+
+## 步骤 3:验证采集
+
+1. 检查采集器 Pod。每个节点一个 agent Pod,另有一个 cluster Pod:
+
+ ```Bash
+ kubectl get pods -n dog -l app.kubernetes.io/instance=dog-k8s-collector -o wide
+ ```
+
+ ```text
+ NAME READY STATUS NODE
+ dog-k8s-collector-agent-7x2kq 1/1 Running node-1
+ dog-k8s-collector-agent-p9m4c 1/1 Running node-2
+ dog-k8s-collector-cluster-6f5d9c8b7d-hq2xn 1/1 Running node-1
+ ```
+
+2. 检查 agent 没有报错。以下命令应当没有任何输出:
+
+ ```Bash
+ kubectl logs -n dog ds/dog-k8s-collector-agent | grep '"level":"error"'
+ ```
+
+3. 在 Doris 中查询数据。使用方式 A 时,用 MySQL 客户端连接已有 Doris FE 的 9030 端口;使用方式 B 时,先转发 FE 端口,再用 MySQL 客户端连接:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-doris-fe-service 9030:9030
+ mysql -h127.0.0.1 -P9030 -uroot
+ ```
+
+ ```sql
+ SELECT service_name, count(*) AS records
+ FROM otel.otel_logs
+ WHERE timestamp > now() - interval 5 minute
+ AND cast(resource_attributes['k8s.cluster.name'] as string) = 'my-cluster'
+ GROUP BY 1 ORDER BY 2 DESC;
+ ```
+
+ agent 启动后 30 秒内会写入第一批数据。`service_name` 为 Pod 所属工作负载的名称,按 Deployment、StatefulSet、DaemonSet、CronJob、Job 的顺序查找;独立运行的 Pod 则为 Pod 名称。Kubernetes Events 显示为 `kubernetes-events`。只采集安装之后写入的日志行,节点上已有的日志文件不会重新读取。
+
+4. 打开 Grafana,检查以下仪表盘:
+
+ - **Logs Explorer**:按命名空间、服务、日志级别和文本过滤日志。
+ - **Kubernetes Events**:Warning 数量、主要原因和事件列表。
+ - **K8s Observability**:节点和 Pod 的 CPU 与内存。
+ - **Collector Self-Monitoring**:agent、cluster 采集器和网关的队列长度、失败和拒绝情况。
+
+### 端到端示例
+
+以下 Pod 每秒输出一行 JSON 日志,可以验证从容器日志到 Doris 的完整链路:
+
+```Bash
+kubectl create namespace demo
+kubectl run json-app -n demo --image=busybox:1.36 --restart=Never -- sh -c \
+ 'i=0; while true; do i=$((i+1)); echo "{\"time\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"level\":\"info\",\"msg\":\"tick $i\"}"; sleep 1; done'
+```
+
+约 30 秒后,在 Doris 中查询:
+
+```sql
+SELECT timestamp, severity_text, body, cast(resource_attributes['k8s.pod.name'] as string) AS pod
+FROM otel.otel_logs
+WHERE cast(resource_attributes['k8s.namespace.name'] as string) = 'demo'
+ORDER BY timestamp DESC LIMIT 5;
+```
+
+如果返回 `severity_text` 为 `INFO`、`body` 类似 `tick 42` 的记录,说明容器日志已被采集、JSON 已被解析,并附加了 Kubernetes 元数据。验证完成后执行 `kubectl delete namespace demo` 清理。
+
+## 配置采集
+
+修改采集配置时,编辑 `collector-values.yaml` 后执行:
+
+```Bash
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+只有受影响的 agent 或 cluster 采集器会重启。`agent.storage.enabled=true` 时,同一节点和存储路径上的读取位置会保留,减少重复读取,但不保证恰好一次交付:重试可能产生重复记录,节点/存储丢失或队列耗尽也可能丢失数据。
+
+### 选择命名空间和容器
+
+```yaml
+logs:
+ namespaces:
+ include: ["*"] # 或列表,例如 [shop, payment]
+ exclude: [kube-system, monitoring]
+ containers:
+ exclude: [istio-proxy, linkerd-proxy] # 容器名称,在所有命名空间中排除
+```
+
+排除优先于包含。采集器自身的 Pod 不会被采集。
+
+### 解析日志
+
+- **JSON 日志**:以 `{` 开头的行会被自动解析。`level` 或 `severity` 写入 `severity_text`,`message` 或 `msg` 写入 `body`,其余字段写入 `log_attributes`。字段名不同时,可设置 `logs.json.severityFields` 和 `logs.json.messageFields`。
+- **常见文本格式**:在规则中使用内置预设。`selector.container` 匹配的是容器名称,而不是 Pod 或 Deployment 名称。
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-api
+ selector: {namespace: "^shop$", container: "^api$"}
+ preset: java-spring
+ ```
+
+ | 预设 | 适用格式 |
+ | --- | --- |
+ | `java-spring` | Spring Boot 3 默认控制台格式,自动合并异常堆栈 |
+ | `python-logging` | Python `logging.basicConfig` 默认格式 |
+ | `go-zap-console` | zap console encoder |
+ | `glog` | C++ 和 Go glog |
+ | `nginx-access`、`nginx-error` | nginx combined 访问日志和错误日志 |
+ | `mysql`、`postgres`、`redis` | 官方镜像的默认格式 |
+ | `doris-fe`、`doris-fe-audit`、`doris-be` | Apache Doris FE、FE 审计和 BE 日志 |
+ | `json-generic` | 单行 JSON |
+
+ Doris FE 的运行日志和审计日志输出在同一个容器中。如果需要同时解析,请使用相同的 selector 添加两条规则,分别设置 `preset: doris-fe` 和 `preset: doris-fe-audit`。
+
+- **自定义格式**:编写带命名分组的正则表达式,分组 `ts` 为时间戳,`level` 为日志级别,`msg` 为消息:
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-worker
+ selector: {namespace: "^shop$", container: "^worker$"}
+ regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<level>[a-z]+)\s+(?P<msg>(?s:.*))$'
+ timestamp: {layout: "%Y-%m-%dT%H:%M:%S.%L%z"}
+ severity: {field: level}
+ multiline: {firstLinePattern: '^\d{4}-\d{2}-\d{2}T'} # 堆栈行合并到上一条记录
+ drop: ['GET /healthz'] # 在节点上直接丢弃
+ ```
+
+ 如果日志时间戳不带时区,且容器不在 UTC 时区运行,需要在 `timestamp` 中增加 `timezone`,例如 `timestamp: {layout: "...", timezone: Asia/Shanghai}`。规则按顺序匹配,第一个匹配的规则生效。每条记录的 `dog.log.rule` 属性标明了解析它的规则。
+
+ 部署规则之前,可以使用仓库中的 `hack/test-rule.sh` 对样例日志进行测试,该脚本依赖 helm、yq 和 Docker:
+
+ ```Bash
+ kubectl logs -n shop -l app=worker -c worker --tail=30 > worker.log
+ hack/test-rule.sh -f collector-values.yaml -n shop -c worker worker.log
+ ```
+
+### 接收应用的 OTLP 数据
+
+接入 OpenTelemetry SDK 的应用可以将 Trace、指标和日志发送到所在节点的 agent,agent 会附加 Pod、命名空间和工作负载元数据。先在节点上开放 agent 端口:
+
+```yaml
+presets:
+ otlp:
+ hostPortHttp: 4318
+ hostPortGrpc: 4317
+```
+
+然后在应用的 Pod 模板中设置地址:
+
+```yaml
+env:
+ - name: HOST_IP
+ valueFrom: {fieldRef: {fieldPath: status.hostIP}}
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://$(HOST_IP):4318
+ - name: OTEL_EXPORTER_OTLP_PROTOCOL
+ value: http/protobuf
+ - name: OTEL_SERVICE_NAME
+ value: checkout
+```
+
+### 抓取 Prometheus 指标
+
+设置 `presets.prometheusScrape.enabled: true`,然后为应用 Pod 添加注解 `prometheus.io/scrape: "true"`、`prometheus.io/port` 和 `prometheus.io/path`。指标写入 `otel_metrics_*` 表,`service_name` 为工作负载名称。
+
+### 采集多个集群
+
+在每个集群中各安装一个采集器 Release,`gateway.endpoint` 指向同一个网关(跨集群时使用 LoadBalancer 或单独配置的 gRPC Ingress 地址),并为每个集群设置不同的 `clusterName`。查询和仪表盘通过 `k8s.cluster.name` 过滤。
+
+## 生产环境建议
+
+| 项目 | 参数 | 说明 |
+| --- | --- | --- |
+| Doris 副本和磁盘 | `doris.internal.cluster.fe.replicas`、`doris.internal.cluster.be.replicas`、`doris.internal.cluster.persistence` | FE 和 BE 各至少三个,并设置 `gateway.dorisExporter.replicationNum: 3`。 |
+| 网关副本和队列 | `gateway.replicas`、`gateway.persistence.size`、`gateway.dorisExporter.sendingQueue` | 每个网关副本的发送队列保存在各自的 PVC 上。写入出现延迟时,在 Collector Self-Monitoring 仪表盘中观察 `otelcol_exporter_queue_size`。 |
+| 数据保留 | `gateway.dorisExporter.historyDays` | 默认 7 天。 |
+| Grafana 密码 | `grafana.adminPassword` 或 `grafana.existingSecret` | 默认密码为 `admin`。 |
+| 外部访问 | `ingress`、`gateway.service.type` | 为 Grafana 配置 Ingress;集群外的应用通过 OTLP/HTTP Ingress 路径或 LoadBalancer 写入。 |
+| 调试输出 | `gateway.debug.enabled: false` | `dev.yaml` 中为开启状态。 |
+| 采集范围 | `logs.namespaces.exclude` | 通常排除 `kube-system`。 |
+| 采集器资源 | `agent.resources`、`agent.queueSize` | 每个 agent 默认申请 100m CPU 和 128 MiB 内存,上限为 1 CPU 和 512 MiB。网关不可达期间,每个节点最多在本地磁盘缓存 `agent.queueSize` 条记录(默认 20,000 条)。 |
+
+仓库中的 `examples/ai-observe-stack/prod.yaml` 可作为生产 values 文件的起点。
+
+默认 `gateway.timeGuard.enabled=true` 会将超出可写分区时间窗口的记录时间改为接收时间:日志在 `log.original_time_unix_nano`、Trace 在 `span.original_start_time_unix_nano` 保留原时间,指标时间直接重写。需要回填历史数据时,应同时调整 `gateway.dorisExporter.createHistoryDays` 和时间保护配置。网关向 Doris 发送失败的默认重试时限为 10 分钟(`gateway.dorisExporter.retryMaxElapsedTime`),超时批次会记录错误并丢弃。
+
+## 升级与卸载
+
+升级或修改配置:
+
+```Bash
+helm upgrade dog ./ai-observe-stack -n dog -f dog-values.yaml
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+卸载:
+
+```Bash
+helm uninstall dog-k8s-collector -n dog
+helm uninstall dog -n dog
+```
+
+卸载采集器不会影响 DOG Stack 以及 Doris 中的数据。节点上 `/var/lib/otelcol` 中的文件读取位置会保留,重新安装后从上次的位置继续采集。使用方式 A 时,卸载 DOG Stack 不会删除已有 Doris 中的数据库。
+
+Helm 不会删除网关的 PersistentVolumeClaim、Doris 的 PersistentVolumeClaim(方式 B 开启持久化时)以及已完成的 `helm test` Pod。如需删除:
+
+```Bash
+kubectl delete pvc -n dog -l app.kubernetes.io/instance=dog
+kubectl delete pvc -n dog -l 'app.doris.ownerreference/name in (dog-ai-observe-stack-doris-fe,dog-ai-observe-stack-doris-be)'
+kubectl delete pod -n dog dog-ai-observe-stack-test-pipeline --ignore-not-found
+```
+
+使用方式 B 时,Helm 也不会删除随 Doris Operator 安装的 CustomResourceDefinition `dorisclusters.doris.selectdb.com`,因为 Helm 从不删除 CRD。删除该 CRD 会同时删除 Kubernetes 集群中所有的 DorisCluster,因此仅在集群中没有其他由 Doris Operator 管理的 Doris 集群时才删除:
+
+```Bash
+kubectl delete crd dorisclusters.doris.selectdb.com
+```
+
+## 常见问题
+
+| 现象 | 原因与解决方法 |
+| --- | --- |
+| `helm install` 报错 `missing in charts/ directory: doris-operator` | 未下载 Chart 依赖。执行 `helm dependency update ./ai-observe-stack`。 |
+| `helm dependency build` 报错 `no repository definition for https://charts.selectdb.com` | `helm dependency build` 只使用通过 `helm repo add` 添加的仓库。改为执行 `helm dependency update ./ai-observe-stack`。 |
+| `helm install` 报错 `gateway.endpoint is required` | 将采集器的 `gateway.endpoint` 设置为网关地址。 |
+| `helm install` 报错 `additional properties 'xxx' not allowed` | values 文件中有拼写错误的顶层键。 |
+| `helm test` 失败,或网关日志中出现 Doris 相关错误 | 检查 Doris FE 的 9030 和 8030 端口是否可访问,以及账号是否能创建目标数据库/表、导入和查询数据。使用 `kubectl logs -n dog dog-ai-observe-stack-otel-gateway-0` 查看网关日志。 |
+| agent Pod 报 `CreateContainerConfigError`、被 Pod Security 拒绝,或日志中出现 `/var/log/pods` 的 `permission denied` | 命名空间启用了 `restricted`。添加 `privileged` 标签,参见步骤 2。 |
+| agent 日志出现 `connection refused` 或 `Unavailable` | 网关地址错误、网关未运行,或网络策略阻止了 4317 端口。问题修复前,记录会缓存在节点上,修复后自动发送。 |
+| agent Pod 一直处于 `ContainerCreating` 状态,事件显示 `MountVolume.SetUp failed for volume "zoneinfo"` | 节点上没有时区数据库(部分精简操作系统镜像)。设置 `agent.tzdata.hostPath: ""`,并将 `timezone` 以及所有规则的 `timestamp.timezone` 保持为 UTC。 |
+| 某个命名空间没有日志 | 被 `logs.namespaces.exclude` 或 `logs.containers.exclude` 排除,或安装后没有写入新的日志行。 |
+| 有日志但 `service_name` 为空 | agent 无法读取 Pod 元数据。检查 agent 日志中是否有 RBAC `forbidden` 错误。 |
+| 解析规则不生效(`dog.log.rule` 为空) | `selector.container` 填写的是 Pod 名称而不是容器名称,或更靠前的规则先匹配。使用 `hack/test-rule.sh` 验证。 |
+| 日志时间相差若干小时 | 日志行不带时区、容器不在 UTC 时区运行,且未设置 `timestamp.timezone`。 |
+
+查看 agent 实际运行的配置:
+
+```Bash
+kubectl get cm -n dog dog-k8s-collector-agent-config -o jsonpath='{.data.config\.yaml}'
+```
+
+两个 Chart 的完整参数说明,请参见仓库 [helm-charts](https://github.com/ai-observe/ai-observe-stack/tree/main/helm-charts) 目录下各 Chart 的 `USER_GUIDE_zh.md` 和 `values.yaml`。
diff --git a/sidebars.ts b/sidebars.ts
index 1f8338fad37..27c9d6c1e0a 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -849,7 +849,14 @@ const sidebars: SidebarsConfig = {
link: {type: 'doc', id: 'observability/dogstack/overview'},
items: [
'observability/dogstack/quickstart',
- 'observability/dogstack/deployment',
+ {
+ type: 'category',
+ label: 'Deployment',
+ items: [
+ 'observability/dogstack/docker',
+ 'observability/dogstack/kubernetes',
+ ],
+ },
'observability/dogstack/search',
'observability/dogstack/trace',
'observability/dogstack/dashboard',
diff --git a/versioned_docs/version-3.x/observability/dogstack/deployment.md b/versioned_docs/version-3.x/observability/dogstack/deployment.md
deleted file mode 100644
index c313ea2e4af..00000000000
--- a/versioned_docs/version-3.x/observability/dogstack/deployment.md
+++ /dev/null
@@ -1,172 +0,0 @@
----
-{
- "title": "Deployment",
- "sidebar_label": "Deployment",
- "language": "en-US",
- "description": "How to deploy DOG Stack in production? This article introduces deployment options and selection guidance for the three core components: OpenTelemetry Collector, Apache Doris, and Grafana with the Doris App plugin.",
- "keywords": [
- "DOG Stack deployment",
- "OpenTelemetry Collector",
- "Apache Doris deployment",
- "Grafana deployment",
- "Doris App plugin",
- "Docker Compose",
- "Kubernetes",
- "observability"
- ]
-}
----
-
-<!-- Knowledge type: operational steps -->
-<!-- Applicable scenario: observability platform / DOG Stack production deployment -->
-
-As shown in the architecture diagram, DOG Stack consists of four components: the data collection tools, the OpenTelemetry Collector, Doris, and Grafana with the Doris App plugin. This deployment document focuses on the latter three components; the data collection tools are covered in the data collection document.
-
-DOG Stack offers multiple deployment options for different environments and purposes.
-
-
-
-## Docker Compose
-
-Best suited for local testing, development, and PoC.
-
-### Prerequisites
-
-- Docker Engine (v20.10+)
-- Docker Compose (v2.0+)
-
-
-
-### Deployment
-
-1. Clone the repository:
-
- `git clone https://github.com/ai-observe/ai-observe-stack.git cd ai-observe-stack/docker`
-
-
-
-2. Start all services:
-
- `docker compose up -d`
-
-
-
-3. Verify that services are running:
-
- `docker compose ps`
-
- All services should show the `running` status.
-
-
-
-4. Access Grafana at http://localhost:3000 and log in with the default credentials `admin` / `admin`.
-
-
-
-### Service endpoints
-
-| Service | Endpoint | Credentials |
-| --------- | --------------------- | ------------- |
-| Grafana | http://localhost:3000 | admin / admin |
-| OTel gRPC | localhost:4317 | - |
-| OTel HTTP | localhost:4318 | - |
-
-### Stop and clean up
-
-Stop services while keeping data:
-
-```Bash
-docker compose down
-```
-
-Stop services and remove all data:
-
-```Bash
-docker compose down -v
-```
-
-
-
-## Kubernetes
-
-Best suited for production deployment, development environments, and scalable setups.
-
-### Prerequisites
-
-- Kubernetes cluster (v1.20+)
-- Helm (v3.0+)
-- kubectl configured to access your cluster
-- PersistentVolume provisioner (for data persistence)
-
-### Deployment
-
-1. Add the DOG Stack and Doris Helm repositories:
-
- `helm repo add dogstack https://charts.velodb.io helm repo update`
-
-
-
-2. Create a namespace for DOG Stack:
-
- `kubectl create namespace dogstack`
-
-
-
-3. Install DOG Stack:
-
- If you already have a Doris cluster, you can specify how to connect to it.
-
- `helm install my-dogstack dogstack/dogstack -n dogstack \ --set doris.mode=external \ --set doris.external.host=<DORIS_FE_HOST> \ --set doris.external.port=9030 \ --set doris.external.feHttpPort=8030 \ --set doris.internal.operator.enabled=false`
-
-
-
- If you want to deploy a new Doris cluster, just install directly with no extra options.
-
- `helm install my-dogstack dogstack/dogstack -n dogstack`
-
-
-
-4. Verify that all pods are running:
-
- `kubectl get pods -n dogstack`
-
- Wait until all pods show the `Running` status.
-
-
-
-5. Access Grafana:
-
- `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack`
-
- Open http://localhost:3000 and log in with `admin` / `admin`.
-
-
-
-### Service endpoints
-
-| Service | Port-forward command |
-| ----------- | ------------------------------------------------------------ |
-| Grafana | `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack` |
-| Doris FE UI | `kubectl port-forward svc/my-dogstack-doris-fe 8030:8030 -n dogstack` |
-| Doris MySQL | `kubectl port-forward svc/my-dogstack-doris-fe 9030:9030 -n dogstack` |
-
-### Uninstall
-
-```Bash
-helm uninstall my-dogstack -n dogstack
-kubectl delete namespace dogstack
-```
-
-
-
-## Manual deployment
-
-1. Deploy a Doris cluster by following the Doris [deployment documentation](https://doris.apache.org/docs/4.x/install/preparation/env-checking). Skip this step if you already have a Doris cluster.
-
-1. Deploy the OpenTelemetry Collector by following the [OpenTelemetry deployment documentation](https://opentelemetry.io/docs/collector/install/).
-
-1. Deploy Grafana by following the [Grafana deployment documentation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/).
-
-1. Open the DOG Stack web UI in your browser.
-
-Visit http://localhost:3000 to access the Grafana UI inside DOG Stack.
diff --git a/versioned_docs/version-3.x/observability/dogstack/docker.md b/versioned_docs/version-3.x/observability/dogstack/docker.md
new file mode 100644
index 00000000000..563b756b102
--- /dev/null
+++ b/versioned_docs/version-3.x/observability/dogstack/docker.md
@@ -0,0 +1,112 @@
+---
+{
+ "title": "Deploy with Docker Compose",
+ "sidebar_label": "Deploy with Docker Compose",
+ "language": "en-US",
+ "description": "How to deploy DOG Stack on a single host with Docker Compose? This article describes how to start the OpenTelemetry Collector, Apache Doris, and Grafana with the Doris App plugin, either with a built-in Doris or with an existing Doris cluster, and how to verify, access, and clean up the deployment.",
+ "keywords": [
+ "DOG Stack deployment",
+ "Docker Compose",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Doris App plugin",
+ "observability"
+ ]
+}
+---
+
+<!-- Knowledge type: operational steps -->
+<!-- Applicable scenario: observability platform / DOG Stack single-host deployment -->
+
+This article describes how to deploy DOG Stack on a single host with Docker Compose. The deployment starts the OpenTelemetry Collector, Grafana with the Doris App plugin, and optionally an all-in-one Doris. It is suited for local testing, development, and PoC. For production or Kubernetes environments, see [Deploy on Kubernetes](./kubernetes.md).
+
+:::tip
+To deploy the components without Docker, follow the [Doris deployment documentation](https://doris.apache.org/docs/4.x/install/preparation/env-checking), the [OpenTelemetry Collector installation documentation](https://opentelemetry.io/docs/collector/install/), and the [Grafana installation documentation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/).
+:::
+
+## Prerequisites
+
+- Docker Engine 20.10 or later
+- Docker Compose 2.0 or later
+
+## Deployment
+
+1. Clone the repository:
+
+ ```Bash
+ git clone https://github.com/ai-observe/ai-observe-stack.git
+ cd ai-observe-stack/docker
+ ```
+
+2. Start the services. Choose one of the following modes.
+
+ - **Built-in Doris**: starts an all-in-one Doris together with the OpenTelemetry Collector and Grafana.
+
+ ```Bash
+ docker compose up -d
+ ```
+
+ - **Existing Doris**: starts only the OpenTelemetry Collector and Grafana, and writes data to your Doris cluster. Create the configuration file first:
+
+ ```Bash
+ cp .env.example .env
+ ```
+
+ Edit the Doris connection settings in `.env`:
+
+ ```Bash
+ DORIS_FE_HTTP_ENDPOINT=http://<DORIS_FE_HOST>:8030
+ DORIS_FE_MYSQL_ENDPOINT=<DORIS_FE_HOST>:9030
+ DORIS_USERNAME=root
+ DORIS_PASSWORD=
+ ```
+
+ Then start the services:
+
+ ```Bash
+ docker compose -f docker-compose-without-doris.yaml up -d
+ ```
+
+ The account must have the `CREATE DATABASE` privilege. The OpenTelemetry Collector creates the database `otel` and its tables automatically.
+
+ The OpenTelemetry Collector writes data through Stream Load: FE redirects each request to a BE at the address the BE registered in Doris (shown by `SHOW BACKENDS`). Therefore the BE addresses and the BE HTTP port (8040 by default) must also be reachable from the OpenTelemetry Collector container, not only the FE.
+
+3. Verify that the services are running:
+
+ ```Bash
+ docker compose ps
+ ```
+
+ In the existing Doris mode, add `-f docker-compose-without-doris.yaml`. The `STATUS` column of every service should show `Up`. With the built-in Doris, the OpenTelemetry Collector starts only after Doris passes its health check.
+
+4. Open http://localhost:3000 and log in to Grafana with `admin` / `admin`.
+
+## Service Endpoints
+
+| Service | Endpoint | Credentials |
+| --- | --- | --- |
+| Grafana | http://localhost:3000 | admin / admin |
+| OTLP gRPC | localhost:4317 | - |
+| OTLP HTTP | localhost:4318 | - |
+| OpenTelemetry Collector health check | http://localhost:13133 | - |
+| Doris FE Web UI (built-in Doris only) | http://localhost:8030 | root / empty password |
+| Doris MySQL protocol (built-in Doris only) | localhost:9030 | root / empty password |
+
+Applications instrumented with an OpenTelemetry SDK send data to `localhost:4317` (gRPC) or `localhost:4318` (HTTP).
+
+## Stop and Clean Up
+
+Stop the services and keep the data:
+
+```Bash
+docker compose down
+```
+
+Stop the services and remove all data:
+
+```Bash
+docker compose down -v
+```
+
+In the existing Doris mode, add `-f docker-compose-without-doris.yaml` to both commands. The data in your Doris cluster is not removed.
diff --git a/versioned_docs/version-3.x/observability/dogstack/kubernetes.md b/versioned_docs/version-3.x/observability/dogstack/kubernetes.md
new file mode 100644
index 00000000000..2e98dccd741
--- /dev/null
+++ b/versioned_docs/version-3.x/observability/dogstack/kubernetes.md
@@ -0,0 +1,485 @@
+---
+{
+ "title": "Deploy on Kubernetes",
+ "sidebar_label": "Deploy on Kubernetes",
+ "language": "en-US",
+ "description": "How to deploy DOG Stack on Kubernetes and collect container logs, metrics, and Events of a cluster? This article uses Helm to install the DOG Stack backend (OpenTelemetry gateway, Apache Doris, Grafana) and the Kubernetes collector, and covers verification, log parsing rules, production settings, and troubleshooting.",
+ "keywords": [
+ "DOG Stack",
+ "Kubernetes",
+ "Helm",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Kubernetes log collection",
+ "observability"
+ ]
+}
+---
+
+<!-- Knowledge type: operational steps -->
+<!-- Applicable scenario: observability platform / DOG Stack Kubernetes deployment and cluster collection -->
+
+This article describes how to deploy DOG Stack on Kubernetes with Helm and collect container logs, kubelet, node, and cluster metrics, and Kubernetes Events of a cluster into it. After completing the steps, you can query the data in Doris and view it in the Grafana dashboards. To deploy on a single host instead, see [Deploy with Docker Compose](./docker.md).
+
+DOG Stack on Kubernetes consists of two independent Helm charts:
+
+| Chart | Role | Workloads |
+| --- | --- | --- |
+| `ai-observe-stack` | The DOG Stack backend. Installed once. It receives OTLP data, writes it into Doris, and displays it in Grafana. It collects nothing by itself. | OpenTelemetry gateway (StatefulSet), Grafana with the Doris App plugin (Deployment), and optionally a Doris cluster managed by the Doris Operator |
+| `dog-k8s-collector` | The Kubernetes collector. Installed once in every cluster to be collected, and pointed at the gateway. | agent (DaemonSet, one per node): container logs, kubelet and node metrics, node-local OTLP entry point; cluster (Deployment, one per cluster): cluster metrics and Kubernetes Events |
+
+The data flow is:
+
+```text
+applications (OTLP SDK) ───────┐
+ │ OTLP
+dog-k8s-collector ─────────────┤
+ agent (DaemonSet) │
+ cluster (Deployment) ▼
+ai-observe-stack
+ gateway OpenTelemetry Collector receives OTLP, writes Doris
+ doris Apache Doris stores logs, metrics, traces
+ grafana Grafana + Doris App shows them
+```
+
+There is no Helm dependency between the two charts: they are installed and upgraded separately. Several Kubernetes clusters can send to one DOG Stack, told apart by the cluster name.
+
+:::tip
+If a DOG Stack is already running (installed with this chart, deployed another way, or in another cluster), skip to [Step 2: Install the Kubernetes Collector](#step-2-install-the-kubernetes-collector). You only need the OTLP gRPC address of its gateway.
+:::
+
+## Prerequisites
+
+- Kubernetes 1.24 or later, Helm 3.8 or later, and `kubectl` configured to access the cluster.
+- Cluster-level RBAC permissions: the collector creates a ClusterRole and a ClusterRoleBinding.
+- Nodes can pull the images `otel/opentelemetry-collector-contrib`, `otel/opentelemetry-collector-k8s`, `grafana/grafana`, `velodb/doris-app-plugin`, and `busybox`. For a private registry, set `global.imagePullSecrets` in `ai-observe-stack` and `imagePullSecrets` in `dog-k8s-collector`, and point the image parameters at your mirror: `gateway.image.repository`, `grafana.image.repository`, `dorisPlugin.image.repository`, and `global.helperImages.busybox` / `global.helperImages.curl` in `ai [...]
+- Doris, one of the following:
+ - An existing Doris cluster whose FE ports 9030 (MySQL protocol) and 8030 (HTTP) are reachable from the Kubernetes cluster, and an account with the `CREATE DATABASE` privilege. The gateway writes data through Stream Load: FE redirects each request to a BE at the address the BE registered in Doris (shown by `SHOW BACKENDS`), so these BE addresses and the BE HTTP port (8040 by default) must also be reachable from the gateway pods.
+ - Or let the chart deploy Doris: a PersistentVolume provisioner, and at least 2 CPU cores and 4 GiB of memory each for FE and BE.
+
+The commands in this article use the release name `dog` and the namespace `dog`. The object names below are derived from them; if you use other names, replace them accordingly.
+
+| Object | Name |
+| --- | --- |
+| Gateway StatefulSet and Service | `dog-ai-observe-stack-otel-gateway` |
+| Grafana Deployment and Service | `dog-ai-observe-stack-grafana` |
+| Grafana admin Secret | `dog-ai-observe-stack-grafana-admin` |
+| DorisCluster (when the chart deploys Doris) | `dog-ai-observe-stack-doris` |
+| Collector agent DaemonSet | `dog-k8s-collector-agent` |
+| Collector cluster Deployment | `dog-k8s-collector-cluster` |
+
+## Get the Charts
+
+Clone the repository and download the chart dependency. The Doris Operator subchart is declared in the chart's dependencies, so this step is required even if you connect to an existing Doris. Use `helm dependency update`, which does not require `helm repo add`.
+
+```Bash
+git clone https://github.com/ai-observe/ai-observe-stack.git
+cd ai-observe-stack/helm-charts
+helm dependency update ./ai-observe-stack
+```
+
+All following commands run in the `helm-charts` directory.
+
+## Step 1: Install the DOG Stack Backend
+
+Choose where Doris runs:
+
+| | Option A: use an existing Doris | Option B: the chart deploys Doris |
+| --- | --- | --- |
+| Suited for | An existing Doris or SelectDB cluster; production | Trials and development when no Doris is available |
+| Data location | Your Doris cluster | PVCs inside the Kubernetes cluster |
+
+### Option A: Use an Existing Doris
+
+1. Create the namespace and store the Doris account in a Secret. The Secret must contain the keys `username` and `password`.
+
+ ```Bash
+ kubectl create namespace dog
+ kubectl create secret generic doris-credentials -n dog \
+ --from-literal=username=<DORIS_USER> --from-literal=password='<DORIS_PASSWORD>'
+ ```
+
+2. Create a values file `dog-values.yaml`:
+
+ ```yaml
+ doris:
+ mode: external
+ database: otel # the gateway creates this database and its tables
+ external:
+ host: <DORIS_FE_HOST> # FE address reachable from the cluster
+ port: 9030 # MySQL protocol port
+ feHttpPort: 8030 # HTTP port, used by Stream Load
+ existingSecret: doris-credentials
+ internal:
+ operator:
+ enabled: false # do not install the Doris Operator
+ ```
+
+ The same file is available as `examples/ai-observe-stack/minimal-external-doris.yaml` in the repository.
+
+3. Install:
+
+ ```Bash
+ helm install dog ./ai-observe-stack -n dog -f dog-values.yaml
+ ```
+
+### Option B: The Chart Deploys Doris
+
+The repository provides two sizes:
+
+- `examples/ai-observe-stack/dev.yaml`: one FE and one BE, no persistence, one gateway, debug output on. For trials only.
+- `examples/ai-observe-stack/prod.yaml`: three FE and three BE, persistence, three gateways, and Ingress. A starting point for production. Before using it, adapt the environment-specific settings: `storageClass` (set to the AWS `gp3`), the Ingress class, hosts, and TLS issuer, and the Grafana password (`CHANGE_ME`).
+
+```Bash
+helm install dog ./ai-observe-stack -n dog --create-namespace -f examples/ai-observe-stack/dev.yaml
+```
+
+Doris FE and BE take two to five minutes to pull images and initialize. The gateway waits until FE ports 9030 and 8030 are reachable and at least one BE is online before starting.
+
+:::caution
+The Doris Operator is cluster-scoped, and only one can run in a Kubernetes cluster. When installing a second DOG Stack in the same cluster, set `doris.internal.operator.enabled: false`.
+:::
+
+### Verify the Backend
+
+When the installation finishes, Helm prints NOTES, which include the OTLP gRPC address of the gateway: `dog-ai-observe-stack-otel-gateway.dog.svc:4317`. Record it for Step 2.
+
+1. Check that the pods are running. You should see the gateway pods (`dog-ai-observe-stack-otel-gateway-0` and `-1` with the default two replicas; `dev.yaml` runs one), the Grafana pod, and, with Option B, the Doris FE and BE pods, all in the `Running` status.
+
+ ```Bash
+ kubectl get pods -n dog
+ ```
+
+2. Run the built-in pipeline test. It sends a log record through the gateway and checks that it arrives in Doris.
+
+ ```Bash
+ helm test dog -n dog --logs
+ ```
+
+ The output ends with `OK: record found in Doris`.
+
+3. Get the Grafana admin password (default `admin`):
+
+ ```Bash
+ kubectl get secret -n dog dog-ai-observe-stack-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d; echo
+ ```
+
+4. Access Grafana:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-grafana 3000:3000
+ ```
+
+ Open http://localhost:3000 and log in as `admin` with the password above. The dashboards are empty at this point, because nothing sends data to the gateway yet.
+
+## Step 2: Install the Kubernetes Collector {#step-2-install-the-kubernetes-collector}
+
+The backend installed in Step 1 only receives data. The collector gathers data from the Kubernetes cluster and sends it to the gateway: on every node, the agent reads the stdout and stderr of all containers and collects kubelet and node metrics; the cluster collector collects cluster-level metrics and Kubernetes Events. Every record carries the pod, namespace, workload, and cluster name. Without the collector, only applications that send OTLP to the gateway directly reach DOG Stack, and [...]
+
+1. Confirm that the gateway is reachable from the cluster to be collected. Replace the address with yours.
+
+ ```Bash
+ kubectl run -it --rm otlp-check --image=busybox:1.36 --restart=Never -- \
+ sh -c 'sleep 2; nc -zv dog-ai-observe-stack-otel-gateway.dog.svc 4317'
+ ```
+
+ `open` in the output means the gateway is reachable. The gateway address depends on where the DOG Stack runs:
+
+ | DOG Stack location | Gateway address (`host:port`, without `http://`) |
+ | --- | --- |
+ | Installed by this chart in the same cluster | `<release>-ai-observe-stack-otel-gateway.<namespace>.svc:4317` |
+ | Your own OpenTelemetry Collector with the Doris exporter | Its OTLP gRPC Service, usually `<service>.<namespace>.svc:4317` |
+ | Another Kubernetes cluster | The gateway's external address: a LoadBalancer IP (`gateway.service.type: LoadBalancer`) or an Ingress host. Port 4317 must be open between the clusters |
+
+2. The agent runs as root and mounts the node's `/var/log/pods`, `/` (read-only), and `/var/lib/otelcol`. If the namespace enforces the `restricted` Pod Security Standard, relax it:
+
+ ```Bash
+ kubectl label namespace dog pod-security.kubernetes.io/enforce=privileged --overwrite
+ ```
+
+3. Create a values file `collector-values.yaml`:
+
+ ```yaml
+ gateway:
+ endpoint: dog-ai-observe-stack-otel-gateway.dog.svc:4317
+ clusterName: my-cluster
+ platform: generic
+ ```
+
+ | Parameter | Required | Default | Description |
+ | --- | --- | --- | --- |
+ | `gateway.endpoint` | Yes | None | OTLP gRPC address of the gateway, in the form `host:port`. |
+ | `clusterName` | No | Release name | Written to every record as `k8s.cluster.name`, to tell clusters apart. |
+ | `platform` | No | `generic` | One of `generic`, `k3s`, `eks`, `gke`, `aks`, `ack`, `openshift`. Selects cloud resource detectors and journald unit names. Use `generic` if unsure. |
+ | `timezone` | No | `UTC` | IANA time zone assumed for log lines whose timestamp has no time zone. A parsing rule can override it with `timestamp.timezone`. |
+
+4. Install:
+
+ ```Bash
+ helm install dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+ ```
+
+The collector enables the following collection presets by default:
+
+| Preset | Default | Collects | Runs in |
+| --- | --- | --- | --- |
+| `logsCollection` | On | stdout and stderr of every container in every namespace | agent |
+| `kubeletMetrics` | On | CPU, memory, network, and filesystem of nodes, pods, containers, and volumes, with limit utilization | agent |
+| `hostMetrics` | On | CPU, memory, load, disk, filesystem, and network of the node | agent |
+| `otlp` | On | Node-local OTLP entry point for application SDKs | agent |
+| `clusterMetrics` | On | Deployment availability, node conditions, pod phases, quotas | cluster |
+| `kubernetesEvents` | On | Kubernetes Events, stored as logs | cluster |
+| `prometheusScrape` | Off | Pods annotated with `prometheus.io/scrape` | agent |
+| `journald` | Off | Node journal. Requires an image that contains `journalctl` | agent |
+
+:::caution
+`examples/ai-observe-stack/dev.yaml` turns on the gateway's debug output, which prints sampled records to the gateway log. When the collector runs in the same cluster, exclude the gateway's log files, otherwise every record is collected again:
+
+```yaml
+logs:
+ excludePaths:
+ - /var/log/pods/dog_dog-ai-observe-stack-otel-gateway-*/*/*.log
+```
+:::
+
+## Step 3: Verify Collection
+
+1. Check the collector pods. There is one agent pod per node and one cluster pod:
+
+ ```Bash
+ kubectl get pods -n dog -l app.kubernetes.io/instance=dog-k8s-collector -o wide
+ ```
+
+ ```text
+ NAME READY STATUS NODE
+ dog-k8s-collector-agent-7x2kq 1/1 Running node-1
+ dog-k8s-collector-agent-p9m4c 1/1 Running node-2
+ dog-k8s-collector-cluster-6f5d9c8b7d-hq2xn 1/1 Running node-1
+ ```
+
+2. Check that the agent reports no errors. The following command should print nothing:
+
+ ```Bash
+ kubectl logs -n dog ds/dog-k8s-collector-agent | grep '"level":"error"'
+ ```
+
+3. Query the data in Doris. With Option A, connect a MySQL client to port 9030 of your Doris FE. With Option B, forward the FE port first and connect with a MySQL client:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-doris-fe-service 9030:9030
+ mysql -h127.0.0.1 -P9030 -uroot
+ ```
+
+ ```sql
+ SELECT service_name, count(*) AS records
+ FROM otel.otel_logs
+ WHERE timestamp > now() - interval 5 minute
+ AND cast(resource_attributes['k8s.cluster.name'] as string) = 'my-cluster'
+ GROUP BY 1 ORDER BY 2 DESC;
+ ```
+
+ The first records arrive within 30 seconds after the agent starts. `service_name` is the name of the workload that owns the pod, looked up in the order Deployment, StatefulSet, DaemonSet, CronJob, and Job, or the pod name for a standalone pod. Kubernetes Events appear as `kubernetes-events`. Only log lines written after the installation are collected; existing log files on the nodes are not read again.
+
+4. Open Grafana and check the dashboards:
+
+ - **Logs Explorer**: filter logs by namespace, service, severity, and text.
+ - **Kubernetes Events**: Warning counts, top reasons, and the event table.
+ - **K8s Observability**: CPU and memory of nodes and pods.
+ - **Collector Self-Monitoring**: queue length, failures, and refusals of the agent, the cluster collector, and the gateway.
+
+### End-to-End Example
+
+The following pod prints one JSON log line per second, which verifies the whole path from container logs to Doris:
+
+```Bash
+kubectl create namespace demo
+kubectl run json-app -n demo --image=busybox:1.36 --restart=Never -- sh -c \
+ 'i=0; while true; do i=$((i+1)); echo "{\"time\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"level\":\"info\",\"msg\":\"tick $i\"}"; sleep 1; done'
+```
+
+After about 30 seconds, query Doris:
+
+```sql
+SELECT timestamp, severity_text, body, cast(resource_attributes['k8s.pod.name'] as string) AS pod
+FROM otel.otel_logs
+WHERE cast(resource_attributes['k8s.namespace.name'] as string) = 'demo'
+ORDER BY timestamp DESC LIMIT 5;
+```
+
+Rows with `severity_text` equal to `INFO` and `body` like `tick 42` show that the container log was collected, the JSON line was parsed, and Kubernetes metadata was attached. Clean up with `kubectl delete namespace demo`.
+
+## Configure Collection
+
+To change the collection, edit `collector-values.yaml` and run:
+
+```Bash
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+Only the affected agent or cluster collector restarts. File offsets on the nodes are kept, so no log line is collected twice.
+
+### Select Namespaces and Containers
+
+```yaml
+logs:
+ namespaces:
+ include: ["*"] # or a list, such as [shop, payment]
+ exclude: [kube-system, monitoring]
+ containers:
+ exclude: [istio-proxy, linkerd-proxy] # container names, excluded in every namespace
+```
+
+Exclusions take precedence over inclusions. The collector's own pods are never collected.
+
+### Parse Logs
+
+- **JSON logs**: lines starting with `{` are parsed automatically. `level` or `severity` becomes `severity_text`, `message` or `msg` becomes `body`, and other keys are stored in `log_attributes`. For other field names, set `logs.json.severityFields` and `logs.json.messageFields`.
+- **Common text formats**: use a built-in preset in a rule. `selector.container` matches the container name, not the pod or Deployment name.
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-api
+ selector: {namespace: "^shop$", container: "^api$"}
+ preset: java-spring
+ ```
+
+ | Preset | Format |
+ | --- | --- |
+ | `java-spring` | Spring Boot 3 default console format, with stack traces joined |
+ | `python-logging` | Python `logging.basicConfig` default format |
+ | `go-zap-console` | zap console encoder |
+ | `glog` | C++ and Go glog |
+ | `nginx-access`, `nginx-error` | nginx combined access log and error log |
+ | `mysql`, `postgres`, `redis` | Defaults of the official images |
+ | `doris-fe`, `doris-fe-audit`, `doris-be` | Apache Doris FE, FE audit, and BE logs |
+ | `json-generic` | Single-line JSON |
+
+ Doris FE writes runtime logs and audit logs to the same container. To parse both, add two rules with the same selector, one with `preset: doris-fe` and one with `preset: doris-fe-audit`.
+
+- **Custom formats**: write a regular expression with named groups `ts` (timestamp), `level` (severity), and `msg` (message):
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-worker
+ selector: {namespace: "^shop$", container: "^worker$"}
+ regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<level>[a-z]+)\s+(?P<msg>(?s:.*))$'
+ timestamp: {layout: "%Y-%m-%dT%H:%M:%S.%L%z"}
+ severity: {field: level}
+ multiline: {firstLinePattern: '^\d{4}-\d{2}-\d{2}T'} # stack trace lines join the previous record
+ drop: ['GET /healthz'] # dropped on the node
+ ```
+
+ If the timestamp in the log line has no time zone and the container does not run in UTC, add `timezone` to `timestamp`, for example `timestamp: {layout: "...", timezone: Asia/Shanghai}`. Rules are matched in order and the first matching rule wins. The attribute `dog.log.rule` of each record shows which rule parsed it.
+
+ Before deploying a rule, you can test it against sample logs with `hack/test-rule.sh` in the repository, which requires helm, yq, and Docker:
+
+ ```Bash
+ kubectl logs -n shop -l app=worker -c worker --tail=30 > worker.log
+ hack/test-rule.sh -f collector-values.yaml -n shop -c worker worker.log
+ ```
+
+### Receive OTLP from Applications
+
+Applications instrumented with an OpenTelemetry SDK can send traces, metrics, and logs to the agent on their node, which adds pod, namespace, and workload metadata. Expose the agent ports on the node:
+
+```yaml
+presets:
+ otlp:
+ hostPortHttp: 4318
+ hostPortGrpc: 4317
+```
+
+Then set the endpoint in the application's pod template:
+
+```yaml
+env:
+ - name: HOST_IP
+ valueFrom: {fieldRef: {fieldPath: status.hostIP}}
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://$(HOST_IP):4318
+ - name: OTEL_SERVICE_NAME
+ value: checkout
+```
+
+### Scrape Prometheus Metrics
+
+Set `presets.prometheusScrape.enabled: true`, then annotate the application pods with `prometheus.io/scrape: "true"`, `prometheus.io/port`, and `prometheus.io/path`. The metrics are written to the `otel_metrics_*` tables, and `service_name` is the workload name.
+
+### Collect Multiple Clusters
+
+Install one collector release in each cluster. Point `gateway.endpoint` at the same gateway (use its LoadBalancer or Ingress address across clusters) and give each cluster a different `clusterName`. Queries and dashboards filter by `k8s.cluster.name`.
+
+## Production Recommendations
+
+| Item | Parameter | Description |
+| --- | --- | --- |
+| Doris replicas and disks | `doris.internal.cluster.fe.replicas`, `doris.internal.cluster.be.replicas`, `doris.internal.cluster.persistence` | At least three FE and three BE, with `gateway.dorisExporter.replicationNum: 3`. |
+| Gateway replicas and queue | `gateway.replicas`, `gateway.persistence.size`, `gateway.dorisExporter.sendingQueue` | Each gateway replica keeps its sending queue on its own PVC. If writes lag, watch `otelcol_exporter_queue_size` on the Collector Self-Monitoring dashboard. |
+| Data retention | `gateway.dorisExporter.historyDays` | 7 days by default. |
+| Grafana password | `grafana.adminPassword` or `grafana.existingSecret` | The default password is `admin`. |
+| External access | `ingress`, `gateway.service.type` | Ingress for Grafana; applications outside the cluster use the OTLP/HTTP Ingress path or a LoadBalancer. |
+| Debug output | `gateway.debug.enabled: false` | Turned on in `dev.yaml`. |
+| Collection scope | `logs.namespaces.exclude` | Usually excludes `kube-system`. |
+| Collector resources | `agent.resources`, `agent.queueSize` | Each agent requests 100m CPU and 128 MiB of memory, and is limited to 1 CPU and 512 MiB. While the gateway is unreachable, each node buffers up to `agent.queueSize` records (20,000 by default) on local disk. |
+
+`examples/ai-observe-stack/prod.yaml` in the repository is a production values file to start from.
+
+## Upgrade and Uninstall
+
+Upgrade or change configuration:
+
+```Bash
+helm upgrade dog ./ai-observe-stack -n dog -f dog-values.yaml
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+Uninstall:
+
+```Bash
+helm uninstall dog-k8s-collector -n dog
+helm uninstall dog -n dog
+```
+
+Uninstalling the collector does not affect the DOG Stack or the data in Doris. The file offsets under `/var/lib/otelcol` on the nodes are kept, and a reinstall continues from the last position. With Option A, uninstalling the DOG Stack does not delete the database in your Doris.
+
+Helm does not delete the PersistentVolumeClaims of the gateway, the PersistentVolumeClaims of Doris (Option B with persistence enabled), or the completed `helm test` pod. To remove them:
+
+```Bash
+kubectl delete pvc -n dog -l app.kubernetes.io/instance=dog
+kubectl delete pvc -n dog -l 'app.doris.ownerreference/name in (dog-ai-observe-stack-doris-fe,dog-ai-observe-stack-doris-be)'
+kubectl delete pod -n dog dog-ai-observe-stack-test-pipeline --ignore-not-found
+```
+
+With Option B, Helm also keeps the CustomResourceDefinition `dorisclusters.doris.selectdb.com` installed with the Doris Operator, because Helm never deletes CRDs. Deleting the CRD deletes every DorisCluster in the Kubernetes cluster, so delete it only when no other Doris cluster is managed by the Doris Operator:
+
+```Bash
+kubectl delete crd dorisclusters.doris.selectdb.com
+```
+
+## Troubleshooting
+
+| Symptom | Cause and solution |
+| --- | --- |
+| `helm install` reports `missing in charts/ directory: doris-operator` | The chart dependency is not downloaded. Run `helm dependency update ./ai-observe-stack`. |
+| `helm dependency build` reports `no repository definition for https://charts.selectdb.com` | `helm dependency build` only uses repositories added with `helm repo add`. Run `helm dependency update ./ai-observe-stack` instead. |
+| `helm install` reports `gateway.endpoint is required` | Set `gateway.endpoint` of the collector to the gateway address. |
+| `helm install` reports `additional properties 'xxx' not allowed` | A top-level key in the values file is misspelled. |
+| `helm test` fails, or the gateway log reports Doris errors | Check that Doris FE ports 9030 and 8030 are reachable and that the account has the `CREATE DATABASE` privilege. View the gateway log with `kubectl logs -n dog dog-ai-observe-stack-otel-gateway-0`. |
+| Agent pod reports `CreateContainerConfigError`, is rejected by Pod Security, or logs `permission denied` on `/var/log/pods` | The namespace enforces `restricted`. Add the `privileged` label, see Step 2. |
+| Agent log reports `connection refused` or `Unavailable` | The gateway address is wrong, the gateway is down, or a network policy blocks port 4317. Records are buffered on the node and sent after the problem is fixed. |
+| Agent pod stays in `ContainerCreating`, and its events report `MountVolume.SetUp failed for volume "zoneinfo"` | The node has no time zone database (some minimal operating system images). Set `agent.tzdata.hostPath: ""`, and keep `timezone` and the `timestamp.timezone` of every rule at UTC. |
+| A namespace has no logs | It is excluded by `logs.namespaces.exclude` or `logs.containers.exclude`, or no new lines were written after the installation. |
+| Logs arrive but `service_name` is empty | The agent cannot read pod metadata. Check the agent log for RBAC `forbidden` errors. |
+| A parsing rule has no effect (`dog.log.rule` is empty) | `selector.container` contains a pod name instead of a container name, or an earlier rule matched first. Verify with `hack/test-rule.sh`. |
+| Log times are off by hours | The log line has no time zone, the container does not run in UTC, and `timestamp.timezone` is not set. |
+
+To view the configuration that the agent actually runs:
+
+```Bash
+kubectl get cm -n dog dog-k8s-collector-agent-config -o jsonpath='{.data.config\.yaml}'
+```
+
+For all parameters of the two charts, see the `USER_GUIDE.md` and `values.yaml` of each chart under [helm-charts](https://github.com/ai-observe/ai-observe-stack/tree/main/helm-charts) in the repository.
diff --git a/versioned_docs/version-4.x/observability/dogstack/deployment.md b/versioned_docs/version-4.x/observability/dogstack/deployment.md
deleted file mode 100644
index c313ea2e4af..00000000000
--- a/versioned_docs/version-4.x/observability/dogstack/deployment.md
+++ /dev/null
@@ -1,172 +0,0 @@
----
-{
- "title": "Deployment",
- "sidebar_label": "Deployment",
- "language": "en-US",
- "description": "How to deploy DOG Stack in production? This article introduces deployment options and selection guidance for the three core components: OpenTelemetry Collector, Apache Doris, and Grafana with the Doris App plugin.",
- "keywords": [
- "DOG Stack deployment",
- "OpenTelemetry Collector",
- "Apache Doris deployment",
- "Grafana deployment",
- "Doris App plugin",
- "Docker Compose",
- "Kubernetes",
- "observability"
- ]
-}
----
-
-<!-- Knowledge type: operational steps -->
-<!-- Applicable scenario: observability platform / DOG Stack production deployment -->
-
-As shown in the architecture diagram, DOG Stack consists of four components: the data collection tools, the OpenTelemetry Collector, Doris, and Grafana with the Doris App plugin. This deployment document focuses on the latter three components; the data collection tools are covered in the data collection document.
-
-DOG Stack offers multiple deployment options for different environments and purposes.
-
-
-
-## Docker Compose
-
-Best suited for local testing, development, and PoC.
-
-### Prerequisites
-
-- Docker Engine (v20.10+)
-- Docker Compose (v2.0+)
-
-
-
-### Deployment
-
-1. Clone the repository:
-
- `git clone https://github.com/ai-observe/ai-observe-stack.git cd ai-observe-stack/docker`
-
-
-
-2. Start all services:
-
- `docker compose up -d`
-
-
-
-3. Verify that services are running:
-
- `docker compose ps`
-
- All services should show the `running` status.
-
-
-
-4. Access Grafana at http://localhost:3000 and log in with the default credentials `admin` / `admin`.
-
-
-
-### Service endpoints
-
-| Service | Endpoint | Credentials |
-| --------- | --------------------- | ------------- |
-| Grafana | http://localhost:3000 | admin / admin |
-| OTel gRPC | localhost:4317 | - |
-| OTel HTTP | localhost:4318 | - |
-
-### Stop and clean up
-
-Stop services while keeping data:
-
-```Bash
-docker compose down
-```
-
-Stop services and remove all data:
-
-```Bash
-docker compose down -v
-```
-
-
-
-## Kubernetes
-
-Best suited for production deployment, development environments, and scalable setups.
-
-### Prerequisites
-
-- Kubernetes cluster (v1.20+)
-- Helm (v3.0+)
-- kubectl configured to access your cluster
-- PersistentVolume provisioner (for data persistence)
-
-### Deployment
-
-1. Add the DOG Stack and Doris Helm repositories:
-
- `helm repo add dogstack https://charts.velodb.io helm repo update`
-
-
-
-2. Create a namespace for DOG Stack:
-
- `kubectl create namespace dogstack`
-
-
-
-3. Install DOG Stack:
-
- If you already have a Doris cluster, you can specify how to connect to it.
-
- `helm install my-dogstack dogstack/dogstack -n dogstack \ --set doris.mode=external \ --set doris.external.host=<DORIS_FE_HOST> \ --set doris.external.port=9030 \ --set doris.external.feHttpPort=8030 \ --set doris.internal.operator.enabled=false`
-
-
-
- If you want to deploy a new Doris cluster, just install directly with no extra options.
-
- `helm install my-dogstack dogstack/dogstack -n dogstack`
-
-
-
-4. Verify that all pods are running:
-
- `kubectl get pods -n dogstack`
-
- Wait until all pods show the `Running` status.
-
-
-
-5. Access Grafana:
-
- `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack`
-
- Open http://localhost:3000 and log in with `admin` / `admin`.
-
-
-
-### Service endpoints
-
-| Service | Port-forward command |
-| ----------- | ------------------------------------------------------------ |
-| Grafana | `kubectl port-forward svc/my-dogstack-grafana 3000:3000 -n dogstack` |
-| Doris FE UI | `kubectl port-forward svc/my-dogstack-doris-fe 8030:8030 -n dogstack` |
-| Doris MySQL | `kubectl port-forward svc/my-dogstack-doris-fe 9030:9030 -n dogstack` |
-
-### Uninstall
-
-```Bash
-helm uninstall my-dogstack -n dogstack
-kubectl delete namespace dogstack
-```
-
-
-
-## Manual deployment
-
-1. Deploy a Doris cluster by following the Doris [deployment documentation](https://doris.apache.org/docs/4.x/install/preparation/env-checking). Skip this step if you already have a Doris cluster.
-
-1. Deploy the OpenTelemetry Collector by following the [OpenTelemetry deployment documentation](https://opentelemetry.io/docs/collector/install/).
-
-1. Deploy Grafana by following the [Grafana deployment documentation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/).
-
-1. Open the DOG Stack web UI in your browser.
-
-Visit http://localhost:3000 to access the Grafana UI inside DOG Stack.
diff --git a/versioned_docs/version-4.x/observability/dogstack/docker.md b/versioned_docs/version-4.x/observability/dogstack/docker.md
new file mode 100644
index 00000000000..664de884d7c
--- /dev/null
+++ b/versioned_docs/version-4.x/observability/dogstack/docker.md
@@ -0,0 +1,116 @@
+---
+{
+ "title": "Deploy with Docker Compose",
+ "sidebar_label": "Deploy with Docker Compose",
+ "language": "en-US",
+ "description": "How to deploy DOG Stack on a single host with Docker Compose? This article describes how to start the OpenTelemetry Collector, Apache Doris, and Grafana with the Doris App plugin, either with a built-in Doris or with an existing Doris cluster, and how to verify, access, and clean up the deployment.",
+ "keywords": [
+ "DOG Stack deployment",
+ "Docker Compose",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Doris App plugin",
+ "observability"
+ ]
+}
+---
+
+<!-- Knowledge type: operational steps -->
+<!-- Applicable scenario: observability platform / DOG Stack single-host deployment -->
+
+This article describes how to deploy DOG Stack on a single host with Docker Compose. The deployment starts the OpenTelemetry Collector, Grafana with the Doris App plugin, and optionally an all-in-one Doris. It is suited for local testing, development, and PoC. For production or Kubernetes environments, see [Deploy on Kubernetes](./kubernetes.md).
+
+:::tip
+To deploy the components without Docker, follow the [Doris deployment documentation](https://doris.apache.org/docs/4.x/install/preparation/env-checking), the [OpenTelemetry Collector installation documentation](https://opentelemetry.io/docs/collector/install/), and the [Grafana installation documentation](https://grafana.com/docs/grafana/latest/setup-grafana/installation/).
+:::
+
+This guide is checked against AI Observe Stack commit [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949). The checkout below pins this revision so later main-branch changes do not alter the parameters. Its built-in Doris image is `4.0.3-all-slim`, intended for stack connectivity checks.
+
+## Prerequisites
+
+- Docker Engine 20.10 or later
+- Docker Compose 2.0 or later
+
+## Deployment
+
+1. Clone the repository:
+
+ ```Bash
+ git clone https://github.com/ai-observe/ai-observe-stack.git
+ cd ai-observe-stack
+ git checkout 2c1fbee831701178c7dddba857e88130ef292949
+ cd docker
+ ```
+
+2. Start the services. Choose one of the following modes.
+
+ - **Built-in Doris**: starts an all-in-one Doris together with the OpenTelemetry Collector and Grafana.
+
+ ```Bash
+ docker compose up -d
+ ```
+
+ - **Existing Doris**: starts only the OpenTelemetry Collector and Grafana, and writes data to your Doris cluster. Create the configuration file first:
+
+ ```Bash
+ cp .env.example .env
+ ```
+
+ Edit the Doris connection settings in `.env`:
+
+ ```Bash
+ DORIS_FE_HTTP_ENDPOINT=http://<DORIS_FE_HOST>:8030
+ DORIS_FE_MYSQL_ENDPOINT=<DORIS_FE_HOST>:9030
+ DORIS_USERNAME=root
+ DORIS_PASSWORD=
+ ```
+
+ Then start the services:
+
+ ```Bash
+ docker compose -f docker-compose-without-doris.yaml up -d
+ ```
+
+ The account must be able to create the target database and tables, load data, and query the tables used by Grafana. The OpenTelemetry Collector creates the database `otel` and its tables automatically.
+
+ The OpenTelemetry Collector writes data through Stream Load: FE redirects each request to a BE at the address the BE registered in Doris (shown by `SHOW BACKENDS`). Therefore the BE addresses and the BE HTTP port (8040 by default) must also be reachable from the OpenTelemetry Collector container, not only the FE.
+
+3. Verify that the services are running:
+
+ ```Bash
+ docker compose ps
+ ```
+
+ In the existing Doris mode, add `-f docker-compose-without-doris.yaml`. The `STATUS` column of every service should show `Up`. With the built-in Doris, the OpenTelemetry Collector starts only after Doris passes its health check.
+
+4. Open http://localhost:3000 and log in to Grafana with `admin` / `admin`.
+
+## Service Endpoints
+
+| Service | Endpoint | Credentials |
+| --- | --- | --- |
+| Grafana | http://localhost:3000 | admin / admin |
+| OTLP gRPC | localhost:4317 | - |
+| OTLP HTTP | localhost:4318 | - |
+| OpenTelemetry Collector health check | http://localhost:13133 | - |
+| Doris FE Web UI (built-in Doris only) | http://localhost:8030 | root / empty password |
+| Doris MySQL protocol (built-in Doris only) | localhost:9030 | root / empty password |
+
+Applications instrumented with an OpenTelemetry SDK send data to `localhost:4317` (gRPC) or `localhost:4318` (HTTP).
+
+## Stop and Clean Up
+
+Stop the services and keep the data:
+
+```Bash
+docker compose down
+```
+
+Stop the services and remove all data:
+
+```Bash
+docker compose down -v
+```
+
+In the existing Doris mode, add `-f docker-compose-without-doris.yaml` to both commands. The data in your Doris cluster is not removed.
diff --git a/versioned_docs/version-4.x/observability/dogstack/kubernetes.md b/versioned_docs/version-4.x/observability/dogstack/kubernetes.md
new file mode 100644
index 00000000000..06eb74bc147
--- /dev/null
+++ b/versioned_docs/version-4.x/observability/dogstack/kubernetes.md
@@ -0,0 +1,503 @@
+---
+{
+ "title": "Deploy on Kubernetes",
+ "sidebar_label": "Deploy on Kubernetes",
+ "language": "en-US",
+ "description": "How to deploy DOG Stack on Kubernetes and collect container logs, metrics, and Events of a cluster? This article uses Helm to install the DOG Stack backend (OpenTelemetry gateway, Apache Doris, Grafana) and the Kubernetes collector, and covers verification, log parsing rules, production settings, and troubleshooting.",
+ "keywords": [
+ "DOG Stack",
+ "Kubernetes",
+ "Helm",
+ "OpenTelemetry Collector",
+ "Apache Doris",
+ "Grafana",
+ "Kubernetes log collection",
+ "observability"
+ ]
+}
+---
+
+<!-- Knowledge type: operational steps -->
+<!-- Applicable scenario: observability platform / DOG Stack Kubernetes deployment and cluster collection -->
+
+This article describes how to deploy DOG Stack on Kubernetes with Helm and collect container logs, kubelet, node, and cluster metrics, and Kubernetes Events of a cluster into it. After completing the steps, you can query the data in Doris and view it in the Grafana dashboards. To deploy on a single host instead, see [Deploy with Docker Compose](./docker.md).
+
+DOG Stack on Kubernetes consists of two independent Helm charts:
+
+| Chart | Role | Workloads |
+| --- | --- | --- |
+| `ai-observe-stack` | The DOG Stack backend. Installed once. It receives OTLP data, writes it into Doris, and displays it in Grafana. It collects nothing by itself. | OpenTelemetry gateway (StatefulSet), Grafana with the Doris App plugin (Deployment), and optionally a Doris cluster managed by the Doris Operator |
+| `dog-k8s-collector` | The Kubernetes collector. Installed once in every cluster to be collected, and pointed at the gateway. | agent (DaemonSet, one per node): container logs, kubelet and node metrics, node-local OTLP entry point; cluster (Deployment, one per cluster): cluster metrics and Kubernetes Events |
+
+The data flow is:
+
+```text
+applications (OTLP SDK) ───────┐
+ │ OTLP
+dog-k8s-collector ─────────────┤
+ agent (DaemonSet) │
+ cluster (Deployment) ▼
+ai-observe-stack
+ gateway OpenTelemetry Collector receives OTLP, writes Doris
+ doris Apache Doris stores logs, metrics, traces
+ grafana Grafana + Doris App shows them
+```
+
+There is no Helm dependency between the two charts: they are installed and upgraded separately. Several Kubernetes clusters can send to one DOG Stack, told apart by the cluster name.
+
+:::tip
+If a DOG Stack is already running (installed with this chart, deployed another way, or in another cluster), skip to [Step 2: Install the Kubernetes Collector](#step-2-install-the-kubernetes-collector). You only need the OTLP gRPC address of its gateway.
+:::
+
+This guide is checked against AI Observe Stack commit [`2c1fbee83170`](https://github.com/ai-observe/ai-observe-stack/tree/2c1fbee831701178c7dddba857e88130ef292949). The checkout below pins this revision so later main-branch changes do not alter the parameters. Its Helm chart version is `0.2.0` and the default Doris images use `4.0.3`.
+
+## Prerequisites
+
+- Kubernetes 1.24 or later, Helm 3.8 or later, and `kubectl` configured to access the cluster.
+- Cluster-level RBAC permissions: the collector creates a ClusterRole and a ClusterRoleBinding.
+- A PersistentVolume provisioner for the default gateway sending queues, including when Doris is external. Alternatively, disable `gateway.persistence.enabled` for a disposable trial; queued data is then lost when a pod is replaced.
+- Nodes can pull the images `otel/opentelemetry-collector-contrib`, `otel/opentelemetry-collector-k8s`, `grafana/grafana`, `velodb/doris-app-plugin`, and `busybox`. For a private registry, set `global.imagePullSecrets` in `ai-observe-stack` and `imagePullSecrets` in `dog-k8s-collector`, and point the image parameters at your mirror: `gateway.image.repository`, `grafana.image.repository`, `dorisPlugin.image.repository`, and `global.helperImages.busybox` / `global.helperImages.curl` in `ai [...]
+- Doris, one of the following:
+ - An existing Doris cluster whose FE ports 9030 (MySQL protocol) and 8030 (HTTP) are reachable from the Kubernetes cluster, and an account that can create the target database and tables, load data, and query the Grafana tables. The gateway writes data through Stream Load: FE redirects each request to a BE at the address the BE registered in Doris (shown by `SHOW BACKENDS`), so these BE addresses and the BE HTTP port (8040 by default) must also be reachable from the gateway pods.
+ - Or let the chart deploy Doris: a PersistentVolume provisioner, and at least 2 CPU cores and 4 GiB of memory each for FE and BE.
+
+The commands in this article use the release name `dog` and the namespace `dog`. The object names below are derived from them; if you use other names, replace them accordingly.
+
+| Object | Name |
+| --- | --- |
+| Gateway StatefulSet and Service | `dog-ai-observe-stack-otel-gateway` |
+| Grafana Deployment and Service | `dog-ai-observe-stack-grafana` |
+| Grafana admin Secret | `dog-ai-observe-stack-grafana-admin` |
+| DorisCluster (when the chart deploys Doris) | `dog-ai-observe-stack-doris` |
+| Collector agent DaemonSet | `dog-k8s-collector-agent` |
+| Collector cluster Deployment | `dog-k8s-collector-cluster` |
+
+## Get the Charts
+
+Clone the repository and download the chart dependency. The Doris Operator subchart is declared in the chart's dependencies, so this step is required even if you connect to an existing Doris. Use `helm dependency update`, which does not require `helm repo add`.
+
+```Bash
+git clone https://github.com/ai-observe/ai-observe-stack.git
+cd ai-observe-stack
+git checkout 2c1fbee831701178c7dddba857e88130ef292949
+cd helm-charts
+helm dependency update ./ai-observe-stack
+```
+
+All following commands run in the `helm-charts` directory.
+
+## Step 1: Install the DOG Stack Backend
+
+Choose where Doris runs:
+
+| | Option A: use an existing Doris | Option B: the chart deploys Doris |
+| --- | --- | --- |
+| Suited for | An existing Doris or SelectDB cluster; production | Trials and development when no Doris is available |
+| Data location | Your Doris cluster | PVCs inside the Kubernetes cluster |
+
+### Option A: Use an Existing Doris
+
+1. Create the namespace and store the Doris account in a Secret. The Secret must contain the keys `username` and `password`.
+
+ ```Bash
+ kubectl create namespace dog
+ kubectl create secret generic doris-credentials -n dog \
+ --from-literal=username='<DORIS_USER>' --from-literal=password='<DORIS_PASSWORD>'
+ ```
+
+2. Create a values file `dog-values.yaml`:
+
+ ```yaml
+ doris:
+ mode: external
+ database: otel # the gateway creates this database and its tables
+ external:
+ host: <DORIS_FE_HOST> # FE address reachable from the cluster
+ port: 9030 # MySQL protocol port
+ feHttpPort: 8030 # HTTP port, used by Stream Load
+ existingSecret: doris-credentials
+ internal:
+ operator:
+ enabled: false # do not install the Doris Operator
+ ```
+
+ The same file is available as `examples/ai-observe-stack/minimal-external-doris.yaml` in the repository.
+
+3. Install:
+
+ ```Bash
+ helm install dog ./ai-observe-stack -n dog -f dog-values.yaml
+ ```
+
+### Option B: The Chart Deploys Doris
+
+The repository provides two sizes:
+
+- `examples/ai-observe-stack/dev.yaml`: one FE and one BE, no persistence, one gateway, debug output on. For trials only.
+- `examples/ai-observe-stack/prod.yaml`: three FE and three BE, persistence, three gateways, and Ingress. A starting point for production. Before using it, adapt the environment-specific settings: `storageClass` (set to the AWS `gp3`), the Ingress class, hosts, and TLS issuer, and the Grafana password (`CHANGE_ME`).
+
+```Bash
+cp examples/ai-observe-stack/dev.yaml dog-values.yaml
+helm install dog ./ai-observe-stack -n dog --create-namespace -f dog-values.yaml
+```
+
+Keep `dog-values.yaml` for later upgrades. For production, copy `prod.yaml` instead and adapt the environment-specific settings listed above before installing.
+
+Doris FE and BE take two to five minutes to pull images and initialize. The gateway waits until FE ports 9030 and 8030 are reachable and at least one BE is online before starting.
+
+:::caution
+The Doris Operator is cluster-scoped, and only one can run in a Kubernetes cluster. When installing a second DOG Stack in the same cluster, set `doris.internal.operator.enabled: false`.
+:::
+
+### Verify the Backend
+
+When the installation finishes, Helm prints NOTES, which include the OTLP gRPC address of the gateway: `dog-ai-observe-stack-otel-gateway.dog.svc:4317`. Record it for Step 2.
+
+1. Check that the pods are running. You should see the gateway pods (`dog-ai-observe-stack-otel-gateway-0` and `-1` with the default two replicas; `dev.yaml` runs one), the Grafana pod, and, with Option B, the Doris FE and BE pods, all in the `Running` status.
+
+ ```Bash
+ kubectl get pods -n dog
+ ```
+
+2. Run the built-in pipeline test. It sends a log record through the gateway and checks that it arrives in Doris.
+
+ ```Bash
+ helm test dog -n dog --logs
+ ```
+
+ The output ends with `OK: record found in Doris`.
+
+3. Get the Grafana admin password (default `admin`):
+
+ ```Bash
+ kubectl get secret -n dog dog-ai-observe-stack-grafana-admin -o jsonpath='{.data.admin-password}' | base64 -d; echo
+ ```
+
+4. Access Grafana:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-grafana 3000:3000
+ ```
+
+ Open http://localhost:3000 and log in as `admin` with the password above. The dashboards are empty at this point, because nothing sends data to the gateway yet.
+
+## Step 2: Install the Kubernetes Collector {#step-2-install-the-kubernetes-collector}
+
+The backend installed in Step 1 only receives data. The collector gathers data from the Kubernetes cluster and sends it to the gateway: on every node, the agent reads the stdout and stderr of all containers and collects kubelet and node metrics; the cluster collector collects cluster-level metrics and Kubernetes Events. Every record carries the pod, namespace, workload, and cluster name. Without the collector, only applications that send OTLP to the gateway directly reach DOG Stack, and [...]
+
+Prepare the namespace in the cluster being collected. Run this idempotent command even if you skipped Step 1:
+
+```Bash
+kubectl create namespace dog --dry-run=client -o yaml | kubectl apply -f -
+```
+
+1. Confirm that the gateway is reachable from the cluster to be collected. Replace the address with yours.
+
+ ```Bash
+ kubectl run -it --rm otlp-check --image=busybox:1.36 --restart=Never -- \
+ sh -c 'sleep 2; nc -zv dog-ai-observe-stack-otel-gateway.dog.svc 4317'
+ ```
+
+ `open` in the output means the gateway is reachable. The gateway address depends on where the DOG Stack runs:
+
+ | DOG Stack location | Gateway address (`host:port`, without `http://`) |
+ | --- | --- |
+ | Installed by this chart in the same cluster | `<release>-ai-observe-stack-otel-gateway.<namespace>.svc:4317` |
+ | Your own OpenTelemetry Collector with the Doris exporter | Its OTLP gRPC Service, usually `<service>.<namespace>.svc:4317` |
+ | Another Kubernetes cluster | The gateway's gRPC address exposed by a LoadBalancer (`gateway.service.type: LoadBalancer`) or a separately configured gRPC ingress. This chart's Ingress exposes OTLP/HTTP, not OTLP/gRPC. The chosen port must be reachable between clusters |
+
+2. The agent runs as root and mounts the node's `/var/log/pods`, `/` (read-only), and `/var/lib/otelcol`. If the namespace enforces the `restricted` Pod Security Standard, relax it:
+
+ ```Bash
+ kubectl label namespace dog pod-security.kubernetes.io/enforce=privileged --overwrite
+ ```
+
+3. Create a values file `collector-values.yaml`:
+
+ ```yaml
+ gateway:
+ endpoint: dog-ai-observe-stack-otel-gateway.dog.svc:4317
+ clusterName: my-cluster
+ platform: generic
+ ```
+
+ | Parameter | Required | Default | Description |
+ | --- | --- | --- | --- |
+ | `gateway.endpoint` | Yes | None | OTLP gRPC address of the gateway, in the form `host:port`. |
+ | `clusterName` | No | Release name | Written to every record as `k8s.cluster.name`, to tell clusters apart. |
+ | `platform` | No | `generic` | One of `generic`, `k3s`, `eks`, `gke`, `aks`, `ack`, `openshift`. Selects cloud resource detectors and journald unit names. Use `generic` if unsure. |
+ | `timezone` | No | `UTC` | IANA time zone assumed for log lines whose timestamp has no time zone. A parsing rule can override it with `timestamp.timezone`. |
+
+4. Install:
+
+ ```Bash
+ helm install dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+ ```
+
+The collector enables the following collection presets by default:
+
+| Preset | Default | Collects | Runs in |
+| --- | --- | --- | --- |
+| `logsCollection` | On | stdout and stderr of every container in every namespace | agent |
+| `kubeletMetrics` | On | CPU, memory, network, and filesystem of nodes, pods, containers, and volumes, with limit utilization | agent |
+| `hostMetrics` | On | CPU, memory, load, disk, filesystem, and network of the node | agent |
+| `otlp` | On | Node-local OTLP entry point for application SDKs | agent |
+| `clusterMetrics` | On | Deployment availability, node conditions, pod phases, quotas | cluster |
+| `kubernetesEvents` | On | Kubernetes Events, stored as logs | cluster |
+| `prometheusScrape` | Off | Pods annotated with `prometheus.io/scrape` | agent |
+| `journald` | Off | Node journal. Requires an image that contains `journalctl` | agent |
+
+:::caution
+`examples/ai-observe-stack/dev.yaml` turns on the gateway's debug output, which prints sampled records to the gateway log. When the collector runs in the same cluster, exclude the gateway's log files, otherwise every record is collected again:
+
+```yaml
+logs:
+ excludePaths:
+ - /var/log/pods/dog_dog-ai-observe-stack-otel-gateway-*/*/*.log
+```
+:::
+
+## Step 3: Verify Collection
+
+1. Check the collector pods. There is one agent pod per node and one cluster pod:
+
+ ```Bash
+ kubectl get pods -n dog -l app.kubernetes.io/instance=dog-k8s-collector -o wide
+ ```
+
+ ```text
+ NAME READY STATUS NODE
+ dog-k8s-collector-agent-7x2kq 1/1 Running node-1
+ dog-k8s-collector-agent-p9m4c 1/1 Running node-2
+ dog-k8s-collector-cluster-6f5d9c8b7d-hq2xn 1/1 Running node-1
+ ```
+
+2. Check that the agent reports no errors. The following command should print nothing:
+
+ ```Bash
+ kubectl logs -n dog ds/dog-k8s-collector-agent | grep '"level":"error"'
+ ```
+
+3. Query the data in Doris. With Option A, connect a MySQL client to port 9030 of your Doris FE. With Option B, forward the FE port first and connect with a MySQL client:
+
+ ```Bash
+ kubectl port-forward -n dog svc/dog-ai-observe-stack-doris-fe-service 9030:9030
+ mysql -h127.0.0.1 -P9030 -uroot
+ ```
+
+ ```sql
+ SELECT service_name, count(*) AS records
+ FROM otel.otel_logs
+ WHERE timestamp > now() - interval 5 minute
+ AND cast(resource_attributes['k8s.cluster.name'] as string) = 'my-cluster'
+ GROUP BY 1 ORDER BY 2 DESC;
+ ```
+
+ The first records arrive within 30 seconds after the agent starts. `service_name` is the name of the workload that owns the pod, looked up in the order Deployment, StatefulSet, DaemonSet, CronJob, and Job, or the pod name for a standalone pod. Kubernetes Events appear as `kubernetes-events`. Only log lines written after the installation are collected; existing log files on the nodes are not read again.
+
+4. Open Grafana and check the dashboards:
+
+ - **Logs Explorer**: filter logs by namespace, service, severity, and text.
+ - **Kubernetes Events**: Warning counts, top reasons, and the event table.
+ - **K8s Observability**: CPU and memory of nodes and pods.
+ - **Collector Self-Monitoring**: queue length, failures, and refusals of the agent, the cluster collector, and the gateway.
+
+### End-to-End Example
+
+The following pod prints one JSON log line per second, which verifies the whole path from container logs to Doris:
+
+```Bash
+kubectl create namespace demo
+kubectl run json-app -n demo --image=busybox:1.36 --restart=Never -- sh -c \
+ 'i=0; while true; do i=$((i+1)); echo "{\"time\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"level\":\"info\",\"msg\":\"tick $i\"}"; sleep 1; done'
+```
+
+After about 30 seconds, query Doris:
+
+```sql
+SELECT timestamp, severity_text, body, cast(resource_attributes['k8s.pod.name'] as string) AS pod
+FROM otel.otel_logs
+WHERE cast(resource_attributes['k8s.namespace.name'] as string) = 'demo'
+ORDER BY timestamp DESC LIMIT 5;
+```
+
+Rows with `severity_text` equal to `INFO` and `body` like `tick 42` show that the container log was collected, the JSON line was parsed, and Kubernetes metadata was attached. Clean up with `kubectl delete namespace demo`.
+
+## Configure Collection
+
+To change the collection, edit `collector-values.yaml` and run:
+
+```Bash
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+Only the affected agent or cluster collector restarts. With `agent.storage.enabled=true`, file offsets are retained on the same node and storage path. This reduces rereading but does not guarantee exactly-once delivery: retries can duplicate records, and node/storage loss or queue exhaustion can lose data.
+
+### Select Namespaces and Containers
+
+```yaml
+logs:
+ namespaces:
+ include: ["*"] # or a list, such as [shop, payment]
+ exclude: [kube-system, monitoring]
+ containers:
+ exclude: [istio-proxy, linkerd-proxy] # container names, excluded in every namespace
+```
+
+Exclusions take precedence over inclusions. The collector's own pods are never collected.
+
+### Parse Logs
+
+- **JSON logs**: lines starting with `{` are parsed automatically. `level` or `severity` becomes `severity_text`, `message` or `msg` becomes `body`, and other keys are stored in `log_attributes`. For other field names, set `logs.json.severityFields` and `logs.json.messageFields`.
+- **Common text formats**: use a built-in preset in a rule. `selector.container` matches the container name, not the pod or Deployment name.
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-api
+ selector: {namespace: "^shop$", container: "^api$"}
+ preset: java-spring
+ ```
+
+ | Preset | Format |
+ | --- | --- |
+ | `java-spring` | Spring Boot 3 default console format, with stack traces joined |
+ | `python-logging` | Python `logging.basicConfig` default format |
+ | `go-zap-console` | zap console encoder |
+ | `glog` | C++ and Go glog |
+ | `nginx-access`, `nginx-error` | nginx combined access log and error log |
+ | `mysql`, `postgres`, `redis` | Defaults of the official images |
+ | `doris-fe`, `doris-fe-audit`, `doris-be` | Apache Doris FE, FE audit, and BE logs |
+ | `json-generic` | Single-line JSON |
+
+ Doris FE writes runtime logs and audit logs to the same container. To parse both, add two rules with the same selector, one with `preset: doris-fe` and one with `preset: doris-fe-audit`.
+
+- **Custom formats**: write a regular expression with named groups `ts` (timestamp), `level` (severity), and `msg` (message):
+
+ ```yaml
+ logs:
+ rules:
+ - name: shop-worker
+ selector: {namespace: "^shop$", container: "^worker$"}
+ regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<level>[a-z]+)\s+(?P<msg>(?s:.*))$'
+ timestamp: {layout: "%Y-%m-%dT%H:%M:%S.%L%z"}
+ severity: {field: level}
+ multiline: {firstLinePattern: '^\d{4}-\d{2}-\d{2}T'} # stack trace lines join the previous record
+ drop: ['GET /healthz'] # dropped on the node
+ ```
+
+ If the timestamp in the log line has no time zone and the container does not run in UTC, add `timezone` to `timestamp`, for example `timestamp: {layout: "...", timezone: Asia/Shanghai}`. Rules are matched in order and the first matching rule wins. The attribute `dog.log.rule` of each record shows which rule parsed it.
+
+ Before deploying a rule, you can test it against sample logs with `hack/test-rule.sh` in the repository, which requires helm, yq, and Docker:
+
+ ```Bash
+ kubectl logs -n shop -l app=worker -c worker --tail=30 > worker.log
+ hack/test-rule.sh -f collector-values.yaml -n shop -c worker worker.log
+ ```
+
+### Receive OTLP from Applications
+
+Applications instrumented with an OpenTelemetry SDK can send traces, metrics, and logs to the agent on their node, which adds pod, namespace, and workload metadata. Expose the agent ports on the node:
+
+```yaml
+presets:
+ otlp:
+ hostPortHttp: 4318
+ hostPortGrpc: 4317
+```
+
+Then set the endpoint in the application's pod template:
+
+```yaml
+env:
+ - name: HOST_IP
+ valueFrom: {fieldRef: {fieldPath: status.hostIP}}
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://$(HOST_IP):4318
+ - name: OTEL_EXPORTER_OTLP_PROTOCOL
+ value: http/protobuf
+ - name: OTEL_SERVICE_NAME
+ value: checkout
+```
+
+### Scrape Prometheus Metrics
+
+Set `presets.prometheusScrape.enabled: true`, then annotate the application pods with `prometheus.io/scrape: "true"`, `prometheus.io/port`, and `prometheus.io/path`. The metrics are written to the `otel_metrics_*` tables, and `service_name` is the workload name.
+
+### Collect Multiple Clusters
+
+Install one collector release in each cluster. Point `gateway.endpoint` at the same gateway (use a LoadBalancer or separately configured gRPC ingress across clusters) and give each cluster a different `clusterName`. Queries and dashboards filter by `k8s.cluster.name`.
+
+## Production Recommendations
+
+| Item | Parameter | Description |
+| --- | --- | --- |
+| Doris replicas and disks | `doris.internal.cluster.fe.replicas`, `doris.internal.cluster.be.replicas`, `doris.internal.cluster.persistence` | At least three FE and three BE, with `gateway.dorisExporter.replicationNum: 3`. |
+| Gateway replicas and queue | `gateway.replicas`, `gateway.persistence.size`, `gateway.dorisExporter.sendingQueue` | Each gateway replica keeps its sending queue on its own PVC. If writes lag, watch `otelcol_exporter_queue_size` on the Collector Self-Monitoring dashboard. |
+| Data retention | `gateway.dorisExporter.historyDays` | 7 days by default. |
+| Grafana password | `grafana.adminPassword` or `grafana.existingSecret` | The default password is `admin`. |
+| External access | `ingress`, `gateway.service.type` | Ingress for Grafana; applications outside the cluster use the OTLP/HTTP Ingress path or a LoadBalancer. |
+| Debug output | `gateway.debug.enabled: false` | Turned on in `dev.yaml`. |
+| Collection scope | `logs.namespaces.exclude` | Usually excludes `kube-system`. |
+| Collector resources | `agent.resources`, `agent.queueSize` | Each agent requests 100m CPU and 128 MiB of memory, and is limited to 1 CPU and 512 MiB. While the gateway is unreachable, each node buffers up to `agent.queueSize` records (20,000 by default) on local disk. |
+
+`examples/ai-observe-stack/prod.yaml` in the repository is a production values file to start from.
+
+By default, `gateway.timeGuard.enabled=true` restamps records outside the writable partition window with receive time. Logs retain the original in `log.original_time_unix_nano`, traces in `span.original_start_time_unix_nano`, and metric timestamps are rewritten directly. For historical backfill, review `gateway.dorisExporter.createHistoryDays` and time-guard settings together. The default Doris export retry limit is 10 minutes (`gateway.dorisExporter.retryMaxElapsedTime`); a batch still [...]
+
+## Upgrade and Uninstall
+
+Upgrade or change configuration:
+
+```Bash
+helm upgrade dog ./ai-observe-stack -n dog -f dog-values.yaml
+helm upgrade dog-k8s-collector ./dog-k8s-collector -n dog -f collector-values.yaml
+```
+
+Uninstall:
+
+```Bash
+helm uninstall dog-k8s-collector -n dog
+helm uninstall dog -n dog
+```
+
+Uninstalling the collector does not affect the DOG Stack or the data in Doris. The file offsets under `/var/lib/otelcol` on the nodes are kept, and a reinstall continues from the last position. With Option A, uninstalling the DOG Stack does not delete the database in your Doris.
+
+Helm does not delete the PersistentVolumeClaims of the gateway, the PersistentVolumeClaims of Doris (Option B with persistence enabled), or the completed `helm test` pod. To remove them:
+
+```Bash
+kubectl delete pvc -n dog -l app.kubernetes.io/instance=dog
+kubectl delete pvc -n dog -l 'app.doris.ownerreference/name in (dog-ai-observe-stack-doris-fe,dog-ai-observe-stack-doris-be)'
+kubectl delete pod -n dog dog-ai-observe-stack-test-pipeline --ignore-not-found
+```
+
+With Option B, Helm also keeps the CustomResourceDefinition `dorisclusters.doris.selectdb.com` installed with the Doris Operator, because Helm never deletes CRDs. Deleting the CRD deletes every DorisCluster in the Kubernetes cluster, so delete it only when no other Doris cluster is managed by the Doris Operator:
+
+```Bash
+kubectl delete crd dorisclusters.doris.selectdb.com
+```
+
+## Troubleshooting
+
+| Symptom | Cause and solution |
+| --- | --- |
+| `helm install` reports `missing in charts/ directory: doris-operator` | The chart dependency is not downloaded. Run `helm dependency update ./ai-observe-stack`. |
+| `helm dependency build` reports `no repository definition for https://charts.selectdb.com` | `helm dependency build` only uses repositories added with `helm repo add`. Run `helm dependency update ./ai-observe-stack` instead. |
+| `helm install` reports `gateway.endpoint is required` | Set `gateway.endpoint` of the collector to the gateway address. |
+| `helm install` reports `additional properties 'xxx' not allowed` | A top-level key in the values file is misspelled. |
+| `helm test` fails, or the gateway log reports Doris errors | Check that Doris FE ports 9030 and 8030 are reachable and that the account can create the target database/tables, load data and query them. View the gateway log with `kubectl logs -n dog dog-ai-observe-stack-otel-gateway-0`. |
+| Agent pod reports `CreateContainerConfigError`, is rejected by Pod Security, or logs `permission denied` on `/var/log/pods` | The namespace enforces `restricted`. Add the `privileged` label, see Step 2. |
+| Agent log reports `connection refused` or `Unavailable` | The gateway address is wrong, the gateway is down, or a network policy blocks port 4317. Records are buffered on the node and sent after the problem is fixed. |
+| Agent pod stays in `ContainerCreating`, and its events report `MountVolume.SetUp failed for volume "zoneinfo"` | The node has no time zone database (some minimal operating system images). Set `agent.tzdata.hostPath: ""`, and keep `timezone` and the `timestamp.timezone` of every rule at UTC. |
+| A namespace has no logs | It is excluded by `logs.namespaces.exclude` or `logs.containers.exclude`, or no new lines were written after the installation. |
+| Logs arrive but `service_name` is empty | The agent cannot read pod metadata. Check the agent log for RBAC `forbidden` errors. |
+| A parsing rule has no effect (`dog.log.rule` is empty) | `selector.container` contains a pod name instead of a container name, or an earlier rule matched first. Verify with `hack/test-rule.sh`. |
+| Log times are off by hours | The log line has no time zone, the container does not run in UTC, and `timestamp.timezone` is not set. |
+
+To view the configuration that the agent actually runs:
+
+```Bash
+kubectl get cm -n dog dog-k8s-collector-agent-config -o jsonpath='{.data.config\.yaml}'
+```
+
+For all parameters of the two charts, see the `USER_GUIDE.md` and `values.yaml` of each chart under [helm-charts](https://github.com/ai-observe/ai-observe-stack/tree/main/helm-charts) in the repository.
diff --git a/versioned_sidebars/version-3.x-sidebars.json b/versioned_sidebars/version-3.x-sidebars.json
index e93352370a2..f3c5feed1f4 100644
--- a/versioned_sidebars/version-3.x-sidebars.json
+++ b/versioned_sidebars/version-3.x-sidebars.json
@@ -549,7 +549,14 @@
"items": [
"observability/dogstack/overview",
"observability/dogstack/quickstart",
- "observability/dogstack/deployment",
+ {
+ "type": "category",
+ "label": "Deployment",
+ "items": [
+ "observability/dogstack/docker",
+ "observability/dogstack/kubernetes"
+ ]
+ },
"observability/dogstack/search",
"observability/dogstack/trace",
"observability/dogstack/dashboard",
diff --git a/versioned_sidebars/version-4.x-sidebars.json b/versioned_sidebars/version-4.x-sidebars.json
index 0a4f3107288..1a46f818ab1 100644
--- a/versioned_sidebars/version-4.x-sidebars.json
+++ b/versioned_sidebars/version-4.x-sidebars.json
@@ -958,7 +958,14 @@
},
"items": [
"observability/dogstack/quickstart",
- "observability/dogstack/deployment",
+ {
+ "type": "category",
+ "label": "Deployment",
+ "items": [
+ "observability/dogstack/docker",
+ "observability/dogstack/kubernetes"
+ ]
+ },
"observability/dogstack/search",
"observability/dogstack/trace",
"observability/dogstack/dashboard",
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org